hansolo 0.0.1.alpha.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.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +47 -0
- data/Rakefile +5 -0
- data/bin/hansolo +49 -0
- data/hansolo.gemspec +28 -0
- data/lib/hansolo/version.rb +3 -0
- data/lib/hansolo.rb +205 -0
- data/tests/hansolo_test.rb +45 -0
- metadata +154 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Brian Kaney
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# Hansolo
|
2
|
+
|
3
|
+
NOTE: This is alpha code.
|
4
|
+
|
5
|
+
Cli tool to automate berkshelf and chef-solo deployment
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'hansolo'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install hansolo
|
20
|
+
|
21
|
+
## Example configuration file
|
22
|
+
|
23
|
+
{
|
24
|
+
"urls": [ "vagrant@localhost:2222" ],
|
25
|
+
"runlist": [ "my_app::deploy" ],
|
26
|
+
"app":"my_app",
|
27
|
+
"keydir":"/Applications/Vagrant/embedded/gems/gems/vagrant-1.1.4/keys/vagrant",
|
28
|
+
"aws_access_key_id":"AAAAAAAAAAAAAAAAAAAA",
|
29
|
+
"aws_secret_access_key":"1111111111111111111111111111111111111111",
|
30
|
+
"aws_bucket_name":"acme-data_bags",
|
31
|
+
"aws_data_bag_keys":["my_app/stage/environment.json"]
|
32
|
+
}
|
33
|
+
|
34
|
+
## Usage
|
35
|
+
|
36
|
+
See the binary:
|
37
|
+
|
38
|
+
$ hansolo -h
|
39
|
+
|
40
|
+
|
41
|
+
## Contributing
|
42
|
+
|
43
|
+
1. Fork it
|
44
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
45
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
46
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
47
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/hansolo
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$: << File.expand_path(File.join(__FILE__, '..', '..', 'lib'))
|
3
|
+
|
4
|
+
require 'hansolo'
|
5
|
+
require 'optparse'
|
6
|
+
|
7
|
+
conf_options = {}; options = {}
|
8
|
+
|
9
|
+
optparse = OptionParser.new do |opts|
|
10
|
+
opts.banner = Hansolo::Cli.banner
|
11
|
+
|
12
|
+
opts.on( '-h', '--help', 'Display this screen' ) do
|
13
|
+
puts opts
|
14
|
+
puts "\n"
|
15
|
+
puts Hansolo::Cli.help
|
16
|
+
exit
|
17
|
+
end
|
18
|
+
|
19
|
+
opts.on( '-c', '--config file', String, 'Path to config file') do |filename|
|
20
|
+
conf_options = JSON.parse(File.read(filename)) if filename != '' and File.exists?(filename)
|
21
|
+
end
|
22
|
+
|
23
|
+
opts.on( '-u', '--urls a,b,c', Array, "Comma-sep list of urls, e.g.: user@host:port/dest/path") do |o|
|
24
|
+
options[:urls] = o
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on( '-k', '--keydir s', String, "Your local ssh key directory") do |o|
|
28
|
+
options[:keydir] = o
|
29
|
+
end
|
30
|
+
|
31
|
+
opts.on( '-a', '--app s', String, "The application name") do |o|
|
32
|
+
options[:app] = o
|
33
|
+
end
|
34
|
+
|
35
|
+
opts.on( '-s', '--stage s', String, "The stage name") do |o|
|
36
|
+
options[:stage] = o
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on( '-r', '--runlist a,b,c', Array, "The runlist you want to effect on the target(s)") do |o|
|
40
|
+
options[:runlist] = o
|
41
|
+
end
|
42
|
+
end.parse!(ARGV)
|
43
|
+
|
44
|
+
opts = conf_options.merge(options).inject({}){|m,(k,v)| m[k.to_sym] = v; m}
|
45
|
+
|
46
|
+
puts opts.inspect
|
47
|
+
|
48
|
+
h = Hansolo::Cli.new conf_options.merge(opts)
|
49
|
+
h.all!
|
data/hansolo.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'hansolo/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "hansolo"
|
8
|
+
spec.version = Hansolo::VERSION
|
9
|
+
spec.authors = ["Brian Kaney"]
|
10
|
+
spec.email = ["brian@vermonster.com"]
|
11
|
+
spec.description = %q{Tool to automate deployment using chef-solo and berkshelf}
|
12
|
+
spec.summary = %q{Vendors Berkshelf cookbooks, uses rsync to sync cookbooks, and NET::SSH to run chef-solo with conventions for building solo.rb and the JSON runlist}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "aws-sdk"
|
22
|
+
spec.add_dependency "net-ssh"
|
23
|
+
spec.add_dependency "json"
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
26
|
+
spec.add_development_dependency "rake"
|
27
|
+
spec.add_development_dependency "mocha"
|
28
|
+
end
|
data/lib/hansolo.rb
ADDED
@@ -0,0 +1,205 @@
|
|
1
|
+
require 'net/ssh'
|
2
|
+
require 'json'
|
3
|
+
require 'aws-sdk'
|
4
|
+
|
5
|
+
module Hansolo
|
6
|
+
class Cli
|
7
|
+
attr_accessor :keydir, :urls, :runlist, :s3conn, :app, :stage, :aws_bucket_name, :aws_data_bag_keys
|
8
|
+
|
9
|
+
def initialize(args={})
|
10
|
+
@keydir = args[:keydir]
|
11
|
+
@urls = args[:urls]
|
12
|
+
@runlist = args[:runlist]
|
13
|
+
@app = args[:app]
|
14
|
+
@stage = args[:stage]
|
15
|
+
@aws_bucket_name = args[:aws_bucket_name]
|
16
|
+
@aws_data_bag_keys = args[:aws_data_bag_keys]
|
17
|
+
@aws_secret_access_key = args[:aws_secret_access_key]
|
18
|
+
@aws_access_key_id = args[:aws_access_key_id]
|
19
|
+
|
20
|
+
if (@aws_secret_access_key && @aws_access_key_id && @aws_bucket_name && @aws_data_bag_keys)
|
21
|
+
@s3conn = AWS::S3.new(:access_key_id => args[:aws_access_key_id],
|
22
|
+
:secret_access_key => args[:aws_secret_access_key])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.banner
|
27
|
+
"Usage: hansolo [OPTS]"
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.help
|
31
|
+
DATA.read
|
32
|
+
end
|
33
|
+
|
34
|
+
def tmpdir
|
35
|
+
'/tmp'
|
36
|
+
end
|
37
|
+
|
38
|
+
def all!
|
39
|
+
vendor_berkshelf!
|
40
|
+
rsync_cookbooks!
|
41
|
+
rsync_data_bags! if s3conn
|
42
|
+
solo!
|
43
|
+
end
|
44
|
+
|
45
|
+
def username(url)
|
46
|
+
@username ||= Util.parse_url(url)[:username]
|
47
|
+
end
|
48
|
+
|
49
|
+
def dest_cookbooks_dir(url)
|
50
|
+
File.join("/", "home", username(url), "cookbooks")
|
51
|
+
end
|
52
|
+
|
53
|
+
def dest_data_bags_dir(url)
|
54
|
+
File.join("/", "home", username(url), "data_bags")
|
55
|
+
end
|
56
|
+
|
57
|
+
def local_cookbooks_tmpdir
|
58
|
+
File.join(tmpdir, 'cookbooks.working')
|
59
|
+
end
|
60
|
+
|
61
|
+
def local_data_bags_tmpdir
|
62
|
+
File.join(tmpdir, 'data_bags.working')
|
63
|
+
end
|
64
|
+
|
65
|
+
def vendor_berkshelf!
|
66
|
+
Util.call_vendor_berkshelf(local_cookbooks_tmpdir)
|
67
|
+
end
|
68
|
+
|
69
|
+
def s3_bucket
|
70
|
+
s3_bucket = s3conn.buckets[aws_bucket_name]
|
71
|
+
if s3_bucket.exists?
|
72
|
+
s3_bucket
|
73
|
+
else
|
74
|
+
s3conn.buckets.create(aws_bucket_name)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
#def s3_key_name
|
79
|
+
#"#{app}/#{stage}/environment.json"
|
80
|
+
#end
|
81
|
+
|
82
|
+
#def s3_item
|
83
|
+
#s3_bucket.objects[s3_key_name]
|
84
|
+
#end
|
85
|
+
|
86
|
+
def rsync_cookbooks!
|
87
|
+
raise ArgumentError, "missing urls array and keydir" unless (urls && keydir)
|
88
|
+
urls.each do |url|
|
89
|
+
opts = Util.parse_url(url).merge(keydir: keydir, sourcedir: local_cookbooks_tmpdir, destdir: dest_cookbooks_dir(url))
|
90
|
+
Util.call_rsync(opts)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def rsync_data_bags!
|
95
|
+
# Grab JSON file from S3, and place it into a conventional place
|
96
|
+
Util.call("mkdir -p #{File.join(local_data_bags_tmpdir, 'app')}")
|
97
|
+
|
98
|
+
aws_data_bag_keys.each do |key_name|
|
99
|
+
item = s3_bucket.objects[key_name]
|
100
|
+
base_key_name = File.basename(key_name)
|
101
|
+
File.open(File.join(local_data_bags_tmpdir, 'app', base_key_name), 'w') do |f|
|
102
|
+
f.write item.read
|
103
|
+
end if item.exists?
|
104
|
+
end
|
105
|
+
|
106
|
+
urls.each do |url|
|
107
|
+
opts = Util.parse_url(url).merge(keydir: keydir, sourcedir: local_data_bags_tmpdir, destdir: dest_data_bags_dir(url))
|
108
|
+
Util.call_rsync(opts)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def solo!
|
113
|
+
raise ArgumentError, "missing urls array and keydir" unless (urls && keydir)
|
114
|
+
urls.each { |url| Util.chef_solo(Util.parse_url(url).merge(keydir: keydir, cookbooks_dir: dest_cookbooks_dir(url), data_bags_dir: dest_data_bags_dir(url), runlist: runlist)) }
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
module Util
|
119
|
+
def self.call(cmd)
|
120
|
+
puts "* #{cmd}"
|
121
|
+
%x{#{cmd}}
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.call_vendor_berkshelf(tmpdir)
|
125
|
+
call("rm -rf #{tmpdir} && bundle exec berks install --path #{tmpdir}")
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.call_rsync(args={})
|
129
|
+
cmd = "rsync -av -e 'ssh -l #{args[:username]} #{ssh_options(["-p #{args[:port]}", "-i #{args[:keydir]}"])}' "
|
130
|
+
cmd << "#{args[:sourcedir]}/ #{args[:username]}@#{args[:hostname]}:#{args[:destdir]}"
|
131
|
+
call cmd
|
132
|
+
end
|
133
|
+
|
134
|
+
def self.chef_solo(args={})
|
135
|
+
# on remote do:
|
136
|
+
# build a solo.rb
|
137
|
+
# build a tmp json file with the contents { "run_list": [ "recipe[my_app::default]" ] }
|
138
|
+
# chef-solo -c solo.rb -j tmp.json
|
139
|
+
|
140
|
+
Net::SSH.start(args[:hostname], args[:username], :port => args[:port], :keys => [ args[:keydir] ]) do |ssh|
|
141
|
+
puts ssh.exec! "echo \"#{solo_rb(args[:tmpdir], args[:cookbooks_dir], args[:data_bags_dir])}\" > /tmp/solo.rb"
|
142
|
+
puts ssh.exec! "echo '#{ { :run_list => args[:runlist] }.to_json }' > /tmp/deploy.json"
|
143
|
+
ssh.exec! 'PATH="$PATH:/opt/vagrant_ruby/bin" sudo chef-solo -l debug -c /tmp/solo.rb -j /tmp/deploy.json' do |ch, stream, line|
|
144
|
+
puts line
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
|
151
|
+
def self.solo_rb(tmpdir, cookbooks_dir, data_bags_dir)
|
152
|
+
[
|
153
|
+
"file_cache_path '#{tmpdir}'",
|
154
|
+
"cookbook_path '#{cookbooks_dir}'",
|
155
|
+
"data_bags_path '#{data_bags_dir}'"
|
156
|
+
].join("\n")
|
157
|
+
end
|
158
|
+
|
159
|
+
def self.parse_url(url)
|
160
|
+
if (url =~ /^([^\@]*)@([^:]*):([0-9]*)$/)
|
161
|
+
return { username: $1, hostname: $2, port: $3.to_i }
|
162
|
+
else
|
163
|
+
raise ArgumentError, "Unable to parse `#{url}', should be in form `user@host:port'"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def self.ssh_options(opts=[])
|
168
|
+
(
|
169
|
+
[
|
170
|
+
"-q",
|
171
|
+
"-o StrictHostKeyChecking=no",
|
172
|
+
"-o UserKnownHostsFile=/dev/null"
|
173
|
+
] + opts
|
174
|
+
).join(' ')
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
require "hansolo/version"
|
180
|
+
|
181
|
+
__END__
|
182
|
+
This is a simple cli program to automate deploy using chef-solo and
|
183
|
+
berkshelf.
|
184
|
+
|
185
|
+
If you pass a filename, put in JSON for the configuration. So in hans.json:
|
186
|
+
|
187
|
+
{ "keydir": "/Applications/Vagrant/embedded/gems/gems/vagrant-1.1.4/keys/vagrant" }
|
188
|
+
|
189
|
+
Then you can pass to the command as:
|
190
|
+
|
191
|
+
$ hansolo -c hans.json
|
192
|
+
|
193
|
+
NOTE: Command-line args trump config settings.
|
194
|
+
|
195
|
+
Example Usage:
|
196
|
+
|
197
|
+
$ hansolo -t /tmp/myapp.cookbooks \
|
198
|
+
|
199
|
+
-k /Applications/Vagrant/embedded/gems/gems/vagrant-1.1.4/keys/vagrant \
|
200
|
+
|
201
|
+
-u user@host1:22/path,user@host2:22/path \
|
202
|
+
|
203
|
+
-r apt::default,myapp::deploy
|
204
|
+
|
205
|
+
$ hansolo -c hans.json
|
@@ -0,0 +1,45 @@
|
|
1
|
+
$: << File.expand_path(File.join(__FILE__, '..', '..', 'lib'))
|
2
|
+
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require 'mocha/setup'
|
5
|
+
|
6
|
+
require 'hansolo'
|
7
|
+
|
8
|
+
describe Hansolo do
|
9
|
+
|
10
|
+
describe Hansolo::Cli do
|
11
|
+
|
12
|
+
describe "#vendor_berkshelf!" do
|
13
|
+
before { @cli = Hansolo::Cli.new }
|
14
|
+
|
15
|
+
it "should shell berkshelf command" do
|
16
|
+
Hansolo::Util.expects(:call).with("rm -rf /tmp/cookbooks.working && bundle exec berks install --path /tmp/cookbooks.working")
|
17
|
+
@cli.vendor_berkshelf!
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#rsync!" do
|
22
|
+
before { @cli = Hansolo::Cli.new(keydir: '/keys', urls: [ 'user@host:22']) }
|
23
|
+
|
24
|
+
it "should shell rsync command" do
|
25
|
+
Hansolo::Util.expects(:call).with("rsync -av -e 'ssh -l user -q -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 22 -i /keys' /tmp/cookbooks.working/ user@host:/home/user/cookbooks")
|
26
|
+
@cli.rsync_cookbooks!
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
describe Hansolo::Util do
|
33
|
+
|
34
|
+
describe ".parse_url" do
|
35
|
+
it "parses" do
|
36
|
+
Hansolo::Util.parse_url('user@host:123').must_equal({ username: 'user', hostname: 'host', port: 123 })
|
37
|
+
end
|
38
|
+
|
39
|
+
it "doesn't parse" do
|
40
|
+
lambda{ Hansolo::Util.parse_url('foob') }.must_raise ArgumentError
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
metadata
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hansolo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1.alpha.1
|
5
|
+
prerelease: 6
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Brian Kaney
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-04-11 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: aws-sdk
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: net-ssh
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: json
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: bundler
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '1.3'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '1.3'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rake
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: mocha
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
description: Tool to automate deployment using chef-solo and berkshelf
|
111
|
+
email:
|
112
|
+
- brian@vermonster.com
|
113
|
+
executables:
|
114
|
+
- hansolo
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- .gitignore
|
119
|
+
- Gemfile
|
120
|
+
- LICENSE.txt
|
121
|
+
- README.md
|
122
|
+
- Rakefile
|
123
|
+
- bin/hansolo
|
124
|
+
- hansolo.gemspec
|
125
|
+
- lib/hansolo.rb
|
126
|
+
- lib/hansolo/version.rb
|
127
|
+
- tests/hansolo_test.rb
|
128
|
+
homepage: ''
|
129
|
+
licenses:
|
130
|
+
- MIT
|
131
|
+
post_install_message:
|
132
|
+
rdoc_options: []
|
133
|
+
require_paths:
|
134
|
+
- lib
|
135
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
136
|
+
none: false
|
137
|
+
requirements:
|
138
|
+
- - ! '>='
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '0'
|
141
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
142
|
+
none: false
|
143
|
+
requirements:
|
144
|
+
- - ! '>'
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: 1.3.1
|
147
|
+
requirements: []
|
148
|
+
rubyforge_project:
|
149
|
+
rubygems_version: 1.8.23
|
150
|
+
signing_key:
|
151
|
+
specification_version: 3
|
152
|
+
summary: Vendors Berkshelf cookbooks, uses rsync to sync cookbooks, and NET::SSH to
|
153
|
+
run chef-solo with conventions for building solo.rb and the JSON runlist
|
154
|
+
test_files: []
|