iv-cli 0.0.1
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/.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
|