berkshelf 2.0.0.beta → 2.0.0

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 (92) hide show
  1. data/CHANGELOG.md +19 -1
  2. data/CONTRIBUTING.md +1 -3
  3. data/Gemfile +0 -20
  4. data/Guardfile +3 -3
  5. data/LICENSE +6 -5
  6. data/README.md +1 -0
  7. data/Thorfile +48 -67
  8. data/berkshelf.gemspec +48 -37
  9. data/features/apply_command.feature +17 -11
  10. data/features/config.feature +11 -11
  11. data/features/configure_command.feature +8 -8
  12. data/features/contingent_command.feature +37 -8
  13. data/features/cookbook_command.feature +17 -14
  14. data/features/groups_install.feature +24 -20
  15. data/features/install_command.feature +24 -33
  16. data/features/licenses.feature +112 -0
  17. data/features/list_command.feature +17 -5
  18. data/features/lockfile.feature +307 -188
  19. data/features/outdated_command.feature +1 -4
  20. data/features/package_command.feature +41 -19
  21. data/features/shelf/list.feature +39 -0
  22. data/features/shelf/show.feature +152 -0
  23. data/features/shelf/uninstall.feature +103 -0
  24. data/features/show_command.feature +49 -17
  25. data/features/step_definitions/filesystem_steps.rb +12 -3
  26. data/features/step_definitions/utility_steps.rb +0 -1
  27. data/features/support/env.rb +11 -4
  28. data/features/update_command.feature +22 -10
  29. data/features/upload_command.feature +174 -127
  30. data/features/vendor_install.feature +6 -6
  31. data/generator_files/Berksfile.erb +1 -1
  32. data/generator_files/metadata.rb.erb +7 -7
  33. data/lib/berkshelf.rb +39 -27
  34. data/lib/berkshelf/base_generator.rb +2 -3
  35. data/lib/berkshelf/berksfile.rb +69 -17
  36. data/lib/berkshelf/cached_cookbook.rb +17 -1
  37. data/lib/berkshelf/chef.rb +2 -4
  38. data/lib/berkshelf/chef/config.rb +51 -75
  39. data/lib/berkshelf/chef/cookbook.rb +1 -2
  40. data/lib/berkshelf/chef/cookbook/chefignore.rb +1 -1
  41. data/lib/berkshelf/cli.rb +144 -194
  42. data/lib/berkshelf/command.rb +11 -12
  43. data/lib/berkshelf/commands/shelf.rb +130 -0
  44. data/lib/berkshelf/commands/test_command.rb +11 -0
  45. data/lib/berkshelf/community_rest.rb +1 -2
  46. data/lib/berkshelf/config.rb +14 -10
  47. data/lib/berkshelf/cookbook_generator.rb +30 -24
  48. data/lib/berkshelf/cookbook_source.rb +1 -1
  49. data/lib/berkshelf/cookbook_store.rb +0 -1
  50. data/lib/berkshelf/core_ext.rb +1 -1
  51. data/lib/berkshelf/core_ext/file.rb +1 -1
  52. data/lib/berkshelf/downloader.rb +3 -1
  53. data/lib/berkshelf/errors.rb +128 -53
  54. data/lib/berkshelf/formatters.rb +2 -6
  55. data/lib/berkshelf/formatters/human_readable.rb +8 -2
  56. data/lib/berkshelf/formatters/json.rb +7 -1
  57. data/lib/berkshelf/formatters/null.rb +0 -1
  58. data/lib/berkshelf/git.rb +16 -16
  59. data/lib/berkshelf/init_generator.rb +28 -26
  60. data/lib/berkshelf/location.rb +12 -11
  61. data/lib/berkshelf/locations/chef_api_location.rb +2 -2
  62. data/lib/berkshelf/locations/git_location.rb +0 -1
  63. data/lib/berkshelf/locations/github_location.rb +0 -1
  64. data/lib/berkshelf/locations/path_location.rb +1 -2
  65. data/lib/berkshelf/locations/site_location.rb +3 -2
  66. data/lib/berkshelf/lockfile.rb +29 -10
  67. data/lib/berkshelf/mixin/config.rb +155 -0
  68. data/lib/berkshelf/mixin/logging.rb +0 -1
  69. data/lib/berkshelf/mixin/shellout.rb +71 -0
  70. data/lib/berkshelf/resolver.rb +7 -4
  71. data/lib/berkshelf/test.rb +1 -3
  72. data/lib/berkshelf/ui.rb +8 -4
  73. data/lib/berkshelf/version.rb +1 -1
  74. data/lib/thor/monkies/shell.rb +0 -1
  75. data/spec/config/berkshelf.pem +27 -0
  76. data/spec/config/knife.rb +12 -0
  77. data/spec/config/validator.pem +27 -0
  78. data/spec/spec_helper.rb +4 -8
  79. data/spec/support/chef_api.rb +14 -9
  80. data/spec/support/chef_server.rb +3 -4
  81. data/spec/unit/berkshelf/berksfile_spec.rb +1 -1
  82. data/spec/unit/berkshelf/cookbook_generator_spec.rb +12 -6
  83. data/spec/unit/berkshelf/cookbook_source_spec.rb +13 -1
  84. data/spec/unit/berkshelf/init_generator_spec.rb +5 -0
  85. data/spec/unit/chef/config_spec.rb +9 -10
  86. metadata +216 -93
  87. data/features/info_command.feature +0 -39
  88. data/features/open_command.feature +0 -36
  89. data/lib/berkshelf/cli_commands/test_command.rb +0 -11
  90. data/lib/berkshelf/mixin.rb +0 -10
  91. data/lib/berkshelf/mixin/path_helpers.rb +0 -30
  92. data/spec/support/knife.rb +0 -18
@@ -1,9 +1,8 @@
1
1
  module Berkshelf
2
2
  # This superclass is responsible for handling common command methods and options.
3
- #
4
- # @author Seth Vargo <sethvargo@gmail.com>
5
3
  module Command
6
4
  # Initialize a new instance of the parent class
5
+ #
7
6
  # @param [Hash] options
8
7
  # the list of options to pass to the installer
9
8
  def initialize(options = {})
@@ -18,26 +17,26 @@ module Berkshelf
18
17
  # if there are conflicting or invalid options
19
18
  def validate_options!
20
19
  if options[:except] && options[:only]
21
- raise ::Berkshelf::ArgumentError, "Cannot specify both :except and :only"
20
+ raise Berkshelf::ArgumentError, 'Cannot specify both :except and :only'
22
21
  end
23
22
 
24
23
  if options[:cookbooks] && (options[:except] || options[:only])
25
- ::Berkshelf.ui.warn ":cookbooks were supplied to update(), so :except and :only are ignored..."
24
+ Berkshelf.ui.warn ':cookbooks were supplied to update(), so :except and :only are ignored...'
26
25
  end
27
26
  end
28
27
 
29
28
  # Ensure the berkshelf directory is created and accessible.
30
29
  def ensure_berkshelf_directory!
31
- unless ::File.exists?(Berkshelf.berkshelf_path)
32
- ::FileUtils.mkdir_p(Berkshelf.berkshelf_path)
30
+ unless File.exists?(Berkshelf.berkshelf_path)
31
+ FileUtils.mkdir_p(Berkshelf.berkshelf_path)
33
32
  end
34
33
  end
35
34
 
36
35
  # Check for the presence of a Berksfile. Berkshelf cannot do anything
37
36
  # without the presence of a Berksfile.lock.
38
37
  def ensure_berksfile!
39
- unless ::File.exists?(options[:berksfile])
40
- raise ::Berkshelf::BerksfileNotFound, "No #{options[:berksfile]} was found at ."
38
+ unless File.exists?(options[:berksfile])
39
+ raise Berkshelf::BerksfileNotFound, "No #{options[:berksfile]} was found at ."
41
40
  end
42
41
  end
43
42
 
@@ -45,8 +44,8 @@ module Berkshelf
45
44
  # an exception to require at least one definition.
46
45
  def ensure_berksfile_content!
47
46
  begin
48
- unless ::File.read(options[:berksfile]).size > 1
49
- raise ::Berksfile::BerksfileNotFound, "Your #{options[:berksfile]} is empty! You need at least one cookbook definition."
47
+ unless File.read(options[:berksfile]).size > 1
48
+ raise Berkshelf::BerksfileNotFound, "Your #{options[:berksfile]} is empty! You need at least one cookbook definition."
50
49
  end
51
50
  rescue Errno::ENOENT
52
51
  ensure_berksfile!
@@ -75,7 +74,7 @@ module Berkshelf
75
74
  # @return [Berkshelf::Berksfile]
76
75
  # the current Berksfile
77
76
  def berksfile
78
- @berksfile ||= ::Berkshelf::Berksfile.from_file(options[:berksfile])
77
+ @berksfile ||= Berkshelf::Berksfile.from_file(options[:berksfile])
79
78
  end
80
79
 
81
80
  # Filter the list of sources from the options passed to the installer.
@@ -100,7 +99,7 @@ module Berkshelf
100
99
  missing_cookbooks = (cookbooks - sources.map(&:name))
101
100
 
102
101
  unless missing_cookbooks.empty?
103
- raise ::Berkshelf::CookbookNotFound, "Could not find cookbooks #{missing_cookbooks.collect{|cookbook| "'#{cookbook}'"}.join(', ')} in any of the sources. #{missing_cookbooks.size == 1 ? 'Is it' : 'Are they' } in your Berksfile?"
102
+ raise Berkshelf::CookbookNotFound, "Could not find cookbooks #{missing_cookbooks.collect{|cookbook| "'#{cookbook}'"}.join(', ')} in any of the sources. #{missing_cookbooks.size == 1 ? 'Is it' : 'Are they' } in your Berksfile?"
104
103
  end
105
104
 
106
105
  sources.select { |source| options[:cookbooks].include?(source.name) }
@@ -0,0 +1,130 @@
1
+ module Berkshelf
2
+ # All tasks that operate on the Berkshelf shelf.
3
+ class Shelf < Thor
4
+ desc 'list', 'List all cookbooks and their versions'
5
+ def list
6
+ cookbooks = store.cookbooks.inject({}) do |hash, cookbook|
7
+ (hash[cookbook.cookbook_name] ||= []).push(cookbook.version)
8
+ hash
9
+ end
10
+
11
+ if cookbooks.empty?
12
+ Berkshelf.formatter.msg 'There are no cookbooks in the Berkshelf shelf'
13
+ else
14
+ Berkshelf.formatter.msg 'Cookbooks in the Berkshelf shelf:'
15
+ cookbooks.sort.each do |cookbook, versions|
16
+ Berkshelf.formatter.msg(" * #{cookbook} (#{versions.sort.join(', ')})")
17
+ end
18
+ end
19
+ end
20
+
21
+ method_option :version, aliases: '-v', type: :string, desc: 'Version to show'
22
+ desc 'show', 'Display information about a cookbook in the Berkshelf shelf'
23
+ def show(name)
24
+ cookbooks = find(name, options[:version])
25
+
26
+ if options[:version]
27
+ Berkshelf.formatter.msg "Displaying '#{name}' (#{options[:version]}) in the Berkshelf shelf:"
28
+ else
29
+ Berkshelf.formatter.msg "Displaying all versions of '#{name}' in the Berkshelf shelf:"
30
+ end
31
+
32
+ cookbooks.each do |cookbook|
33
+ Berkshelf.formatter.show(cookbook)
34
+ Berkshelf.formatter.msg("\n")
35
+ end
36
+ end
37
+
38
+ method_option :version, aliases: '-v', type: :string, desc: 'Version to remove'
39
+ method_option :force, aliases: '-f', type: :boolean, desc: 'Force removal, even if other cookbooks are contingent', default: false
40
+ desc 'uninstall', 'Remove a cookbook from the Berkshelf shelf'
41
+ def uninstall(name)
42
+ cookbooks = find(name, options[:version])
43
+ cookbooks.each { |c| uninstall_cookbook(c, options[:force]) }
44
+ end
45
+
46
+ no_tasks do
47
+ # Shortcut helper to the CookbookStore
48
+ #
49
+ # @return [Berkshelf::CookbookStore]
50
+ def store
51
+ Berkshelf.cookbook_store
52
+ end
53
+
54
+ # Find a cookbook in the store by name and version. If the no version
55
+ # is given, all cookbooks with the given name are returned. Otherwise,
56
+ # only the cookbook matching the given version is returned.
57
+ #
58
+ # @param [String] name
59
+ # the name of the cookbook to find
60
+ # @param [String, nil] version
61
+ # the version of the cookbook to find
62
+ #
63
+ # @raise [Berkshelf::CookbookNotFound]
64
+ # if the cookbook does not exist
65
+ #
66
+ # @return [Array<Berkshelf::CachedCookbook>]
67
+ # the list of cookbooks that match the parameters - this is always an
68
+ # array!
69
+ def find(name, version = nil)
70
+ cookbooks = if version
71
+ [store.cookbook(name, version)].compact
72
+ else
73
+ store.cookbooks(name).sort
74
+ end
75
+
76
+ if cookbooks.empty?
77
+ if version
78
+ raise Berkshelf::CookbookNotFound, "Cookbook '#{name}' (#{version}) is not in the Berkshelf shelf"
79
+ else
80
+ raise Berkshelf::CookbookNotFound, "Cookbook '#{name}' is not in the Berkshelf shelf"
81
+ end
82
+ end
83
+
84
+ cookbooks
85
+ end
86
+
87
+ # Uninstall a cookbook from the CookbookStore. This method assumes the
88
+ # cookbook exists, so perform that validation elsewhere.
89
+ #
90
+ # By default, this method will request confirmation from the user to
91
+ # delete a cookbook that is a dependency on another (contingent). This
92
+ # behavior can be overridden by setting the second parameter `force` to
93
+ # true.
94
+ #
95
+ # @param [Berkshelf::CachedCookbook] cookbook
96
+ # the cookbook to uninstall
97
+ # @param [Boolean] force
98
+ # if false, the user will need to confirm before uninstalling
99
+ # if contingencies exist
100
+ def uninstall_cookbook(cookbook, force = false)
101
+ unless options[:force] || (contingent = contingencies(cookbook)).empty?
102
+ contingent = contingent.map { |c| "#{c.cookbook_name} (#{c.version})" }.join(', ')
103
+ confirm = Berkshelf.ui.ask("[#{contingent}] depend on #{cookbook.cookbook_name}.\n\nAre you sure you want to continue? (y/N)")
104
+
105
+ exit unless confirm.upcase[0] == 'Y'
106
+ end
107
+
108
+ FileUtils.rm_rf(cookbook.path)
109
+ Berkshelf.formatter.msg("Successfully uninstalled #{cookbook.cookbook_name} (#{cookbook.version})")
110
+ end
111
+
112
+ # Return a list of all cookbooks which are contingent upon the given
113
+ # cookbook.
114
+ #
115
+ # @param [Berkshelf::CachedCookbook] cookbook
116
+ # the cached cookbook to search for dependencies against
117
+ #
118
+ # @return [Array<Berkshelf::CachedCookbook>]
119
+ # the list of cookbooks which depend on the parameter
120
+ def contingencies(cookbook)
121
+ store.cookbooks.select { |c| c.dependencies.include?(cookbook.cookbook_name) }
122
+ end
123
+ end
124
+ end
125
+
126
+ class Cli < Thor
127
+ desc 'shelf SUBCOMMAND', 'Interact with the cookbook store'
128
+ subcommand 'shelf', Berkshelf::Shelf
129
+ end
130
+ end
@@ -0,0 +1,11 @@
1
+ require 'kitchen/cli'
2
+
3
+ module Berkshelf
4
+ class TestCommand < Kitchen::CLI
5
+ namespace "test"
6
+ end
7
+
8
+ class Cli < Thor
9
+ register(Berkshelf::TestCommand, 'test', 'test [COMMAND]', 'Testing tasks for your cookbook', hide: true)
10
+ end
11
+ end
@@ -3,7 +3,6 @@ require 'retryable'
3
3
  require 'addressable/uri'
4
4
 
5
5
  module Berkshelf
6
- # @author Jamie Winsor <reset@riotgames.com>
7
6
  class CommunityREST < Faraday::Connection
8
7
  class << self
9
8
  # @param [String] target
@@ -74,7 +73,7 @@ module Berkshelf
74
73
  @retry_interval = options[:retry_interval]
75
74
 
76
75
  builder = Faraday::Builder.new do |b|
77
- b.response :json
76
+ b.response :parse_json
78
77
  b.request :retry,
79
78
  max: @retries,
80
79
  interval: @retry_interval,
@@ -1,8 +1,6 @@
1
1
  require 'chozo/config'
2
2
 
3
3
  module Berkshelf
4
- # @author Justin Campbell <justin.campbell@riotgames.com>
5
- # @author Jamie Winsor <reset@riotgames.com>
6
4
  class Config < Chozo::Config::JSON
7
5
  LOCATIONS = [
8
6
  File.join('.', '.berkshelf', 'config.json').freeze,
@@ -70,28 +68,34 @@ module Berkshelf
70
68
 
71
69
  attribute 'chef.chef_server_url',
72
70
  type: String,
73
- default: Berkshelf::Chef::Config.instance[:chef_server_url]
71
+ default: Berkshelf.chef_config[:chef_server_url]
74
72
  attribute 'chef.validation_client_name',
75
73
  type: String,
76
- default: Berkshelf::Chef::Config.instance[:validation_client_name]
74
+ default: Berkshelf.chef_config[:validation_client_name]
77
75
  attribute 'chef.validation_key_path',
78
76
  type: String,
79
- default: Berkshelf::Chef::Config.instance[:validation_key]
77
+ default: Berkshelf.chef_config[:validation_key]
80
78
  attribute 'chef.client_key',
81
79
  type: String,
82
- default: Berkshelf::Chef::Config.instance[:client_key]
80
+ default: Berkshelf.chef_config[:client_key]
83
81
  attribute 'chef.node_name',
84
82
  type: String,
85
- default: Berkshelf::Chef::Config.instance[:node_name]
83
+ default: Berkshelf.chef_config[:node_name]
86
84
  attribute 'cookbook.copyright',
87
85
  type: String,
88
- default: Berkshelf::Chef::Config.instance[:cookbook_copyright]
86
+ default: Berkshelf.chef_config[:cookbook_copyright]
89
87
  attribute 'cookbook.email',
90
88
  type: String,
91
- default: Berkshelf::Chef::Config.instance[:cookbook_email]
89
+ default: Berkshelf.chef_config[:cookbook_email]
92
90
  attribute 'cookbook.license',
93
91
  type: String,
94
- default: Berkshelf::Chef::Config.instance[:cookbook_license]
92
+ default: Berkshelf.chef_config[:cookbook_license]
93
+ attribute 'allowed_licenses',
94
+ type: Array,
95
+ default: Array.new
96
+ attribute 'raise_license_exception',
97
+ type: Boolean,
98
+ default: false
95
99
  attribute 'vagrant.vm.box',
96
100
  type: String,
97
101
  default: 'Berkshelf-CentOS-6.3-x86_64-minimal',
@@ -1,6 +1,7 @@
1
1
  module Berkshelf
2
- # @author Jamie Winsor <reset@riotgames.com>
3
2
  class CookbookGenerator < BaseGenerator
3
+ require_relative 'config'
4
+
4
5
  argument :name,
5
6
  type: :string,
6
7
  required: true
@@ -13,6 +14,11 @@ module Berkshelf
13
14
  type: :boolean,
14
15
  default: false
15
16
 
17
+ class_option :skip_test_kitchen,
18
+ type: :boolean,
19
+ default: false,
20
+ desc: 'Skip adding a testing environment to your cookbook'
21
+
16
22
  class_option :foodcritic,
17
23
  type: :boolean,
18
24
  default: false
@@ -42,19 +48,19 @@ module Berkshelf
42
48
  default: Berkshelf::Config.instance.cookbook.email
43
49
 
44
50
  def generate
45
- empty_directory target.join("files/default")
46
- empty_directory target.join("templates/default")
47
- empty_directory target.join("attributes")
48
- empty_directory target.join("definitions")
49
- empty_directory target.join("libraries")
50
- empty_directory target.join("providers")
51
- empty_directory target.join("recipes")
52
- empty_directory target.join("resources")
53
-
54
- template "default_recipe.erb", target.join("recipes/default.rb")
55
- template "metadata.rb.erb", target.join("metadata.rb")
56
- template license_file, target.join("LICENSE")
57
- template "README.md.erb", target.join("README.md")
51
+ empty_directory target.join('files/default')
52
+ empty_directory target.join('templates/default')
53
+ empty_directory target.join('attributes')
54
+ empty_directory target.join('definitions')
55
+ empty_directory target.join('libraries')
56
+ empty_directory target.join('providers')
57
+ empty_directory target.join('recipes')
58
+ empty_directory target.join('resources')
59
+
60
+ template 'default_recipe.erb', target.join('recipes/default.rb')
61
+ template 'metadata.rb.erb', target.join('metadata.rb')
62
+ template license_file, target.join('LICENSE')
63
+ template 'README.md.erb', target.join('README.md')
58
64
 
59
65
  Berkshelf::InitGenerator.new([target], options.merge(default_options)).invoke_all
60
66
  end
@@ -67,11 +73,11 @@ module Berkshelf
67
73
 
68
74
  def license_name
69
75
  case options[:license]
70
- when "apachev2"; "Apache 2.0"
71
- when "gplv2"; "GNU Public License 2.0"
72
- when "gplv3"; "GNU Public License 3.0"
73
- when "mit"; "MIT"
74
- when "reserved"; "All rights reserved"
76
+ when 'apachev2'; 'Apache 2.0'
77
+ when 'gplv2'; 'GNU Public License 2.0'
78
+ when 'gplv3'; 'GNU Public License 3.0'
79
+ when 'mit'; 'MIT'
80
+ when 'reserved'; 'All rights reserved'
75
81
  else
76
82
  raise Berkshelf::InternalError, "Unknown license: '#{options[:license]}'"
77
83
  end
@@ -83,11 +89,11 @@ module Berkshelf
83
89
 
84
90
  def license_file
85
91
  case options[:license]
86
- when "apachev2"; "licenses/apachev2.erb"
87
- when "gplv2"; "licenses/gplv2.erb"
88
- when "gplv3"; "licenses/gplv3.erb"
89
- when "mit"; "licenses/mit.erb"
90
- when "reserved"; "licenses/reserved.erb"
92
+ when 'apachev2'; 'licenses/apachev2.erb'
93
+ when 'gplv2'; 'licenses/gplv2.erb'
94
+ when 'gplv3'; 'licenses/gplv3.erb'
95
+ when 'mit'; 'licenses/mit.erb'
96
+ when 'reserved'; 'licenses/reserved.erb'
91
97
  else
92
98
  raise Berkshelf::InternalError, "Unknown license: '#{options[:license]}'"
93
99
  end
@@ -1,5 +1,4 @@
1
1
  module Berkshelf
2
- # @author Jamie Winsor <reset@riotgames.com>
3
2
  class CookbookSource
4
3
  class << self
5
4
  @@valid_options = [:constraint, :locations, :group, :locked_version]
@@ -206,6 +205,7 @@ module Berkshelf
206
205
  if location.kind_of?(GitLocation)
207
206
  h[:git] = location.uri
208
207
  h[:ref] = location.ref
208
+ h[:rel] = location.rel if location.rel
209
209
  end
210
210
 
211
211
  # Path is intentionally left relative here for cross-team compatibility
@@ -1,7 +1,6 @@
1
1
  require 'fileutils'
2
2
 
3
3
  module Berkshelf
4
- # @author Jamie Winsor <reset@riotgames.com>
5
4
  class CookbookStore
6
5
  # @return [String]
7
6
  # filepath to where cookbooks are stored
@@ -1,3 +1,3 @@
1
1
  Dir["#{File.dirname(__FILE__)}/core_ext/*.rb"].sort.each do |path|
2
- require "berkshelf/core_ext/#{File.basename(path, '.rb')}"
2
+ require_relative "core_ext/#{File.basename(path, '.rb')}"
3
3
  end
@@ -7,7 +7,7 @@ class File
7
7
  #
8
8
  # @return [Boolean]
9
9
  def cookbook?(path)
10
- File.exists?(File.join(path, "metadata.rb"))
10
+ File.exists?(File.join(path, 'metadata.rb'))
11
11
  end
12
12
  alias_method :chef_cookbook?, :cookbook?
13
13
  end
@@ -1,6 +1,8 @@
1
1
  module Berkshelf
2
- # @author Jamie Winsor <reset@riotgames.com>
3
2
  class Downloader
3
+ require_relative 'cookbook_source'
4
+ require_relative 'location'
5
+
4
6
  extend Forwardable
5
7
 
6
8
  DEFAULT_LOCATIONS = [
@@ -15,7 +15,7 @@ module Berkshelf
15
15
  class ArgumentError < InternalError; end
16
16
  class AbstractFunction < InternalError
17
17
  def to_s
18
- "Function must be implemented on includer"
18
+ 'Function must be implemented on includer'
19
19
  end
20
20
  end
21
21
 
@@ -25,51 +25,67 @@ module Berkshelf
25
25
  class CookbookNotFound < BerkshelfError; status_code(103); end
26
26
  class GitError < BerkshelfError
27
27
  status_code(104)
28
- attr_reader :stderr
29
28
 
29
+ # @param [#to_s] stderr
30
+ # the error that came from stderr
30
31
  def initialize(stderr)
31
- @stderr = stderr
32
+ @stderr = stderr.to_s
33
+ end
34
+
35
+ # A common header for all git errors. The #to_s method should
36
+ # use this before outputting any specific errors.
37
+ #
38
+ # @return [String]
39
+ def header
40
+ 'An error occurred during Git execution:'
32
41
  end
33
42
 
34
43
  def to_s
35
- out = "An error occured during Git execution:\n"
36
- out << stderr.prepend_each("\n", "\t")
44
+ [
45
+ header,
46
+ "",
47
+ " " + @stderr.to_s.split("\n").map(&:strip).join("\n "),
48
+ ""
49
+ ].join("\n")
37
50
  end
38
51
  end
39
- class PrivateGitRepo < GitError; end
40
- class AmbiguousGitRef < GitError
41
- attr_reader :ref
42
52
 
53
+ class AmbiguousGitRef < GitError
43
54
  def initialize(ref)
44
55
  @ref = ref
45
56
  end
46
57
 
47
58
  def to_s
48
- out = "An error occurred during Git execution:\n"
49
- out << "Ambiguous Git ref: #{ref}"
59
+ [
60
+ header,
61
+ "",
62
+ " Ambiguous Git ref: '#{@ref}'",
63
+ "",
64
+ ].join("\n")
50
65
  end
51
66
  end
52
- class InvalidGitRef < GitError
53
- attr_reader :ref
54
67
 
68
+ class InvalidGitRef < GitError
55
69
  def initialize(ref)
56
70
  @ref = ref
57
71
  end
58
72
 
59
73
  def to_s
60
- out = "An error occurred during Git execution:\n"
61
- out << "Invalid Git ref: #{ref}"
74
+ [
75
+ header,
76
+ "",
77
+ " Invalid Git ref: '#{@ref}'",
78
+ "",
79
+ ].join("\n")
62
80
  end
63
81
  end
64
82
 
65
83
  class DuplicateSourceDefined < BerkshelfError; status_code(105); end
66
84
  class NoSolution < BerkshelfError; status_code(106); end
67
85
  class CookbookSyntaxError < BerkshelfError; status_code(107); end
68
- class BerksConfigNotFound < BerkshelfError; status_code(109); end
69
86
 
70
87
  class InvalidGitURI < BerkshelfError
71
88
  status_code(110)
72
- attr_reader :uri
73
89
 
74
90
  # @param [String] uri
75
91
  def initialize(uri)
@@ -77,13 +93,12 @@ module Berkshelf
77
93
  end
78
94
 
79
95
  def to_s
80
- "'#{uri}' is not a valid Git URI."
96
+ "'#{@uri}' is not a valid Git URI"
81
97
  end
82
98
  end
83
99
 
84
100
  class UnknownGitHubProtocol < BerkshelfError
85
101
  status_code(110)
86
- attr_reader :protocol
87
102
 
88
103
  # @param [String] protocol
89
104
  def initialize(protocol)
@@ -91,7 +106,7 @@ module Berkshelf
91
106
  end
92
107
 
93
108
  def to_s
94
- "'#{self.protocol}' is not a supported Git protocol for the 'github' location key. Please use 'git' instead."
109
+ "'#{@protocol}' is not supported for the 'github' location key - please use 'git' instead"
95
110
  end
96
111
  end
97
112
 
@@ -99,13 +114,14 @@ module Berkshelf
99
114
  status_code(110)
100
115
 
101
116
  def to_s
102
- "Could not find a Git executable in your path. Please add it and try again."
117
+ 'Could not find a Git executable in your path - please add it and try again'
103
118
  end
104
119
  end
105
120
 
106
121
  class ConstraintNotSatisfied < BerkshelfError; status_code(111); end
107
122
  class InvalidChefAPILocation < BerkshelfError; status_code(112); end
108
123
  class BerksfileReadError < BerkshelfError
124
+ # @param [#status_code] original_error
109
125
  def initialize(original_error)
110
126
  @original_error = original_error
111
127
  end
@@ -115,18 +131,19 @@ module Berkshelf
115
131
  def status_code
116
132
  @original_error.respond_to?(:status_code) ? @original_error.status_code : 113
117
133
  end
134
+
135
+ def to_s
136
+ [
137
+ "An error occurred while reading the Berksfile:",
138
+ "",
139
+ " " + @original_error.to_s.split("\n").map(&:strip).join("\n "),
140
+ ].join("\n")
141
+ end
118
142
  end
119
143
 
120
- # @author Seth Vargo <sethvargo@gmail.com>
121
144
  class MismatchedCookbookName < BerkshelfError
122
145
  status_code(114)
123
146
 
124
- # @return [Berkshelf::Location]
125
- attr_reader :location
126
-
127
- # @return [Berkshelf::CachedCookbook]
128
- attr_reader :cached_cookbook
129
-
130
147
  # @param [Berkshelf::Location] location
131
148
  # the location that is mismatched
132
149
  # @param [Berkshelf::CachedCookbook] cached_cookbook
@@ -140,13 +157,13 @@ module Berkshelf
140
157
  [
141
158
  "In your Berksfile, you have:",
142
159
  "",
143
- " cookbook '#{location.name}'",
160
+ " cookbook '#{@location.name}'",
144
161
  "",
145
- "But that cookbook is actually named '#{cached_cookbook.cookbook_name}'.",
162
+ "But that cookbook is actually named '#{@cached_cookbook.cookbook_name}'",
146
163
  "",
147
- "This can cause potentially unwanted side-effects in the future.",
164
+ "This can cause potentially unwanted side-effects in the future",
148
165
  "",
149
- "NOTE: If you don't explicitly set the `name` attribute in the metadata, the name of the directory will be used!"
166
+ "NOTE: If you don't explicitly set the `name` attribute in the metadata, the name of the directory will be used!",
150
167
  ].join("\n")
151
168
  end
152
169
  end
@@ -159,15 +176,10 @@ module Berkshelf
159
176
  end
160
177
 
161
178
  def to_s
162
- strings = ["Invalid configuration:"]
163
-
164
- @errors.each do |key, errors|
165
- errors.each do |error|
166
- strings << " #{key} #{error}"
167
- end
168
- end
169
-
170
- strings.join "\n"
179
+ [
180
+ 'Invalid configuration:',
181
+ @errors.map { |key, errors| errors.map { |error| " #{key} #{error}" } },
182
+ ].join("\n")
171
183
  end
172
184
  end
173
185
 
@@ -200,7 +212,7 @@ module Berkshelf
200
212
  "does not satisfy the version constraint:",
201
213
  " #{@cached_cookbook.cookbook_name} (#{@location.version_constraint})",
202
214
  "",
203
- "This occurs when the Chef Server has a cookbook with a missing/mis-matched version number in its `metadata.rb`."
215
+ "This occurs when the Chef Server has a cookbook with a missing/mis-matched version number in its `metadata.rb`",
204
216
  ].join("\n")
205
217
  end
206
218
  end
@@ -219,16 +231,17 @@ module Berkshelf
219
231
  end
220
232
 
221
233
  def to_s
222
- "Unknown site shortname: #{@shortname.inspect}. Supported shortnames are: #{SiteLocation::SHORTNAMES.keys.map(&:inspect).join(',')}"
234
+ [
235
+ "Unknown site shortname '#{@shortname}' - supported shortnames are:",
236
+ "",
237
+ " * " + SiteLocation::SHORTNAMES.keys.join("\n * "),
238
+ ].join("\n")
223
239
  end
224
240
  end
225
241
 
226
242
  class OutdatedCookbookSource < BerkshelfError
227
243
  status_code(128)
228
244
 
229
- # @return [Berkshelf::CookbookSource]
230
- attr_reader :locked_source, :source
231
-
232
245
  # @param [Berkshelf::CookbookSource] source
233
246
  # the cookbook source that is outdated
234
247
  def initialize(locked_source, source)
@@ -238,14 +251,14 @@ module Berkshelf
238
251
 
239
252
  def to_s
240
253
  [
241
- "Berkshelf could not find compatible versions for cookbook '#{source.name}':",
254
+ "Berkshelf could not find compatible versions for cookbook '#{@source.name}':",
242
255
  " In Berksfile:",
243
- " #{locked_source.name} (#{locked_source.locked_version})",
256
+ " #{@locked_source.name} (#{@locked_source.locked_version})",
244
257
  "",
245
258
  " In Berksfile.lock:",
246
- " #{source.name} (#{source.version_constraint})",
259
+ " #{@source.name} (#{@source.version_constraint})",
247
260
  "",
248
- "Try running `berks update #{source.name}, which will try to find '#{source.name}' matching '#{source.version_constraint}'."
261
+ "Try running `berks update #{@source.name}, which will try to find '#{@source.name}' matching '#{@source.version_constraint}'.",
249
262
  ].join("\n")
250
263
  end
251
264
  end
@@ -258,7 +271,7 @@ module Berkshelf
258
271
  end
259
272
 
260
273
  def to_s
261
- %Q[The environment "#{@environment_name}" does not exist.]
274
+ "The environment '#{@environment_name}' does not exist"
262
275
  end
263
276
  end
264
277
 
@@ -266,11 +279,10 @@ module Berkshelf
266
279
  status_code(130)
267
280
 
268
281
  def to_s
269
- "There was an error connecting to the chef server."
282
+ 'There was an error connecting to the Chef Server'
270
283
  end
271
284
  end
272
285
 
273
- # @author Seth Vargo <sethvargo@gmail.com>
274
286
  class UnknownCompressionType < BerkshelfError
275
287
  status_code(131)
276
288
 
@@ -279,8 +291,71 @@ module Berkshelf
279
291
  end
280
292
 
281
293
  def to_s
282
- "The file at '#{@destination}' is not a known compression type!"
294
+ "The file at '#{@destination}' is not a known compression type"
295
+ end
296
+ end
297
+
298
+ # Raised when a cookbook or its recipes contain a space or invalid
299
+ # character in the path.
300
+ #
301
+ # @param [Berkshelf::CachedCookbook] cookbook
302
+ # the cookbook that failed validation
303
+ # @param [Array<#to_s>] files
304
+ # the list of files that were not valid
305
+ class InvalidCookbookFiles < BerkshelfError
306
+ status_code(132)
307
+
308
+ def initialize(cookbook, files)
309
+ @cookbook = cookbook
310
+ @files = files
311
+ end
312
+
313
+ def to_s
314
+ [
315
+ "The cookbook '#{@cookbook.cookbook_name}' has invalid filenames:",
316
+ "",
317
+ " " + @files.map(&:to_s).join("\n "),
318
+ "",
319
+ "Please note, spaces are not a valid character in filenames",
320
+ ].join("\n")
321
+ end
322
+ end
323
+
324
+ # Raised when a CachedCookbook has a license file that isn't allowed
325
+ # by the Berksfile.
326
+ #
327
+ # @param [Berkshelf::CachedCookbook] cookbook
328
+ # the cookbook that failed license validation
329
+ class LicenseNotAllowed < BerkshelfError
330
+ status_code(133)
331
+
332
+ def initialize(cookbook)
333
+ @cookbook = cookbook
334
+ end
335
+
336
+ def to_s
337
+ msg = "'#{@cookbook.cookbook_name}' has a license of '#{@cookbook.metadata.license}', but"
338
+ msg << " '#{@cookbook.metadata.license}' is not in your list of allowed licenses"
339
+ msg
283
340
  end
341
+ end
284
342
 
343
+ # Raised when a cookbook or its recipes contain a space or invalid
344
+ # character in the path.
345
+ class ConfigNotFound < BerkshelfError
346
+ status_code(133)
347
+
348
+ # @param [String] type
349
+ # the type of config that was not found (Berkshelf, Chef, etc)
350
+ # @param [#to_s] path
351
+ # the path to the specified Chef config that did not exist
352
+ def initialize(type, path)
353
+ @type = type.to_s
354
+ @path = path
355
+ end
356
+
357
+ def to_s
358
+ "No #{@type.capitalize} config file found at: '#{@path}'!"
359
+ end
285
360
  end
286
361
  end