master-cap 0.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/Rakefile +2 -0
- data/Readme.markdown +101 -0
- data/lib/master-cap/chef.rb +105 -0
- data/lib/master-cap/default_translation_strategy.rb +14 -0
- data/lib/master-cap/git_repos_manager.rb +48 -0
- data/lib/master-cap/misc.rb +90 -0
- data/lib/master-cap/topology-directory.rb +64 -0
- data/lib/master-cap/topology.rb +71 -0
- data/master-cap.gemspec +25 -0
- metadata +87 -0
data/Rakefile
ADDED
data/Readme.markdown
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
# What is it ?
|
2
|
+
|
3
|
+
Here are [capistrano](https://github.com/capistrano/capistrano/wiki) extensions to be used with [master-chef](https://github.com/octo-technology/master-chef)
|
4
|
+
|
5
|
+
# How to use it
|
6
|
+
|
7
|
+
* Add ``master-cap`` to your Gemfile
|
8
|
+
* Load ``master-cap`` in your Capfile: ``require 'master-cap/topology-directory.rb'``
|
9
|
+
* Create a subdirectory named ``topology``, and add your toplogy YAML files into
|
10
|
+
* Enjoy :)
|
11
|
+
|
12
|
+
# Topology file
|
13
|
+
|
14
|
+
Example of a topology file: ``integ.yml``
|
15
|
+
|
16
|
+
```yml
|
17
|
+
:topology:
|
18
|
+
:app:
|
19
|
+
:hostname: my_app_server.mydomain.net
|
20
|
+
:type: linux_chef
|
21
|
+
:roles:
|
22
|
+
- app_server
|
23
|
+
:db:
|
24
|
+
:hostname: db_server.mydomain.net
|
25
|
+
:type: linux_chef
|
26
|
+
:roles:
|
27
|
+
- db_server
|
28
|
+
:redis:
|
29
|
+
:hostname: redise.mydomain.net
|
30
|
+
:type: linux_chef
|
31
|
+
:roles:
|
32
|
+
- redis_server
|
33
|
+
:cap_override:
|
34
|
+
:my_specific_cap_param: 'toto'
|
35
|
+
:default_role_list:
|
36
|
+
- base
|
37
|
+
```
|
38
|
+
|
39
|
+
# Capistrano commands
|
40
|
+
|
41
|
+
## Node selection
|
42
|
+
|
43
|
+
* ``cap integ show``
|
44
|
+
* ``cap app-integ show``
|
45
|
+
* ``cap integ_db_server show``
|
46
|
+
|
47
|
+
## SSH command
|
48
|
+
|
49
|
+
* ``cap integ check``: try to connect on each nodes with ssh
|
50
|
+
* ``cap integ ssh_cmd -s cmd=uname``: exec ``uname``command on each nodes
|
51
|
+
|
52
|
+
# Master-chef binding
|
53
|
+
|
54
|
+
## Configuration
|
55
|
+
|
56
|
+
If you use only master-chef (no custom repo with Chef recipes), you have nothing to do.
|
57
|
+
|
58
|
+
If you use a custom repo, please add following lines before requiring master-cap
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
set :git_repos_manager_class, 'SimpleGitReposManager'
|
62
|
+
|
63
|
+
set :git_repos, [
|
64
|
+
{
|
65
|
+
:url => "http://github.com/octo-technology/master-chef.git",
|
66
|
+
:ref => "aa597911b6d394dff27338c825aa966105cb6607",
|
67
|
+
},
|
68
|
+
{
|
69
|
+
:url => "git@github.com:xxxx/yyyy.git",
|
70
|
+
:local_path => "../yyyy",
|
71
|
+
}
|
72
|
+
]
|
73
|
+
```
|
74
|
+
|
75
|
+
Note:
|
76
|
+
* ``:ref`` is used to lock master-chef version
|
77
|
+
* ``:local_path`` is used to run ``chef:local``
|
78
|
+
|
79
|
+
# Cap commands
|
80
|
+
|
81
|
+
* ``cap integ chef``: run chef on all servers
|
82
|
+
* ``cap app-integ chef:local``: run chef on app server, using local recipes. Usefull for debugging.
|
83
|
+
* ``cap app-integ chef:generate_local_json``: generate the ``local.json`` file using current topology.
|
84
|
+
* ``cap app-integ chef:stack``: output last chef stack trace
|
85
|
+
* ``cap integ chef:stack``: purge all master-chef git repos caches
|
86
|
+
|
87
|
+
# License
|
88
|
+
|
89
|
+
Copyright 2012 Bertrand Paquet
|
90
|
+
|
91
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
92
|
+
you may not use this file except in compliance with the License.
|
93
|
+
You may obtain a copy of the License at
|
94
|
+
|
95
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
96
|
+
|
97
|
+
Unless required by applicable law or agreed to in writing, software
|
98
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
99
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
100
|
+
See the License for the specific language governing permissions and
|
101
|
+
limitations under the License.
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
require File.join(File.dirname(__FILE__), 'git_repos_manager.rb')
|
5
|
+
|
6
|
+
Capistrano::Configuration.instance.load do
|
7
|
+
|
8
|
+
namespace :chef do
|
9
|
+
|
10
|
+
set :master_chef_path, fetch(:master_chef_path, '../master-chef')
|
11
|
+
set :git_repos_manager, Object.const_get(fetch(:git_repos_manager_class, 'EmptyGitReposManager')).new(self)
|
12
|
+
|
13
|
+
task :generate_local_json do
|
14
|
+
set :user, chef_user
|
15
|
+
env = check_only_one_env
|
16
|
+
find_servers(:roles => chef_role).each do |s|
|
17
|
+
env, node = find_node s.host
|
18
|
+
r = []
|
19
|
+
r += (TOPOLOGY[env][:default_role_list] || []) unless node[:no_default_role]
|
20
|
+
r += node[:roles]
|
21
|
+
json = JSON.pretty_generate({
|
22
|
+
:repos => {
|
23
|
+
:git => git_repos_manager.list,
|
24
|
+
},
|
25
|
+
:run_list => r.map{|x| "role[#{x}]"},
|
26
|
+
:node_config => {
|
27
|
+
:topology_node_name => node[:topology_name]
|
28
|
+
}
|
29
|
+
})
|
30
|
+
puts json
|
31
|
+
f = Tempfile.new File.basename("local_json_#{name}")
|
32
|
+
f.write json
|
33
|
+
f.close
|
34
|
+
upload f.path, "/etc/chef/local.json", {:hosts => [s]}
|
35
|
+
end
|
36
|
+
git_repos_manager.list.each do |git_repo|
|
37
|
+
if git_repo =~ /^.+@.+:.+\.git$/
|
38
|
+
run "sudo ssh -o StrictHostKeyChecking=no #{git_repo.split(':')[0]} echo toto || true > /dev/null 2>&1", :roles => chef_role
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_prefix
|
44
|
+
prefix = ""
|
45
|
+
prefix += "http_proxy=#{http_proxy} https_proxy=#{http_proxy}" if exists? :http_proxy
|
46
|
+
prefix
|
47
|
+
end
|
48
|
+
|
49
|
+
task :upload_git_tag_override, :roles => :linux_chef do
|
50
|
+
set :user, chef_user
|
51
|
+
env = check_only_one_env
|
52
|
+
|
53
|
+
git_tag_override = git_repos_manager.compute_override(env)
|
54
|
+
|
55
|
+
if git_tag_override
|
56
|
+
f = Tempfile.new File.basename("git_tag_override")
|
57
|
+
f.write JSON.dump(git_tag_override)
|
58
|
+
f.close
|
59
|
+
|
60
|
+
upload_to_root f.path, "/etc/chef/local.json.git_tag_override"
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
task :upload_topology, :roles => :linux_chef do
|
66
|
+
set :user, chef_user
|
67
|
+
env = check_only_one_env
|
68
|
+
|
69
|
+
f = Tempfile.new File.basename("topology_env")
|
70
|
+
f.write YAML.dump(TOPOLOGY[env])
|
71
|
+
f.close
|
72
|
+
upload_to_root f.path, "/etc/chef/topology.yml"
|
73
|
+
end
|
74
|
+
|
75
|
+
task :default, :roles => chef_role do
|
76
|
+
set :user, chef_user
|
77
|
+
upload_topology
|
78
|
+
upload_git_tag_override
|
79
|
+
run "#{get_prefix} /etc/chef/update.sh"
|
80
|
+
end
|
81
|
+
|
82
|
+
task :stack, :roles => chef_role do
|
83
|
+
set :user, chef_user
|
84
|
+
run "sudo cat /var/chef/cache/chef-stacktrace.out"
|
85
|
+
end
|
86
|
+
|
87
|
+
task :purge_cache, :roles => chef_role do
|
88
|
+
set :user, chef_user
|
89
|
+
run "sudo rm -rf /var/chef/cache/git_repos"
|
90
|
+
end
|
91
|
+
|
92
|
+
task :local, :roles => chef_role do
|
93
|
+
set :user, chef_user
|
94
|
+
upload_topology
|
95
|
+
find_servers(:roles => chef_role).each do |x|
|
96
|
+
prefix = ""
|
97
|
+
prefix = "PROXY=#{http_proxy}" if exists? :http_proxy
|
98
|
+
command = "sh -c \"#{prefix} #{master_chef_path}/runtime/chef_local.rb #{x} #{git_repos_manager.compute_local_path}\""
|
99
|
+
abort unless system command
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
|
2
|
+
class DefaultTranslationStrategy
|
3
|
+
|
4
|
+
def node_name env, name, node, topology
|
5
|
+
return name.to_s if topology[:no_node_suffix]
|
6
|
+
"#{name}-#{env}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def node_hostname env, name, node, topology
|
10
|
+
return node[:hostname] if node[:hostname]
|
11
|
+
raise "No hostname in #{node}"
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
|
2
|
+
class EmptyGitReposManager
|
3
|
+
|
4
|
+
def initialize cap
|
5
|
+
@cap = cap
|
6
|
+
end
|
7
|
+
|
8
|
+
def compute_override env
|
9
|
+
if @cap.exists? :master_chef_version
|
10
|
+
return {"http://github.com/octo-technology/master-chef.git" => @cap.master_chef_version}
|
11
|
+
end
|
12
|
+
nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def list
|
16
|
+
["http://github.com/octo-technology/master-chef.git"]
|
17
|
+
end
|
18
|
+
|
19
|
+
def compute_local_path
|
20
|
+
""
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
class SimpleGitReposManager
|
26
|
+
|
27
|
+
def initialize cap
|
28
|
+
@cap = cap
|
29
|
+
@repos = @cap.fetch(:git_repos, [])
|
30
|
+
end
|
31
|
+
|
32
|
+
def compute_override env
|
33
|
+
result = {}
|
34
|
+
@repos.each do |x|
|
35
|
+
result[x[:url]] = x[:ref] if x[:ref]
|
36
|
+
end
|
37
|
+
result.size == 0 ? nil : result
|
38
|
+
end
|
39
|
+
|
40
|
+
def list
|
41
|
+
@repos.map{|x| x[:url]}
|
42
|
+
end
|
43
|
+
|
44
|
+
def compute_local_path
|
45
|
+
@repos.map{|x| x[:local_path] ? File.expand_path(x[:local_path]) : ""}.join(' ')
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
|
2
|
+
require 'timeout'
|
3
|
+
|
4
|
+
unless Capistrano::Configuration.respond_to?(:instance)
|
5
|
+
abort "master-cap requires Capistrano 2"
|
6
|
+
end
|
7
|
+
|
8
|
+
Capistrano::Configuration.instance.load do
|
9
|
+
|
10
|
+
def exec_local_with_timeout cmd, timeout
|
11
|
+
pid = Process.fork { exec cmd }
|
12
|
+
begin
|
13
|
+
Timeout.timeout(timeout) do
|
14
|
+
Process.wait(pid)
|
15
|
+
raise "Wrong return code for #{cmd} : #{$?.exitstatus}" unless $?.exitstatus == 0
|
16
|
+
end
|
17
|
+
rescue Timeout::Error
|
18
|
+
Process.kill('TERM', pid)
|
19
|
+
raise "Timeout when executing #{cmd}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def exec_local cmd
|
24
|
+
begin
|
25
|
+
abort "#{cmd} failed. Aborting..." unless system cmd
|
26
|
+
rescue
|
27
|
+
abort "#{cmd} failed. Aborting..."
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def capture_local cmd
|
32
|
+
begin
|
33
|
+
result = %x{#{cmd}}
|
34
|
+
abort "#{cmd} failed. Aborting..." unless $? == 0
|
35
|
+
result
|
36
|
+
rescue
|
37
|
+
abort "#{cmd} failed. Aborting..."
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def upload_to_root local, remote, options = {}
|
42
|
+
tmp_file = "/tmp/#{File.basename(remote)}"
|
43
|
+
upload local, tmp_file, options
|
44
|
+
run_root "mv #{tmp_file} #{remote}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def run_root cmd
|
48
|
+
run "sudo sh -c '#{cmd}'"
|
49
|
+
end
|
50
|
+
|
51
|
+
def error msg
|
52
|
+
abort "Error : #{msg}"
|
53
|
+
end
|
54
|
+
|
55
|
+
def fill s, k
|
56
|
+
s.length >= k ? s : fill(s.to_s + " ", k)
|
57
|
+
end
|
58
|
+
|
59
|
+
def multiple_capture command, options = nil
|
60
|
+
result = {}
|
61
|
+
launch = true
|
62
|
+
launch = false if options && options[:hosts] && options[:hosts] == []
|
63
|
+
if launch
|
64
|
+
begin
|
65
|
+
parallel(options) do |session|
|
66
|
+
session.else command do |channel, stream, data|
|
67
|
+
env, name, node = find_node(channel.properties[:server].host)
|
68
|
+
if block_given?
|
69
|
+
result[name] = yield name, node, data
|
70
|
+
else
|
71
|
+
result[name] = data
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
rescue Capistrano::ConnectionError => e
|
76
|
+
puts "\e[31m#{e}\e[0m"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
if options
|
80
|
+
default_value = options[:default_value]
|
81
|
+
if default_value
|
82
|
+
find_servers.map{|x| find_node x.host}.each do |env, name, node|
|
83
|
+
result[name] = default_value unless result[name]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
result
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
|
2
|
+
require File.join(File.dirname(__FILE__), 'topology.rb')
|
3
|
+
|
4
|
+
Capistrano::Configuration.instance.load do
|
5
|
+
|
6
|
+
topology_directory = fetch(:topology_directory, 'topology')
|
7
|
+
|
8
|
+
task :load_topology do
|
9
|
+
Dir["#{topology_directory}/*.yml"].each do |f|
|
10
|
+
env = File.basename(f).split('.')[0]
|
11
|
+
TOPOLOGY[env] = YAML.load(File.read(f))
|
12
|
+
nodes = []
|
13
|
+
roles_map = {}
|
14
|
+
default_role_nodes = []
|
15
|
+
TOPOLOGY[env][:topology].each do |k, v|
|
16
|
+
v[:topology_name] = translation_strategy.node_name(env, k, v, TOPOLOGY[env])
|
17
|
+
v[:topology_hostname] = begin translation_strategy.node_hostname(env, k, v, TOPOLOGY[env]) rescue nil end
|
18
|
+
next unless v[:topology_hostname]
|
19
|
+
node_roles = v[:roles].map{|x| x.to_sym}
|
20
|
+
node_roles << v[:type].to_sym if v[:type]
|
21
|
+
n = {:name => k.to_s, :host => v[:topology_hostname], :roles => node_roles}
|
22
|
+
nodes << n
|
23
|
+
default_role_nodes << n unless v[:no_default_role]
|
24
|
+
task v[:topology_name] do
|
25
|
+
server n[:host], *node_roles if n[:host]
|
26
|
+
load_cap_override env
|
27
|
+
end
|
28
|
+
node_roles.each do |r|
|
29
|
+
roles_map[r] = [] unless roles_map[r]
|
30
|
+
roles_map[r] << n
|
31
|
+
end
|
32
|
+
end
|
33
|
+
roles_map.each do |r, v|
|
34
|
+
task "#{env}_#{r}" do
|
35
|
+
v.each do |k|
|
36
|
+
server k[:host], *(k[:roles]) if k[:host]
|
37
|
+
load_cap_override env
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
task "#{env}_default_role" do
|
42
|
+
default_role_nodes.each do |k|
|
43
|
+
server k[:host], *(k[:roles])
|
44
|
+
load_cap_override env
|
45
|
+
end
|
46
|
+
end
|
47
|
+
task env do
|
48
|
+
nodes.each do |k|
|
49
|
+
server k[:host], *(k[:roles]) if k[:host]
|
50
|
+
load_cap_override env
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
on :load, "load_topology"
|
57
|
+
|
58
|
+
def load_cap_override env
|
59
|
+
TOPOLOGY[env][:cap_override].each do |k, v|
|
60
|
+
set k, v
|
61
|
+
end if TOPOLOGY[env][:cap_override]
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
|
2
|
+
require File.join(File.dirname(__FILE__), 'misc.rb')
|
3
|
+
require File.join(File.dirname(__FILE__), 'default_translation_strategy.rb')
|
4
|
+
|
5
|
+
unless Capistrano::Configuration.respond_to?(:instance)
|
6
|
+
abort "master-cap requires Capistrano 2"
|
7
|
+
end
|
8
|
+
|
9
|
+
TOPOLOGY = {}
|
10
|
+
|
11
|
+
Capistrano::Configuration.instance.load do
|
12
|
+
|
13
|
+
set :chef_role, fetch(:chef_role, :linux_chef)
|
14
|
+
set :chef_user, fetch(:chef_user, "chef")
|
15
|
+
set :translation_strategy, Object.const_get(fetch(:translation_strategy_class, 'DefaultTranslationStrategy')).new
|
16
|
+
|
17
|
+
task :check do
|
18
|
+
find_servers(:roles => chef_role).sort_by{|s| s.host}.each do |s|
|
19
|
+
env, node = find_node s.host
|
20
|
+
begin
|
21
|
+
exec_local_with_timeout "ssh -o StrictHostKeyChecking=no #{chef_user}@#{node[:topology_hostname]} uname > /dev/null 2>&1", fetch(:check_timeout, 10)
|
22
|
+
puts "OK : #{node[:topology_hostname]}"
|
23
|
+
rescue
|
24
|
+
puts "ERROR : Unable to join #{node[:topology_hostname]}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
task :ssh_cmd, :roles => chef_role do
|
30
|
+
error "Please specify command with -s cmd=" unless exists? :cmd
|
31
|
+
set :user, chef_user
|
32
|
+
run cmd
|
33
|
+
end
|
34
|
+
|
35
|
+
task :show do
|
36
|
+
ss = find_servers
|
37
|
+
puts "Number of selected servers: #{ss.length}"
|
38
|
+
ss.each do |s|
|
39
|
+
env, node = find_node s.host
|
40
|
+
puts "#{fill(node[:topology_name], 30)} #{fill(node[:topology_hostname], 50)} [#{node[:roles].sort.join(',')}]"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def find_node node_name
|
45
|
+
TOPOLOGY.each do |k, v|
|
46
|
+
v[:topology].each do |name, node|
|
47
|
+
return [k, node] if translation_strategy.node_hostname(k, name, node, v) == node_name
|
48
|
+
end
|
49
|
+
end
|
50
|
+
error "Node not found #{node_name}"
|
51
|
+
end
|
52
|
+
|
53
|
+
def check_only_one_env servers = nil
|
54
|
+
servers = find_servers unless servers
|
55
|
+
env_list = {}
|
56
|
+
servers.each do |s|
|
57
|
+
env, name, node = find_node(s.is_a?(String) ? s : s.host)
|
58
|
+
env_list[env] = :toto
|
59
|
+
end
|
60
|
+
error "Please, do not launch this command without env" if env_list.keys.size == 0
|
61
|
+
error "Please, do not launch this command on two env : #{env_list.keys.join(' ')}" if env_list.keys.size != 1
|
62
|
+
env = env_list.keys.first
|
63
|
+
|
64
|
+
check_only_one_env_callback(env, servers) if exists? :check_only_one_env_callback
|
65
|
+
|
66
|
+
env
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
require File.join(File.dirname(__FILE__), 'chef.rb')
|
data/master-cap.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
|
6
|
+
s.name = "master-cap"
|
7
|
+
s.version = "0.1"
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Bertrand Paquet"]
|
10
|
+
s.email = ["bertrand.paquet@gmail.com"]
|
11
|
+
s.homepage = "http://github.com/bpaquet/master-cap"
|
12
|
+
s.summary = "Capistrano tasks designed to work with master-chef"
|
13
|
+
s.description = "Capistrano tasks designed to work with master-chef"
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
s.extra_rdoc_files = [
|
19
|
+
"Readme.markdown"
|
20
|
+
]
|
21
|
+
|
22
|
+
s.specification_version = 3
|
23
|
+
s.add_runtime_dependency(%q<capistrano>, [">= 2"])
|
24
|
+
s.add_runtime_dependency(%q<json>)
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: master-cap
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Bertrand Paquet
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-02-21 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: capistrano
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2'
|
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: '2'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: json
|
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
|
+
description: Capistrano tasks designed to work with master-chef
|
47
|
+
email:
|
48
|
+
- bertrand.paquet@gmail.com
|
49
|
+
executables: []
|
50
|
+
extensions: []
|
51
|
+
extra_rdoc_files:
|
52
|
+
- Readme.markdown
|
53
|
+
files:
|
54
|
+
- Rakefile
|
55
|
+
- Readme.markdown
|
56
|
+
- lib/master-cap/chef.rb
|
57
|
+
- lib/master-cap/default_translation_strategy.rb
|
58
|
+
- lib/master-cap/git_repos_manager.rb
|
59
|
+
- lib/master-cap/misc.rb
|
60
|
+
- lib/master-cap/topology-directory.rb
|
61
|
+
- lib/master-cap/topology.rb
|
62
|
+
- master-cap.gemspec
|
63
|
+
homepage: http://github.com/bpaquet/master-cap
|
64
|
+
licenses: []
|
65
|
+
post_install_message:
|
66
|
+
rdoc_options: []
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ! '>='
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
+
none: false
|
77
|
+
requirements:
|
78
|
+
- - ! '>='
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
requirements: []
|
82
|
+
rubyforge_project:
|
83
|
+
rubygems_version: 1.8.24
|
84
|
+
signing_key:
|
85
|
+
specification_version: 3
|
86
|
+
summary: Capistrano tasks designed to work with master-chef
|
87
|
+
test_files: []
|