Devops 0.0.3

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.
@@ -0,0 +1,3 @@
1
+ *.swo
2
+ *.swp
3
+ *.gem
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ # A sample Gemfile
2
+ source "https://rubygems.org"
3
+
4
+ gem "json"
5
+ gem "net-ssh"
6
+ gem "aws-sdk"
7
+ gem "i18n"
8
+ gem "rake"
9
+
10
+ group :development do
11
+ gem "rspec"
12
+ end
@@ -0,0 +1,33 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ aws-sdk (1.8.3)
5
+ json (~> 1.4)
6
+ nokogiri (>= 1.4.4)
7
+ uuidtools (~> 2.1)
8
+ diff-lcs (1.1.3)
9
+ i18n (0.6.1)
10
+ json (1.7.7)
11
+ net-ssh (2.6.5)
12
+ nokogiri (1.5.6)
13
+ rake (10.0.3)
14
+ rspec (2.11.0)
15
+ rspec-core (~> 2.11.0)
16
+ rspec-expectations (~> 2.11.0)
17
+ rspec-mocks (~> 2.11.0)
18
+ rspec-core (2.11.1)
19
+ rspec-expectations (2.11.2)
20
+ diff-lcs (~> 1.1.3)
21
+ rspec-mocks (2.11.2)
22
+ uuidtools (2.1.3)
23
+
24
+ PLATFORMS
25
+ ruby
26
+
27
+ DEPENDENCIES
28
+ aws-sdk
29
+ i18n
30
+ json
31
+ net-ssh
32
+ rake
33
+ rspec
@@ -0,0 +1,54 @@
1
+ # Ops
2
+
3
+ This repo serves as a container for utilitys and apps related
4
+ to Operations tasks.
5
+
6
+ ## Using Ops tools:
7
+
8
+ ### Requirements
9
+
10
+ ruby 1.8.7
11
+ openssh
12
+
13
+ ### Getting Started
14
+
15
+ Add the following to your .bashrc or .bash_profile_
16
+
17
+ export $OPS_HOME="/path to your ops repo"
18
+ source $OPS_HOME/autocomplete
19
+
20
+ Add the following symbolic link so you can use this from any directory
21
+
22
+ # ln -s this somewhere else if you're using a shared computer
23
+ # maybe create a local bin folder in ~/ and add it to your path
24
+
25
+ ln -s $OPS_HOME /usr/local/bin/ops
26
+
27
+ run the following command
28
+
29
+ bundle install
30
+
31
+ create a config.json file with the following contents:
32
+
33
+ {
34
+ "IdentityLocations" : [ "~/.ssh", ... ],
35
+ "AWS" : {
36
+ "AccessKeyId" : "Your Access Key",
37
+ "SecretAccessKey" : "Your Secret Access Key"
38
+ }
39
+ }
40
+
41
+ ### Connecting to a host:
42
+
43
+ usage: ops [ host ]:ssh
44
+
45
+ ### Other commands:
46
+
47
+ ops -T - List all tasks available
48
+
49
+ ops hosts:list - List all the hosts
50
+
51
+ ops hosts:sync - Sync hosts.json with EC2 instance list
52
+
53
+ ops hosts:add host=[ alias name ] hostname=[ ip address or hostname ] \
54
+ identity=[ private key name ] user=[ user ]
@@ -0,0 +1,5 @@
1
+ function _build_ops_completion() {
2
+ ops -T | awk '{ print $2 }'
3
+ }
4
+
5
+ complete -W "$(_build_ops_completion)" ops
data/bin/ops ADDED
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'Ops'
4
+
5
+ def exit_failure( reason="", code=1 )
6
+ puts reason; exit
7
+ end
8
+
9
+ bootstrap_files = [
10
+ File.join( 'tasks', 'init.rb' ) ]
11
+
12
+ Rake.application.init( File.basename( $0 ) )
13
+
14
+ begin
15
+ bootstrap_files.each do | f |
16
+ File.join Ops::root_dir, f
17
+ require f
18
+ end
19
+ rescue => e
20
+ puts e
21
+ end
22
+
23
+ bootstrap_files.each do | f |
24
+ f = File.join Ops::pwd_dir, f
25
+ require f if File.exists? f
26
+ end
27
+
28
+ begin
29
+ Rake.application.top_level
30
+ rescue => e
31
+ puts "Failed to call task: #{ e.message }"
32
+ end
33
+
34
+ exit
@@ -0,0 +1,23 @@
1
+ require 'lib/version.rb'
2
+
3
+ files = `git ls-files`.split("\n")
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'Devops'
7
+
8
+ s.version = Ops::version
9
+
10
+ s.date = Time.new
11
+
12
+ s.summary = "Ops tool for remote servers"
13
+ s.description = "Ops tool for remote servers"
14
+
15
+ s.authors = [ "Hardeep Shoker", "Ryan Willoughby" ]
16
+ s.email = 'hardeepshoker@gmail.com'
17
+ s.homepage = 'https://github.com/hardeep/ops'
18
+
19
+ s.files = files
20
+
21
+ s.bindir = 'bin'
22
+ s.executables = [ 'ops' ]
23
+ end
@@ -0,0 +1,123 @@
1
+ module Host
2
+ class Default
3
+
4
+ attr_reader :alias, :host_name, :ssh_pem, :user, :tags
5
+
6
+ def initialize( host = 'unspecified', info = {}, opts = {} )
7
+ @alias = host
8
+
9
+ @host_name = info[ "HostName" ] || nil
10
+ @user = info[ "User" ] || Etc.getlogin
11
+ @ssh_pem = info[ "IdentityFile" ] || nil
12
+ @ssh_port = info[ "Port" ] || nil
13
+ @type = info[ "Type" ] || :default
14
+ @tags = info[ "Tags" ] || {}
15
+ @pem_dirs = opts[ "IdentityLocations" ] || nil
16
+ end
17
+
18
+ def type
19
+ @type.to_sym
20
+ end
21
+
22
+ def tags=( tags )
23
+ raise IOError, "tags must be a hash" unless tags.kind_of?( Hash )
24
+ @tags = tags
25
+ end
26
+
27
+ def matches?( tags )
28
+ tags.each do | tag, value |
29
+ is_regex = value.match( /^\/(.*)\/$/ )
30
+
31
+ if is_regex
32
+ regex = Regexp.new( is_regex[ 1 ] )
33
+
34
+ unless @tags.has_key?( tag ) && @tags[ tag ].match( regex )
35
+ return false
36
+ end
37
+ else
38
+ unless @tags.has_key?( tag ) && @tags[ tag ] == value
39
+ return false
40
+ end
41
+ end
42
+ end
43
+
44
+ true
45
+ end
46
+
47
+ def ssh_pem
48
+ ssh_pem = nil
49
+
50
+ unless @ssh_pem.nil?
51
+ unless @pem_dirs.nil?
52
+ @pem_dirs.each do | dir |
53
+ f = File.expand_path File.join( dir, "#{ @ssh_pem }*" )
54
+ glob = Dir.glob( f )
55
+ ssh_pem = glob.first unless glob.empty?
56
+ end
57
+ end
58
+
59
+ raise( IOError,
60
+ "Error: pem - #{ ssh_pem } not found or not accessable." ) unless
61
+ File.stat( ssh_pem )
62
+ end
63
+
64
+ ssh_pem
65
+ end
66
+
67
+ def shell!( opts = nil )
68
+ ssh_cmd = [ "ssh" ]
69
+
70
+ raise( IOError, "Error: HostName invalid." ) if @host_name.nil?
71
+
72
+ ssh_cmd << [ "-l", @user ]
73
+ ssh_cmd << @host_name
74
+
75
+ pem = ssh_pem
76
+ ssh_cmd << [ "-i", pem ] unless pem.nil?
77
+
78
+ begin
79
+ exec( ssh_cmd.join(" ") )
80
+ rescue => e
81
+ raise( IOError, "Could not call ssh." )
82
+ end
83
+ end
84
+
85
+ def shell_exec! command
86
+ ssh_pem
87
+
88
+ options = { :keys => ssh_pem }
89
+
90
+ color = Color.random_color
91
+
92
+ Net::SSH.start( host_name, user, options ) do | s |
93
+
94
+ channel = s.open_channel do |ch|
95
+ ch.exec( command ) do | ch, success |
96
+ raise( IOError,
97
+ "#{ host_name } > could not execute command" ) unless
98
+ success
99
+
100
+ ch.on_data do | c, data |
101
+ data.split("\n").each do | line |
102
+ puts "#{ Color.print(
103
+ self.alias, [ :bold, color ] ) } > #{ line }"
104
+ end
105
+ end
106
+
107
+ ch.on_extended_data do |c, type, data|
108
+ data.split("\n").each do | line |
109
+ puts "#{ Color.print(
110
+ self.alias, [ :bold, color ] ) } > #{ line }"
111
+ end
112
+ end
113
+
114
+ ch.on_close do
115
+ puts "#{ Color.print(
116
+ self.alias, [ :bold, color ] ) } > COMMAND finished"
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,11 @@
1
+ module Host
2
+ class EC2 < Host::Default
3
+
4
+ attr_reader :alias
5
+
6
+ def initialize( host = 'unspecified', info = {}, opts = {} )
7
+ super
8
+ @type = info[ "Type" ] || :ec2
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Host
2
+
3
+ class List < Array
4
+
5
+ def filter( tags )
6
+ self.select do | i |
7
+ i.matches? tags
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,31 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ require 'bundler'
6
+ end
7
+
8
+ # standard gems
9
+ require 'json'
10
+ require 'etc'
11
+ require 'pathname'
12
+ require 'pp'
13
+
14
+ # external gems
15
+ require 'net/ssh'
16
+ require 'i18n'
17
+ require 'rake'
18
+ require 'aws-sdk'
19
+
20
+ # local includes
21
+ require 'version'
22
+ require 'host/list'
23
+ require 'host/default'
24
+ require 'host/e_c_2'
25
+ require 'ops/common'
26
+ require 'ops/console'
27
+
28
+ # load all i18n strings
29
+ string_search = File.join( Ops::root_dir, "res", "strings/**/*.yml" )
30
+ string_files = Dir[ string_search ]
31
+ I18n.load_path << string_files
@@ -0,0 +1,52 @@
1
+ module Ops
2
+
3
+ def self.root_dir
4
+ File.expand_path(
5
+ File.join( File.dirname( __FILE__ ), "..", ".." ) )
6
+ end
7
+
8
+ def self.pwd_dir
9
+ Dir.pwd
10
+ end
11
+
12
+ def self.has_bash?
13
+ `which bash`
14
+ ( $? == 0 ) ? true : false
15
+ end
16
+
17
+ def self.read_config
18
+ config_file = File.join( self.pwd_dir, "config.json" )
19
+
20
+ begin
21
+ config = JSON.parse File.read( config_file )
22
+ rescue JSON::ParserError
23
+ raise IOError, "Error parsing config file: #{ config_file }."
24
+ end
25
+ end
26
+
27
+ def self.read_hosts
28
+ config = read_config
29
+
30
+ hosts = {}
31
+
32
+ hosts_files = [
33
+ File.join( self.pwd_dir, "hosts.json" ),
34
+ File.join( self.pwd_dir, 'tmp' ,"hosts.json" ), ]
35
+
36
+ hosts_files.each do | file |
37
+ if File.exists? file
38
+ begin
39
+ json = JSON.parse File.read( file )
40
+ json.each do | n, i |
41
+ hosts[ n ] = Host::Default.new( n, i, config )
42
+ end
43
+ rescue JSON::ParserError => e
44
+ raise( IOError,
45
+ "Error parsing hosts file: #{ file }. #{ e.message }" )
46
+ end
47
+ end
48
+ end
49
+
50
+ hosts
51
+ end
52
+ end
@@ -0,0 +1,45 @@
1
+ module Ops
2
+ module Console
3
+ OPTIONS = { :bold => 1, :underline => 4 }
4
+
5
+ COLORS = { :black => 30,
6
+ :red => 31, :green => 32, :yellow => 33, :blue => 34,
7
+ :magenta => 35, :cyan => 36, :white => 37 }
8
+
9
+ BG_COLORS = { :black => 40, :red => 41, :green => 42,
10
+ :yellow => 43, :blue => 44, :magenta => 45, :cyan => 46,
11
+ :white => 47 }
12
+
13
+ def self.reload!
14
+
15
+ end
16
+
17
+ def self.is_bash?
18
+ `which bash`
19
+ ( $? == 0 ) ? true : false
20
+ end
21
+
22
+ def self.bash_exec!( cmd )
23
+ bash = `which bash`.strip
24
+ `#{ bash } -c #{ cmd }"`
25
+ end
26
+
27
+ def print( string, opts = [] )
28
+ c = []
29
+ opts.each { | o | c << COLORS[ o ] if COLORS.has_key?( o ) }
30
+ opts.each { | o | c << OPTIONS[ o ] if OPTIONS.has_key?( o ) }
31
+
32
+ "\033[#{ c.join( ";" ) }m#{ string }\033[0m"
33
+ end
34
+
35
+ def random_color
36
+ colors = COLORS.keys + BG_COLORS.keys
37
+ colors.reject!{ | c | [ :white, :black ].include? c }
38
+ colors.choice
39
+ end
40
+ end
41
+ end
42
+
43
+ class Color
44
+ extend Ops::Console
45
+ end
@@ -0,0 +1,50 @@
1
+ namespace "hosts" do
2
+
3
+ namespace "ec2" do
4
+
5
+ ## Sync EC2 Hosts
6
+ desc "hosts.sync"
7
+ task "sync" do
8
+ ec2 = AWS::EC2.new(
9
+ :access_key_id => $config[ "AWS" ][ "AccessKeyId" ],
10
+ :secret_access_key => $config[ "AWS" ][ "SecretAccessKey" ] )
11
+
12
+ hosts = {}
13
+
14
+ # used if no name is given
15
+ count = 0
16
+
17
+ ec2.instances.each do | h |
18
+ name = h.tags[ "Name" ]
19
+
20
+ if name.nil? || name.empty?
21
+ name = "noname-#{ count }"
22
+ count += 1
23
+ end
24
+
25
+ ip = h.dns_name || "stopped"
26
+
27
+ puts "Discovered: #{ name } -> #{ ip }"
28
+
29
+ hosts[ name ] = {
30
+ "HostName" => ip,
31
+ "User" => h.tags[ "User" ],
32
+ "IdentityFile" => h.key_name,
33
+ "Tags" => h.tags.to_h.to_hash,
34
+ "Type" => "EC2" }
35
+ end
36
+
37
+ tmp_dir = File.join( Ops::pwd_dir, 'tmp' )
38
+ Dir.mkdir( tmp_dir ) unless File.directory? tmp_dir
39
+
40
+ host_file = File.join( tmp_dir, 'hosts.json' )
41
+ File.open( host_file, 'w' ) { | f | f.write( hosts.to_json ) }
42
+
43
+ if Ops::has_bash?
44
+ bash = `which bash`.strip
45
+ `#{ bash } -c "source #{
46
+ File.join( Ops::root_dir, 'autocomplete' ) }"`
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,48 @@
1
+ namespace "hosts" do
2
+
3
+ desc I18n.t( "hosts.list.desc" )
4
+ task "list" do
5
+ hosts = Ops::read_hosts
6
+ hosts.each do | i, h |
7
+ puts "#{ h.alias }"
8
+ end
9
+ end
10
+
11
+ desc I18n.t( "hosts.add.desc" )
12
+ task "add" do
13
+ required = [ 'hostname', 'host', 'user' ]
14
+
15
+ required.each do | p |
16
+ fail I18n.t( "hosts.add.no_#{ p }" ) if ENV[ p ].nil? ||
17
+ ENV[ p ].empty?
18
+ end
19
+
20
+ hostname = ENV[ 'hostname' ]
21
+ host = ENV[ 'host' ]
22
+ user = ENV[ 'user' ]
23
+ type = ENV[ 'type' ] || "default"
24
+ identity = File.basename( ENV[ 'identity' ] )
25
+
26
+
27
+ puts "Adding host: #{ host }"
28
+ puts " => #{ hostname }"
29
+ puts " => using #{ identity }" unless identity.nil?
30
+
31
+ hosts = {}
32
+ hosts_file = File.join( pwd, "hosts.json" )
33
+
34
+ hosts = Ops::read_hosts
35
+
36
+ exit_failure( "Error: #{ host } is already defined" ) if
37
+ hosts.has_key? host
38
+
39
+ hosts[ host ] = {
40
+ 'HostName' => hostname,
41
+ 'User' => user,
42
+ 'IdentityFile' => identity,
43
+ "Type" => "type"
44
+ }
45
+
46
+ File.open( hosts_file, 'w' ) { | f | f.write( hosts.to_json ) }
47
+ end
48
+ end
@@ -0,0 +1,4 @@
1
+ require 'tasks/ops.rb'
2
+ require 'tasks/hosts.rb'
3
+ require 'tasks/ec2.rb'
4
+ require 'tasks/ssh.rb'
@@ -0,0 +1,33 @@
1
+ task "default" do
2
+ Rake.application.options.show_task_pattern = //
3
+ Rake.application.display_tasks_and_comments()
4
+ end
5
+
6
+ desc I18n.t( "ops.version.desc" )
7
+ task "version" do
8
+ puts "Version: #{ Ops.version }"
9
+ end
10
+
11
+ ## Project Initialization
12
+
13
+ desc I18n.t( "ops.init.desc" )
14
+ task "init" do
15
+
16
+ name = ENV[ 'name' ]
17
+ fail I18n.t( "ops.init.no_name" ) if name.nil? || name.empty?
18
+
19
+ FileUtils.cp_r( File.join( Ops::root_dir, "res", "samples", "default" ),
20
+ File.join( Ops::pwd_dir, name ) )
21
+ end
22
+
23
+ begin
24
+ $hosts = Ops::read_hosts
25
+ rescue
26
+ $hosts = {}
27
+ end
28
+
29
+ begin
30
+ $config = Ops::read_config
31
+ rescue
32
+ $config = {}
33
+ end
@@ -0,0 +1,22 @@
1
+ namespace "hosts" do
2
+
3
+ $hosts.each do | i, host |
4
+
5
+ namespace host.alias do
6
+
7
+ desc I18n.t( "host.ssh", :host => host.alias )
8
+ task "ssh" do
9
+ host.shell!
10
+ end
11
+
12
+ desc "Execute a command over ssh"
13
+ task "ssh:exec" do
14
+ command = ENV[ "command" ]
15
+ raise IOError, "Command not specified" if command.nil? ||
16
+ command.empty?
17
+
18
+ host.shell_exec! command
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,6 @@
1
+ module Ops
2
+
3
+ def self.version
4
+ "0.0.3"
5
+ end
6
+ end
File without changes
@@ -0,0 +1,7 @@
1
+ {
2
+ "IdentityLocations" : [ "~/.ssh" ],
3
+ "AWS" : {
4
+ "AccessKeyId" : "",
5
+ "SecretAccessKey" : ""
6
+ }
7
+ }
@@ -0,0 +1,14 @@
1
+ en:
2
+ messages:
3
+ reload_bash: "To have autocompletion for new/modified hosts reload
4
+ your bash profile/configs. Use `$ source [ location to
5
+ .bashrc or .bash_profile ]` on most machines."
6
+ ops:
7
+ init:
8
+ desc: "Initialize a new project"
9
+ no_name: "project name not provided"
10
+ version:
11
+ desc: "Print version number"
12
+
13
+ host:
14
+ ssh: "SSH into %{host}"
@@ -0,0 +1,15 @@
1
+ en:
2
+ hosts:
3
+
4
+ list:
5
+ desc: "List all hosts form hosts config file."
6
+
7
+ sync:
8
+ desc: "Syncronize local hosts file with instances from EC2."
9
+ in_progress: "Syncing hosts.json with EC2..."
10
+
11
+ add:
12
+ desc: "Add a host to the hosts file"
13
+ no_hostname: "No hostname was provided."
14
+ no_host: "No host was provided."
15
+ no_user: "No use was provided."
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe Host::Default do
4
+
5
+ context "with tags" do
6
+
7
+ before :each do
8
+ @host = Host::Default.new
9
+ end
10
+
11
+ it "should allow you to add tags" do
12
+ @host.tags = { "Platform" => "Windows" }
13
+ end
14
+
15
+ describe ".matches?" do
16
+
17
+ before :each do
18
+ @host.tags = { "Platform" => "Windows" }
19
+ end
20
+
21
+ it "should return true if all tags match" do
22
+ @host.matches?( { "Platform" => "Windows" } ).should == true
23
+ end
24
+
25
+ it "should return true if all tags match" do
26
+ @host.matches?( {
27
+ "Platform" => "Windows",
28
+ "Role" => "Worker" } ).should == false
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ describe Host::List do
4
+
5
+ before :each do
6
+ @list = Host::List.new
7
+ end
8
+
9
+ it "should be an extenstion of the array class" do
10
+ @list.kind_of?( Array ).should == true
11
+ end
12
+
13
+ describe ".filter" do
14
+
15
+ before :each do
16
+ @host = Host::Default.new( "host1" )
17
+ @host.tags = { "Platform" => "Windows" }
18
+ @list << @host
19
+ end
20
+
21
+ it "should iterate through the items and call .matches?" do
22
+ @host.should_receive( :matches? )
23
+ @list.filter( { "Platform" => "Windows" } )
24
+ end
25
+
26
+ it "should return the matched host" do
27
+ hosts = @list.filter( { "Platform" => "Windows" } )
28
+ hosts.length.should == 1
29
+ end
30
+
31
+ it "should return a empty list" do
32
+ hosts = @list.filter( { "Platform" => "Linux" } )
33
+ hosts.length.should == 0
34
+ end
35
+ end
36
+ end
@@ -0,0 +1 @@
1
+ require 'ops.rb'
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: Devops
3
+ version: !ruby/object:Gem::Version
4
+ hash: 25
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 3
10
+ version: 0.0.3
11
+ platform: ruby
12
+ authors:
13
+ - Hardeep Shoker
14
+ - Ryan Willoughby
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2013-03-25 00:00:00 Z
20
+ dependencies: []
21
+
22
+ description: Ops tool for remote servers
23
+ email: hardeepshoker@gmail.com
24
+ executables:
25
+ - ops
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - .gitignore
32
+ - .rspec
33
+ - Gemfile
34
+ - Gemfile.lock
35
+ - README.md
36
+ - autocomplete
37
+ - bin/ops
38
+ - devops.gemspec
39
+ - lib/host/default.rb
40
+ - lib/host/e_c_2.rb
41
+ - lib/host/list.rb
42
+ - lib/ops.rb
43
+ - lib/ops/common.rb
44
+ - lib/ops/console.rb
45
+ - lib/tasks/ec2.rb
46
+ - lib/tasks/hosts.rb
47
+ - lib/tasks/init.rb
48
+ - lib/tasks/ops.rb
49
+ - lib/tasks/ssh.rb
50
+ - lib/version.rb
51
+ - pkg/.gitfile
52
+ - res/samples/default/config.json
53
+ - res/strings/strings.yml
54
+ - res/strings/tasks/hosts.yml
55
+ - spec/lib/host/default.rb
56
+ - spec/lib/host/list.rb
57
+ - spec/spec_helper.rb
58
+ homepage: https://github.com/hardeep/ops
59
+ licenses: []
60
+
61
+ post_install_message:
62
+ rdoc_options: []
63
+
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ hash: 3
72
+ segments:
73
+ - 0
74
+ version: "0"
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ hash: 3
81
+ segments:
82
+ - 0
83
+ version: "0"
84
+ requirements: []
85
+
86
+ rubyforge_project:
87
+ rubygems_version: 1.8.24
88
+ signing_key:
89
+ specification_version: 3
90
+ summary: Ops tool for remote servers
91
+ test_files: []
92
+