iv-cli 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/Guardfile +18 -0
- data/LICENSE +22 -0
- data/README.md +62 -0
- data/Rakefile +2 -0
- data/bin/iv +7 -0
- data/features/init.feature +35 -0
- data/features/nodes.feature +83 -0
- data/features/step_definitions/.gitkeep +0 -0
- data/features/step_definitions/chef_steps.rb +10 -0
- data/features/step_definitions/cucumber_steps.rb +5 -0
- data/features/step_definitions/init_steps.rb +36 -0
- data/features/support/aruba.rb +6 -0
- data/features/support/env.rb +9 -0
- data/features/support/mocha.rb +15 -0
- data/features/support/test_env.rb +2 -0
- data/features/usage.feature +24 -0
- data/iv-cli.gemspec +29 -0
- data/lib/iv-cli/app.rb +26 -0
- data/lib/iv-cli/command.rb +58 -0
- data/lib/iv-cli/commands/init.rb +50 -0
- data/lib/iv-cli/commands/nodes.rb +83 -0
- data/lib/iv-cli/config.rb +5 -0
- data/lib/iv-cli/helpers/errors.rb +9 -0
- data/lib/iv-cli/usage.rb +8 -0
- data/lib/iv-cli/version.rb +5 -0
- data/lib/iv-cli.rb +8 -0
- data/spec/lib/config_spec.rb +5 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/support/config.rb +18 -0
- data/spec/support/env.rb +38 -0
- metadata +254 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard 'rspec', :version => 2 do
|
5
|
+
watch(%r{^spec/.+_spec\.rb$})
|
6
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
7
|
+
watch(%r{spec/support/.+\.rb}) { "spec" }
|
8
|
+
watch('spec/spec_helper.rb') { "spec" }
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
guard 'cucumber' do
|
13
|
+
watch(%r{^bin/(.+)$}) { |m| "features/#{m[1]}.feature" }
|
14
|
+
watch(%r{^lib/iv-cli/commands/(.+)$}) { |m| "features/#{m[1]}.feature" }
|
15
|
+
watch(%r{^features/.+\.feature$})
|
16
|
+
watch(%r{^features/support/.+$}) { 'features' }
|
17
|
+
watch(%r{^features/step_definitions/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'features' }
|
18
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Eric Saxby
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
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:
|
12
|
+
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# IV::CLI
|
2
|
+
|
3
|
+
Infrastructure Visualization. This is the command line interface and
|
4
|
+
core classes for iv, a gem for visualizing cloud infrastructure.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'iv-cli'
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
Or install it yourself as:
|
17
|
+
|
18
|
+
$ gem install iv-cli
|
19
|
+
|
20
|
+
Then initialize the config files
|
21
|
+
|
22
|
+
$ gem init
|
23
|
+
$ vim ~/.iv/config
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
IV is built around a core executable, `iv`, which gives access to
|
28
|
+
multiple subcommands.
|
29
|
+
|
30
|
+
### init
|
31
|
+
|
32
|
+
Creates config files in the user's home directory.
|
33
|
+
|
34
|
+
$ iv init
|
35
|
+
Generating config directory in ~/.iv
|
36
|
+
Generating config file ~/.iv/config.yml
|
37
|
+
|
38
|
+
Options:
|
39
|
+
|
40
|
+
Usage: iv init [options]
|
41
|
+
-f, --force Overwrite pre-existing config files
|
42
|
+
-h, --help Show this help message
|
43
|
+
|
44
|
+
### nodes
|
45
|
+
|
46
|
+
Lists out all nodes in all of your environments
|
47
|
+
|
48
|
+
$ iv nodes
|
49
|
+
+ status + name + ip + os + org +
|
50
|
+
| 2 minutes ago | node.name.domain.com | 0.0.0.0 | my os | demo |
|
51
|
+
| 1 minute ago | node2.name.domain.com | 0.0.0.0 | my os | stage |
|
52
|
+
|
53
|
+
Please note that all nodes should have a NAME and an FQDN registered
|
54
|
+
with your chef server in order for the formatting to work correctly.
|
55
|
+
|
56
|
+
## Contributing
|
57
|
+
|
58
|
+
1. Fork it
|
59
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
60
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
61
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
62
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/iv
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
Feature: User can initialize config files
|
2
|
+
|
3
|
+
Scenario: init directory/file creation
|
4
|
+
Given There is no iv configuration file
|
5
|
+
When I run `iv init`
|
6
|
+
Then There should be an iv configuration directory
|
7
|
+
And There should be a default iv configuration file
|
8
|
+
And the exit status should be 0
|
9
|
+
|
10
|
+
Scenario: init output
|
11
|
+
When I run `iv init`
|
12
|
+
Then the stdout should contain:
|
13
|
+
"""
|
14
|
+
Generating config directory in ~/.iv
|
15
|
+
Generating config file ~/.iv/config.yml
|
16
|
+
"""
|
17
|
+
|
18
|
+
Scenario: config file already exists
|
19
|
+
Given a directory named "tmp/test/.iv"
|
20
|
+
And an empty file named "tmp/test/.iv/config.yml"
|
21
|
+
When I run `iv init`
|
22
|
+
Then the stderr should contain:
|
23
|
+
"""
|
24
|
+
Config file already exists at ~/.iv/config.yml
|
25
|
+
Use --force to overwrite file
|
26
|
+
"""
|
27
|
+
And the exit status should be 10
|
28
|
+
|
29
|
+
Scenario: overwrite pre-existing file
|
30
|
+
Given a directory named "tmp/test/.iv"
|
31
|
+
And an empty file named "tmp/test/.iv/config.yml"
|
32
|
+
When I run `iv init --force`
|
33
|
+
Then There should be a default iv configuration file
|
34
|
+
And the exit status should be 0
|
35
|
+
|
@@ -0,0 +1,83 @@
|
|
1
|
+
Feature: User can interact with opscode nodes
|
2
|
+
|
3
|
+
Scenario: User gets error message without config file
|
4
|
+
Given there is no iv configuration file
|
5
|
+
When I run `iv nodes`
|
6
|
+
Then the stderr should contain:
|
7
|
+
"""
|
8
|
+
Error: Unable to find IV configuration.
|
9
|
+
Please run 'iv init' and update config.yml with your credentials
|
10
|
+
"""
|
11
|
+
And the exit status should be 4
|
12
|
+
|
13
|
+
Scenario: User can list nodes
|
14
|
+
Given the following iv config file:
|
15
|
+
"""
|
16
|
+
---
|
17
|
+
opscode:
|
18
|
+
demo:
|
19
|
+
acct: "my_org"
|
20
|
+
user: "user"
|
21
|
+
key: "key"
|
22
|
+
"""
|
23
|
+
And I expect Chef to return the following nodes for "my_org", with user "user" and key "key":
|
24
|
+
| status | name | fqdn | ip | os |
|
25
|
+
| 1 minute ago | host.1 | host.1 | 10.10.10.10 | solaris2 |
|
26
|
+
| 2 minutes ago | host.2 | host.2 | 10.10.10.11 | solaris2 |
|
27
|
+
When I run `iv nodes`
|
28
|
+
Then the exit status should be 0
|
29
|
+
Then the stdout should contain:
|
30
|
+
"""
|
31
|
+
+ status + name + ip + os + org +
|
32
|
+
| 1 minute ago | host.1 | 10.10.10.10 | solaris2 | demo |
|
33
|
+
| 2 minutes ago | host.2 | 10.10.10.11 | solaris2 | demo |
|
34
|
+
"""
|
35
|
+
|
36
|
+
Scenario: User lists nodes from multiple organizations
|
37
|
+
Given the following iv config file:
|
38
|
+
"""
|
39
|
+
---
|
40
|
+
opscode:
|
41
|
+
demo:
|
42
|
+
acct: "my_org"
|
43
|
+
user: "user"
|
44
|
+
key: "key"
|
45
|
+
stage:
|
46
|
+
acct: "other_org"
|
47
|
+
user: "user"
|
48
|
+
key: "key"
|
49
|
+
"""
|
50
|
+
And I expect Chef to return the following nodes for "my_org", with user "user" and key "key":
|
51
|
+
| status | name | fqdn | ip | os |
|
52
|
+
| 1 minute ago | host.1 | host.1 | 10.10.10.10 | solaris2 |
|
53
|
+
| 2 minutes ago | host.2 | host.2 | 10.10.10.11 | solaris2 |
|
54
|
+
And I expect Chef to return the following nodes for "other_org", with user "user" and key "key":
|
55
|
+
| status | name | fqdn | ip | os |
|
56
|
+
| 1 hour ago | host.3 | host.3 | 10.10.10.12 | solaris2 |
|
57
|
+
| 2 hours ago | host.4 | host.4 | 10.10.10.13 | solaris2 |
|
58
|
+
When I run `iv nodes`
|
59
|
+
Then the stderr should contain ""
|
60
|
+
Then the exit status should be 0
|
61
|
+
Then the stdout should contain:
|
62
|
+
"""
|
63
|
+
+ status + name + ip + os + org +
|
64
|
+
| 1 minute ago | host.1 | 10.10.10.10 | solaris2 | demo |
|
65
|
+
| 2 minutes ago | host.2 | 10.10.10.11 | solaris2 | demo |
|
66
|
+
| 1 hour ago | host.3 | 10.10.10.12 | solaris2 | stage |
|
67
|
+
| 2 hours ago | host.4 | 10.10.10.13 | solaris2 | stage |
|
68
|
+
"""
|
69
|
+
|
70
|
+
Scenario: User gets error message if config file does not include opscode info
|
71
|
+
Given the following iv config file:
|
72
|
+
"""
|
73
|
+
---
|
74
|
+
stuff:
|
75
|
+
other:
|
76
|
+
stuff: "wow"
|
77
|
+
"""
|
78
|
+
When I run `iv nodes`
|
79
|
+
Then the stderr should contain:
|
80
|
+
"""
|
81
|
+
Unable to read opscode account info from config.
|
82
|
+
"""
|
83
|
+
And the exit status should be 4
|
File without changes
|
@@ -0,0 +1,10 @@
|
|
1
|
+
Given /^I expect Chef to return the following nodes for "([^"]*)", with user "([^"]*)" and key "([^"]*)":$/ do |organization, user, key, table|
|
2
|
+
nodes = table.rows.map{|r| r.join(', ')}.join("\n")
|
3
|
+
steps %{
|
4
|
+
Given I double `knife status -s https://api.opscode.com/organizations/#{organization} -u #{user} -k #{key} --no-color` with exit status 0 and stdout:
|
5
|
+
"""
|
6
|
+
#{nodes}
|
7
|
+
"""
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
Given /There is no iv configuration file/i do
|
2
|
+
steps %{
|
3
|
+
Given a file named "tmp/test/.iv/configuration.yml" should not exist
|
4
|
+
}
|
5
|
+
end
|
6
|
+
|
7
|
+
Then /^There should be an iv configuration directory$/i do
|
8
|
+
steps %{
|
9
|
+
Then a directory named "tmp/test/.iv" should exist
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
Then /^There should be a default iv configuration file$/i do
|
14
|
+
steps %{
|
15
|
+
Then a file named "tmp/test/.iv/config.yml" should exist
|
16
|
+
And the file "tmp/test/.iv/config.yml" should contain exactly:
|
17
|
+
"""
|
18
|
+
---
|
19
|
+
opscode:
|
20
|
+
demo: # arbitrary identifier
|
21
|
+
acct: "Your opscode organization/account name"
|
22
|
+
user: "Your opscode username"
|
23
|
+
key: "Path to the .pem file for your user"
|
24
|
+
|
25
|
+
"""
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
Given /^the following iv config file:$/i do |contents|
|
30
|
+
steps %{
|
31
|
+
Given a directory named "tmp/test/.iv"
|
32
|
+
}
|
33
|
+
::File.open("tmp/test/.iv/config.yml","w") do |file|
|
34
|
+
file.puts contents
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
|
2
|
+
|
3
|
+
require 'aruba-doubles/cucumber'
|
4
|
+
require 'aruba/cucumber'
|
5
|
+
require 'fileutils'
|
6
|
+
require 'rspec/expectations'
|
7
|
+
require 'iv-cli'
|
8
|
+
|
9
|
+
Dir["#{File.dirname(__FILE__)}/../../spec/support/**/*.rb"].each { |f| require f unless /_spec\.rb$/.match(f) }
|
@@ -0,0 +1,24 @@
|
|
1
|
+
Feature: User sees usage information
|
2
|
+
|
3
|
+
Scenario: No command given
|
4
|
+
Given I run `iv`
|
5
|
+
Then the stderr should contain:
|
6
|
+
"""
|
7
|
+
Error: No command given
|
8
|
+
|
9
|
+
Usage: iv command [options]
|
10
|
+
|
11
|
+
init -- generate configuration file
|
12
|
+
nodes -- list out nodes for configured organizations
|
13
|
+
"""
|
14
|
+
And the exit status should be 1
|
15
|
+
|
16
|
+
Scenario: invalid command given
|
17
|
+
Given I run `iv blah`
|
18
|
+
Then the stderr should contain:
|
19
|
+
"""
|
20
|
+
Error: Unable to match command: blah
|
21
|
+
|
22
|
+
Usage: iv command [options]
|
23
|
+
"""
|
24
|
+
And the exit status should be 3
|
data/iv-cli.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/iv-cli/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Eric Saxby"]
|
6
|
+
gem.email = ["sax@livinginthepast.org"]
|
7
|
+
gem.description = %q{Peer into your cloud infrastructure, using APIs from various providers}
|
8
|
+
gem.summary = %q{Infrastructure Visualization}
|
9
|
+
gem.homepage = ""
|
10
|
+
|
11
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
|
+
gem.files = `git ls-files`.split("\n")
|
13
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
gem.name = "iv-cli"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = IV::CLI::VERSION
|
17
|
+
|
18
|
+
gem.add_development_dependency "rspec", "~> 2.10.0"
|
19
|
+
gem.add_development_dependency "aruba"
|
20
|
+
gem.add_development_dependency "aruba-doubles", "~>1.2.1"
|
21
|
+
gem.add_development_dependency "guard-rspec"
|
22
|
+
gem.add_development_dependency "guard-cucumber"
|
23
|
+
gem.add_development_dependency "mocha"
|
24
|
+
gem.add_development_dependency "growl"
|
25
|
+
|
26
|
+
gem.add_dependency "mixlib-cli"
|
27
|
+
gem.add_dependency "command_line_reporter"
|
28
|
+
gem.add_dependency "chef"
|
29
|
+
end
|
data/lib/iv-cli/app.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'mixlib/cli'
|
2
|
+
|
3
|
+
class IV::CLI::App
|
4
|
+
include Mixlib::CLI
|
5
|
+
include IV::CLI::Helpers::Errors
|
6
|
+
|
7
|
+
def run
|
8
|
+
validate
|
9
|
+
IV::CLI::Command.run_command(ARGV, options)
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def validate
|
16
|
+
if no_command_given?
|
17
|
+
fail(1, "No command given")
|
18
|
+
elsif IV::CLI::Command.invalid_command?(ARGV.first)
|
19
|
+
fail(3, "Unable to match command: #{ARGV.first}")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def no_command_given?
|
24
|
+
ARGV.empty?
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
class IV::CLI::Command
|
2
|
+
include Mixlib::CLI
|
3
|
+
include IV::CLI::Helpers::Errors
|
4
|
+
|
5
|
+
attr_reader :config
|
6
|
+
|
7
|
+
def self.run_command args, options = {}
|
8
|
+
command_class(args.shift).new.run(args, options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.invalid_command? command
|
12
|
+
! commands.include? command
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def self.commands
|
19
|
+
@@commands ||= {}
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.command_name
|
23
|
+
name.split("::").last.downcase
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.command_class(command)
|
27
|
+
commands[command]
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.config_dir
|
31
|
+
"#{ENV['HOME']}/.iv"
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.config
|
35
|
+
"#{config_dir}/config.yml"
|
36
|
+
end
|
37
|
+
|
38
|
+
def parse_config
|
39
|
+
unless ::File.exists?(self.class.config)
|
40
|
+
fail(4, %{Unable to find IV configuration.\nPlease run 'iv init' and update config.yml with your credentials})
|
41
|
+
end
|
42
|
+
@config = YAML.load_file(self.class.config)
|
43
|
+
end
|
44
|
+
|
45
|
+
def ensure_opscode_config
|
46
|
+
parse_config unless config
|
47
|
+
unless config["opscode"]
|
48
|
+
fail(4, %{Unable to read opscode account info from config.})
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def self.inherited(subclass)
|
55
|
+
commands[subclass.command_name] = subclass
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
class IV::CLI::Command::Init < IV::CLI::Command
|
2
|
+
banner "Usage: iv init [options]"
|
3
|
+
|
4
|
+
option :force,
|
5
|
+
:short => '-f',
|
6
|
+
:long => '--force',
|
7
|
+
:description => "Overwrite pre-existing config files",
|
8
|
+
:boolean => true
|
9
|
+
|
10
|
+
option :help,
|
11
|
+
:short => '-h',
|
12
|
+
:long => '--help',
|
13
|
+
:description => "Show this help message",
|
14
|
+
:on => :tail,
|
15
|
+
:boolean => true,
|
16
|
+
:show_options => true,
|
17
|
+
:exit => 0
|
18
|
+
|
19
|
+
def run(args, options = {})
|
20
|
+
parse_options(args)
|
21
|
+
validate
|
22
|
+
|
23
|
+
puts "Generating config directory in ~/.iv"
|
24
|
+
::FileUtils.mkdir_p(self.class.config_dir)
|
25
|
+
|
26
|
+
puts "Generating config file ~/.iv/config.yml"
|
27
|
+
::File.open(self.class.config, "w") do |f|
|
28
|
+
f.puts "---"
|
29
|
+
write_default_opscode(f)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def validate
|
36
|
+
if !config[:force] && ::File.exists?(self.class.config)
|
37
|
+
STDERR.puts "Config file already exists at ~/.iv/config.yml"
|
38
|
+
STDERR.puts "Use --force to overwrite file"
|
39
|
+
Process.exit 10
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def write_default_opscode file
|
44
|
+
file.puts %{opscode: }
|
45
|
+
file.puts %{ demo: # arbitrary identifier}
|
46
|
+
file.puts %{ acct: "Your opscode organization/account name"}
|
47
|
+
file.puts %{ user: "Your opscode username"}
|
48
|
+
file.puts %{ key: "Path to the .pem file for your user"}
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'pty'
|
2
|
+
|
3
|
+
class IV::CLI::Command::Nodes < IV::CLI::Command
|
4
|
+
|
5
|
+
banner "Usage: iv nodes [options]"
|
6
|
+
|
7
|
+
def run(args, options = {})
|
8
|
+
parse_options(args)
|
9
|
+
parse_config
|
10
|
+
ensure_opscode_config
|
11
|
+
|
12
|
+
stream_converge_status
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def stream_converge_status
|
18
|
+
node_info = []
|
19
|
+
config["opscode"].each_pair do |org, settings|
|
20
|
+
command = "knife status -s https://api.opscode.com/organizations/#{settings["acct"]} -u #{settings["user"]} -k #{settings["key"]} --no-color 2> /dev/null"
|
21
|
+
begin
|
22
|
+
PTY.spawn(command) do |stdin, stdout, pid|
|
23
|
+
begin
|
24
|
+
stdin.each do |node|
|
25
|
+
node_info << translate_node_status(node.gsub(/\033\[[0-9;]*m/,''), org)
|
26
|
+
if node_info.length%30 == 0
|
27
|
+
output_node_info(node_info.slice!(0..29))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
rescue Errno::EIO
|
31
|
+
end
|
32
|
+
end
|
33
|
+
rescue PTY::ChildExited
|
34
|
+
end
|
35
|
+
end
|
36
|
+
output_node_info(node_info)
|
37
|
+
end
|
38
|
+
|
39
|
+
def translate_node_status(knife_output, org)
|
40
|
+
node = knife_output.chomp.split(", ")
|
41
|
+
{:status => node[0], :name => node[1], :fqdn => node[2], :ip => node[3], :os => node[4], :org => org}
|
42
|
+
end
|
43
|
+
|
44
|
+
def output_node_info(nodes)
|
45
|
+
column_width = {}
|
46
|
+
column_width[:status] = largest_size(nodes, :status)
|
47
|
+
column_width[:name] = largest_size(nodes, :name)
|
48
|
+
column_width[:ip] = largest_size(nodes, :ip)
|
49
|
+
column_width[:os] = largest_size(nodes, :os)
|
50
|
+
column_width[:org] = largest_size(nodes, :org)
|
51
|
+
|
52
|
+
headers = []
|
53
|
+
[:status, :name, :ip, :os, :org].each do |key|
|
54
|
+
headers << sprintf(" %#{column_width[key]}s ", key.to_s)
|
55
|
+
end
|
56
|
+
print_row(headers, "+")
|
57
|
+
|
58
|
+
nodes.each do |node|
|
59
|
+
node_output = []
|
60
|
+
[:status, :name, :ip, :os, :org].each do |key|
|
61
|
+
node_output << sprintf(" %-#{column_width[key]}s ", node[key])
|
62
|
+
end
|
63
|
+
print_row(node_output)
|
64
|
+
end
|
65
|
+
STDOUT.flush
|
66
|
+
end
|
67
|
+
|
68
|
+
def largest_size(array, key)
|
69
|
+
array.inject(0) do |largest, member|
|
70
|
+
begin
|
71
|
+
size = member[key].size; largest > size ? largest : size
|
72
|
+
rescue NoMethodError
|
73
|
+
largest
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def print_row(array, separator = "|")
|
79
|
+
STDOUT.print separator
|
80
|
+
STDOUT.print array.join(separator)
|
81
|
+
STDOUT.puts separator
|
82
|
+
end
|
83
|
+
end
|
data/lib/iv-cli/usage.rb
ADDED
data/lib/iv-cli.rb
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
+
# Require this file using `require "spec_helper.rb"` to ensure that it is only
|
4
|
+
# loaded once.
|
5
|
+
#
|
6
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
|
+
|
8
|
+
require 'iv-cli'
|
9
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f unless /_spec\.rb$/.match(f) }
|
10
|
+
|
11
|
+
RSpec.configure do |config|
|
12
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
13
|
+
config.run_all_when_everything_filtered = true
|
14
|
+
config.filter_run :focus
|
15
|
+
|
16
|
+
IV::Test::Config.init_rspec(config)
|
17
|
+
IV::Test::Env.init_rspec(config)
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module IV
|
2
|
+
module Test
|
3
|
+
module Config
|
4
|
+
DEFAULT_OPTIONS = IV::CLI::Config::OPTIONS
|
5
|
+
|
6
|
+
def self.init_rspec(config)
|
7
|
+
config.before(:each) do
|
8
|
+
IV::Test::Config.reset
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.reset
|
13
|
+
IV::CLI::Config.const_set "OPTIONS", DEFAULT_OPTIONS
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
data/spec/support/env.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
module IV
|
2
|
+
module Test
|
3
|
+
module Env
|
4
|
+
PWD = ENV["PWD"]
|
5
|
+
|
6
|
+
def self.init_rspec(config)
|
7
|
+
config.before(:each) do
|
8
|
+
IV::Test::Env.reset
|
9
|
+
end
|
10
|
+
|
11
|
+
config.after(:suite) do
|
12
|
+
IV::Test::Env.cleanup
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.reset # :nodoc:
|
17
|
+
cleanup
|
18
|
+
setup
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.cleanup # :nodoc:
|
22
|
+
::FileUtils.rm_rf("tmp")
|
23
|
+
ENV["PWD"] = PWD
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.test_home # :nodoc:
|
27
|
+
::File.expand_path("tmp/test")
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def self.setup
|
33
|
+
::FileUtils.mkdir_p(test_home)
|
34
|
+
ENV["PWD"] = "#{PWD}/tmp"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
metadata
ADDED
@@ -0,0 +1,254 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: iv-cli
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Eric Saxby
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-05-25 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 2.10.0
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 2.10.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: aruba
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: aruba-doubles
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 1.2.1
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.2.1
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: guard-rspec
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: guard-cucumber
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: mocha
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: growl
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: mixlib-cli
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
type: :runtime
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: command_line_reporter
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ! '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
type: :runtime
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
- !ruby/object:Gem::Dependency
|
159
|
+
name: chef
|
160
|
+
requirement: !ruby/object:Gem::Requirement
|
161
|
+
none: false
|
162
|
+
requirements:
|
163
|
+
- - ! '>='
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
166
|
+
type: :runtime
|
167
|
+
prerelease: false
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ! '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
description: Peer into your cloud infrastructure, using APIs from various providers
|
175
|
+
email:
|
176
|
+
- sax@livinginthepast.org
|
177
|
+
executables:
|
178
|
+
- iv
|
179
|
+
extensions: []
|
180
|
+
extra_rdoc_files: []
|
181
|
+
files:
|
182
|
+
- .gitignore
|
183
|
+
- .rspec
|
184
|
+
- Gemfile
|
185
|
+
- Guardfile
|
186
|
+
- LICENSE
|
187
|
+
- README.md
|
188
|
+
- Rakefile
|
189
|
+
- bin/iv
|
190
|
+
- features/init.feature
|
191
|
+
- features/nodes.feature
|
192
|
+
- features/step_definitions/.gitkeep
|
193
|
+
- features/step_definitions/chef_steps.rb
|
194
|
+
- features/step_definitions/cucumber_steps.rb
|
195
|
+
- features/step_definitions/init_steps.rb
|
196
|
+
- features/support/aruba.rb
|
197
|
+
- features/support/env.rb
|
198
|
+
- features/support/mocha.rb
|
199
|
+
- features/support/test_env.rb
|
200
|
+
- features/usage.feature
|
201
|
+
- iv-cli.gemspec
|
202
|
+
- lib/iv-cli.rb
|
203
|
+
- lib/iv-cli/app.rb
|
204
|
+
- lib/iv-cli/command.rb
|
205
|
+
- lib/iv-cli/commands/init.rb
|
206
|
+
- lib/iv-cli/commands/nodes.rb
|
207
|
+
- lib/iv-cli/config.rb
|
208
|
+
- lib/iv-cli/helpers/errors.rb
|
209
|
+
- lib/iv-cli/usage.rb
|
210
|
+
- lib/iv-cli/version.rb
|
211
|
+
- spec/lib/config_spec.rb
|
212
|
+
- spec/spec_helper.rb
|
213
|
+
- spec/support/config.rb
|
214
|
+
- spec/support/env.rb
|
215
|
+
homepage: ''
|
216
|
+
licenses: []
|
217
|
+
post_install_message:
|
218
|
+
rdoc_options: []
|
219
|
+
require_paths:
|
220
|
+
- lib
|
221
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
222
|
+
none: false
|
223
|
+
requirements:
|
224
|
+
- - ! '>='
|
225
|
+
- !ruby/object:Gem::Version
|
226
|
+
version: '0'
|
227
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
228
|
+
none: false
|
229
|
+
requirements:
|
230
|
+
- - ! '>='
|
231
|
+
- !ruby/object:Gem::Version
|
232
|
+
version: '0'
|
233
|
+
requirements: []
|
234
|
+
rubyforge_project:
|
235
|
+
rubygems_version: 1.8.22
|
236
|
+
signing_key:
|
237
|
+
specification_version: 3
|
238
|
+
summary: Infrastructure Visualization
|
239
|
+
test_files:
|
240
|
+
- features/init.feature
|
241
|
+
- features/nodes.feature
|
242
|
+
- features/step_definitions/.gitkeep
|
243
|
+
- features/step_definitions/chef_steps.rb
|
244
|
+
- features/step_definitions/cucumber_steps.rb
|
245
|
+
- features/step_definitions/init_steps.rb
|
246
|
+
- features/support/aruba.rb
|
247
|
+
- features/support/env.rb
|
248
|
+
- features/support/mocha.rb
|
249
|
+
- features/support/test_env.rb
|
250
|
+
- features/usage.feature
|
251
|
+
- spec/lib/config_spec.rb
|
252
|
+
- spec/spec_helper.rb
|
253
|
+
- spec/support/config.rb
|
254
|
+
- spec/support/env.rb
|