Skip to content

Commit e41f6cd

Browse files
sdelanoSeth Falcon
authored andcommitted
check for version conflicts in run_list expansion
1 parent e9c7c8a commit e41f6cd

5 files changed

Lines changed: 147 additions & 2 deletions

File tree

chef/lib/chef/exceptions.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,8 @@ class ResourceNotFound < RuntimeError; end
6262
class InvalidResourceSpecification < ArgumentError; end
6363
class SolrConnectionError < RuntimeError; end
6464
class IllegalChecksumRevert < RuntimeError; end
65+
class RecipeVersionConflict < StandardError; end
66+
class CookbookVersionConflict < StandardError; end
67+
class CookbookVersionNameMismatch < ArgumentError; end
6568
end
6669
end

chef/lib/chef/run_list.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
require 'chef/run_list/run_list_item'
2222
require 'chef/run_list/run_list_expansion'
23+
require 'chef/run_list/versioned_recipe_list'
2324
require 'chef/mixin/params_validate'
2425

2526
class Chef

chef/lib/chef/run_list/run_list_expansion.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def initialize(run_list_items, source=nil)
5353
@default_attrs = Mash.new
5454
@override_attrs = Mash.new
5555

56-
@recipes = []
56+
@recipes = Chef::RunList::VersionedRecipeList.new
5757

5858
@applied_roles = {}
5959
end
@@ -71,7 +71,7 @@ def expand(environment='_default')
7171
@run_list_items.each_with_index do |entry, index|
7272
case entry.type
7373
when :recipe
74-
recipes << entry.name unless recipes.include?(entry.name)
74+
recipes.add_recipe entry.name, entry.version
7575
when :role
7676
if role = inflate_role(entry.name)
7777
apply_role_attributes(role)
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#
2+
# Author:: Stephen Delano (<stephen@opscode.com>)
3+
# Copyright:: Copyright (c) 2010 Opscode, Inc.
4+
# License:: Apache License, Version 2.0
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+
class Chef
19+
class RunList
20+
class VersionedRecipeList < Array
21+
22+
def initialize
23+
super
24+
@versions = Hash.new
25+
end
26+
27+
def add_recipe(name, version=nil)
28+
if version && @versions.has_key?(name)
29+
unless Gem::Version.new(@versions[name]) == Gem::Version.new(version)
30+
raise Chef::Exceptions::RecipeVersionConflict, "Run list requires recipe #{name} at versions #{@versions[name]} and #{version}"
31+
end
32+
end
33+
@versions[name] = version if version
34+
self << name unless self.include?(name)
35+
end
36+
37+
def with_versions
38+
self.map {|i| {:name => i, :version => @versions[i]}}
39+
end
40+
end
41+
end
42+
end
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#
2+
# Author:: Stephen Delano (<stephen@opscode.com>)
3+
# Copyright:: Copyright (c) 2010 Opscode, Inc.
4+
# License:: Apache License, Version 2.0
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+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
19+
20+
describe Chef::RunList::VersionedRecipeList do
21+
22+
describe "initialize" do
23+
it "should create an empty array" do
24+
l = Chef::RunList::VersionedRecipeList.new
25+
l.should == []
26+
end
27+
end
28+
29+
describe "add_recipe" do
30+
before(:each) do
31+
@list = Chef::RunList::VersionedRecipeList.new
32+
@list << "apt"
33+
@list << "god"
34+
@list << "apache2"
35+
end
36+
37+
it "should append the recipe to the end of the list" do
38+
@list.add_recipe "rails"
39+
@list.should == ["apt", "god", "apache2", "rails"]
40+
end
41+
42+
it "should not duplicate entries" do
43+
@list.add_recipe "apt"
44+
@list.should == ["apt", "god", "apache2"]
45+
end
46+
47+
it "should allow you to specify a version" do
48+
@list.add_recipe "rails", "1.0.0"
49+
@list.should == ["apt", "god", "apache2", "rails"]
50+
@list.with_versions.should include({:name => "rails", :version => "1.0.0"})
51+
end
52+
53+
it "should allow you to specify a version for a recipe that already exists" do
54+
@list.add_recipe "apt", "1.2.3"
55+
@list.should == ["apt", "god", "apache2"]
56+
@list.with_versions.should include({:name => "apt", :version => "1.2.3"})
57+
end
58+
59+
it "should allow you to specify the same version of a recipe twice" do
60+
@list.add_recipe "rails", "1.0.0"
61+
@list.add_recipe "rails", "1.0.0"
62+
@list.with_versions.should include({:name => "rails", :version => "1.0.0"})
63+
end
64+
65+
it "should allow you to spcify no version, even when a version already exists" do
66+
@list.add_recipe "rails", "1.0.0"
67+
@list.add_recipe "rails"
68+
@list.with_versions.should include({:name => "rails", :version => "1.0.0"})
69+
end
70+
71+
it "should not allow multiple versions of the same recipe" do
72+
@list.add_recipe "rails", "1.0.0"
73+
lambda {@list.add_recipe "rails", "0.1.0"}.should raise_error Chef::Exceptions::RecipeVersionConflict
74+
end
75+
end
76+
77+
describe "with_versions" do
78+
before(:each) do
79+
@recipes = [
80+
{:name => "apt", :version => "1.0.0"},
81+
{:name => "god", :version => nil},
82+
{:name => "apache2", :version => "0.0.1"}
83+
]
84+
@list = Chef::RunList::VersionedRecipeList.new
85+
@recipes.each {|i| @list.add_recipe i[:name], i[:version]}
86+
end
87+
88+
it "should return an array of hashes with :name and :version" do
89+
@list.with_versions.should == @recipes
90+
end
91+
92+
it "should retain the same order as the version-less list" do
93+
with_versions = @list.with_versions
94+
@list.each_with_index do |item, index|
95+
with_versions[index][:name].should == item
96+
end
97+
end
98+
end
99+
end

0 commit comments

Comments
 (0)