knife_cookbook_dependencies 0.0.3 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/.gitignore +4 -1
  2. data/README.rdoc +41 -2
  3. data/Rakefile +42 -0
  4. data/features/lib/chef/knife/error_messages.feature +16 -0
  5. data/features/lib/chef/knife/lockfile.feature +25 -0
  6. data/features/lib/chef/knife/without.feature +27 -0
  7. data/features/support/env.rb +30 -0
  8. data/features/support/step_definitions.rb +22 -0
  9. data/knife_cookbook_dependencies.gemspec +6 -2
  10. data/lib/chef/knife/cookbook_dependencies_install.rb +12 -6
  11. data/lib/kcd.rb +1 -0
  12. data/lib/{knife_cookbook_dependencies → kcd}/cookbook.rb +72 -40
  13. data/lib/{knife_cookbook_dependencies → kcd}/cookbookfile.rb +12 -11
  14. data/lib/kcd/dsl.rb +13 -0
  15. data/lib/{knife_cookbook_dependencies → kcd}/error_messages.rb +1 -1
  16. data/lib/{knife_cookbook_dependencies → kcd}/git.rb +0 -0
  17. data/lib/{knife_cookbook_dependencies → kcd}/knife_utils.rb +1 -1
  18. data/lib/{knife_cookbook_dependencies → kcd}/lockfile.rb +8 -5
  19. data/lib/{knife_cookbook_dependencies → kcd}/metacookbook.rb +1 -1
  20. data/lib/{knife_cookbook_dependencies → kcd}/shelf.rb +30 -5
  21. data/lib/{knife_cookbook_dependencies → kcd}/version.rb +1 -1
  22. data/lib/knife_cookbook_dependencies.rb +20 -13
  23. data/spec/acceptance/knife_cookbook_dependencies_spec.rb +1 -11
  24. data/spec/fixtures/lockfile_spec/with_lock/Cookbookfile +1 -0
  25. data/spec/fixtures/lockfile_spec/without_lock/Cookbookfile.lock +5 -0
  26. data/spec/lib/{knife_cookbook_dependencies → kcd}/cookbook_spec.rb +43 -14
  27. data/spec/lib/{knife_cookbook_dependencies → kcd}/cookbookfile_spec.rb +3 -3
  28. data/spec/lib/kcd/dsl_spec.rb +56 -0
  29. data/spec/lib/{knife_cookbook_dependencies → kcd}/git_spec.rb +0 -0
  30. data/spec/lib/kcd/lockfile_spec.rb +54 -0
  31. data/spec/lib/kcd/shelf_spec.rb +81 -0
  32. data/spec/spec_helper.rb +22 -2
  33. data/todo.txt +8 -8
  34. metadata +98 -51
  35. data/lib/knife_cookbook_dependencies/dependency_reader.rb +0 -46
  36. data/lib/knife_cookbook_dependencies/dsl.rb +0 -7
  37. data/spec/lib/knife_cookbook_dependencies/dependency_reader_spec.rb +0 -42
  38. data/spec/lib/knife_cookbook_dependencies/dsl_spec.rb +0 -29
  39. data/spec/lib/knife_cookbook_dependencies/shelf_spec.rb +0 -37
@@ -1,37 +1,38 @@
1
- require 'knife_cookbook_dependencies/dsl'
2
-
3
1
  module KnifeCookbookDependencies
4
2
  class Cookbookfile
5
3
  class << self
6
4
  include DSL
7
- def read content
5
+
6
+ def read(content)
8
7
  # This will populate KnifeCookbookDependencies.shelf. TODO: consider making this
9
8
  # build and return the shelf rather than building the shelf as
10
9
  # a side effect.
11
10
  instance_eval(content)
12
11
  end
13
12
 
14
- def process_install
13
+ def process_install(without=nil)
15
14
  # TODO: friendly error message when the file doesn't exist
16
15
 
17
- filename = KnifeCookbookDependencies::DEFAULT_FILENAME + ".lock"
16
+ filename = KCD::DEFAULT_FILENAME + ".lock"
18
17
  lockfile = false
19
18
 
20
19
  if File.exist?(filename)
21
20
  lockfile = true
22
21
  else
23
- filename = KnifeCookbookDependencies::DEFAULT_FILENAME unless File.exist?(filename)
22
+ filename = KCD::DEFAULT_FILENAME unless File.exist?(filename)
24
23
  end
25
24
 
26
25
  begin
27
- read File.open(filename).read
26
+ read File.read(filename)
28
27
  rescue Errno::ENOENT => e
29
- KnifeCookbookDependencies.ui.fatal ErrorMessages.missing_cookbookfile
28
+ KCD.ui.fatal ErrorMessages.missing_cookbookfile
30
29
  exit 100
31
30
  end
32
- KnifeCookbookDependencies.shelf.resolve_dependencies
33
- KnifeCookbookDependencies.shelf.populate_cookbooks_directory
34
- KnifeCookbookDependencies.shelf.write_lockfile unless lockfile
31
+
32
+ KCD.shelf.exclude(without)
33
+ KCD.shelf.resolve_dependencies
34
+ KCD.shelf.populate_cookbooks_directory
35
+ KCD.shelf.write_lockfile unless lockfile
35
36
  end
36
37
  end
37
38
  end
data/lib/kcd/dsl.rb ADDED
@@ -0,0 +1,13 @@
1
+ module KnifeCookbookDependencies
2
+ module DSL
3
+ def cookbook(*args)
4
+ KCD.shelf.shelve_cookbook(*args)
5
+ end
6
+
7
+ def group *args
8
+ KCD.shelf.active_group = args
9
+ yield
10
+ KCD.shelf.active_group = nil
11
+ end
12
+ end
13
+ end
@@ -1,7 +1,7 @@
1
1
  module KnifeCookbookDependencies
2
2
  module ErrorMessages
3
3
  class << self
4
- def missing_cookbook name
4
+ def missing_cookbook(name)
5
5
  "The cookbook #{name} was not found on the Opscode Community site. Provide a git or path key for #{name} if it is unpublished."
6
6
  end
7
7
 
File without changes
@@ -4,7 +4,7 @@ require 'chef/config'
4
4
  module KnifeCookbookDependencies
5
5
  module KnifeUtils
6
6
  def self.capture_knife_output(knife_obj)
7
- knife_obj.ui = Chef::Knife::UI.new(StringIO.new, StringIO.new, StringIO.new, { :format => :json })
7
+ knife_obj.ui = Chef::Knife::UI.new(StringIO.new, StringIO.new, StringIO.new, :format => :json)
8
8
  knife_obj.run
9
9
  knife_obj.ui.stdout.rewind
10
10
  knife_obj.ui.stdout.read
@@ -1,12 +1,10 @@
1
- require 'knife_cookbook_dependencies/cookbookfile'
2
-
3
1
  module KnifeCookbookDependencies
4
2
  class Lockfile
5
3
  def initialize(cookbooks)
6
4
  @cookbooks = cookbooks
7
5
  end
8
6
 
9
- def write(filename = KnifeCookbookDependencies::DEFAULT_FILENAME)
7
+ def write(filename = KCD::DEFAULT_FILENAME)
10
8
  content = @cookbooks.map do |cookbook|
11
9
  get_cookbook_definition(cookbook)
12
10
  end.join("\n")
@@ -14,9 +12,14 @@ module KnifeCookbookDependencies
14
12
  end
15
13
 
16
14
  def get_cookbook_definition(cookbook)
17
- definition = "cookbook '#{cookbook.name}', :locked_version => '#{cookbook.locked_version}'"
18
- if cookbook.git_repo
15
+ definition = "cookbook '#{cookbook.name}'"
16
+
17
+ if cookbook.from_git?
19
18
  definition += ", :git => '#{cookbook.git_repo}', :ref => '#{cookbook.git_ref || 'HEAD'}'"
19
+ elsif cookbook.from_path?
20
+ definition += ", :path => '#{cookbook.local_path}'"
21
+ else
22
+ definition += ", :locked_version => '#{cookbook.locked_version}'"
20
23
  end
21
24
 
22
25
  return definition
@@ -2,7 +2,7 @@ module KnifeCookbookDependencies
2
2
  class MetaCookbook
3
3
  attr_reader :name, :dependencies
4
4
 
5
- def initialize name, dependencies
5
+ def initialize(name, dependencies)
6
6
  @name = name
7
7
  @dependencies = dependencies
8
8
  end
@@ -1,10 +1,8 @@
1
- require 'knife_cookbook_dependencies/lockfile'
2
-
3
1
  module KnifeCookbookDependencies
4
2
  class Shelf
5
3
  META_COOKBOOK_NAME = 'cookbook_dependencies_shelf'
6
4
 
7
- attr_accessor :cookbooks
5
+ attr_accessor :cookbooks, :active_group, :excluded_groups
8
6
 
9
7
  def initialize
10
8
  @cookbooks = []
@@ -17,8 +15,10 @@ module KnifeCookbookDependencies
17
15
  def resolve_dependencies
18
16
  graph = DepSelector::DependencyGraph.new
19
17
 
18
+ post_exclusions = requested_cookbooks
19
+ cookbooks_to_install = @cookbooks.select {|c| post_exclusions.include?(c.name)}
20
20
  # all cookbooks in the Cookbookfile are dependencies of the shelf
21
- shelf = MetaCookbook.new(META_COOKBOOK_NAME, @cookbooks)
21
+ shelf = MetaCookbook.new(META_COOKBOOK_NAME, cookbooks_to_install)
22
22
 
23
23
  self.class.populate_graph graph, shelf
24
24
 
@@ -29,7 +29,7 @@ module KnifeCookbookDependencies
29
29
  end
30
30
 
31
31
  def write_lockfile
32
- KnifeCookbookDependencies::Lockfile.new(@cookbooks).write
32
+ KCD::Lockfile.new(@cookbooks).write
33
33
  end
34
34
 
35
35
  def get_cookbook(name)
@@ -50,6 +50,31 @@ module KnifeCookbookDependencies
50
50
  @cookbooks = @cookbooks.uniq.reject { |x| x.locked_version.nil? }
51
51
  end
52
52
 
53
+ def exclude(groups)
54
+ groups = groups.to_s.split(/[,:]/) unless groups.is_a?(Array)
55
+ @excluded_groups = groups.collect {|c| c.to_sym}
56
+ end
57
+
58
+ def cookbook_groups
59
+ {}.tap do |groups|
60
+ @cookbooks.each do |cookbook|
61
+ cookbook.groups.each do |group|
62
+ groups[group] ||= []
63
+ groups[group] << cookbook.name
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ def requested_cookbooks
70
+ return @cookbooks.collect(&:name) unless @excluded_groups
71
+ [].tap do |r|
72
+ cookbook_groups.each do |group, cookbooks|
73
+ r << cookbooks unless @excluded_groups.include?(group.to_sym)
74
+ end
75
+ end.flatten.uniq
76
+ end
77
+
53
78
  class << self
54
79
  def populate_graph(graph, cookbook)
55
80
  package = graph.package cookbook.name
@@ -1,3 +1,3 @@
1
1
  module KnifeCookbookDependencies
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.5"
3
3
  end
@@ -1,28 +1,32 @@
1
1
  require 'dep_selector'
2
2
  require 'zlib'
3
3
  require 'archive/tar/minitar'
4
- require 'chef/config'
5
- require 'chef/knife/cookbook_site_download'
6
-
7
- require 'knife_cookbook_dependencies/version'
8
- require 'knife_cookbook_dependencies/shelf'
9
- require 'knife_cookbook_dependencies/cookbook'
10
- require 'knife_cookbook_dependencies/metacookbook'
11
- require 'knife_cookbook_dependencies/dependency_reader'
12
- require 'knife_cookbook_dependencies/dsl'
13
- require 'knife_cookbook_dependencies/cookbookfile'
14
- require 'knife_cookbook_dependencies/git'
15
- require 'knife_cookbook_dependencies/error_messages'
4
+
5
+ require 'kcd/version'
6
+ require 'kcd/shelf'
7
+ require 'kcd/cookbook'
8
+ require 'kcd/metacookbook'
9
+ require 'kcd/dsl'
10
+ require 'kcd/cookbookfile'
11
+ require 'kcd/lockfile'
12
+ require 'kcd/git'
13
+ require 'kcd/error_messages'
16
14
 
17
15
  module KnifeCookbookDependencies
18
16
  DEFAULT_FILENAME = 'Cookbookfile'
19
17
  COOKBOOKS_DIRECTORY = 'cookbooks'
20
18
 
19
+ autoload :KnifeUtils, 'kcd/knife_utils'
20
+
21
21
  class << self
22
22
  attr_accessor :ui
23
23
 
24
+ def root
25
+ File.join(File.dirname(__FILE__), '..')
26
+ end
27
+
24
28
  def shelf
25
- @shelf ||= KnifeCookbookDependencies::Shelf.new
29
+ @shelf ||= KCD::Shelf.new
26
30
  end
27
31
 
28
32
  def clear_shelf!
@@ -39,3 +43,6 @@ module KnifeCookbookDependencies
39
43
  end
40
44
  end
41
45
  end
46
+
47
+ # Alias for {KnifeCookbookDependencies}
48
+ KCD = KnifeCookbookDependencies
@@ -7,16 +7,6 @@ describe "knife cookbook dependencies install" do
7
7
  describe "should print a friendly error message" do
8
8
  include Aruba::Api
9
9
 
10
- it "for missing cookbooks" do
11
- pending "Knife commands produce no output when run with aruba. I'm not sure why." # TODO FIXME
12
-
13
- cookbook_name = 'thisisamissingcookbook'
14
- with_cookbookfile %Q[cookbook "#{cookbook_name}"] do
15
- cmd = 'cat nofile' #'knife cookbook dependencies install'
16
- process = run(cmd)
17
- process.output(true).should match(/#{KnifeCookbookDependencies::ErrorMessages.missing_cookbook(cookbook_name)}/)
18
- end
19
- end
20
- it "for missing Cookbookfile"
10
+ it "for --without option"
21
11
  end
22
12
  end
@@ -0,0 +1 @@
1
+ cookbook 'nginx', '= 0.101.0'
@@ -0,0 +1,5 @@
1
+ cookbook 'nginx', :locked_version => '0.101.0'
2
+ cookbook 'build-essential', :locked_version => '1.0.0'
3
+ cookbook 'runit', :locked_version => '0.15.0'
4
+ cookbook 'bluepill', :locked_version => '1.0.4'
5
+ cookbook 'ohai', :locked_version => '1.0.2'
@@ -15,7 +15,7 @@ module KnifeCookbookDependencies
15
15
 
16
16
  # FIXME: This test is flakey
17
17
  it "should raise an error if the cookbook is unpacked without being downloaded first" do
18
- -> { subject.unpack(subject.unpacked_cookbook_path, true, false) }.should raise_error
18
+ -> { subject.unpack(subject.unpacked_cookbook_path, :clean => true, :download => false) }.should raise_error
19
19
  end
20
20
 
21
21
  describe '#unpacked_cookbook_path' do
@@ -36,7 +36,7 @@ module KnifeCookbookDependencies
36
36
  describe "#copy_to" do
37
37
  it "should copy from the unpacked cookbook directory to the target" do
38
38
  example_cookbook_from_path.copy_to_cookbooks_directory
39
- File.exists?(File.join(KnifeCookbookDependencies::COOKBOOKS_DIRECTORY, example_cookbook_from_path.name)).should be_true
39
+ File.exists?(File.join(KCD::COOKBOOKS_DIRECTORY, example_cookbook_from_path.name)).should be_true
40
40
  end
41
41
  end
42
42
  end
@@ -47,17 +47,9 @@ module KnifeCookbookDependencies
47
47
  end
48
48
  end
49
49
 
50
- describe '#version_from_metadata_file' do
51
- it "should be able to handle single quoted strings" do
52
- Cookbook.any_instance.stub(:metadata_file).and_return(%Q{version '1.2.3'})
53
-
54
- subject.version_from_metadata_file.should == DepSelector::Version.new('1.2.3')
55
- end
56
-
57
- it "should be able to handle double quoted strings" do
58
- Cookbook.any_instance.stub(:metadata_file).and_return(%Q{version "1.2.3"})
59
-
60
- subject.version_from_metadata_file.should == DepSelector::Version.new('1.2.3')
50
+ describe '#version_from_metadata' do
51
+ it "should return the correct version" do
52
+ subject.version_from_metadata.should == DepSelector::Version.new('1.1.8')
61
53
  end
62
54
  end
63
55
 
@@ -90,7 +82,14 @@ module KnifeCookbookDependencies
90
82
  describe '#dependencies' do
91
83
  it "should not contain the cookbook itself" do
92
84
  # TODO: Mock
93
- Cookbook.new('riot_base', git: 'git@github.riotgames.com:cookbooks/riot_base.git').dependencies.collect(&:name).include?('riot_base').should_not be_true
85
+ Cookbook.new('nginx').dependencies.collect(&:name).include?('nginx').should_not be_true
86
+ end
87
+
88
+ it "should compute the correct dependencies" do
89
+ cookbook = Cookbook.new('mysql')
90
+ cookbook.dependencies.should == [Cookbook.new('openssl')]
91
+ # Second computation is intentional, to make sure it doesn't change the dependency list.
92
+ cookbook.dependencies.should == [Cookbook.new('openssl')]
94
93
  end
95
94
  end
96
95
 
@@ -102,5 +101,35 @@ module KnifeCookbookDependencies
102
101
  # subject.unpack
103
102
  # end
104
103
  # end
104
+
105
+ describe '#groups' do
106
+ it "should have the default group" do
107
+ subject.groups.should == [:default]
108
+ end
109
+ end
110
+
111
+ describe '#add_group' do
112
+ it "should store strings as symbols" do
113
+ subject.add_group "foo"
114
+ subject.groups.should == [:default, :foo]
115
+ end
116
+
117
+ it "should not store duplicate groups" do
118
+ subject.add_group "bar"
119
+ subject.add_group "bar"
120
+ subject.add_group :bar
121
+ subject.groups.should == [:default, :bar]
122
+ end
123
+
124
+ it "should add multiple groups" do
125
+ subject.add_group "baz", "quux"
126
+ subject.groups.should == [:default, :baz, :quux]
127
+ end
128
+
129
+ it "should handle multiple groups as an array" do
130
+ subject.add_group ["baz", "quux"]
131
+ subject.groups.should == [:default, :baz, :quux]
132
+ end
133
+ end
105
134
  end
106
135
  end
@@ -4,7 +4,7 @@ module KnifeCookbookDependencies
4
4
  describe Cookbookfile do
5
5
  describe '::read' do
6
6
  after do
7
- KnifeCookbookDependencies.shelf.cookbooks.each(&:clean)
7
+ KCD.shelf.cookbooks.each(&:clean)
8
8
  end
9
9
 
10
10
  it "should read the cookbookfile and build a dependency list" do
@@ -16,10 +16,10 @@ cookbook 'ssh_known_hosts2', :git => 'https://github.com/erikh/chef-ssh_known_ho
16
16
  COOKBOOKFILE
17
17
 
18
18
  ['ntp', 'mysql', 'nginx', 'ssh_known_hosts2'].each do |dep|
19
- KnifeCookbookDependencies.shelf.cookbooks.collect(&:name).should include dep
19
+ KCD.shelf.cookbooks.collect(&:name).should include dep
20
20
  end
21
21
 
22
- KnifeCookbookDependencies.shelf.populate_cookbooks_directory
22
+ KCD.shelf.populate_cookbooks_directory
23
23
  end
24
24
  end
25
25
  end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+ require 'kcd/dsl'
3
+
4
+ module KnifeCookbookDependencies
5
+ describe DSL do
6
+ include DSL
7
+
8
+ describe "#cookbook" do
9
+ after do
10
+ KnifeCookbookDependencies.shelf.cookbooks.each(&:clean)
11
+ end
12
+
13
+ it 'should add the cookbooks to the shelf' do
14
+ cookbook "ntp"
15
+ cookbook "nginx"
16
+
17
+ ['ntp', 'nginx'].each do |cookbook|
18
+ KnifeCookbookDependencies.shelf.cookbooks.collect(&:name).should include cookbook
19
+ end
20
+ end
21
+
22
+ it 'should take version constraints' do
23
+ Cookbook.any_instance.stub(:clean)
24
+ cookbook 'ntp', '= 1.2.3'
25
+ KnifeCookbookDependencies.shelf.cookbooks.select {|c| c.name == 'ntp'}.first.version_constraints.first.should == DepSelector::VersionConstraint.new('= 1.2.3')
26
+ end
27
+
28
+ it 'should take group' do
29
+ cookbook 'nginx', :group => 'web'
30
+ KnifeCookbookDependencies.shelf.cookbooks.select {|c| c.name == 'nginx'}.first.groups.should == [:web]
31
+ end
32
+ end
33
+
34
+ describe '#group' do
35
+ it "should set the group on all cookbooks" do
36
+ cookbooks = %w[hashbrowns mashed_potatoes bourbon]
37
+ group "awesome" do
38
+ cookbooks.each {|c| cookbook c}
39
+ end
40
+ cookbooks.each do |c|
41
+ KnifeCookbookDependencies.shelf.cookbooks.select {|n| n.name == c}.first.groups.should == [:awesome]
42
+ end
43
+ end
44
+
45
+ it "should not set the group on cookbooks after the group" do
46
+ cookbooks = %w[apple orange strawberry]
47
+ group "fruit" do
48
+ cookbooks.each {|c| cookbook c}
49
+ end
50
+ cookbook 'sesame_chicken'
51
+ KnifeCookbookDependencies.shelf.cookbooks.select {|n| n.name == 'sesame_chicken'}.first.groups.should == [:default]
52
+ end
53
+
54
+ end
55
+ end
56
+ end