Devops 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+