knife_cookbook_dependencies 0.0.3 → 0.0.5

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.
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