spiceweasel 1.2.0 → 2.0.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.
- 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
@@ -0,0 +1,53 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Matt Ray (<matt@opscode.com>)
|
3
|
+
#
|
4
|
+
# Copyright:: 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
|
+
module Spiceweasel
|
20
|
+
class Clusters
|
21
|
+
|
22
|
+
attr_reader :create, :delete
|
23
|
+
|
24
|
+
def initialize(clusters, cookbooks, environments, roles)
|
25
|
+
@create = Array.new
|
26
|
+
@delete = Array.new
|
27
|
+
if clusters
|
28
|
+
Spiceweasel::Log.debug("clusters: #{clusters}")
|
29
|
+
clusters.each do |cluster|
|
30
|
+
cluster_name = cluster.keys.first
|
31
|
+
Spiceweasel::Log.debug("cluster: '#{cluster_name}' '#{cluster[cluster_name]}'")
|
32
|
+
# add a tag to the Nodes
|
33
|
+
cluster[cluster_name].each do |node|
|
34
|
+
node_name = node.keys.first
|
35
|
+
run_list = node[node_name]['run_list'] || ''
|
36
|
+
options = node[node_name]['options'] || ''
|
37
|
+
# cluster tag is the cluster name + runlist
|
38
|
+
tag = " -j '{\"tags\":[\"#{cluster_name}+#{run_list.gsub(/[ ,\[\]:]/, '')}\"]}'"
|
39
|
+
Spiceweasel::Log.debug("cluster: #{cluster_name}:#{node_name}:tag:#{tag}")
|
40
|
+
#push the tag back on the options
|
41
|
+
node[node_name]['options'] = options + tag
|
42
|
+
end
|
43
|
+
Spiceweasel::Log.debug("cluster2: '#{cluster_name}' '#{cluster[cluster_name]}'")
|
44
|
+
# let's reuse the Nodes logic
|
45
|
+
nodes = Spiceweasel::Nodes.new(cluster[cluster_name], cookbooks, environments, roles)
|
46
|
+
@create.concat(nodes.create)
|
47
|
+
@delete.concat(nodes.delete)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Matt Ray (<matt@opscode.com>)
|
3
|
+
#
|
4
|
+
# Copyright:: 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 'mixlib/config'
|
20
|
+
|
21
|
+
module Spiceweasel
|
22
|
+
class Config
|
23
|
+
extend Mixlib::Config
|
24
|
+
|
25
|
+
debug false
|
26
|
+
|
27
|
+
# logging
|
28
|
+
log_level :info
|
29
|
+
log_location STDOUT
|
30
|
+
|
31
|
+
knife_options ''
|
32
|
+
|
33
|
+
# do we really need these?
|
34
|
+
delete false
|
35
|
+
execute false
|
36
|
+
extractjson false
|
37
|
+
extractlocal false
|
38
|
+
extractyaml false
|
39
|
+
help false
|
40
|
+
novalidation false
|
41
|
+
parallel false
|
42
|
+
rebuild false
|
43
|
+
siteinstall false
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,105 @@
|
|
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 'chef/cookbook/metadata'
|
20
|
+
|
21
|
+
module Spiceweasel
|
22
|
+
class Cookbooks
|
23
|
+
|
24
|
+
attr_reader :cookbook_list, :create, :delete
|
25
|
+
|
26
|
+
def initialize(cookbooks = [])
|
27
|
+
@create = Array.new
|
28
|
+
@delete = Array.new
|
29
|
+
@cookbook_list = Hash.new
|
30
|
+
@dependencies = Array.new
|
31
|
+
#validate each of the cookbooks specified in the manifest
|
32
|
+
if cookbooks
|
33
|
+
Spiceweasel::Log.debug("cookbooks: #{cookbooks}")
|
34
|
+
cookbooks.each do |cookbook|
|
35
|
+
name = cookbook.keys.first
|
36
|
+
if cookbook[name]
|
37
|
+
version = cookbook[name]['version']
|
38
|
+
options = cookbook[name]['options']
|
39
|
+
end
|
40
|
+
Spiceweasel::Log.debug("cookbook: #{name} #{version} #{options}")
|
41
|
+
if File.directory?("cookbooks")
|
42
|
+
if File.directory?("cookbooks/#{name}") #TODO use the name from metadata
|
43
|
+
validateMetadata(name,version) unless Spiceweasel::Config[:novalidation]
|
44
|
+
else
|
45
|
+
if Spiceweasel::Config[:siteinstall] #use knife cookbook site install
|
46
|
+
@create.push("knife cookbook#{Spiceweasel::Config[:knife_options]} site install #{name} #{version} #{options}")
|
47
|
+
else #use knife cookbook site download, untar and then remove the tarball
|
48
|
+
@create.push("knife cookbook#{Spiceweasel::Config[:knife_options]} site download #{name} #{version} --file cookbooks/#{name}.tgz #{options}")
|
49
|
+
@create.push("tar -C cookbooks/ -xf cookbooks/#{name}.tgz")
|
50
|
+
@create.push("rm -f cookbooks/#{name}.tgz")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
elsif !Spiceweasel::Config[:novalidation]
|
54
|
+
STDERR.puts "ERROR: 'cookbooks' directory not found, unable to validate, download and load cookbooks"
|
55
|
+
exit(-1)
|
56
|
+
end
|
57
|
+
@create.push("knife cookbook#{Spiceweasel::Config[:knife_options]} upload #{name} #{options}")
|
58
|
+
@delete.push("knife cookbook#{Spiceweasel::Config[:knife_options]} delete #{name} #{version} -a -y")
|
59
|
+
@cookbook_list[name] = version #used for validation
|
60
|
+
end
|
61
|
+
validateDependencies() unless Spiceweasel::Config[:novalidation]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
#check the metadata for versions and gather deps
|
66
|
+
def validateMetadata(cookbook,version)
|
67
|
+
#check metadata.rb for requested version
|
68
|
+
metadata_file = "cookbooks/#{cookbook}/metadata.rb"
|
69
|
+
metadata = Chef::Cookbook::Metadata.new
|
70
|
+
metadata.from_file(metadata_file)
|
71
|
+
Spiceweasel::Log.debug("validateMetadata: #{cookbook} #{metadata.name} #{metadata.version}")
|
72
|
+
# Should the cookbook directory match the name in the metadata?
|
73
|
+
if metadata.name.empty?
|
74
|
+
Spiceweasel::Log.warn("No cookbook name in the #{cookbook} metadata.rb.")
|
75
|
+
elsif cookbook != metadata.name
|
76
|
+
STDERR.puts "ERROR: Cookbook '#{cookbook}' does not match the name '#{metadata.name}' in #{cookbook}/metadata.rb."
|
77
|
+
exit(-1)
|
78
|
+
end
|
79
|
+
if version && metadata.version != version
|
80
|
+
STDERR.puts "ERROR: Invalid version '#{version}' of '#{cookbook}' requested, '#{metadata.version}' is already in the cookbooks directory."
|
81
|
+
exit(-1)
|
82
|
+
end
|
83
|
+
metadata.dependencies.each do |dependency|
|
84
|
+
Spiceweasel::Log.debug("cookbook #{cookbook} metadata dependency: #{dependency}")
|
85
|
+
@dependencies.push(dependency[0])
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
#compare the list of cookbook deps with those specified
|
90
|
+
def validateDependencies()
|
91
|
+
Spiceweasel::Log.debug("cookbook validateDependencies: '#{@dependencies}'")
|
92
|
+
@dependencies.each do |dep|
|
93
|
+
if !member?(dep)
|
94
|
+
STDERR.puts "ERROR: Cookbook dependency '#{dep}' is missing from the list of cookbooks in the manifest."
|
95
|
+
exit(-1)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def member?(cookbook)
|
101
|
+
cookbook_list.keys.include?(cookbook)
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,89 @@
|
|
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
|
+
module Spiceweasel
|
22
|
+
class DataBags
|
23
|
+
|
24
|
+
attr_reader :create, :delete
|
25
|
+
|
26
|
+
def initialize(data_bags = [])
|
27
|
+
@create = Array.new
|
28
|
+
@delete = Array.new
|
29
|
+
if data_bags
|
30
|
+
Spiceweasel::Log.debug("data bags: #{data_bags}")
|
31
|
+
data_bags.each do |data_bag|
|
32
|
+
db = data_bag.keys.first
|
33
|
+
#check directories
|
34
|
+
if !File.directory?("data_bags") && !Spiceweasel::Config[:novalidation]
|
35
|
+
STDERR.puts "ERROR: 'data_bags' directory not found, unable to validate or load data bag items"
|
36
|
+
exit(-1)
|
37
|
+
end
|
38
|
+
if !File.directory?("data_bags/#{db}") && !Spiceweasel::Config[:novalidation]
|
39
|
+
STDERR.puts "ERROR: 'data_bags/#{db}' directory not found, unable to validate or load data bag items"
|
40
|
+
exit(-1)
|
41
|
+
end
|
42
|
+
@create.push("knife data bag#{Spiceweasel::Config[:knife_options]} create #{db}")
|
43
|
+
@delete.push("knife data bag#{Spiceweasel::Config[:knife_options]} delete #{db} -y")
|
44
|
+
if data_bag[db]
|
45
|
+
items = data_bag[db]['items']
|
46
|
+
secret = data_bag[db]['secret']
|
47
|
+
if secret && !File.exists?(File.expand_path(secret)) && !Spiceweasel::Config[:novalidation]
|
48
|
+
STDERR.puts "ERROR: secret key #{secret} not found, unable to load encrypted data bags for data bag #{db}."
|
49
|
+
exit(-1)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
items = [] if items.nil?
|
53
|
+
Spiceweasel::Log.debug("data bag: #{db} #{secret} #{items}")
|
54
|
+
items.each do |item|
|
55
|
+
Spiceweasel::Log.debug("data bag #{db} item: #{item}")
|
56
|
+
if item =~ /\*/ #wildcard support, will fail if directory not present
|
57
|
+
files = Dir.glob("data_bags/#{db}/*.json")
|
58
|
+
items.concat(files.collect {|x| x[x.rindex('/')+1..-6]})
|
59
|
+
Spiceweasel::Log.debug("found items '#{items}' for data bag: #{db}")
|
60
|
+
next
|
61
|
+
end
|
62
|
+
validateItem(db, item) unless Spiceweasel::Config[:novalidation]
|
63
|
+
if secret
|
64
|
+
@create.push("knife data bag#{Spiceweasel::Config[:knife_options]} from file #{db} #{item}.json --secret-file #{secret}")
|
65
|
+
else
|
66
|
+
@create.push("knife data bag#{Spiceweasel::Config[:knife_options]} from file #{db} #{item}.json")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
#validate the item to be loaded
|
74
|
+
def validateItem(db, item)
|
75
|
+
if !File.exists?("data_bags/#{db}/#{item}.json")
|
76
|
+
STDERR.puts "ERROR: data bag '#{db}' item '#{item}' file 'data_bags/#{db}/#{item}.json' does not exist"
|
77
|
+
exit(-1)
|
78
|
+
end
|
79
|
+
f = File.read("data_bags/#{db}/#{item}.json")
|
80
|
+
itemfile = JSON.parse(f) #invalid JSON will throw a trace
|
81
|
+
#validate the id matches the file name
|
82
|
+
if !item.eql?(itemfile['id'])
|
83
|
+
STDERR.puts "ERROR: data bag '#{db}' item '#{item}' listed in the manifest does not match the id '#{itemfile['id']}' within the 'data_bags/#{db}/#{item}.json' file."
|
84
|
+
exit(-1)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,103 @@
|
|
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
|
+
module Spiceweasel
|
22
|
+
class Environments
|
23
|
+
|
24
|
+
attr_reader :environment_list, :create, :delete
|
25
|
+
|
26
|
+
def initialize(environments = [], cookbooks = {})
|
27
|
+
@create = Array.new
|
28
|
+
@delete = Array.new
|
29
|
+
@environment_list = Array.new
|
30
|
+
if environments
|
31
|
+
Spiceweasel::Log.debug("environments: #{environments}")
|
32
|
+
environments.each do |env|
|
33
|
+
name = env.keys[0]
|
34
|
+
Spiceweasel::Log.debug("environment: #{name}")
|
35
|
+
if File.directory?("environments")
|
36
|
+
validate(name, cookbooks) unless Spiceweasel::Config[:novalidation]
|
37
|
+
elsif !Spiceweasel::Config[:novalidation]
|
38
|
+
STDERR.puts "'environments' directory not found, unable to validate or load environments"
|
39
|
+
exit(-1)
|
40
|
+
end
|
41
|
+
if File.exists?("environments/#{name}.json")
|
42
|
+
@create.push("knife environment#{Spiceweasel::Config[:knife_options]} from file #{name}.json")
|
43
|
+
else #assume no .json means they want .rb and catchall for misssing dir
|
44
|
+
@create.push("knife environment#{Spiceweasel::Config[:knife_options]} from file #{name}.rb")
|
45
|
+
end
|
46
|
+
@delete.push("knife environment#{Spiceweasel::Config[:knife_options]} delete #{name} -y")
|
47
|
+
@environment_list.push(name)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
#validate the content of the environment file
|
53
|
+
def validate(environment, cookbooks)
|
54
|
+
#validate the environments passed in match the name of either the .rb or .json
|
55
|
+
if File.exists?("environments/#{environment}.rb")
|
56
|
+
#validate that the name inside the file matches
|
57
|
+
name = File.open("environments/#{environment}.rb").grep(/^name/)[0].split()[1].gsub(/"/,'').to_s
|
58
|
+
if !environment.eql?(name)
|
59
|
+
STDERR.puts "ERROR: Environment '#{environment}' listed in the manifest does not match the name '#{name}' within the environments/#{environment}.rb file."
|
60
|
+
exit(-1)
|
61
|
+
end
|
62
|
+
#validate the cookbooks exist if they're mentioned
|
63
|
+
envcookbooks = File.open("environments/#{environment}.rb").grep(/^cookbook /)
|
64
|
+
envcookbooks.each do |cb|
|
65
|
+
dep = cb.split()[1].gsub(/"/,'').gsub(/,/,'')
|
66
|
+
Spiceweasel::Log.debug("environment: '#{environment}' cookbook: '#{dep}'")
|
67
|
+
if !cookbooks.member?(dep)
|
68
|
+
STDERR.puts "ERROR: Cookbook dependency '#{dep}' from environment '#{environment}' is missing from the list of cookbooks in the manifest."
|
69
|
+
exit(-1)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
elsif File.exists?("environments/#{environment}.json")
|
73
|
+
#load the json, don't symbolize since we don't need json_class
|
74
|
+
f = File.read("environments/#{environment}.json")
|
75
|
+
JSON.create_id = nil
|
76
|
+
envfile = JSON.parse(f, {:symbolize_names => false})
|
77
|
+
Spiceweasel::Log.debug("environment: '#{environment}' file: '#{envfile}'")
|
78
|
+
#validate that the name inside the file matches
|
79
|
+
Spiceweasel::Log.debug("environment: '#{environment}' name: '#{envfile['name']}'")
|
80
|
+
if !environment.eql?(envfile['name'])
|
81
|
+
STDERR.puts "ERROR: Environment '#{environment}' listed in the manifest does not match the name '#{envfile['name']}' within the 'environments/#{environment}.json' file."
|
82
|
+
exit(-1)
|
83
|
+
end
|
84
|
+
#validate the cookbooks exist if they're mentioned
|
85
|
+
envfile['cookbook_versions'].keys.each do |cb|
|
86
|
+
Spiceweasel::Log.debug("environment: '#{environment}' cookbook: '#{cb}'")
|
87
|
+
if !cookbooks.member?(cb.to_s)
|
88
|
+
STDERR.puts "ERROR: Cookbook dependency '#{cb}' from environment '#{environment}' is missing from the list of cookbooks in the manifest."
|
89
|
+
exit(-1)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
else #environment is not here
|
93
|
+
STDERR.puts "ERROR: Invalid Environment '#{environment}' listed in the manifest but not found in the environments directory."
|
94
|
+
exit(-1)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def member?(environment)
|
99
|
+
environment_list.include?(environment)
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Matt Ray (<matt@opscode.com>)
|
3
|
+
#
|
4
|
+
# Copyright:: 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 'mixlib/shellout'
|
20
|
+
|
21
|
+
module Spiceweasel
|
22
|
+
class Execute
|
23
|
+
|
24
|
+
# run the commands passed in
|
25
|
+
def initialize(commands)
|
26
|
+
# for now we're shelling out
|
27
|
+
commands.each do | cmd |
|
28
|
+
knife = Mixlib::ShellOut.new(cmd)
|
29
|
+
# check for parallel? and eventually use threads
|
30
|
+
knife.run_command
|
31
|
+
puts cmd
|
32
|
+
puts knife.stdout
|
33
|
+
puts knife.stderr
|
34
|
+
Spiceweasel::Log.debug(cmd)
|
35
|
+
Spiceweasel::Log.debug(knife.stdout)
|
36
|
+
Spiceweasel::Log.fatal(knife.stderr) if !knife.stderr.empty?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
@@ -0,0 +1,130 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Geoff Meakin
|
3
|
+
# Author:: Matt Ray (<matt@opscode.com>)
|
4
|
+
#
|
5
|
+
# Copyright:: 2012, Opscode, Inc <legal@opscode.com>
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
|
20
|
+
require 'chef/cookbook/metadata'
|
21
|
+
|
22
|
+
module Spiceweasel
|
23
|
+
class ExtractLocal
|
24
|
+
|
25
|
+
def self.parse_objects
|
26
|
+
objects = {'cookbooks' => nil, 'roles' => nil, 'environments' => nil, 'data bags' => nil, 'nodes' => nil}
|
27
|
+
|
28
|
+
# COOKBOOKS
|
29
|
+
cookbooks = Array.new
|
30
|
+
Dir.glob('cookbooks/*').each do |cookbook_full_path|
|
31
|
+
metadata = Chef::Cookbook::Metadata.new
|
32
|
+
metadata.from_file("#{cookbook_full_path}/metadata.rb")
|
33
|
+
if metadata.name.empty?
|
34
|
+
Spiceweasel::Log.fatal("No cookbook name in the #{cookbook_full_path}/metadata.rb.")
|
35
|
+
exit(-1)
|
36
|
+
end
|
37
|
+
Spiceweasel::Log.debug("dir_ext: #{metadata.name} #{metadata.version}")
|
38
|
+
cookbooks.push(metadata)
|
39
|
+
end
|
40
|
+
cookbooks = self.order_cookbooks_by_dependency(cookbooks)
|
41
|
+
objects['cookbooks'] = cookbooks unless cookbooks.empty?
|
42
|
+
|
43
|
+
# ROLES
|
44
|
+
roles = []
|
45
|
+
Dir.glob("roles/*.{rb,json}").each do |role_full_path|
|
46
|
+
role = self.grab_name_from_path(role_full_path)
|
47
|
+
Spiceweasel::Log.debug("dir_ext: role: '#{role}'")
|
48
|
+
roles << {role => nil}
|
49
|
+
end
|
50
|
+
objects['roles'] = roles unless roles.nil?
|
51
|
+
|
52
|
+
# ENVIRONMENTS
|
53
|
+
environments = []
|
54
|
+
Dir.glob("environments/*.{rb,json}").each do |environment_full_path|
|
55
|
+
environment = self.grab_name_from_path(environment_full_path)
|
56
|
+
Spiceweasel::Log.debug("dir_ext: environment: '#{environment}'")
|
57
|
+
environments << {environment => nil}
|
58
|
+
end
|
59
|
+
objects['environments'] = environments unless environments.empty?
|
60
|
+
|
61
|
+
# DATA BAGS
|
62
|
+
data_bags = []
|
63
|
+
Dir.glob('data_bags/*').each do |data_bag_full_path|
|
64
|
+
data_bag = data_bag_full_path.split('/').last
|
65
|
+
Spiceweasel::Log.debug("dir_ext: data_bag: '#{data_bag}'")
|
66
|
+
data_bag_items = []
|
67
|
+
Dir.glob("#{data_bag_full_path}/*.{rb,json}").each do |data_bag_item_full_path|
|
68
|
+
Spiceweasel::Log.debug("dir_ext: data_bag: '#{data_bag}':'#{data_bag_item_full_path}'")
|
69
|
+
data_bag_items << self.grab_name_from_path(data_bag_item_full_path)
|
70
|
+
end if File.directory?(data_bag_full_path)
|
71
|
+
data_bags << {data_bag => {'items' => data_bag_items}}
|
72
|
+
end
|
73
|
+
objects['data bags'] = data_bags unless data_bags.empty?
|
74
|
+
|
75
|
+
# NODES
|
76
|
+
# TODO: Cant use this yet as node_list.rb doesnt support node from file syntax but expects the node info to be part of the objects passed in
|
77
|
+
# nodes = []
|
78
|
+
# Dir.glob("nodes/*.{rb,json}").each do |node_full_path|
|
79
|
+
# node = self.grab_name_from_path(node_full_path)
|
80
|
+
# nodes << {node => nil}
|
81
|
+
# end
|
82
|
+
# objects['nodes'] = nodes unless nodes.empty?
|
83
|
+
|
84
|
+
objects
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.grab_name_from_path(path)
|
88
|
+
name = path.split('/').last.split('.')
|
89
|
+
if name.length > 1
|
90
|
+
name.pop
|
91
|
+
end
|
92
|
+
name.join('.')
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.order_cookbooks_by_dependency(cookbooks)
|
96
|
+
# Weak algorithm, not particularly elegant, ignores version info as unlikely to have two versions of a cookbook anyway
|
97
|
+
# We're going to find the cookbooks with their dependencies matched and keep going until all we have is unmatched deps
|
98
|
+
sorted_cookbooks = Array.new
|
99
|
+
unsorted_cookbooks = cookbooks
|
100
|
+
scount = 0
|
101
|
+
#keep looping until no more cookbooks are left or can't remove remainders
|
102
|
+
while unsorted_cookbooks.any? and scount < cookbooks.length
|
103
|
+
cookbook = unsorted_cookbooks.shift
|
104
|
+
Spiceweasel::Log.debug("dir_ext: cookbook.dependencies: '#{cookbook.name}' #{cookbook.dependencies}")
|
105
|
+
#if all the cookbook dependencies are in sorted_cookbooks
|
106
|
+
if sorted_cookbooks.eql?(sorted_cookbooks | cookbook.dependencies.collect {|x| x[0]})
|
107
|
+
sorted_cookbooks.push(cookbook.name)
|
108
|
+
scount = 0
|
109
|
+
else #put it back in the list
|
110
|
+
unsorted_cookbooks.push(cookbook)
|
111
|
+
scount = scount + 1
|
112
|
+
end
|
113
|
+
Spiceweasel::Log.debug("dir_ext: sorted_cookbooks: '#{sorted_cookbooks}' #{scount}")
|
114
|
+
end
|
115
|
+
if scount > 0
|
116
|
+
remainders = unsorted_cookbooks.collect {|x| x.name}
|
117
|
+
Spiceweasel::Log.debug("dir_ext: remainders: '#{remainders}'")
|
118
|
+
if Spiceweasel::Config[:novalidation] #stuff is missing, oh well
|
119
|
+
sorted_cookbooks.push(remainders).flatten!
|
120
|
+
else
|
121
|
+
deps = unsorted_cookbooks.collect {|x| x.dependencies.collect {|x| x[0]} - sorted_cookbooks}
|
122
|
+
STDERR.puts "ERROR: Dependencies not satisfied or circular dependencies in cookbook(s): #{remainders} depend(s) on #{deps}"
|
123
|
+
exit(-1)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
#hack to get the format same as yaml/json parse
|
127
|
+
sorted_cookbooks.collect { |x| { x => nil } }
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|