spiceweasel 1.2.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +201 -0
- data/README.md +141 -241
- data/bin/spiceweasel +2 -104
- data/lib/spiceweasel.rb +5 -11
- data/lib/spiceweasel/cli.rb +249 -83
- data/lib/spiceweasel/clusters.rb +53 -0
- data/lib/spiceweasel/config.rb +46 -0
- data/lib/spiceweasel/cookbooks.rb +105 -0
- data/lib/spiceweasel/data_bags.rb +89 -0
- data/lib/spiceweasel/environments.rb +103 -0
- data/lib/spiceweasel/execute.rb +42 -0
- data/lib/spiceweasel/extract_local.rb +130 -0
- data/lib/spiceweasel/log.rb +30 -0
- data/lib/spiceweasel/nodes.rb +126 -0
- data/lib/spiceweasel/roles.rb +131 -0
- data/lib/spiceweasel/version.rb +1 -1
- data/spec/bin/spiceweasel_spec.rb +44 -12
- metadata +106 -25
- data/lib/spiceweasel/cookbook_data.rb +0 -66
- data/lib/spiceweasel/cookbook_list.rb +0 -96
- data/lib/spiceweasel/data_bag_list.rb +0 -81
- data/lib/spiceweasel/directory_extractor.rb +0 -121
- data/lib/spiceweasel/environment_list.rb +0 -96
- data/lib/spiceweasel/node_list.rb +0 -98
- data/lib/spiceweasel/role_list.rb +0 -124
@@ -1,98 +0,0 @@
|
|
1
|
-
class Spiceweasel::NodeList
|
2
|
-
def initialize(nodes, cookbooks, environments, roles, options = {})
|
3
|
-
@create = @delete = ''
|
4
|
-
if nodes
|
5
|
-
nodes.each do |node|
|
6
|
-
nname = node.keys[0]
|
7
|
-
STDOUT.puts "DEBUG: node: '#{nname}'" if Spiceweasel::DEBUG
|
8
|
-
#convert spaces to commas, drop multiple commas
|
9
|
-
run_list = node[nname][0].gsub(/ /,',').gsub(/,+/,',')
|
10
|
-
STDOUT.puts "DEBUG: node: 'node[nname]' run_list: '#{run_list}'" if Spiceweasel::DEBUG
|
11
|
-
validateRunList(nname, run_list, cookbooks, roles) unless Spiceweasel::NOVALIDATION
|
12
|
-
noptions = node[nname][1]
|
13
|
-
STDOUT.puts "DEBUG: node: 'node[nname]' options: '#{noptions}'" if Spiceweasel::DEBUG
|
14
|
-
validateOptions(nname, noptions, environments) unless Spiceweasel::NOVALIDATION
|
15
|
-
#provider support
|
16
|
-
if nname.start_with?("bluebox ","clodo ","cs ","ec2 ","gandi ","hp ","openstack ","rackspace ","slicehost ","terremark ","voxel ")
|
17
|
-
provider = nname.split()
|
18
|
-
count = 1
|
19
|
-
if (provider.length == 2)
|
20
|
-
count = provider[1]
|
21
|
-
end
|
22
|
-
if Spiceweasel::PARALLEL
|
23
|
-
@create += "seq #{count} | parallel -j 0 -v \""
|
24
|
-
@create += "knife #{provider[0]}#{options['knife_options']} server create #{noptions}".gsub(/\{\{n\}\}/, '{}')
|
25
|
-
if run_list.length > 0
|
26
|
-
@create += " -r '#{run_list}'\"\n"
|
27
|
-
end
|
28
|
-
else
|
29
|
-
count.to_i.times do |i|
|
30
|
-
@create += "knife #{provider[0]}#{options['knife_options']} server create #{noptions}".gsub(/\{\{n\}\}/, (i + 1).to_s)
|
31
|
-
if run_list.length > 0
|
32
|
-
@create += " -r '#{run_list}'\n"
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
@delete += "knife node#{options['knife_options']} list | xargs knife #{provider[0]} server delete -y\n"
|
37
|
-
elsif nname.start_with?("windows") #windows node bootstrap support
|
38
|
-
nodeline = nname.split()
|
39
|
-
provider = nodeline.shift.split('_') #split on 'windows_ssh' etc
|
40
|
-
nodeline.each do |server|
|
41
|
-
@create += "knife bootstrap #{provider[0]} #{provider[1]}#{options['knife_options']} #{server} #{noptions}"
|
42
|
-
if run_list.length > 0
|
43
|
-
@create += " -r '#{run_list}'\n"
|
44
|
-
end
|
45
|
-
@delete += "knife node#{options['knife_options']} delete #{server} -y\n"
|
46
|
-
end
|
47
|
-
@delete += "knife node#{options['knife_options']} list | xargs knife #{provider[0]} server delete -y\n"
|
48
|
-
else #node bootstrap support
|
49
|
-
nname.split.each_with_index do |server, i|
|
50
|
-
@create += "knife bootstrap#{options['knife_options']} #{server} #{noptions}".gsub(/\{\{n\}\}/, (i + 1).to_s)
|
51
|
-
if run_list.length > 0
|
52
|
-
@create += " -r '#{run_list}'\n"
|
53
|
-
end
|
54
|
-
@delete += "knife node#{options['knife_options']} delete #{server} -y\n"
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
@delete += "knife node#{options['knife_options']} bulk delete .* -y\n"
|
60
|
-
end
|
61
|
-
|
62
|
-
#ensure run_list contents are listed previously.
|
63
|
-
def validateRunList(node, run_list, cookbooks, roles)
|
64
|
-
run_list.split(',').each do |item|
|
65
|
-
if item.start_with?("recipe[")
|
66
|
-
#recipe[foo] or recipe[foo::bar]
|
67
|
-
cb = item.split(/\[|\]/)[1].split(':')[0]
|
68
|
-
unless cookbooks.member?(cb)
|
69
|
-
STDERR.puts "ERROR: '#{node}' run list cookbook '#{cb}' is missing from the list of cookbooks in the manifest."
|
70
|
-
exit(-1)
|
71
|
-
end
|
72
|
-
elsif item.start_with?("role[")
|
73
|
-
#role[blah]
|
74
|
-
role = item.split(/\[|\]/)[1]
|
75
|
-
unless roles.member?(role)
|
76
|
-
STDERR.puts "ERROR: '#{node}' run list role '#{role}' is missing from the list of roles in the manifest."
|
77
|
-
exit(-1)
|
78
|
-
end
|
79
|
-
else
|
80
|
-
STDERR.puts "ERROR: '#{node}' run list '#{item}' is an invalid run list entry in the manifest."
|
81
|
-
exit(-1)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
#for now, just check that -E is legit
|
87
|
-
def validateOptions(node, options, environments)
|
88
|
-
if options =~ /-E/ #check for environments
|
89
|
-
env = options.split('-E')[1].split()[0]
|
90
|
-
unless environments.member?(env)
|
91
|
-
STDERR.puts "ERROR: '#{node}' environment '#{env}' is missing from the list of environments in the manifest."
|
92
|
-
exit(-1)
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
attr_reader :create, :delete
|
98
|
-
end
|
@@ -1,124 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# Author:: Matt Ray (<matt@opscode.com>)
|
3
|
-
#
|
4
|
-
# Copyright:: 2011-2012, Opscode, Inc <legal@opscode.com>
|
5
|
-
#
|
6
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
-
# you may not use this file except in compliance with the License.
|
8
|
-
# You may obtain a copy of the License at
|
9
|
-
#
|
10
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
-
#
|
12
|
-
# Unless required by applicable law or agreed to in writing, software
|
13
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
-
# See the License for the specific language governing permissions and
|
16
|
-
# limitations under the License.
|
17
|
-
#
|
18
|
-
|
19
|
-
require 'json'
|
20
|
-
|
21
|
-
class Spiceweasel::RoleList
|
22
|
-
def initialize(roles = {}, environments = [], cookbooks = {}, options = {})
|
23
|
-
@create = @delete = ''
|
24
|
-
@role_list = []
|
25
|
-
if roles
|
26
|
-
flatroles = roles.collect {|x| x.keys}.flatten
|
27
|
-
flatroles.each do |role|
|
28
|
-
STDOUT.puts "DEBUG: role: #{role}" if Spiceweasel::DEBUG
|
29
|
-
if File.directory?("roles")
|
30
|
-
validate(role, environments, cookbooks, flatroles) unless Spiceweasel::NOVALIDATION
|
31
|
-
else
|
32
|
-
STDERR.puts "ERROR: 'roles' directory not found, unable to validate or load roles" unless Spiceweasel::NOVALIDATION
|
33
|
-
end
|
34
|
-
if File.exists?("roles/#{role}.json")
|
35
|
-
@create += "knife role#{options['knife_options']} from file #{role}.json\n"
|
36
|
-
else #assume no .json means they want .rb and catchall for misssing dir
|
37
|
-
@create += "knife role#{options['knife_options']} from file #{role}.rb\n"
|
38
|
-
end
|
39
|
-
@delete += "knife role#{options['knife_options']} delete #{role} -y\n"
|
40
|
-
@role_list << role
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
#validate the content of the role file
|
46
|
-
def validate(role, environments, cookbooks, roles)
|
47
|
-
#validate the role passed in match the name of either the .rb or .json
|
48
|
-
if File.exists?("roles/#{role}.rb")
|
49
|
-
#validate that the name inside the file matches
|
50
|
-
name = File.open("roles/#{role}.rb").grep(/^name/)[0].split()[1].gsub(/"/,'').to_s
|
51
|
-
STDOUT.puts "DEBUG: role: '#{role}' name: '#{name}'" if Spiceweasel::DEBUG
|
52
|
-
if !role.eql?(name)
|
53
|
-
STDERR.puts "ERROR: Role '#{role}' listed in the manifest does not match the name '#{name}' within the roles/#{role}.rb file."
|
54
|
-
exit(-1)
|
55
|
-
end
|
56
|
-
#grab any lines with 'recipe[' or 'role['
|
57
|
-
rolerl = File.open("roles/#{role}.rb").grep(/recipe\[|role\[/)
|
58
|
-
rolerl.each do |line|
|
59
|
-
STDOUT.puts "DEBUG: role: '#{role}' line: '#{line}'" if Spiceweasel::DEBUG
|
60
|
-
line.strip.split(',').each do |rl|
|
61
|
-
if rl =~ /recipe\[/ #it's a cookbook
|
62
|
-
#split on the brackets and any colons
|
63
|
-
dep = rl.split(/\[|\]/)[1].split(':')[0]
|
64
|
-
STDOUT.puts "DEBUG: role: '#{role}' cookbook: '#{rl}': dep: '#{dep}'" if Spiceweasel::DEBUG
|
65
|
-
if !cookbooks.member?(dep)
|
66
|
-
STDERR.puts "ERROR: Cookbook dependency '#{dep}' from role '#{role}' is missing from the list of cookbooks in the manifest."
|
67
|
-
exit(-1)
|
68
|
-
end
|
69
|
-
elsif rl =~ /role\[/ #it's a role
|
70
|
-
#split on the brackets
|
71
|
-
dep = rl.split(/\[|\]/)[1]
|
72
|
-
STDOUT.puts "DEBUG: role: '#{role}' role: '#{rl}': dep: '#{dep}'" if Spiceweasel::DEBUG
|
73
|
-
if !roles.member?(dep)
|
74
|
-
STDERR.puts "ERROR: Role dependency '#{dep}' from role '#{role}' is missing from the list of roles in the manifest."
|
75
|
-
exit(-1)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
#TODO validate any environment-specific runlists
|
81
|
-
elsif File.exists?("roles/#{role}.json")
|
82
|
-
#load the json, don't symbolize since we don't need json_class
|
83
|
-
f = File.read("roles/#{role}.json")
|
84
|
-
JSON.create_id = nil
|
85
|
-
rolefile = JSON.parse(f, {:symbolize_names => false})
|
86
|
-
#validate that the name inside the file matches
|
87
|
-
STDOUT.puts "DEBUG: role: '#{role}' name: '#{rolefile['name']}'" if Spiceweasel::DEBUG
|
88
|
-
if !role.eql?(rolefile['name'])
|
89
|
-
STDERR.puts "ERROR: Role '#{role}' listed in the manifest does not match the name '#{rolefile['name']}' within the 'roles/#{role}.json' file."
|
90
|
-
exit(-1)
|
91
|
-
end
|
92
|
-
#validate the cookbooks and roles exist if they're mentioned in run_lists
|
93
|
-
rolefile['run_list'].each do |rl|
|
94
|
-
if rl =~ /recipe\[/ #it's a cookbook
|
95
|
-
#split on the brackets and any colons
|
96
|
-
dep = rl.split(/\[|\]/)[1].split(':')[0]
|
97
|
-
STDOUT.puts "DEBUG: role: '#{role}' cookbook: '#{rl}': dep: '#{dep}'" if Spiceweasel::DEBUG
|
98
|
-
if !cookbooks.member?(dep)
|
99
|
-
STDERR.puts "ERROR: Cookbook dependency '#{dep}' from role '#{role}' is missing from the list of cookbooks in the manifest."
|
100
|
-
exit(-1)
|
101
|
-
end
|
102
|
-
elsif rl =~ /role\[/ #it's a role
|
103
|
-
#split on the brackets
|
104
|
-
dep = rl.split(/\[|\]/)[1]
|
105
|
-
STDOUT.puts "DEBUG: role: '#{role}' role: '#{rl}': dep: '#{dep}'" if Spiceweasel::DEBUG
|
106
|
-
if !roles.member?(dep)
|
107
|
-
STDERR.puts "ERROR: Role dependency '#{dep}' from role '#{role}' is missing from the list of roles in the manifest."
|
108
|
-
exit(-1)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
#TODO validate any environment-specific runlists
|
113
|
-
else #role is not here
|
114
|
-
STDERR.puts "ERROR: Invalid Role '#{role}' listed in the manifest but not found in the roles directory."
|
115
|
-
exit(-1)
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
attr_reader :role_list, :create, :delete
|
120
|
-
|
121
|
-
def member?(role)
|
122
|
-
role_list.include?(role)
|
123
|
-
end
|
124
|
-
end
|