stackadmin 0.1.0 → 0.1.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.
- checksums.yaml +4 -4
- data/.gitignore +5 -0
- data/CHANGELOG +14 -0
- data/Gemfile +8 -9
- data/Gemfile.lock +11 -98
- data/README.md +71 -0
- data/Rakefile +35 -9
- data/lib/stackadmin/audit.rb +41 -1
- data/lib/stackadmin/base.rb +48 -0
- data/lib/stackadmin/exceptions.rb +13 -0
- data/lib/stackadmin/net-ssh.rb +19 -2
- data/lib/stackadmin/patch.rb +91 -18
- data/lib/stackadmin/version.rb +2 -1
- data/lib/stackadmin/yaml.rb +5 -0
- data/stackadmin.gemspec +26 -0
- data/test/helpers/vagrant.rb +31 -0
- metadata +10 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1e2ddf654357669ce50978cf4616b55f25244cbf
|
|
4
|
+
data.tar.gz: 483e8a3ba68b7995367b40af91791b1aca24e5e0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bb485c2bb8ea145d9247945efc5d2a7f8d520e7f9b83c3e810a3508e1fc86a24c10541bcf16cb919c11544a68a216df02ce4fd39b3fc9d01819e6d501726335b
|
|
7
|
+
data.tar.gz: 33fa85e6175d55f656aacfd9d4eb685bba74eb798663972fef9684f337831f8591c6d211b87afcd16eec659786ac83621eaf57d759d7c690380cc71894ae1480
|
data/.gitignore
CHANGED
data/CHANGELOG
ADDED
data/Gemfile
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
source 'https://rubygems.org'
|
|
2
|
+
ruby '2.0.0'
|
|
2
3
|
|
|
3
|
-
gem 'net-ssh'
|
|
4
|
-
gem 'json'
|
|
4
|
+
gem 'net-ssh', '~> 2.9.2'
|
|
5
|
+
gem 'json', '~> 1.8.2'
|
|
5
6
|
|
|
6
7
|
group :development do
|
|
7
|
-
gem 'minitest'
|
|
8
|
-
gem 'minitest-reporters'
|
|
9
|
-
gem '
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
group :plugins do
|
|
13
|
-
gem 'vagrant-s3auth'
|
|
8
|
+
gem 'minitest', '~> 4.3.2'
|
|
9
|
+
gem 'minitest-reporters', '~> 0.14.24'
|
|
10
|
+
gem 'rake', '~> 10.4.2'
|
|
11
|
+
gem 'vagrant-wrapper', '~> 2.0.2'
|
|
12
|
+
gem 'yard', '~> 0.8.7.6'
|
|
14
13
|
end
|
data/Gemfile.lock
CHANGED
|
@@ -1,119 +1,32 @@
|
|
|
1
|
-
GIT
|
|
2
|
-
remote: https://github.com/mitchellh/vagrant.git
|
|
3
|
-
revision: b2c722ef54e57c9de5cdbe712b27e52faacec5da
|
|
4
|
-
specs:
|
|
5
|
-
vagrant (1.7.2)
|
|
6
|
-
bundler (>= 1.5.2, < 1.8.0)
|
|
7
|
-
childprocess (~> 0.5.0)
|
|
8
|
-
erubis (~> 2.7.0)
|
|
9
|
-
hashicorp-checkpoint (~> 0.1.1)
|
|
10
|
-
i18n (~> 0.6.0)
|
|
11
|
-
listen (~> 2.8.0)
|
|
12
|
-
log4r (~> 1.1.9, < 1.1.11)
|
|
13
|
-
net-scp (~> 1.1.0)
|
|
14
|
-
net-sftp (~> 2.1)
|
|
15
|
-
net-ssh (>= 2.6.6, < 2.10.0)
|
|
16
|
-
nokogiri (= 1.6.3.1)
|
|
17
|
-
rb-kqueue (~> 0.2.0)
|
|
18
|
-
rest-client (>= 1.6.0, < 2.0)
|
|
19
|
-
wdm (~> 0.1.0)
|
|
20
|
-
winrm (~> 1.3.0)
|
|
21
|
-
winrm-fs (~> 0.1.0)
|
|
22
|
-
|
|
23
1
|
GEM
|
|
24
2
|
remote: https://rubygems.org/
|
|
25
3
|
specs:
|
|
26
4
|
ansi (1.5.0)
|
|
27
|
-
aws-sdk (1.59.1)
|
|
28
|
-
aws-sdk-v1 (= 1.59.1)
|
|
29
|
-
aws-sdk-v1 (1.59.1)
|
|
30
|
-
json (~> 1.4)
|
|
31
|
-
nokogiri (>= 1.4.4)
|
|
32
5
|
builder (3.2.2)
|
|
33
|
-
celluloid (0.16.0)
|
|
34
|
-
timers (~> 4.0.0)
|
|
35
|
-
childprocess (0.5.5)
|
|
36
|
-
ffi (~> 1.0, >= 1.0.11)
|
|
37
|
-
erubis (2.7.0)
|
|
38
|
-
ffi (1.9.6)
|
|
39
|
-
gssapi (1.2.0)
|
|
40
|
-
ffi (>= 1.0.1)
|
|
41
|
-
gyoku (1.2.2)
|
|
42
|
-
builder (>= 2.1.2)
|
|
43
|
-
hashicorp-checkpoint (0.1.4)
|
|
44
6
|
hashie (3.4.0)
|
|
45
|
-
hitimes (1.2.2)
|
|
46
|
-
httpclient (2.6.0.1)
|
|
47
|
-
i18n (0.6.11)
|
|
48
7
|
json (1.8.2)
|
|
49
|
-
|
|
50
|
-
celluloid (>= 0.15.2)
|
|
51
|
-
rb-fsevent (>= 0.9.3)
|
|
52
|
-
rb-inotify (>= 0.9)
|
|
53
|
-
little-plugger (1.1.3)
|
|
54
|
-
log4r (1.1.10)
|
|
55
|
-
logging (1.8.2)
|
|
56
|
-
little-plugger (>= 1.1.3)
|
|
57
|
-
multi_json (>= 1.8.4)
|
|
58
|
-
mime-types (2.4.3)
|
|
59
|
-
mini_portile (0.6.0)
|
|
60
|
-
minitest (4.3.2)
|
|
8
|
+
minitest (4.3.3)
|
|
61
9
|
minitest-reporters (0.14.24)
|
|
62
10
|
ansi
|
|
63
11
|
builder
|
|
64
12
|
minitest (>= 2.12, < 5.0)
|
|
65
13
|
powerbar
|
|
66
|
-
multi_json (1.10.1)
|
|
67
|
-
net-scp (1.1.2)
|
|
68
|
-
net-ssh (>= 2.6.5)
|
|
69
|
-
net-sftp (2.1.2)
|
|
70
|
-
net-ssh (>= 2.6.5)
|
|
71
14
|
net-ssh (2.9.2)
|
|
72
|
-
netrc (0.10.2)
|
|
73
|
-
nokogiri (1.6.3.1)
|
|
74
|
-
mini_portile (= 0.6.0)
|
|
75
|
-
nori (2.4.0)
|
|
76
15
|
powerbar (1.0.12)
|
|
77
16
|
ansi (~> 1.5.0)
|
|
78
17
|
hashie (>= 1.1.0)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
rb-kqueue (0.2.3)
|
|
83
|
-
ffi (>= 0.5.0)
|
|
84
|
-
rest-client (1.7.2)
|
|
85
|
-
mime-types (>= 1.16, < 3.0)
|
|
86
|
-
netrc (~> 0.7)
|
|
87
|
-
rubyntlm (0.4.0)
|
|
88
|
-
rubyzip (1.1.7)
|
|
89
|
-
timers (4.0.1)
|
|
90
|
-
hitimes
|
|
91
|
-
uuidtools (2.1.5)
|
|
92
|
-
vagrant-s3auth (1.0.2)
|
|
93
|
-
aws-sdk (~> 1.59.1)
|
|
94
|
-
wdm (0.1.0)
|
|
95
|
-
winrm (1.3.0)
|
|
96
|
-
builder (>= 2.1.2)
|
|
97
|
-
gssapi (~> 1.2)
|
|
98
|
-
gyoku (~> 1.0)
|
|
99
|
-
httpclient (~> 2.2, >= 2.2.0.2)
|
|
100
|
-
logging (~> 1.6, >= 1.6.1)
|
|
101
|
-
nori (~> 2.0)
|
|
102
|
-
rubyntlm (~> 0.4.0)
|
|
103
|
-
uuidtools (~> 2.1.2)
|
|
104
|
-
winrm-fs (0.1.0)
|
|
105
|
-
erubis (~> 2.7)
|
|
106
|
-
logging (~> 1.6, >= 1.6.1)
|
|
107
|
-
rubyzip (~> 1.1)
|
|
108
|
-
winrm (~> 1.3.0)
|
|
18
|
+
rake (10.4.2)
|
|
19
|
+
vagrant-wrapper (2.0.2)
|
|
20
|
+
yard (0.8.7.6)
|
|
109
21
|
|
|
110
22
|
PLATFORMS
|
|
111
23
|
ruby
|
|
112
24
|
|
|
113
25
|
DEPENDENCIES
|
|
114
|
-
json
|
|
115
|
-
minitest
|
|
116
|
-
minitest-reporters
|
|
117
|
-
net-ssh
|
|
118
|
-
|
|
119
|
-
vagrant-
|
|
26
|
+
json (~> 1.8.2)
|
|
27
|
+
minitest (~> 4.3.2)
|
|
28
|
+
minitest-reporters (~> 0.14.24)
|
|
29
|
+
net-ssh (~> 2.9.2)
|
|
30
|
+
rake (~> 10.4.2)
|
|
31
|
+
vagrant-wrapper (~> 2.0.2)
|
|
32
|
+
yard (~> 0.8.7.6)
|
data/README.md
CHANGED
|
@@ -1,2 +1,73 @@
|
|
|
1
1
|
# stackadmin
|
|
2
2
|
Gem to facilitate administration of Stackato deployments
|
|
3
|
+
|
|
4
|
+
## Install
|
|
5
|
+
```
|
|
6
|
+
gem install stackadmin
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Then, `require 'stackadmin'` in your application.
|
|
10
|
+
|
|
11
|
+
## Requirements
|
|
12
|
+
- Ruby 2.0.0
|
|
13
|
+
- Target Stackato nodes must be set up for [Passwordless SSH access](http://www.linuxproblem.org/art_9.html).
|
|
14
|
+
|
|
15
|
+
## Testing
|
|
16
|
+
### The happy path
|
|
17
|
+
```
|
|
18
|
+
bundle install
|
|
19
|
+
bundle exec rake test
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
That should get everything set up for you. It will spin up a vagrant VM, run all tests, and then destroy the vagrant VM.
|
|
23
|
+
|
|
24
|
+
Problems? Read on.
|
|
25
|
+
|
|
26
|
+
### Setting up the test env
|
|
27
|
+
Install your dependencies:
|
|
28
|
+
```
|
|
29
|
+
bundle install
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The test environment depends on [Vagrant](http://www.vagrantup.com/) (>= 1.5.1) and [vagrant-s3auth](https://github.com/WhoopInc/vagrant-s3auth).
|
|
33
|
+
|
|
34
|
+
The Vagrant VM depends on a custom basebox stored in our private S3 bucket.
|
|
35
|
+
|
|
36
|
+
You should have access credentials stored in the standard environment variables (i.e. `ENV['AWS_ACCESS_KEY_ID']` and `ENV['AWS_SECRET_ACCESS_KEY']`) in order to have Vagrant download the basebox automatically.
|
|
37
|
+
|
|
38
|
+
To verify that these requirements are met:
|
|
39
|
+
```
|
|
40
|
+
bundle exec rake test:setup_vagrant
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
To spin up the Vagrant box:
|
|
44
|
+
```
|
|
45
|
+
bundle exec rake test:start_env
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
To fulfill the passwordless SSH requirement to the `stackato` user in the test env:
|
|
49
|
+
```
|
|
50
|
+
bundle exec rake test:setup_env
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
That last command assumes that your personal public key is located in `~/.ssh/keys/id_rsa.pub`. If your key is located elsewhere, you can specify the location as follows:
|
|
54
|
+
```
|
|
55
|
+
bundle exec rake test:setup_env['~/.ssh/certs/me.pub']
|
|
56
|
+
```
|
|
57
|
+
where `<pub_key_location>` is the path to your public key.
|
|
58
|
+
|
|
59
|
+
You can also provide the public key location when using `rake test`:
|
|
60
|
+
```
|
|
61
|
+
bundle exec rake test['~/.ssh/certs/me.pub']
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Run tests
|
|
65
|
+
Currently, all tests are in a single suite:
|
|
66
|
+
```
|
|
67
|
+
bundle exec rake test:run
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Destroy the test env
|
|
71
|
+
```
|
|
72
|
+
bundle exec rake test:destroy_env
|
|
73
|
+
```
|
data/Rakefile
CHANGED
|
@@ -1,24 +1,42 @@
|
|
|
1
1
|
# Encoding: utf-8
|
|
2
2
|
require 'rake/testtask'
|
|
3
|
+
require_relative 'test/helpers/vagrant'
|
|
3
4
|
|
|
4
5
|
task :default => 'test'
|
|
5
6
|
|
|
6
7
|
namespace :test do
|
|
8
|
+
desc 'Setup vagrant'
|
|
9
|
+
task :setup_vagrant do
|
|
10
|
+
vagrant = VagrantWrapper.require_or_help_install(">= #{MIN_VAGRANT}")
|
|
11
|
+
puts "Vagrant v#{vagrant.vagrant_version} found!"
|
|
12
|
+
raise 'Unable to install "vagrant-s3auth" plugin!' unless vagrant.install_plugin('vagrant-s3auth')
|
|
13
|
+
|
|
14
|
+
missing = missing_aws_creds
|
|
15
|
+
if missing.empty?
|
|
16
|
+
puts 'S3 credentials found!'
|
|
17
|
+
else
|
|
18
|
+
raise "The following environment variables must be defined: #{missing.join(', ')}"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
7
22
|
desc 'Spin up test env'
|
|
8
23
|
task :start_env do
|
|
9
|
-
Dir.chdir('test') { system(
|
|
24
|
+
Dir.chdir('test') { system("#{VAGRANT} up") }
|
|
10
25
|
end
|
|
11
26
|
|
|
12
27
|
desc 'Setup test env'
|
|
13
|
-
task :
|
|
14
|
-
pub_key_location = args[:pub_key_location] ||
|
|
28
|
+
task :setup_env, :pub_key_location do |_, args|
|
|
29
|
+
pub_key_location = args[:pub_key_location] || '~/.ssh/keys/id_rsa.pub'
|
|
30
|
+
pub_key_location = File.expand_path(pub_key_location)
|
|
15
31
|
pub_key = File.open(pub_key_location, 'r').read
|
|
16
|
-
Dir.chdir('test')
|
|
32
|
+
Dir.chdir('test') do
|
|
33
|
+
system("#{VAGRANT} ssh --command 'echo \"#{pub_key}\" | sudo tee -a /home/stackato/.ssh/authorized_keys' > /dev/null")
|
|
34
|
+
end
|
|
17
35
|
end
|
|
18
36
|
|
|
19
37
|
desc 'Destroy test env'
|
|
20
38
|
task :destroy_env do
|
|
21
|
-
Dir.chdir('test') { system(
|
|
39
|
+
Dir.chdir('test') { system("#{VAGRANT} destroy --force") }
|
|
22
40
|
end
|
|
23
41
|
end
|
|
24
42
|
|
|
@@ -27,11 +45,19 @@ Rake::TestTask.new('test:run') do |t|
|
|
|
27
45
|
end
|
|
28
46
|
|
|
29
47
|
desc 'Run all test tasks'
|
|
30
|
-
task :test do
|
|
31
|
-
%w( start_env
|
|
48
|
+
task :test, :pub_key_location do |_, args|
|
|
49
|
+
%w( setup_vagrant start_env setup_env run destroy_env ).each do |j|
|
|
32
50
|
job = "test:#{j}"
|
|
33
|
-
|
|
34
|
-
|
|
51
|
+
print "\e[35mRunning #{job}"
|
|
52
|
+
|
|
53
|
+
if j == 'setup_env' && !args[:pub_key_location].nil?
|
|
54
|
+
puts "['#{args[:pub_key_location]}']...\e[0m"
|
|
55
|
+
Rake::Task[job].invoke(args[:pub_key_location])
|
|
56
|
+
else
|
|
57
|
+
puts "...\e[0m"
|
|
58
|
+
Rake::Task[job].invoke
|
|
59
|
+
end
|
|
60
|
+
|
|
35
61
|
puts
|
|
36
62
|
end
|
|
37
63
|
end
|
data/lib/stackadmin/audit.rb
CHANGED
|
@@ -7,6 +7,11 @@ require_relative 'net-ssh'
|
|
|
7
7
|
class Stackadmin
|
|
8
8
|
private
|
|
9
9
|
|
|
10
|
+
# SSHes into the target node and populates the instance variables with the
|
|
11
|
+
# results of the audit
|
|
12
|
+
#
|
|
13
|
+
# @param target [String] the hostname, FQDN, or IP address of the target node
|
|
14
|
+
# @raise [Net::SSH::Exception] if unable to SSH to target
|
|
10
15
|
def audit(target = @id)
|
|
11
16
|
@id = target
|
|
12
17
|
log "Auditing #{@id}"
|
|
@@ -25,6 +30,10 @@ class Stackadmin
|
|
|
25
30
|
end
|
|
26
31
|
end
|
|
27
32
|
|
|
33
|
+
# Retrieves the current version designation of the target Stackato node
|
|
34
|
+
#
|
|
35
|
+
# @param ssh [Net::SSH::Connection::Session] an ssh session to the target node
|
|
36
|
+
# @return [String] the target node's installed Stackato version
|
|
28
37
|
def parse_version(ssh)
|
|
29
38
|
cmd = ssh.exec_sc!('kato info | grep Version')
|
|
30
39
|
ver = cmd[:stdout].split.last
|
|
@@ -36,6 +45,10 @@ class Stackadmin
|
|
|
36
45
|
ver
|
|
37
46
|
end
|
|
38
47
|
|
|
48
|
+
# Retrieves the current license assigned to the target Stackato node
|
|
49
|
+
#
|
|
50
|
+
# @param (see #parse_version)
|
|
51
|
+
# @return [Hash] the details of the installed license
|
|
39
52
|
def parse_license(ssh)
|
|
40
53
|
license = Hash.new
|
|
41
54
|
|
|
@@ -47,6 +60,10 @@ class Stackadmin
|
|
|
47
60
|
license
|
|
48
61
|
end
|
|
49
62
|
|
|
63
|
+
# Maps the target cluster's nodes into a hash
|
|
64
|
+
#
|
|
65
|
+
# @param (see #parse_version)
|
|
66
|
+
# @return [Hash] the details of each node in the cluster
|
|
50
67
|
def map_nodes(ssh)
|
|
51
68
|
cluster = Hash.new
|
|
52
69
|
|
|
@@ -58,7 +75,11 @@ class Stackadmin
|
|
|
58
75
|
cluster
|
|
59
76
|
end
|
|
60
77
|
|
|
61
|
-
#
|
|
78
|
+
# Audits the installation status of each patch listed in the manifest
|
|
79
|
+
#
|
|
80
|
+
# @param (see #parse_version)
|
|
81
|
+
# @todo It never seems to hit "Marked #{patch} as patched for #{k}"
|
|
82
|
+
# @todo I don't see how this catches a 'no patches available' state
|
|
62
83
|
def parse_patch_status(ssh)
|
|
63
84
|
# Initialize 'patched' hash for each cluster node
|
|
64
85
|
@nodes.each_value do |node|
|
|
@@ -85,6 +106,15 @@ class Stackadmin
|
|
|
85
106
|
end
|
|
86
107
|
end
|
|
87
108
|
|
|
109
|
+
# Parses `kato patch status` into a hash
|
|
110
|
+
#
|
|
111
|
+
# @param status [String] the output of `kato patch status`
|
|
112
|
+
# @return [Hash] list of uninstalled patches with list of nodes
|
|
113
|
+
# @example Returned Hash
|
|
114
|
+
# {
|
|
115
|
+
# 'patch1' => ['127.0.0.1', '171.234.56.78'],
|
|
116
|
+
# 'patch2' => ['127.0.0.1']
|
|
117
|
+
# }
|
|
88
118
|
def hashify_patch_status(status)
|
|
89
119
|
patches = Hash.new
|
|
90
120
|
|
|
@@ -108,6 +138,11 @@ class Stackadmin
|
|
|
108
138
|
patches
|
|
109
139
|
end
|
|
110
140
|
|
|
141
|
+
# Strips the header info from the output of `kato patch status`
|
|
142
|
+
#
|
|
143
|
+
# @param (see #hashify_patch_status)
|
|
144
|
+
# @return [String] status sans header info
|
|
145
|
+
# @raise [InvalidPatchStatus] if the given status is invalid
|
|
111
146
|
def strip_header(status)
|
|
112
147
|
log 'Stripping header'
|
|
113
148
|
|
|
@@ -127,6 +162,11 @@ class Stackadmin
|
|
|
127
162
|
s.join("\n")
|
|
128
163
|
end
|
|
129
164
|
|
|
165
|
+
# Strips the footer info from the output of `kato patch status`
|
|
166
|
+
#
|
|
167
|
+
# @param (see #hashify_patch_status)
|
|
168
|
+
# @return [String] status sans footer info
|
|
169
|
+
# @raise (see #strip_header)
|
|
130
170
|
def strip_footer(status)
|
|
131
171
|
log 'Stripping footer'
|
|
132
172
|
|
data/lib/stackadmin/base.rb
CHANGED
|
@@ -3,20 +3,62 @@
|
|
|
3
3
|
require 'json'
|
|
4
4
|
require 'open-uri'
|
|
5
5
|
|
|
6
|
+
# Core class of the Stackadmin gem
|
|
7
|
+
# @!attribute [r] id
|
|
8
|
+
# @return [String] the hostname/FQDN/IP address of the core Stackato node
|
|
9
|
+
# @!attribute [r] version
|
|
10
|
+
# @return [String] the version of Stackato installed on the node
|
|
11
|
+
# @!attribute [r] license
|
|
12
|
+
# @return [Hash] the details of the Stackato license installed on the node
|
|
13
|
+
# @example Returned Hash
|
|
14
|
+
# {
|
|
15
|
+
# 'organization' => 'org',
|
|
16
|
+
# 'serial' => 'S0F1E2D3C4D5',
|
|
17
|
+
# 'type' => 'paid',
|
|
18
|
+
# 'memory_limit' => 10,
|
|
19
|
+
# 'expiration' => '2014-12-31'
|
|
20
|
+
# }
|
|
21
|
+
# @!attribute [r] nodes
|
|
22
|
+
# @return [Hash] the details of each node in the Stackato cluster
|
|
23
|
+
# @example Returned Hash
|
|
24
|
+
# {
|
|
25
|
+
# '127.0.0.1' => {
|
|
26
|
+
# roles: ['base', 'core']
|
|
27
|
+
# },
|
|
28
|
+
# '171.234.56.78' => {
|
|
29
|
+
# roles: ['base', 'dea']
|
|
30
|
+
# }
|
|
31
|
+
# }
|
|
32
|
+
# @!attribute [r] fresh
|
|
33
|
+
# @return [Boolean] whether the available node data is from a fresh audit
|
|
6
34
|
class Stackadmin
|
|
7
35
|
attr_reader :id, :version, :license, :nodes, :fresh
|
|
8
36
|
|
|
37
|
+
# Initializes a new Stackadmin object
|
|
38
|
+
# @param target [String] hostname/FQDN/IP address of the target node
|
|
39
|
+
# @param port [Fixnum] port used to SSH into the target node
|
|
40
|
+
# @param debug [Boolean] toggles display of debug information
|
|
41
|
+
# @param yml_file [String, nil] pre-populated YAML file to pull node information from
|
|
42
|
+
# @return [Stackadmin] new, populated object
|
|
43
|
+
# @todo All paramaters, -target, should be in an opt[] hash
|
|
9
44
|
def initialize(target, port = 22, debug = false, yml_file = nil)
|
|
10
45
|
@port = port
|
|
11
46
|
@debug = debug
|
|
12
47
|
yml_file ? parse_yaml(target, yml_file) : audit(target)
|
|
13
48
|
end
|
|
14
49
|
|
|
50
|
+
# Refresh object data with a fresh audit
|
|
51
|
+
# @param target [String, nil] optional hostname/FQDN/IP address of the target node
|
|
52
|
+
# @todo Probably unneccessary? Deprecate?
|
|
15
53
|
def refresh(target = nil)
|
|
16
54
|
@id ||= target
|
|
17
55
|
audit
|
|
18
56
|
end
|
|
19
57
|
|
|
58
|
+
# Parse through an SSH config to find possible Stackato hostnames
|
|
59
|
+
# @param ssh_config [String] path to the SSH config file
|
|
60
|
+
# @param filter [Array<String>] list of regex strings to filter out of found hostnames
|
|
61
|
+
# @return [Array<String>] (optionally filtered) list of Stackato hostnames found
|
|
20
62
|
def self.find_instances(ssh_config = '~/.ssh/config', filter = [])
|
|
21
63
|
instances = IO.read(File.expand_path(ssh_config))
|
|
22
64
|
.scan(/^\s*Host (.*stackato.*)/)
|
|
@@ -25,6 +67,9 @@ class Stackadmin
|
|
|
25
67
|
instances
|
|
26
68
|
end
|
|
27
69
|
|
|
70
|
+
# Retrieves patch manifest
|
|
71
|
+
# @param uri [String, nil] path to the manifest.json
|
|
72
|
+
# @return [Hash] the patch manifest
|
|
28
73
|
def manifest(uri = nil)
|
|
29
74
|
unless @manifest
|
|
30
75
|
uri ||= "https://get.stackato.com/kato-patch/#{@version}/manifest.json"
|
|
@@ -36,6 +81,9 @@ class Stackadmin
|
|
|
36
81
|
|
|
37
82
|
private
|
|
38
83
|
|
|
84
|
+
# Outputs formatted debug messages to stderr
|
|
85
|
+
# @param msg [String] debug message to output
|
|
86
|
+
# @return [String] formatted debug message
|
|
39
87
|
def log(msg)
|
|
40
88
|
$stderr.puts ">> #{msg}" if @debug
|
|
41
89
|
end
|
|
@@ -1,16 +1,29 @@
|
|
|
1
1
|
class Stackadmin
|
|
2
|
+
# A general exception class, to act as the ancestor to all other
|
|
3
|
+
# Stackadmin exception classes.
|
|
2
4
|
class Exception < StandardError
|
|
3
5
|
end
|
|
4
6
|
|
|
7
|
+
# This exception is raised when the target node
|
|
8
|
+
# is not found in the provided YAML file
|
|
5
9
|
class YAMLTargetNotFound < Stackadmin::Exception
|
|
6
10
|
end
|
|
7
11
|
|
|
12
|
+
# This exception is raised when the output of a
|
|
13
|
+
# `kato patch status` call does not follow the
|
|
14
|
+
# expected template
|
|
8
15
|
class InvalidPatchStatus < Stackadmin::Exception
|
|
9
16
|
end
|
|
10
17
|
|
|
18
|
+
# This exception is raised when an invalid flag/option
|
|
19
|
+
# is used (or an invalid combination of flags/options)
|
|
20
|
+
# in a `kato` subcommand
|
|
11
21
|
class InvalidFlags < Stackadmin::Exception
|
|
12
22
|
end
|
|
13
23
|
|
|
24
|
+
# This exception is raised when a `kato` subcommand
|
|
25
|
+
# is called that does not follow the subcommand's
|
|
26
|
+
# prototype
|
|
14
27
|
class InvalidCommand < Stackadmin::Exception
|
|
15
28
|
end
|
|
16
29
|
end
|
data/lib/stackadmin/net-ssh.rb
CHANGED
|
@@ -2,15 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
require 'net/ssh'
|
|
4
4
|
|
|
5
|
-
#
|
|
6
|
-
# Yanked this from http://stackoverflow.com/a/13436242
|
|
5
|
+
# @see http://net-ssh.github.io/net-ssh/classes/Net/SSH/Connection/Session.html Class Net::SSH::Connection::Session docs
|
|
7
6
|
class Net::SSH::Connection::Session
|
|
7
|
+
# This exception is raised when the ssh call
|
|
8
|
+
# returns a non-zero exit code
|
|
8
9
|
class CommandFailed < Net::SSH::Exception
|
|
9
10
|
end
|
|
10
11
|
|
|
12
|
+
# This exception is raised when the ssh call
|
|
13
|
+
# is unable to execute
|
|
11
14
|
class CommandExecutionFailed < Net::SSH::Exception
|
|
12
15
|
end
|
|
13
16
|
|
|
17
|
+
# A convenience method for executing a command and interacting with it.
|
|
18
|
+
# @see http://net-ssh.github.io/net-ssh/classes/Net/SSH/Connection/Session.html#method-i-exec-21 Net::SSH::Connection::Session::exec! docs
|
|
19
|
+
# @see http://stackoverflow.com/a/13436242 Yanked from Stack Overflow
|
|
20
|
+
# @param command [String] command to execute over the SSH connection
|
|
21
|
+
# @raise [CommandExecutionFailed] if the command was unable to execute
|
|
22
|
+
# @raise [CommandFailed] if the command returns a non-zero exit code
|
|
23
|
+
# @return [Hash] A hash containing the STDOUT, STDERR, exit code & exit signal returned by the command
|
|
24
|
+
# @example Returned Hash
|
|
25
|
+
# {
|
|
26
|
+
# stdout: "Distributor ID:\tUbuntu\nDescription:\tUbuntu 12.04.5 LTS\nRelease:\t12.04\nCodename:\tprecise\n",
|
|
27
|
+
# stderr: "No LSB modules are available.\n",
|
|
28
|
+
# exit_code: 0,
|
|
29
|
+
# exit_signal: nil
|
|
30
|
+
# }
|
|
14
31
|
def exec_sc!(command)
|
|
15
32
|
stdout_data, stderr_data = '', ''
|
|
16
33
|
exit_code, exit_signal = nil, nil
|
data/lib/stackadmin/patch.rb
CHANGED
|
@@ -6,8 +6,27 @@ require_relative 'exceptions'
|
|
|
6
6
|
require_relative 'audit'
|
|
7
7
|
|
|
8
8
|
class Stackadmin
|
|
9
|
-
#
|
|
10
|
-
#
|
|
9
|
+
# Installs patch(es) to the targeted Stackato cluster
|
|
10
|
+
# @param patches [String, Array<String>] the name of the patch (or list of patch names) to install
|
|
11
|
+
# @param node [nil, String] limit command to this node (given as an IP address)
|
|
12
|
+
# @param local [Boolean] limit command to the currently targeted Stackadmin node
|
|
13
|
+
# @param restart [Boolean] set to allow the target node's services to restart after a successful patch install
|
|
14
|
+
# @param force_update [Boolean] force an update to the patch manifest before installing any patches
|
|
15
|
+
# @param manifest_file [nil, String] execute the command with this manifest file
|
|
16
|
+
# @return [Hash] hash of hashes indicating the success status of each patch install to each node
|
|
17
|
+
# @example Returned Hash
|
|
18
|
+
# {
|
|
19
|
+
# '127.0.0.1' => {
|
|
20
|
+
# installed: ['patch1', 'patch3'],
|
|
21
|
+
# not_installed: ['patch2']
|
|
22
|
+
# },
|
|
23
|
+
# '171.234.56.78' => {
|
|
24
|
+
# installed: ['patch1', 'patch2', 'patch3'],
|
|
25
|
+
# not_installed: []
|
|
26
|
+
# }
|
|
27
|
+
# }
|
|
28
|
+
# @todo DRY this with ::mark! & ::reinstall! & ::revert!
|
|
29
|
+
# @todo (see #reset!)
|
|
11
30
|
def install!(patches = [], node = nil, local = false, restart = true, force_update = false, manifest_file = nil)
|
|
12
31
|
status = Hash.new
|
|
13
32
|
output = patch('install', patches, node: node, local: local, no_restart: !restart, force_update: force_update, manifest: manifest_file)
|
|
@@ -18,8 +37,6 @@ class Stackadmin
|
|
|
18
37
|
p.gsub!(/\e\[\d+m/, '') # Strip ASCII color
|
|
19
38
|
|
|
20
39
|
p.scan(/^Failed installing update (.+?) on (.+?)\.$/).each do |fp|
|
|
21
|
-
# fp = Array of failed patches
|
|
22
|
-
# e.g. [["patch1-name", "node1-ip"], ["patch2-name", "node2-ip"]]
|
|
23
40
|
status[fp[1]] = {
|
|
24
41
|
installed: [],
|
|
25
42
|
not_installed: []
|
|
@@ -29,7 +46,6 @@ class Stackadmin
|
|
|
29
46
|
end
|
|
30
47
|
|
|
31
48
|
p.scan(/^Successfully installed update (.+?) on (.+?)\.$/).each do |sp|
|
|
32
|
-
# sp = Array of successful patches. See fp example above
|
|
33
49
|
status[sp[1]] = {
|
|
34
50
|
installed: [],
|
|
35
51
|
not_installed: []
|
|
@@ -42,11 +58,22 @@ class Stackadmin
|
|
|
42
58
|
status
|
|
43
59
|
end
|
|
44
60
|
|
|
61
|
+
# Marks all of the targeted Stackato cluster's patch updates as uninstalled
|
|
62
|
+
# @param node [nil, String] limit command to this node (given as an IP address)
|
|
63
|
+
# @param local [Boolean] limit command to the currently targeted Stackadmin node
|
|
64
|
+
# @return [Boolean] whether or not the reset was successfully executed
|
|
65
|
+
# @todo collapse args into opt{}
|
|
45
66
|
def reset!(node = nil, local = false)
|
|
46
67
|
output = patch('reset', [], node: node, local: local)
|
|
47
68
|
output[0][:stdout] =~ /^Updates state reset.$/ ? true : false
|
|
48
69
|
end
|
|
49
70
|
|
|
71
|
+
# Updates the patch manifest for the targeted Stackato cluster
|
|
72
|
+
# @param node [nil, String] limit command to this node (given as an IP address)
|
|
73
|
+
# @param local [Boolean] limit command to the currently targeted Stackadmin node
|
|
74
|
+
# @param manifest_file [nil, String] execute the command with this manifest file
|
|
75
|
+
# @return [Boolean] whether or not the manifest was successfully updated
|
|
76
|
+
# @todo collapse args into opt{}
|
|
50
77
|
def update!(node = nil, local = false, manifest_file = nil)
|
|
51
78
|
output = v3_action_check('update') do
|
|
52
79
|
patch('update', [], node: node, local: local, manifest: manifest_file)
|
|
@@ -55,8 +82,20 @@ class Stackadmin
|
|
|
55
82
|
output[0][:stdout] =~ /^done!$/ ? true : false
|
|
56
83
|
end
|
|
57
84
|
|
|
85
|
+
# Marks patch(es) as installed or uninstalled on the targeted Stackato cluster
|
|
86
|
+
# @param patches [String, Array<String>] the name of the patch (or list of patch names) to mark
|
|
87
|
+
# @param mark_installed [Boolean] toggle marking as installed/uninstalled (true/false)
|
|
88
|
+
# @param node [nil, String] limit command to this node (given as an IP address)
|
|
89
|
+
# @param local [Boolean] limit command to the currently targeted Stackadmin node
|
|
90
|
+
# @return [Hash] hash of hashes listing which nodes had which patches successfully marked
|
|
91
|
+
# @example Returned Hash
|
|
92
|
+
# {
|
|
93
|
+
# '127.0.0.1' => ['patch1', 'patch2'],
|
|
94
|
+
# '171.234.56.78' => ['patch1', 'patch3']
|
|
95
|
+
# }
|
|
58
96
|
# Returns hash: { node1: [patch1, patch2], etc }
|
|
59
|
-
#
|
|
97
|
+
# @todo DRY this with ::install! & ::reinstall! & ::revert!
|
|
98
|
+
# @todo (see #reset!)
|
|
60
99
|
def mark!(patches = [], mark_installed = false, node = nil, local = false)
|
|
61
100
|
status = Hash.new
|
|
62
101
|
output = v3_action_check('mark') do
|
|
@@ -79,8 +118,12 @@ class Stackadmin
|
|
|
79
118
|
status
|
|
80
119
|
end
|
|
81
120
|
|
|
82
|
-
#
|
|
83
|
-
#
|
|
121
|
+
# Reinstalls patch(es) to the targeted Stackato cluster
|
|
122
|
+
# @param (see #install!)
|
|
123
|
+
# @return (see #install!)
|
|
124
|
+
# @example (see #install!)
|
|
125
|
+
# @todo DRY this with ::install! & ::mark! & ::revert!
|
|
126
|
+
# @todo (see #reset!)
|
|
84
127
|
def reinstall!(patches = [], node = nil, local = false, restart = true, force_update = false, manifest_file = nil)
|
|
85
128
|
if patches.empty?
|
|
86
129
|
raise InvalidCommand, "kato patch reinstall requires a specific patchname!"
|
|
@@ -119,8 +162,24 @@ class Stackadmin
|
|
|
119
162
|
end
|
|
120
163
|
end
|
|
121
164
|
|
|
122
|
-
#
|
|
123
|
-
#
|
|
165
|
+
# Revert patch(es) on the targeted Stackato cluster
|
|
166
|
+
# @param (see #install!)
|
|
167
|
+
# @return [Hash] hash of hashes indicating whether patches were successfully reverted on each node
|
|
168
|
+
# @example Returned Hash
|
|
169
|
+
# {
|
|
170
|
+
# '127.0.0.1' => {
|
|
171
|
+
# reverted: [patch1, patch4],
|
|
172
|
+
# not_reverted: [patch5],
|
|
173
|
+
# unable_to_revert: [patch3]
|
|
174
|
+
# },
|
|
175
|
+
# '171.234.56.78' => {
|
|
176
|
+
# reverted: [patch1, patch2],
|
|
177
|
+
# not_reverted: [],
|
|
178
|
+
# unable_to_revert: [patch3, patch4, patch5]
|
|
179
|
+
# }
|
|
180
|
+
# }
|
|
181
|
+
# @todo DRY this with ::install! & ::mark!
|
|
182
|
+
# @todo (see #reset!)
|
|
124
183
|
def revert!(patches = [], node = nil, local = false, restart = true, force_update = false, manifest_file = nil)
|
|
125
184
|
status = Hash.new
|
|
126
185
|
output = v3_action_check('revert') do
|
|
@@ -161,6 +220,9 @@ class Stackadmin
|
|
|
161
220
|
|
|
162
221
|
private
|
|
163
222
|
|
|
223
|
+
# Wrapper to throw an InvalidCommand exception on v3-only actions
|
|
224
|
+
# @param action [String] v3-only action
|
|
225
|
+
# @raise [InvalidCommand] if the targeted Stackato is not version 3
|
|
164
226
|
def v3_action_check(action)
|
|
165
227
|
if @version.to_i == 3
|
|
166
228
|
yield
|
|
@@ -169,14 +231,25 @@ class Stackadmin
|
|
|
169
231
|
end
|
|
170
232
|
end
|
|
171
233
|
|
|
172
|
-
#
|
|
173
|
-
#
|
|
174
|
-
#
|
|
175
|
-
#
|
|
176
|
-
#
|
|
177
|
-
#
|
|
178
|
-
#
|
|
179
|
-
#
|
|
234
|
+
# Helper method to parse and execute `kato patch` commands
|
|
235
|
+
# @param action [String] kato patch subcommand to execute
|
|
236
|
+
# @param patches [String, Array<String>] patch(es) to apply with action
|
|
237
|
+
# @param opts [Hash] flags/arguments to apply with the action
|
|
238
|
+
# @option opts [String] :node limit action to this node (given as an IP address)
|
|
239
|
+
# @option opts [Boolean] :local limit command to the currently targeted Stackadmin node
|
|
240
|
+
# @option opts [String] :manifest manifest file to use with the action
|
|
241
|
+
# @option opts [Boolean] :force_update force an update to the patch manifest before executing the action
|
|
242
|
+
# @option opts [Boolean] :mark_installed true marks the patch(es) as installed, false marks the patch(es) as uninstalled
|
|
243
|
+
# @option opts [Boolean] :no_restart set to prevent the action from restarting any associated services
|
|
244
|
+
# @raise [InvalidFlags] if both opts[:local] and opts[:node] are set
|
|
245
|
+
# @return [Hash] A hash containing the STDOUT, STDERR, exit code & exit signal returned by the action
|
|
246
|
+
# @example Returned Hash
|
|
247
|
+
# {
|
|
248
|
+
# stdout: "Updates state reset.\nWARNING: Stackato - no license installed\nUsing 31GB of 4GB (11GB over licensed limit)\nBuy a license: http://www.activestate.com/contact-stackato\n",
|
|
249
|
+
# stderr: nil,
|
|
250
|
+
# exit_code: 0,
|
|
251
|
+
# exit_signal: nil
|
|
252
|
+
# }
|
|
180
253
|
def patch(action, patches = [], opts = {})
|
|
181
254
|
raise InvalidFlags, "Can't target both #{opts[:node]} & \"local\"!" if (opts[:node] && opts[:local])
|
|
182
255
|
|
data/lib/stackadmin/version.rb
CHANGED
data/lib/stackadmin/yaml.rb
CHANGED
|
@@ -4,6 +4,7 @@ require 'yaml'
|
|
|
4
4
|
require_relative 'exceptions'
|
|
5
5
|
|
|
6
6
|
class Stackadmin
|
|
7
|
+
# @return [String] YAML-formatted target Stackato cluster info
|
|
7
8
|
def to_yaml
|
|
8
9
|
{ 'id' => @id,
|
|
9
10
|
'version' => @version,
|
|
@@ -13,6 +14,10 @@ class Stackadmin
|
|
|
13
14
|
|
|
14
15
|
private
|
|
15
16
|
|
|
17
|
+
# Parses a given YAML file into the current Stackadmin object attributes
|
|
18
|
+
# @param target [String] the target cluster's 'id'
|
|
19
|
+
# @param yml_file [String] a path to the YAML file to be parsed
|
|
20
|
+
# @raise [YAMLTargetNotFound] if given target is not found in the given yml_file
|
|
16
21
|
def parse_yaml(target, yml_file)
|
|
17
22
|
instances = YAML.load(File.read(yml_file))
|
|
18
23
|
|
data/stackadmin.gemspec
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
|
+
require 'stackadmin/version'
|
|
6
|
+
|
|
7
|
+
Gem::Specification.new do |spec|
|
|
8
|
+
spec.name = 'stackadmin'
|
|
9
|
+
spec.version = Stackadmin::VERSION
|
|
10
|
+
spec.authors = ['Andres Rojas']
|
|
11
|
+
spec.email = ['andres.rojas@mtnsat.com']
|
|
12
|
+
spec.summary = 'A helper class for administering Stackato deployments'
|
|
13
|
+
spec.homepage = "https://github.com/mtnsat/stackadmin"
|
|
14
|
+
|
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
|
16
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
17
|
+
spec.require_paths = ['lib']
|
|
18
|
+
|
|
19
|
+
spec.add_dependency 'net-ssh'
|
|
20
|
+
spec.add_dependency 'json'
|
|
21
|
+
spec.add_development_dependency 'minitest'
|
|
22
|
+
spec.add_development_dependency 'minitest-reporters'
|
|
23
|
+
spec.add_development_dependency 'vagrant-wrapper'
|
|
24
|
+
spec.add_development_dependency 'rake'
|
|
25
|
+
spec.add_development_dependency 'yard'
|
|
26
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'vagrant-wrapper'
|
|
4
|
+
|
|
5
|
+
MIN_VAGRANT = '1.5.1'
|
|
6
|
+
VAGRANT = "vagrant --min-ver=#{MIN_VAGRANT}"
|
|
7
|
+
|
|
8
|
+
class VagrantWrapper
|
|
9
|
+
def plugin_installed?(plugin)
|
|
10
|
+
(get_output('plugin list') =~ /^#{plugin} \((\d|\.)+\)$/) ? true : false
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def install_plugin(plugin)
|
|
14
|
+
installed = false
|
|
15
|
+
|
|
16
|
+
if plugin_installed?(plugin)
|
|
17
|
+
installed = true
|
|
18
|
+
else
|
|
19
|
+
puts "Installing #{plugin}..."
|
|
20
|
+
output = get_output("plugin install #{plugin}")
|
|
21
|
+
installed = true if output =~ /^Installed the plugin '#{plugin} \((\d|\.)+\)'!$/
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
puts "#{plugin} installed!" if installed
|
|
25
|
+
installed
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def missing_aws_creds
|
|
30
|
+
%w( AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY ) - ENV.keys
|
|
31
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: stackadmin
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andres Rojas
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2015-
|
|
11
|
+
date: 2015-03-11 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: net-ssh
|
|
@@ -67,7 +67,7 @@ dependencies:
|
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
68
|
version: '0'
|
|
69
69
|
- !ruby/object:Gem::Dependency
|
|
70
|
-
name: vagrant
|
|
70
|
+
name: vagrant-wrapper
|
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
|
72
72
|
requirements:
|
|
73
73
|
- - '>='
|
|
@@ -81,7 +81,7 @@ dependencies:
|
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
82
|
version: '0'
|
|
83
83
|
- !ruby/object:Gem::Dependency
|
|
84
|
-
name:
|
|
84
|
+
name: rake
|
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
|
86
86
|
requirements:
|
|
87
87
|
- - '>='
|
|
@@ -95,7 +95,7 @@ dependencies:
|
|
|
95
95
|
- !ruby/object:Gem::Version
|
|
96
96
|
version: '0'
|
|
97
97
|
- !ruby/object:Gem::Dependency
|
|
98
|
-
name:
|
|
98
|
+
name: yard
|
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
|
100
100
|
requirements:
|
|
101
101
|
- - '>='
|
|
@@ -116,6 +116,7 @@ extensions: []
|
|
|
116
116
|
extra_rdoc_files: []
|
|
117
117
|
files:
|
|
118
118
|
- .gitignore
|
|
119
|
+
- CHANGELOG
|
|
119
120
|
- Gemfile
|
|
120
121
|
- Gemfile.lock
|
|
121
122
|
- README.md
|
|
@@ -128,7 +129,9 @@ files:
|
|
|
128
129
|
- lib/stackadmin/patch.rb
|
|
129
130
|
- lib/stackadmin/version.rb
|
|
130
131
|
- lib/stackadmin/yaml.rb
|
|
132
|
+
- stackadmin.gemspec
|
|
131
133
|
- test/Vagrantfile
|
|
134
|
+
- test/helpers/vagrant.rb
|
|
132
135
|
- test/stackadmin_test.rb
|
|
133
136
|
homepage: https://github.com/mtnsat/stackadmin
|
|
134
137
|
licenses: []
|
|
@@ -155,4 +158,6 @@ specification_version: 4
|
|
|
155
158
|
summary: A helper class for administering Stackato deployments
|
|
156
159
|
test_files:
|
|
157
160
|
- test/Vagrantfile
|
|
161
|
+
- test/helpers/vagrant.rb
|
|
158
162
|
- test/stackadmin_test.rb
|
|
163
|
+
has_rdoc:
|