spiceweasel 1.2.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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