berkshelf 0.1.1
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.
- data/.gitignore +20 -0
- data/.rbenv-version +1 -0
- data/Gemfile +3 -0
- data/Guardfile +23 -0
- data/LICENSE +22 -0
- data/README.rdoc +102 -0
- data/Thorfile +47 -0
- data/berkshelf.gemspec +39 -0
- data/features/config.sample.yml +3 -0
- data/features/init_command.feature +40 -0
- data/features/install.feature +55 -0
- data/features/lockfile.feature +22 -0
- data/features/step_definitions/chef_server_steps.rb +13 -0
- data/features/step_definitions/cli_steps.rb +56 -0
- data/features/step_definitions/filesystem_steps.rb +79 -0
- data/features/support/env.rb +55 -0
- data/features/update.feature +23 -0
- data/features/upload_command.feature +43 -0
- data/features/without.feature +25 -0
- data/lib/berkshelf.rb +90 -0
- data/lib/berkshelf/berksfile.rb +170 -0
- data/lib/berkshelf/cached_cookbook.rb +253 -0
- data/lib/berkshelf/cookbook_source.rb +139 -0
- data/lib/berkshelf/cookbook_source/git_location.rb +54 -0
- data/lib/berkshelf/cookbook_source/path_location.rb +27 -0
- data/lib/berkshelf/cookbook_source/site_location.rb +206 -0
- data/lib/berkshelf/cookbook_store.rb +77 -0
- data/lib/berkshelf/core_ext.rb +3 -0
- data/lib/berkshelf/core_ext/file.rb +14 -0
- data/lib/berkshelf/core_ext/kernel.rb +33 -0
- data/lib/berkshelf/core_ext/pathname.rb +24 -0
- data/lib/berkshelf/downloader.rb +92 -0
- data/lib/berkshelf/dsl.rb +39 -0
- data/lib/berkshelf/errors.rb +20 -0
- data/lib/berkshelf/generator_files/Berksfile.erb +3 -0
- data/lib/berkshelf/generator_files/chefignore +44 -0
- data/lib/berkshelf/git.rb +70 -0
- data/lib/berkshelf/init_generator.rb +38 -0
- data/lib/berkshelf/lockfile.rb +42 -0
- data/lib/berkshelf/resolver.rb +176 -0
- data/lib/berkshelf/tx_result.rb +12 -0
- data/lib/berkshelf/tx_result_set.rb +37 -0
- data/lib/berkshelf/uploader.rb +153 -0
- data/lib/berkshelf/version.rb +3 -0
- data/lib/chef/knife/berks_init.rb +29 -0
- data/lib/chef/knife/berks_install.rb +27 -0
- data/lib/chef/knife/berks_update.rb +23 -0
- data/lib/chef/knife/berks_upload.rb +39 -0
- data/spec/fixtures/Berksfile +3 -0
- data/spec/fixtures/cookbooks/example_cookbook-0.5.0/README.md +12 -0
- data/spec/fixtures/cookbooks/example_cookbook-0.5.0/metadata.rb +6 -0
- data/spec/fixtures/cookbooks/example_cookbook-0.5.0/recipes/default.rb +8 -0
- data/spec/fixtures/cookbooks/example_cookbook/README.md +12 -0
- data/spec/fixtures/cookbooks/example_cookbook/metadata.rb +6 -0
- data/spec/fixtures/cookbooks/example_cookbook/recipes/default.rb +8 -0
- data/spec/fixtures/cookbooks/invalid_ruby_files-1.0.0/recipes/default.rb +1 -0
- data/spec/fixtures/cookbooks/invalid_template_files-1.0.0/templates/default/broken.erb +1 -0
- data/spec/fixtures/cookbooks/nginx-0.100.5/README.md +77 -0
- data/spec/fixtures/cookbooks/nginx-0.100.5/attributes/default.rb +65 -0
- data/spec/fixtures/cookbooks/nginx-0.100.5/definitions/nginx_site.rb +35 -0
- data/spec/fixtures/cookbooks/nginx-0.100.5/files/default/mime.types +73 -0
- data/spec/fixtures/cookbooks/nginx-0.100.5/files/ubuntu/mime.types +73 -0
- data/spec/fixtures/cookbooks/nginx-0.100.5/libraries/nginxlib.rb +1 -0
- data/spec/fixtures/cookbooks/nginx-0.100.5/metadata.rb +91 -0
- data/spec/fixtures/cookbooks/nginx-0.100.5/providers/defprovider.rb +1 -0
- data/spec/fixtures/cookbooks/nginx-0.100.5/recipes/default.rb +59 -0
- data/spec/fixtures/cookbooks/nginx-0.100.5/resources/defresource.rb +1 -0
- data/spec/fixtures/cookbooks/nginx-0.100.5/templates/default/nginx.pill.erb +15 -0
- data/spec/fixtures/cookbooks/nginx-0.100.5/templates/default/plugins/nginx.rb.erb +66 -0
- data/spec/fixtures/lockfile_spec/with_lock/Berksfile +1 -0
- data/spec/fixtures/lockfile_spec/without_lock/Berksfile.lock +5 -0
- data/spec/spec_helper.rb +92 -0
- data/spec/support/chef_api.rb +27 -0
- data/spec/support/matchers/file_system_matchers.rb +115 -0
- data/spec/support/matchers/filepath_matchers.rb +19 -0
- data/spec/unit/berkshelf/cached_cookbook_spec.rb +420 -0
- data/spec/unit/berkshelf/cookbook_source/git_location_spec.rb +59 -0
- data/spec/unit/berkshelf/cookbook_source/path_location_spec.rb +34 -0
- data/spec/unit/berkshelf/cookbook_source/site_location_spec.rb +166 -0
- data/spec/unit/berkshelf/cookbook_source_spec.rb +194 -0
- data/spec/unit/berkshelf/cookbook_store_spec.rb +71 -0
- data/spec/unit/berkshelf/cookbookfile_spec.rb +160 -0
- data/spec/unit/berkshelf/downloader_spec.rb +82 -0
- data/spec/unit/berkshelf/dsl_spec.rb +42 -0
- data/spec/unit/berkshelf/git_spec.rb +63 -0
- data/spec/unit/berkshelf/init_generator_spec.rb +52 -0
- data/spec/unit/berkshelf/lockfile_spec.rb +25 -0
- data/spec/unit/berkshelf/resolver_spec.rb +126 -0
- data/spec/unit/berkshelf/tx_result_set_spec.rb +77 -0
- data/spec/unit/berkshelf/tx_result_spec.rb +21 -0
- data/spec/unit/berkshelf/uploader_spec.rb +71 -0
- data/spec/unit/berkshelf_spec.rb +29 -0
- metadata +411 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
require 'aruba/api'
|
|
2
|
+
|
|
3
|
+
World(Aruba::Api)
|
|
4
|
+
|
|
5
|
+
Given /^a cookbook named "(.*?)"$/ do |name|
|
|
6
|
+
steps %{
|
|
7
|
+
Given a directory named "#{name}"
|
|
8
|
+
And an empty file named "#{name}/metadata.rb"
|
|
9
|
+
}
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
Given /^I do not have a Berksfile$/ do
|
|
13
|
+
in_current_dir { FileUtils.rm_f(Berkshelf::DEFAULT_FILENAME) }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
Given /^I do not have a Berksfile\.lock$/ do
|
|
17
|
+
in_current_dir { FileUtils.rm_f(Berkshelf::Lockfile::DEFAULT_FILENAME) }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
Given /^the cookbook "(.*?)" has the file "(.*?)" with:$/ do |cookbook_name, file_name, content|
|
|
21
|
+
write_file(File.join(cookbook_name, file_name), content)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
Then /^the cookbook store should have the cookbooks:$/ do |cookbooks|
|
|
25
|
+
cookbooks.raw.each do |name, version|
|
|
26
|
+
cookbook_store.should have_structure {
|
|
27
|
+
directory "#{name}-#{version}" do
|
|
28
|
+
file "metadata.rb" do
|
|
29
|
+
contains version
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
Then /^the cookbook store should not have the cookbooks:$/ do |cookbooks|
|
|
37
|
+
cookbooks.raw.each do |name, version|
|
|
38
|
+
cookbook_store.should_not have_structure {
|
|
39
|
+
directory "#{name}-#{version}"
|
|
40
|
+
}
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
Then /^the cookbook "(.*?)" should have the following files:$/ do |name, files|
|
|
45
|
+
check_file_presence(files.raw.map{|file_row| File.join(name, file_row[0])}, true)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
Then /^the file "(.*?)" in the cookbook "(.*?)" should contain:$/ do |file_name, cookbook_name, content|
|
|
49
|
+
Pathname.new(current_dir).join(cookbook_name).should have_structure {
|
|
50
|
+
file "Berksfile" do
|
|
51
|
+
contains content
|
|
52
|
+
end
|
|
53
|
+
file ".chefignore"
|
|
54
|
+
}
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
Then /^the directory "(.*?)" should have the following files:$/ do |name, files|
|
|
58
|
+
check_file_presence(files.raw.map{|file_row| File.join(name, file_row[0])}, true)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
Then /^the directory "(.*?)" should not have the following files:$/ do |name, files|
|
|
62
|
+
check_file_presence(files.raw.map{|file_row| File.join(name, file_row[0])}, false)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
Then /^the file "(.*?)" in the directory "(.*?)" should not contain:$/ do |file_name, directory_name, content|
|
|
66
|
+
Pathname.new(current_dir).join(directory_name).should_not have_structure {
|
|
67
|
+
file "Berksfile" do
|
|
68
|
+
contains content
|
|
69
|
+
end
|
|
70
|
+
}
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
Then /^the current directory should have the following files:$/ do |files|
|
|
74
|
+
check_file_presence(files.raw.map{|file_row| file_row[0]}, true)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
Then /^the current directory should not have the following files:$/ do |files|
|
|
78
|
+
check_file_presence(files.raw.map{|file_row| file_row[0]}, false)
|
|
79
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'bundler'
|
|
3
|
+
Bundler.setup
|
|
4
|
+
require 'spork'
|
|
5
|
+
|
|
6
|
+
Spork.prefork do
|
|
7
|
+
require 'rspec'
|
|
8
|
+
require 'pp'
|
|
9
|
+
require 'aruba/cucumber'
|
|
10
|
+
require 'vcr'
|
|
11
|
+
|
|
12
|
+
APP_ROOT = File.expand_path('../../../', __FILE__)
|
|
13
|
+
|
|
14
|
+
ENV["BERKSHELF_PATH"] = File.join(APP_ROOT, "tmp", "berkshelf")
|
|
15
|
+
|
|
16
|
+
Dir[File.join(APP_ROOT, "spec/support/**/*.rb")].each {|f| require f}
|
|
17
|
+
|
|
18
|
+
Around do |scenario, block|
|
|
19
|
+
VCR.use_cassette(scenario.title) do
|
|
20
|
+
block.call
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
Before do
|
|
25
|
+
Chef::Config[:chef_server_url] = config['chef_server_url']
|
|
26
|
+
Chef::Config[:client_key] = config['client_key']
|
|
27
|
+
Chef::Config[:node_name] = config['node_name']
|
|
28
|
+
clean_cookbook_store
|
|
29
|
+
@aruba_io_wait_seconds = 5
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
Before('@slow_process') do
|
|
33
|
+
@aruba_timeout_seconds = 15
|
|
34
|
+
@aruba_io_wait_seconds = 10
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def cookbook_store
|
|
38
|
+
Pathname.new(ENV["BERKSHELF_PATH"])
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def clean_cookbook_store
|
|
42
|
+
FileUtils.rm_rf(cookbook_store)
|
|
43
|
+
FileUtils.mkdir_p(cookbook_store)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def config
|
|
47
|
+
@config ||= YAML.load(File.read(File.join(APP_ROOT, "features", "config.yml")))
|
|
48
|
+
rescue Errno::ENOENT
|
|
49
|
+
raise "Please create a config file at features/config.yml from the sample found at features/config.sample.yml"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
Spork.each_run do
|
|
54
|
+
require 'berkshelf'
|
|
55
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Feature: update
|
|
2
|
+
As a user
|
|
3
|
+
I want a way to update the versions without clearing out the files I've downloaded
|
|
4
|
+
So that I can update faster than a clean install
|
|
5
|
+
|
|
6
|
+
Scenario: knife berkshelf update
|
|
7
|
+
Given I write to "Berksfile" with:
|
|
8
|
+
"""
|
|
9
|
+
cookbook "mysql"
|
|
10
|
+
"""
|
|
11
|
+
Given I write to "Berksfile.lock" with:
|
|
12
|
+
"""
|
|
13
|
+
cookbook 'mysql', :locked_version => '0.0.1'
|
|
14
|
+
cookbook 'openssl', :locked_version => '0.0.1'
|
|
15
|
+
"""
|
|
16
|
+
When I run the update command
|
|
17
|
+
Then the file "Berksfile.lock" should contain exactly:
|
|
18
|
+
"""
|
|
19
|
+
cookbook 'mysql', :locked_version => '1.2.6'
|
|
20
|
+
cookbook 'openssl', :locked_version => '1.0.0'
|
|
21
|
+
cookbook 'chef_handler', :locked_version => '1.0.6'
|
|
22
|
+
cookbook 'windows', :locked_version => '1.3.0'
|
|
23
|
+
"""
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
Feature: upload command
|
|
2
|
+
As a Berkshelf CLI user
|
|
3
|
+
I need a way to upload cookbooks to a Chef server that I have installed into my Bookshelf
|
|
4
|
+
So they are available to Chef clients
|
|
5
|
+
|
|
6
|
+
@wip @slow_process
|
|
7
|
+
Scenario: running the upload command when the Sources in the Berksfile are already installed
|
|
8
|
+
Given I write to "Berksfile" with:
|
|
9
|
+
"""
|
|
10
|
+
cookbook "mysql", "1.2.4"
|
|
11
|
+
"""
|
|
12
|
+
And the Chef server does not have the cookbooks:
|
|
13
|
+
| mysql | 1.2.4 |
|
|
14
|
+
| openssl | 1.0.0 |
|
|
15
|
+
And I run the install command
|
|
16
|
+
When I run the upload command
|
|
17
|
+
Then the output should not contain "Using mysql (1.2.4)"
|
|
18
|
+
And the output should not contain "Using openssl (1.0.0)"
|
|
19
|
+
And the output should contain "Uploading mysql (1.2.4) to:"
|
|
20
|
+
And the output should contain "Uploading openssl (1.0.0) to:"
|
|
21
|
+
And the Chef server should have the cookbooks:
|
|
22
|
+
| mysql | 1.2.4 |
|
|
23
|
+
| openssl | 1.0.0 |
|
|
24
|
+
And the exit status should be 0
|
|
25
|
+
|
|
26
|
+
@slow_process
|
|
27
|
+
Scenario: running the upload command when the Sources in the Berksfile have not been installed
|
|
28
|
+
Given I write to "Berksfile" with:
|
|
29
|
+
"""
|
|
30
|
+
cookbook "mysql", "1.2.4"
|
|
31
|
+
"""
|
|
32
|
+
And the Chef server does not have the cookbooks:
|
|
33
|
+
| mysql | 1.2.4 |
|
|
34
|
+
| openssl | 1.0.0 |
|
|
35
|
+
When I run the upload command
|
|
36
|
+
Then the output should contain "Installing mysql (1.2.4) from site:"
|
|
37
|
+
And the output should contain "Installing openssl (1.0.0) from site:"
|
|
38
|
+
And the output should contain "Uploading mysql (1.2.4) to:"
|
|
39
|
+
And the output should contain "Uploading openssl (1.0.0) to:"
|
|
40
|
+
And the Chef server should have the cookbooks:
|
|
41
|
+
| mysql | 1.2.4 |
|
|
42
|
+
| openssl | 1.0.0 |
|
|
43
|
+
And the exit status should be 0
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Feature: --without block
|
|
2
|
+
As a user
|
|
3
|
+
I want to be able to exclude blocks in my Berksfile
|
|
4
|
+
So I can have cookbooks organized for use in different situations in a single Berksfile
|
|
5
|
+
|
|
6
|
+
@slow_process
|
|
7
|
+
Scenario: Exclude a block
|
|
8
|
+
Given I write to "Berksfile" with:
|
|
9
|
+
"""
|
|
10
|
+
group :notme do
|
|
11
|
+
cookbook "nginx", "= 0.101.2"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
cookbook "mysql", "= 1.2.4"
|
|
15
|
+
|
|
16
|
+
group :takeme do
|
|
17
|
+
cookbook "ntp", "= 1.1.8"
|
|
18
|
+
end
|
|
19
|
+
"""
|
|
20
|
+
When I run `knife berks install --without notme`
|
|
21
|
+
Then the cookbook store should have the cookbooks:
|
|
22
|
+
| mysql | 1.2.4 |
|
|
23
|
+
| ntp | 1.1.8 |
|
|
24
|
+
And the cookbook store should not have the cookbooks:
|
|
25
|
+
| nginx | 0.101.2 |
|
data/lib/berkshelf.rb
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
require 'pathname'
|
|
2
|
+
require 'dep_selector'
|
|
3
|
+
require 'zlib'
|
|
4
|
+
require 'archive/tar/minitar'
|
|
5
|
+
require 'chef/knife'
|
|
6
|
+
require 'chef/rest'
|
|
7
|
+
require 'chef/platform'
|
|
8
|
+
require 'chef/cookbook/metadata'
|
|
9
|
+
|
|
10
|
+
require 'berkshelf/version'
|
|
11
|
+
require 'berkshelf/core_ext'
|
|
12
|
+
require 'berkshelf/errors'
|
|
13
|
+
|
|
14
|
+
Chef::Config[:cache_options][:path] = Dir.mktmpdir
|
|
15
|
+
|
|
16
|
+
module Berkshelf
|
|
17
|
+
DEFAULT_STORE_PATH = File.expand_path("~/.berkshelf").freeze
|
|
18
|
+
DEFAULT_FILENAME = 'Berksfile'.freeze
|
|
19
|
+
|
|
20
|
+
autoload :DSL, 'berkshelf/dsl'
|
|
21
|
+
autoload :Git, 'berkshelf/git'
|
|
22
|
+
autoload :Berksfile, 'berkshelf/berksfile'
|
|
23
|
+
autoload :Lockfile, 'berkshelf/lockfile'
|
|
24
|
+
autoload :InitGenerator, 'berkshelf/init_generator'
|
|
25
|
+
autoload :CookbookSource, 'berkshelf/cookbook_source'
|
|
26
|
+
autoload :CookbookStore, 'berkshelf/cookbook_store'
|
|
27
|
+
autoload :CachedCookbook, 'berkshelf/cached_cookbook'
|
|
28
|
+
autoload :TXResult, 'berkshelf/tx_result'
|
|
29
|
+
autoload :TXResultSet, 'berkshelf/tx_result_set'
|
|
30
|
+
autoload :Downloader, 'berkshelf/downloader'
|
|
31
|
+
autoload :Uploader, 'berkshelf/uploader'
|
|
32
|
+
autoload :Resolver, 'berkshelf/resolver'
|
|
33
|
+
|
|
34
|
+
class << self
|
|
35
|
+
attr_accessor :ui
|
|
36
|
+
attr_accessor :cookbook_store
|
|
37
|
+
attr_accessor :downloader
|
|
38
|
+
|
|
39
|
+
def root
|
|
40
|
+
File.join(File.dirname(__FILE__), '..')
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def ui
|
|
44
|
+
@ui ||= Chef::Knife::UI.new(null_stream, null_stream, STDIN, {})
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Returns the filepath to the location Cookbooks will be downloaded to
|
|
48
|
+
# or uploaded from. By default this is '~/.berkshelf' but can be overridden
|
|
49
|
+
# by specifying a value for the ENV variable 'BERKSHELF_PATH'.
|
|
50
|
+
#
|
|
51
|
+
# @return [String]
|
|
52
|
+
def berkshelf_path
|
|
53
|
+
ENV["BERKSHELF_PATH"] || DEFAULT_STORE_PATH
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def cookbook_store
|
|
57
|
+
@cookbook_store ||= CookbookStore.new(berkshelf_path)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def downloader
|
|
61
|
+
@downloader ||= Downloader.new(cookbook_store)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Ascend the directory structure from the given path to find a
|
|
65
|
+
# metadata.rb file of a Chef Cookbook. If no metadata.rb file
|
|
66
|
+
# was found, nil is returned.
|
|
67
|
+
#
|
|
68
|
+
# @return [Pathname]
|
|
69
|
+
# path to metadata.rb
|
|
70
|
+
def find_metadata(path = Dir.pwd)
|
|
71
|
+
path = Pathname.new(path)
|
|
72
|
+
path.ascend do |potential_root|
|
|
73
|
+
if potential_root.entries.collect(&:to_s).include?('metadata.rb')
|
|
74
|
+
return potential_root.join('metadata.rb')
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
def null_stream
|
|
82
|
+
@null ||= begin
|
|
83
|
+
strm = STDOUT.clone
|
|
84
|
+
strm.reopen(RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL:' : '/dev/null')
|
|
85
|
+
strm.sync = true
|
|
86
|
+
strm
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
module Berkshelf
|
|
2
|
+
# @author Jamie Winsor <jamie@vialstudios.com>
|
|
3
|
+
class Berksfile
|
|
4
|
+
include DSL
|
|
5
|
+
|
|
6
|
+
class << self
|
|
7
|
+
def from_file(file)
|
|
8
|
+
content = File.read(file)
|
|
9
|
+
read(content)
|
|
10
|
+
rescue Errno::ENOENT => e
|
|
11
|
+
raise BerksfileNotFound, "No Berksfile or Berksfile.lock found at: #{file}"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def read(content)
|
|
15
|
+
object = new
|
|
16
|
+
object.instance_eval(content)
|
|
17
|
+
|
|
18
|
+
object
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @param [Array] sources
|
|
22
|
+
# an array of sources to filter
|
|
23
|
+
# @param [Array, Symbol] excluded
|
|
24
|
+
# an array of symbols or a symbol representing the group or group(s)
|
|
25
|
+
# to exclude
|
|
26
|
+
#
|
|
27
|
+
# @return [Array<Berkshelf::CookbookSource>]
|
|
28
|
+
# an array of sources that are not members of the excluded group(s)
|
|
29
|
+
def filter_sources(sources, excluded)
|
|
30
|
+
excluded = Array(excluded)
|
|
31
|
+
excluded.collect!(&:to_sym)
|
|
32
|
+
|
|
33
|
+
sources.select { |source| (excluded & source.groups).empty? }
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def initialize
|
|
38
|
+
@sources = Hash.new
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Add the given source to the sources array. A DuplicateSourceDefined
|
|
42
|
+
# exception will be raised if a source is added whose name conflicts
|
|
43
|
+
# with a source who has already been added.
|
|
44
|
+
#
|
|
45
|
+
# @param [Berkshelf::CookbookSource] source
|
|
46
|
+
# the source to add
|
|
47
|
+
#
|
|
48
|
+
# @return [Array<Berkshelf::CookbookSource]
|
|
49
|
+
def add_source(source)
|
|
50
|
+
raise DuplicateSourceDefined if has_source?(source)
|
|
51
|
+
@sources[source.to_s] = source
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @param [#to_s] source
|
|
55
|
+
# the source to remove
|
|
56
|
+
#
|
|
57
|
+
# @return [Berkshelf::CookbookSource]
|
|
58
|
+
def remove_source(source)
|
|
59
|
+
@sources.delete(source.to_s)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# @param [#to_s] source
|
|
63
|
+
# the source to check presence of
|
|
64
|
+
#
|
|
65
|
+
# @return [Boolean]
|
|
66
|
+
def has_source?(source)
|
|
67
|
+
@sources.has_key?(source.to_s)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# @param [Hash] options
|
|
71
|
+
# a hash of options
|
|
72
|
+
#
|
|
73
|
+
# Options:
|
|
74
|
+
# exclude: An array of groups to exclude from the returned Array
|
|
75
|
+
# of sources
|
|
76
|
+
#
|
|
77
|
+
# @return [Array<Berkshelf::CookbookSource>]
|
|
78
|
+
def sources(options = {})
|
|
79
|
+
l_sources = @sources.collect { |name, source| source }.flatten
|
|
80
|
+
|
|
81
|
+
if options[:exclude]
|
|
82
|
+
self.class.filter_sources(l_sources, options[:exclude])
|
|
83
|
+
else
|
|
84
|
+
l_sources
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# @return [Hash]
|
|
89
|
+
# a hash containing group names as keys and an array of CookbookSources
|
|
90
|
+
# that are a member of that group as values
|
|
91
|
+
#
|
|
92
|
+
# Example:
|
|
93
|
+
# {
|
|
94
|
+
# nautilus: [
|
|
95
|
+
# #<Berkshelf::CookbookSource @name="nginx">,
|
|
96
|
+
# #<Berkshelf::CookbookSource @name="mysql">,
|
|
97
|
+
# ],
|
|
98
|
+
# skarner: [
|
|
99
|
+
# #<Berkshelf::CookbookSource @name="nginx">
|
|
100
|
+
# ]
|
|
101
|
+
# }
|
|
102
|
+
def groups
|
|
103
|
+
{}.tap do |groups|
|
|
104
|
+
@sources.each_pair do |name, source|
|
|
105
|
+
source.groups.each do |group|
|
|
106
|
+
groups[group] ||= []
|
|
107
|
+
groups[group] << source
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# @param [String] name
|
|
114
|
+
# name of the source to return
|
|
115
|
+
#
|
|
116
|
+
# @return [Berkshelf::CookbookSource]
|
|
117
|
+
def [](name)
|
|
118
|
+
@sources[name]
|
|
119
|
+
end
|
|
120
|
+
alias_method :get_source, :[]
|
|
121
|
+
|
|
122
|
+
# @param [Hash] options
|
|
123
|
+
# a hash of options
|
|
124
|
+
#
|
|
125
|
+
# Options:
|
|
126
|
+
# without: An array of groups to exclude which will cause any sources
|
|
127
|
+
# marked as a member of the group to not be installed
|
|
128
|
+
def install(options = {})
|
|
129
|
+
resolver = Resolver.new(Berkshelf.downloader, sources(exclude: options[:without]))
|
|
130
|
+
resolver.resolve
|
|
131
|
+
write_lockfile(resolver.sources) unless lockfile_present?
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# @param [String] chef_server_url
|
|
135
|
+
# the full URL to the Chef Server to upload to
|
|
136
|
+
#
|
|
137
|
+
# Example:
|
|
138
|
+
# "https://api.opscode.com/organizations/vialstudios"
|
|
139
|
+
#
|
|
140
|
+
# @param [Hash] options
|
|
141
|
+
# a hash of options
|
|
142
|
+
#
|
|
143
|
+
# Options:
|
|
144
|
+
# without: An array of groups to exclude which will cause any sources
|
|
145
|
+
# marked as a member of the group to not be installed
|
|
146
|
+
# force: Upload the Cookbook even if the version already exists and is
|
|
147
|
+
# frozen on the target Chef Server
|
|
148
|
+
# freeze: Freeze the uploaded Cookbook on the Chef Server so that it
|
|
149
|
+
# cannot be overwritten
|
|
150
|
+
def upload(chef_server_url, options = {})
|
|
151
|
+
uploader = Uploader.new(Berkshelf.cookbook_store, chef_server_url)
|
|
152
|
+
resolver = Resolver.new(Berkshelf.downloader, sources(exclude: options[:without]))
|
|
153
|
+
|
|
154
|
+
resolver.resolve.each do |name, version|
|
|
155
|
+
Berkshelf.ui.info "Uploading #{name} (#{version}) to: #{chef_server_url}"
|
|
156
|
+
uploader.upload!(name, version, options)
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
private
|
|
161
|
+
|
|
162
|
+
def lockfile_present?
|
|
163
|
+
File.exist?(Berkshelf::Lockfile::DEFAULT_FILENAME)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def write_lockfile(sources)
|
|
167
|
+
Berkshelf::Lockfile.new(sources).write
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|