devops 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/.rspec +1 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +33 -0
- data/README.md +54 -0
- data/autocomplete +5 -0
- data/bin/ops +41 -0
- data/devops.gemspec +24 -0
- data/lib/devops.rb +31 -0
- data/lib/host/default.rb +136 -0
- data/lib/host/e_c_2.rb +11 -0
- data/lib/host/list.rb +11 -0
- data/lib/ops/common.rb +55 -0
- data/lib/ops/console.rb +57 -0
- data/lib/tasks/ec2.rb +50 -0
- data/lib/tasks/hosts.rb +61 -0
- data/lib/tasks/init.rb +4 -0
- data/lib/tasks/ops.rb +32 -0
- data/lib/tasks/ssh.rb +22 -0
- data/lib/version.rb +6 -0
- data/pkg/.gitfile +0 -0
- data/res/samples/default/config.json +7 -0
- data/res/strings/strings.yml +14 -0
- data/res/strings/tasks/hosts.yml +15 -0
- data/spec/lib/host/default_spec.rb +32 -0
- data/spec/lib/host/list_spec.rb +36 -0
- data/spec/spec_helper.rb +1 -0
- metadata +73 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -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
|
data/README.md
ADDED
@@ -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 ]
|
data/autocomplete
ADDED
data/bin/ops
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'devops'
|
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
|
+
Dir.chdir Ops::pwd_dir do
|
13
|
+
|
14
|
+
unless Ops::pwd_dir == Dir.pwd
|
15
|
+
puts "Executing from ( #{ Ops::pwd_dir } )".warning
|
16
|
+
end
|
17
|
+
|
18
|
+
Rake.application.init( File.basename( $0 ) )
|
19
|
+
|
20
|
+
begin
|
21
|
+
bootstrap_files.each do | f |
|
22
|
+
File.join Ops::root_dir, f
|
23
|
+
require f
|
24
|
+
end
|
25
|
+
rescue => e
|
26
|
+
puts e
|
27
|
+
end
|
28
|
+
|
29
|
+
bootstrap_files.each do | f |
|
30
|
+
f = File.join Ops::pwd_dir, f
|
31
|
+
require f if File.exists? f
|
32
|
+
end
|
33
|
+
|
34
|
+
begin
|
35
|
+
Rake.application.top_level
|
36
|
+
rescue => e
|
37
|
+
puts "Failed to call task: #{ e.message }"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
exit
|
data/devops.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
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.require_paths = ["lib"]
|
23
|
+
s.executables = [ 'ops' ]
|
24
|
+
end
|
data/lib/devops.rb
ADDED
@@ -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
|
data/lib/host/default.rb
ADDED
@@ -0,0 +1,136 @@
|
|
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, opts = {} )
|
86
|
+
ssh_pem
|
87
|
+
|
88
|
+
options = { :keys => ssh_pem }
|
89
|
+
|
90
|
+
color = Color.random_color
|
91
|
+
|
92
|
+
begin
|
93
|
+
Net::SSH.start( host_name, user, options ) do | s |
|
94
|
+
|
95
|
+
channel = s.open_channel do |ch|
|
96
|
+
channel.request_pty do |ch, success|
|
97
|
+
if success
|
98
|
+
puts "pty successfully obtained"
|
99
|
+
else
|
100
|
+
puts "could not obtain pty"
|
101
|
+
end
|
102
|
+
end if opts.has_key? :pty
|
103
|
+
|
104
|
+
ch.exec( command ) do | ch, success |
|
105
|
+
raise( IOError,
|
106
|
+
"#{ host_name } > could not execute command" ) unless
|
107
|
+
success
|
108
|
+
|
109
|
+
ch.on_data do | c, data |
|
110
|
+
data.split("\n").each do | line |
|
111
|
+
puts "#{ Color.print(
|
112
|
+
self.alias, [ :bold, color ] ) } > #{ line }"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
ch.on_extended_data do |c, type, data|
|
117
|
+
data.split("\n").each do | line |
|
118
|
+
puts "#{ Color.print(
|
119
|
+
self.alias, [ :bold, color ] ) } > #{ line }"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
ch.on_close do
|
124
|
+
puts "#{ Color.print(
|
125
|
+
self.alias, [ :bold, color ] ) } > COMMAND finished"
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
rescue => e
|
132
|
+
puts "Error: #{ self.alias } > #{ e.message }".error
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
data/lib/host/e_c_2.rb
ADDED
data/lib/host/list.rb
ADDED
data/lib/ops/common.rb
ADDED
@@ -0,0 +1,55 @@
|
|
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
|
+
Pathname.new( Dir.pwd ).ascend do | p |
|
10
|
+
return p if File.exists? File.join( p, 'config.json' )
|
11
|
+
end
|
12
|
+
nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.has_bash?
|
16
|
+
`which bash`
|
17
|
+
( $? == 0 ) ? true : false
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.read_config
|
21
|
+
config_file = File.join( self.pwd_dir, "config.json" )
|
22
|
+
|
23
|
+
begin
|
24
|
+
config = JSON.parse File.read( config_file )
|
25
|
+
rescue JSON::ParserError
|
26
|
+
raise IOError, "Error parsing config file: #{ config_file }."
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.read_hosts
|
31
|
+
config = read_config
|
32
|
+
|
33
|
+
hosts = {}
|
34
|
+
|
35
|
+
hosts_files = [
|
36
|
+
File.join( self.pwd_dir, "hosts.json" ),
|
37
|
+
File.join( self.pwd_dir, 'tmp' ,"hosts.json" ), ]
|
38
|
+
|
39
|
+
hosts_files.each do | file |
|
40
|
+
if File.exists? file
|
41
|
+
begin
|
42
|
+
json = JSON.parse File.read( file )
|
43
|
+
json.each do | n, i |
|
44
|
+
hosts[ n ] = Host::Default.new( n, i, config )
|
45
|
+
end
|
46
|
+
rescue JSON::ParserError => e
|
47
|
+
raise( IOError,
|
48
|
+
"Error parsing hosts file: #{ file }. #{ e.message }" )
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
hosts
|
54
|
+
end
|
55
|
+
end
|
data/lib/ops/console.rb
ADDED
@@ -0,0 +1,57 @@
|
|
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.sample
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class Color
|
44
|
+
extend Ops::Console
|
45
|
+
end
|
46
|
+
|
47
|
+
class String
|
48
|
+
extend Ops::Console
|
49
|
+
|
50
|
+
def error
|
51
|
+
replace Color.print( self , [ :bold, :red ] )
|
52
|
+
end
|
53
|
+
|
54
|
+
def warning
|
55
|
+
replace Color.print( self , [ :bold, :yellow ] )
|
56
|
+
end
|
57
|
+
end
|
data/lib/tasks/ec2.rb
ADDED
@@ -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
|
data/lib/tasks/hosts.rb
ADDED
@@ -0,0 +1,61 @@
|
|
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 = nil
|
25
|
+
tags = {}
|
26
|
+
|
27
|
+
unless ENV[ 'identity' ].nil?
|
28
|
+
identity = File.basename( ENV[ 'identity' ] )
|
29
|
+
end
|
30
|
+
|
31
|
+
unless ENV[ 'tags' ].nil?
|
32
|
+
tags = File.basename( ENV[ 'tags' ] )
|
33
|
+
tags = JSON.parse( tags )
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
puts "Adding host: #{ host }"
|
38
|
+
puts " => #{ hostname }"
|
39
|
+
puts " => using #{ identity }" unless identity.nil?
|
40
|
+
|
41
|
+
hosts = {}
|
42
|
+
hosts_file = File.join( pwd, "hosts.json" )
|
43
|
+
|
44
|
+
if File.exists? hosts_file
|
45
|
+
hosts = JSON.parse( File.read( hosts_file ) )
|
46
|
+
end
|
47
|
+
|
48
|
+
exit_failure( "Error: #{ host } is already defined" ) if
|
49
|
+
hosts.has_key? host
|
50
|
+
|
51
|
+
hosts[ host ] = {
|
52
|
+
'HostName' => hostname,
|
53
|
+
'User' => user,
|
54
|
+
'IdentityFile' => identity,
|
55
|
+
"Type" => "type",
|
56
|
+
"Tags" => tags
|
57
|
+
}
|
58
|
+
|
59
|
+
File.open( hosts_file, 'w' ) { | f | f.write( hosts.to_json ) }
|
60
|
+
end
|
61
|
+
end
|
data/lib/tasks/init.rb
ADDED
data/lib/tasks/ops.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
task "default" do
|
2
|
+
puts "run ops -T for a full list of commands"
|
3
|
+
end
|
4
|
+
|
5
|
+
desc I18n.t( "ops.version.desc" )
|
6
|
+
task "version" do
|
7
|
+
puts "Version: #{ Ops.version }"
|
8
|
+
end
|
9
|
+
|
10
|
+
## Project Initialization
|
11
|
+
|
12
|
+
desc I18n.t( "ops.init.desc" )
|
13
|
+
task "init" do
|
14
|
+
|
15
|
+
name = ENV[ 'name' ]
|
16
|
+
fail I18n.t( "ops.init.no_name" ) if name.nil? || name.empty?
|
17
|
+
|
18
|
+
FileUtils.cp_r( File.join( Ops::root_dir, "res", "samples", "default" ),
|
19
|
+
File.join( Ops::pwd_dir, name ) )
|
20
|
+
end
|
21
|
+
|
22
|
+
begin
|
23
|
+
$hosts = Ops::read_hosts
|
24
|
+
rescue
|
25
|
+
$hosts = {}
|
26
|
+
end
|
27
|
+
|
28
|
+
begin
|
29
|
+
$config = Ops::read_config
|
30
|
+
rescue
|
31
|
+
$config = {}
|
32
|
+
end
|
data/lib/tasks/ssh.rb
ADDED
@@ -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
|
data/lib/version.rb
ADDED
data/pkg/.gitfile
ADDED
File without changes
|
@@ -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
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'ops.rb'
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: devops
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.8
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Hardeep Shoker
|
9
|
+
- Ryan Willoughby
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2013-05-06 00:00:00.000000000 Z
|
14
|
+
dependencies: []
|
15
|
+
description: Ops tool for remote servers
|
16
|
+
email: hardeepshoker@gmail.com
|
17
|
+
executables:
|
18
|
+
- ops
|
19
|
+
extensions: []
|
20
|
+
extra_rdoc_files: []
|
21
|
+
files:
|
22
|
+
- .gitignore
|
23
|
+
- .rspec
|
24
|
+
- Gemfile
|
25
|
+
- Gemfile.lock
|
26
|
+
- README.md
|
27
|
+
- autocomplete
|
28
|
+
- bin/ops
|
29
|
+
- devops.gemspec
|
30
|
+
- lib/devops.rb
|
31
|
+
- lib/host/default.rb
|
32
|
+
- lib/host/e_c_2.rb
|
33
|
+
- lib/host/list.rb
|
34
|
+
- lib/ops/common.rb
|
35
|
+
- lib/ops/console.rb
|
36
|
+
- lib/tasks/ec2.rb
|
37
|
+
- lib/tasks/hosts.rb
|
38
|
+
- lib/tasks/init.rb
|
39
|
+
- lib/tasks/ops.rb
|
40
|
+
- lib/tasks/ssh.rb
|
41
|
+
- lib/version.rb
|
42
|
+
- pkg/.gitfile
|
43
|
+
- res/samples/default/config.json
|
44
|
+
- res/strings/strings.yml
|
45
|
+
- res/strings/tasks/hosts.yml
|
46
|
+
- spec/lib/host/default_spec.rb
|
47
|
+
- spec/lib/host/list_spec.rb
|
48
|
+
- spec/spec_helper.rb
|
49
|
+
homepage: https://github.com/hardeep/ops
|
50
|
+
licenses: []
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ! '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ! '>='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
requirements: []
|
68
|
+
rubyforge_project:
|
69
|
+
rubygems_version: 1.8.23
|
70
|
+
signing_key:
|
71
|
+
specification_version: 3
|
72
|
+
summary: Ops tool for remote servers
|
73
|
+
test_files: []
|