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.
- data/.gitignore +4 -1
- data/README.rdoc +41 -2
- data/Rakefile +42 -0
- data/features/lib/chef/knife/error_messages.feature +16 -0
- data/features/lib/chef/knife/lockfile.feature +25 -0
- data/features/lib/chef/knife/without.feature +27 -0
- data/features/support/env.rb +30 -0
- data/features/support/step_definitions.rb +22 -0
- data/knife_cookbook_dependencies.gemspec +6 -2
- data/lib/chef/knife/cookbook_dependencies_install.rb +12 -6
- data/lib/kcd.rb +1 -0
- data/lib/{knife_cookbook_dependencies → kcd}/cookbook.rb +72 -40
- data/lib/{knife_cookbook_dependencies → kcd}/cookbookfile.rb +12 -11
- data/lib/kcd/dsl.rb +13 -0
- data/lib/{knife_cookbook_dependencies → kcd}/error_messages.rb +1 -1
- data/lib/{knife_cookbook_dependencies → kcd}/git.rb +0 -0
- data/lib/{knife_cookbook_dependencies → kcd}/knife_utils.rb +1 -1
- data/lib/{knife_cookbook_dependencies → kcd}/lockfile.rb +8 -5
- data/lib/{knife_cookbook_dependencies → kcd}/metacookbook.rb +1 -1
- data/lib/{knife_cookbook_dependencies → kcd}/shelf.rb +30 -5
- data/lib/{knife_cookbook_dependencies → kcd}/version.rb +1 -1
- data/lib/knife_cookbook_dependencies.rb +20 -13
- data/spec/acceptance/knife_cookbook_dependencies_spec.rb +1 -11
- data/spec/fixtures/lockfile_spec/with_lock/Cookbookfile +1 -0
- data/spec/fixtures/lockfile_spec/without_lock/Cookbookfile.lock +5 -0
- data/spec/lib/{knife_cookbook_dependencies → kcd}/cookbook_spec.rb +43 -14
- data/spec/lib/{knife_cookbook_dependencies → kcd}/cookbookfile_spec.rb +3 -3
- data/spec/lib/kcd/dsl_spec.rb +56 -0
- data/spec/lib/{knife_cookbook_dependencies → kcd}/git_spec.rb +0 -0
- data/spec/lib/kcd/lockfile_spec.rb +54 -0
- data/spec/lib/kcd/shelf_spec.rb +81 -0
- data/spec/spec_helper.rb +22 -2
- data/todo.txt +8 -8
- metadata +98 -51
- data/lib/knife_cookbook_dependencies/dependency_reader.rb +0 -46
- data/lib/knife_cookbook_dependencies/dsl.rb +0 -7
- data/spec/lib/knife_cookbook_dependencies/dependency_reader_spec.rb +0 -42
- data/spec/lib/knife_cookbook_dependencies/dsl_spec.rb +0 -29
- 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
|
-
|
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 =
|
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 =
|
22
|
+
filename = KCD::DEFAULT_FILENAME unless File.exist?(filename)
|
24
23
|
end
|
25
24
|
|
26
25
|
begin
|
27
|
-
read File.
|
26
|
+
read File.read(filename)
|
28
27
|
rescue Errno::ENOENT => e
|
29
|
-
|
28
|
+
KCD.ui.fatal ErrorMessages.missing_cookbookfile
|
30
29
|
exit 100
|
31
30
|
end
|
32
|
-
|
33
|
-
|
34
|
-
|
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
@@ -1,7 +1,7 @@
|
|
1
1
|
module KnifeCookbookDependencies
|
2
2
|
module ErrorMessages
|
3
3
|
class << self
|
4
|
-
def missing_cookbook
|
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,
|
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 =
|
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}'
|
18
|
-
|
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
|
@@ -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,
|
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
|
-
|
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,28 +1,32 @@
|
|
1
1
|
require 'dep_selector'
|
2
2
|
require 'zlib'
|
3
3
|
require 'archive/tar/minitar'
|
4
|
-
|
5
|
-
require '
|
6
|
-
|
7
|
-
require '
|
8
|
-
require '
|
9
|
-
require '
|
10
|
-
require '
|
11
|
-
require '
|
12
|
-
require '
|
13
|
-
require '
|
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 ||=
|
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
|
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'
|
@@ -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(
|
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 '#
|
51
|
-
it "should
|
52
|
-
|
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('
|
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
|
-
|
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
|
-
|
19
|
+
KCD.shelf.cookbooks.collect(&:name).should include dep
|
20
20
|
end
|
21
21
|
|
22
|
-
|
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
|