apollo-tools 0.1.0
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.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/README.md +49 -0
- data/Rakefile +6 -0
- data/Vagrantfile +15 -0
- data/apollo.gemspec +26 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/apollo.rb +152 -0
- data/lib/apollo/version.rb +3 -0
- metadata +124 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cf47e43e21817b0f44a20841de0ed23d30513bf7
|
4
|
+
data.tar.gz: f2b4cfb6abb1b9923787d5a1d8cd3a0a30bf1ed2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cfdb188c89f2f0b7de51419707bed1cbe85761f0ca29a1aa7a10ef92003652ddf20cdcf318ddb4ff1000d8821ed9ad6ae83e769ac5b8f56a3d9b1d5512034f44
|
7
|
+
data.tar.gz: e23b5cd5f8bfbb740cad53d9f662e7fc1658d9d79e0ac74047d581550948003c52b44c9183f815b4d6db7fd33760423c3c772bc21c28e1e15978c4c7e9bbebd6
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Apollo
|
2
|
+
|
3
|
+
Apollo is a gem for interacting with remote hosts. Its based around the idea of an inventory defining the available
|
4
|
+
resources. So for this inventory:
|
5
|
+
|
6
|
+
```yaml
|
7
|
+
---
|
8
|
+
hosts:
|
9
|
+
vagrant:
|
10
|
+
ip: 192.168.100.4
|
11
|
+
user: vagrant
|
12
|
+
```
|
13
|
+
|
14
|
+
You would be able to run an ssh command by running `cluster.run(:vagrant, '/bin/true')`. This is very much a work in
|
15
|
+
progress as I figure out what needs to be in here.
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
Add this line to your application's Gemfile:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
gem 'apollo'
|
23
|
+
```
|
24
|
+
|
25
|
+
And then execute:
|
26
|
+
|
27
|
+
$ bundle
|
28
|
+
|
29
|
+
Or install it yourself as:
|
30
|
+
|
31
|
+
$ gem install apollo
|
32
|
+
|
33
|
+
## Usage
|
34
|
+
|
35
|
+
TODO: Write usage instructions here
|
36
|
+
|
37
|
+
## Development
|
38
|
+
|
39
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
|
40
|
+
|
41
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
42
|
+
|
43
|
+
## Contributing
|
44
|
+
|
45
|
+
1. Fork it ( https://github.com/signalvine/apollo/fork )
|
46
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
47
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
48
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
49
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/Vagrantfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
Vagrant.configure(2) do |config|
|
2
|
+
# Using a 32 bit box so that everyone can run the tests
|
3
|
+
config.vm.box = "hashicorp/precise32"
|
4
|
+
|
5
|
+
# Force vagrant to use virtualbox as there is only a virtualbox image for hashicorp/precise32
|
6
|
+
config.vm.provider "virtualbox" do |v|
|
7
|
+
v.memory = 1024
|
8
|
+
v.cpus = 1
|
9
|
+
end
|
10
|
+
|
11
|
+
# Disable the random key generation so that we can connect in the test suite
|
12
|
+
config.ssh.insert_key = false
|
13
|
+
|
14
|
+
config.vm.network "private_network", ip: "192.168.100.4"
|
15
|
+
end
|
data/apollo.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'apollo/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "apollo-tools"
|
8
|
+
spec.version = Apollo::VERSION
|
9
|
+
spec.authors = ['Brendan Tobolaski']
|
10
|
+
spec.email = ['brendan@signalvine.com']
|
11
|
+
|
12
|
+
spec.summary = 'A gem for interacting with remote hosts in various ways'
|
13
|
+
spec.homepage = 'https://github.com/signalvine/apollo'
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
16
|
+
spec.bindir = "exe"
|
17
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_development_dependency "bundler", "~> 1.8"
|
21
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
22
|
+
spec.add_development_dependency 'minitest', '~> 5.5.1'
|
23
|
+
|
24
|
+
spec.add_dependency 'rabbitmq_manager', '~> 0.3.0'
|
25
|
+
spec.add_dependency 'net-ssh', '~> 2.9.2'
|
26
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "apollo"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/lib/apollo.rb
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
require "apollo/version"
|
2
|
+
require "yaml"
|
3
|
+
require 'net/ssh'
|
4
|
+
|
5
|
+
module Apollo
|
6
|
+
class Cluster
|
7
|
+
|
8
|
+
# Creates a new cluster
|
9
|
+
#
|
10
|
+
# @param opts [Hash].
|
11
|
+
# @option opts [String] :filename The complete path to the inventory file ("#{Dir.pwd}/inventory.yml")
|
12
|
+
# @return [Cluster]
|
13
|
+
def initialize(opts = {})
|
14
|
+
filename = opts.fetch(:filename, "#{Dir.pwd}/inventory.yml")
|
15
|
+
inventory = YAML.load_file(filename)
|
16
|
+
|
17
|
+
begin
|
18
|
+
hosts = inventory.fetch('hosts')
|
19
|
+
raise 'host list empty' if hosts.nil?
|
20
|
+
@hosts = process_host_list hosts
|
21
|
+
rescue NoMethodError
|
22
|
+
raise 'host key not defined in inventory file'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Gets a host
|
27
|
+
#
|
28
|
+
# @param host [Symbol] The host to get
|
29
|
+
# @return [Hash, nil] Either the host or nil if the host doesn't exist
|
30
|
+
def get_host(host)
|
31
|
+
@hosts[host]
|
32
|
+
end
|
33
|
+
|
34
|
+
# Connects to the rabbitmq admin port on the specified host and waits until the specified queue has no messags
|
35
|
+
#
|
36
|
+
# @param host [Symbol] The host that the queue is on
|
37
|
+
# @param queue [String] The name of the queue to wait on
|
38
|
+
#
|
39
|
+
# @param opts [Hash]
|
40
|
+
# @option opts [Float] :sleep_duration The number of seconds (1) to wait between checking the queue
|
41
|
+
# @option opts [Float, nil] :timeout The number of seconds (nil) to wait before throwing an exception
|
42
|
+
# @option opts [String] :vhost The vhost ('/') that the queue is in
|
43
|
+
#
|
44
|
+
# @raise [RuntimeError] when the host is not in the inventory or the timeout has been exceeded
|
45
|
+
#
|
46
|
+
# @return [void] Only returns when the queue is empty
|
47
|
+
def wait_for_queue_drain(host, queue, opts = {})
|
48
|
+
raise "host #{host} not configured in the inventory" if @hosts[host].nil?
|
49
|
+
|
50
|
+
sleep_duration = opts.fetch(:sleep_duration, 1)
|
51
|
+
timeout = opts.fetch(:timeout, nil)
|
52
|
+
|
53
|
+
start = Time.now
|
54
|
+
while check_queue_length(host, queue, opts) != 0
|
55
|
+
if not timeout.nil? and (Time.now.to_f - start.to_f) > timeout
|
56
|
+
raise 'wait_for_queue_drain exceeded timeout'
|
57
|
+
end
|
58
|
+
sleep sleep_duration
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Connects to the rabbitmq admin port on the specified host and gets the number of messages waiting in the specified
|
63
|
+
# queue
|
64
|
+
# @param host [Symbol] The host of the rabbitmq server
|
65
|
+
# @param queue [String] The queue to check
|
66
|
+
#
|
67
|
+
# @param opts [Hash]
|
68
|
+
# @option opts [String] :vhost The vhost ('/') that the queue is in
|
69
|
+
#
|
70
|
+
# @return [Integer] The number of messages waiting in the specified queue
|
71
|
+
def check_queue_length(host, queue, opts={})
|
72
|
+
host = @hosts[host]
|
73
|
+
vhost = opts.fetch(:vhost, '/')
|
74
|
+
username = CGI.escape host.fetch('rmq_username', 'guest')
|
75
|
+
password = CGI.escape host.fetch('rmq_password', 'guest')
|
76
|
+
port = host.fetch('rmq_port', 15672)
|
77
|
+
|
78
|
+
manager = RabbitMQManager.new "http://#{username}:#{password}@#{address host}:#{port}"
|
79
|
+
manager.queue(vhost, queue)['messages']
|
80
|
+
end
|
81
|
+
|
82
|
+
# Runs the specified command on the specified host
|
83
|
+
#
|
84
|
+
# @param on [Symbol] The host to run the command on
|
85
|
+
# @param command [String] The command ('/bin/true') to run
|
86
|
+
#
|
87
|
+
# @param opts [Hash] Additional options for the ssh connection. For the complete list, see http://net-ssh.github.io/net-ssh/classes/Net/SSH.html#method-c-start
|
88
|
+
# @option opts [bool] :forward_agent (true) whether to forward the current user's agent
|
89
|
+
# @option opts [bool] :allow_unsuccessful (false) whether a non-zero exit call raises an exception or not
|
90
|
+
#
|
91
|
+
# @return [String]
|
92
|
+
# @raises [RuntimeError] when the host
|
93
|
+
def run(on, command = '/bin/true', opts= {})
|
94
|
+
host = @hosts[on]
|
95
|
+
raise "#{on} doesn't exist in the inventory" if host.nil?
|
96
|
+
opts[:forward_agent] = opts.fetch(:forward_agent, true)
|
97
|
+
|
98
|
+
output = ""
|
99
|
+
rc = 0
|
100
|
+
Net::SSH.start(address(host), host['user'], opts) do |ssh|
|
101
|
+
chan = ssh.open_channel do |ch|
|
102
|
+
ch.exec command do |ch, success|
|
103
|
+
raise "#{command} didn't complete successfully" if not success and not opts.fetch(:allow_unsuccessful, false)
|
104
|
+
end
|
105
|
+
|
106
|
+
ch.on_data do |c, data|
|
107
|
+
output += data
|
108
|
+
end
|
109
|
+
|
110
|
+
ch.on_extended_data do |c, type, data|
|
111
|
+
output += data
|
112
|
+
end
|
113
|
+
|
114
|
+
ch.on_request "exit-status" do |ch, data|
|
115
|
+
rc = data.read_long
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
chan.wait
|
120
|
+
raise "#{command} didn't complete successfully" unless rc == 0
|
121
|
+
return output
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
|
127
|
+
# Parses the inventory that gets read off of disk. It converts the keys to symbols.
|
128
|
+
# @param host_list [Hash]
|
129
|
+
# @return [Hash]
|
130
|
+
# @raise [RuntimeError] when some required fields aren't specified
|
131
|
+
def process_host_list(host_list)
|
132
|
+
list = {}
|
133
|
+
host_list.each do |key, value|
|
134
|
+
raise "host #{key} is not addressable" if value['ip'].nil? and value['hostname'].nil?
|
135
|
+
raise "host #{key} has an invalid rmq_port #{value['rmq_port']}" unless value['rmq_port'].nil? or value['rmq_port'].is_a? Numeric
|
136
|
+
list[key.to_sym] = value
|
137
|
+
end
|
138
|
+
list
|
139
|
+
end
|
140
|
+
|
141
|
+
# Gets the specified host's proper addressing
|
142
|
+
# @param host [Hash] The host that we want to address
|
143
|
+
# @return [String] The address to connect to the host on
|
144
|
+
def address(host)
|
145
|
+
unless host['ip'].nil?
|
146
|
+
host['ip']
|
147
|
+
else
|
148
|
+
host['hostname']
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
metadata
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: apollo-tools
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Brendan Tobolaski
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-03-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.8'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.8'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 5.5.1
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 5.5.1
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rabbitmq_manager
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.3.0
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.3.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: net-ssh
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 2.9.2
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 2.9.2
|
83
|
+
description:
|
84
|
+
email:
|
85
|
+
- brendan@signalvine.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- ".gitignore"
|
91
|
+
- ".travis.yml"
|
92
|
+
- Gemfile
|
93
|
+
- README.md
|
94
|
+
- Rakefile
|
95
|
+
- Vagrantfile
|
96
|
+
- apollo.gemspec
|
97
|
+
- bin/console
|
98
|
+
- bin/setup
|
99
|
+
- lib/apollo.rb
|
100
|
+
- lib/apollo/version.rb
|
101
|
+
homepage: https://github.com/signalvine/apollo
|
102
|
+
licenses: []
|
103
|
+
metadata: {}
|
104
|
+
post_install_message:
|
105
|
+
rdoc_options: []
|
106
|
+
require_paths:
|
107
|
+
- lib
|
108
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
requirements: []
|
119
|
+
rubyforge_project:
|
120
|
+
rubygems_version: 2.4.5
|
121
|
+
signing_key:
|
122
|
+
specification_version: 4
|
123
|
+
summary: A gem for interacting with remote hosts in various ways
|
124
|
+
test_files: []
|