berkshelf 2.0.0.beta → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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