berkshelf 6.3.4 → 7.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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +10 -0
  4. data/.travis.yml +6 -8
  5. data/CHANGELOG.md +7 -19
  6. data/Gemfile +10 -0
  7. data/Gemfile.lock +68 -103
  8. data/Thorfile +2 -2
  9. data/berkshelf.gemspec +6 -8
  10. data/features/commands/info.feature +50 -8
  11. data/features/commands/shelf/show.feature +10 -40
  12. data/features/commands/upload.feature +73 -0
  13. data/features/commands/vendor.feature +43 -0
  14. data/features/json_formatter.feature +1 -1
  15. data/features/step_definitions/chef_server_steps.rb +2 -2
  16. data/features/step_definitions/filesystem_steps.rb +16 -0
  17. data/features/support/env.rb +11 -10
  18. data/lib/berkshelf.rb +15 -20
  19. data/lib/berkshelf/berksfile.rb +57 -47
  20. data/lib/berkshelf/cached_cookbook.rb +120 -19
  21. data/lib/berkshelf/chef_config_compat.rb +50 -0
  22. data/lib/berkshelf/chef_repo_universe.rb +2 -2
  23. data/lib/berkshelf/cli.rb +3 -42
  24. data/lib/berkshelf/community_rest.rb +40 -61
  25. data/lib/berkshelf/config.rb +92 -118
  26. data/lib/berkshelf/cookbook_store.rb +3 -2
  27. data/lib/berkshelf/core_ext/file.rb +1 -1
  28. data/lib/berkshelf/dependency.rb +1 -10
  29. data/lib/berkshelf/downloader.rb +19 -7
  30. data/lib/berkshelf/errors.rb +3 -0
  31. data/lib/berkshelf/location.rb +1 -1
  32. data/lib/berkshelf/locations/base.rb +1 -1
  33. data/lib/berkshelf/lockfile.rb +17 -13
  34. data/lib/berkshelf/logger.rb +62 -1
  35. data/lib/berkshelf/packager.rb +1 -1
  36. data/lib/berkshelf/resolver.rb +1 -1
  37. data/lib/berkshelf/ridley_compat.rb +22 -3
  38. data/lib/berkshelf/uploader.rb +76 -48
  39. data/lib/berkshelf/version.rb +1 -1
  40. data/spec/fixtures/cookbook-path-uploader/apt-2.3.6/metadata.rb +2 -0
  41. data/spec/fixtures/cookbook-path-uploader/build-essential-1.4.2/metadata.rb +2 -0
  42. data/spec/fixtures/cookbook-path-uploader/jenkins-2.0.3/metadata.rb +5 -0
  43. data/spec/fixtures/cookbook-path-uploader/jenkins-config-0.1.0/metadata.rb +4 -0
  44. data/spec/fixtures/cookbook-path-uploader/runit-1.5.8/metadata.rb +5 -0
  45. data/spec/fixtures/cookbook-path-uploader/yum-3.0.6/metadata.rb +2 -0
  46. data/spec/fixtures/cookbook-path-uploader/yum-epel-0.2.0/metadata.rb +3 -0
  47. data/spec/spec_helper.rb +2 -2
  48. data/spec/support/chef_api.rb +4 -4
  49. data/spec/support/chef_server.rb +1 -1
  50. data/spec/support/matchers/file_system_matchers.rb +1 -3
  51. data/spec/support/path_helpers.rb +1 -1
  52. data/spec/unit/berkshelf/berksfile_spec.rb +3 -24
  53. data/spec/unit/berkshelf/cached_cookbook_spec.rb +13 -15
  54. data/spec/unit/berkshelf/community_rest_spec.rb +3 -12
  55. data/spec/unit/berkshelf/config_spec.rb +4 -4
  56. data/spec/unit/berkshelf/downloader_spec.rb +6 -11
  57. data/spec/unit/berkshelf/lockfile_spec.rb +10 -7
  58. data/spec/unit/berkshelf/source_spec.rb +1 -1
  59. data/spec/unit/berkshelf/ssl_policies_spec.rb +2 -5
  60. data/spec/unit/berkshelf/uploader_spec.rb +60 -10
  61. data/spec/unit/berkshelf/visualizer_spec.rb +2 -2
  62. metadata +49 -102
  63. data/features/commands/cookbook.feature +0 -35
  64. data/features/commands/init.feature +0 -27
  65. data/features/config.feature +0 -111
  66. data/lib/berkshelf/base_generator.rb +0 -42
  67. data/lib/berkshelf/cookbook_generator.rb +0 -133
  68. data/lib/berkshelf/init_generator.rb +0 -195
  69. data/lib/berkshelf/streaming_file_adapter.rb +0 -22
  70. data/spec/unit/berkshelf/cookbook_generator_spec.rb +0 -108
  71. data/spec/unit/berkshelf/init_generator_spec.rb +0 -265
@@ -1,9 +1,8 @@
1
- require "berkshelf/streaming_file_adapter"
2
1
  require "retryable"
3
2
  require "mixlib/archive"
4
3
 
5
4
  module Berkshelf
6
- class CommunityREST < Faraday::Connection
5
+ class CommunityREST
7
6
  class << self
8
7
  # @param [String] target
9
8
  # file path to the tar.gz archive on disk
@@ -58,6 +57,8 @@ module Berkshelf
58
57
  # @return [Float]
59
58
  # time to wait between retries
60
59
  attr_reader :retry_interval
60
+ # @return [Berkshelf::RidleyCompat]
61
+ attr_reader :connection
61
62
 
62
63
  # @param [String] uri (CommunityREST::V1_API)
63
64
  # location of community site to connect to
@@ -67,23 +68,14 @@ module Berkshelf
67
68
  # @option options [Float] :retry_interval (0.5)
68
69
  # how often we should pause between retries
69
70
  def initialize(uri = V1_API, options = {})
70
- options = options.reverse_merge(retries: 5, retry_interval: 0.5, ssl: Berkshelf::Config.instance.ssl)
71
+ options = options.dup
72
+ options = { retries: 5, retry_interval: 0.5, ssl: Berkshelf::Config.instance.ssl }.merge(options)
71
73
  @api_uri = uri
74
+ options[:server_url] = uri
72
75
  @retries = options.delete(:retries)
73
76
  @retry_interval = options.delete(:retry_interval)
74
77
 
75
- options[:builder] ||= Faraday::RackBuilder.new do |b|
76
- b.response :parse_json
77
- b.response :follow_redirects
78
- b.request :retry,
79
- max: @retries,
80
- interval: @retry_interval,
81
- exceptions: [Faraday::Error::TimeoutError]
82
-
83
- b.use Berkshelf::StreamingFileAdapter
84
- end
85
-
86
- super(api_uri, options)
78
+ @connection = Berkshelf::RidleyCompatJSON.new(options)
87
79
  end
88
80
 
89
81
  # Download and extract target cookbook archive to the local file system,
@@ -97,79 +89,69 @@ module Berkshelf
97
89
  # @return [String, nil]
98
90
  # cookbook filepath, or nil if archive does not contain a cookbook
99
91
  def download(name, version)
100
- archive = stream(find(name, version)[:file])
92
+ archive = stream(find(name, version)["file"])
101
93
  scratch = Dir.mktmpdir
102
94
  extracted = self.class.unpack(archive.path, scratch)
103
95
 
104
96
  if File.cookbook?(extracted)
105
97
  extracted
106
98
  else
107
- dir = Dir.chdir(extracted) do
108
- Dir.glob("*").find do |dir|
109
- File.cookbook?(dir)
110
- end
99
+ Dir.glob("#{extracted}/*").find do |dir|
100
+ File.cookbook?(dir)
111
101
  end
112
- File.join(extracted, dir) if dir
113
102
  end
114
103
  ensure
115
104
  archive.unlink unless archive.nil?
116
105
  end
117
106
 
118
107
  def find(name, version)
119
- response = get("cookbooks/#{name}/versions/#{self.class.uri_escape_version(version)}")
108
+ body = connection.get("cookbooks/#{name}/versions/#{self.class.uri_escape_version(version)}")
120
109
 
121
110
  # Artifactory responds with a 200 and blank body for unknown cookbooks.
122
- if response.status == 200 && response.body.to_s == ""
123
- response.env.status = 404
124
- end
125
-
126
- case response.status
127
- when (200..299)
128
- response.body
129
- when 404
130
- raise CookbookNotFound.new(name, nil, "at `#{api_uri}'")
131
- else
132
- raise CommunitySiteError.new(api_uri, "'#{name}' (#{version})")
133
- end
111
+ raise CookbookNotFound.new(name, nil, "at `#{api_uri}'") if body.nil?
112
+
113
+ body
114
+ rescue CookbookNotFound
115
+ raise
116
+ rescue Berkshelf::APIClient::ServiceNotFound
117
+ raise CookbookNotFound.new(name, nil, "at `#{api_uri}'")
118
+ rescue
119
+ raise CommunitySiteError.new(api_uri, "'#{name}' (#{version})")
134
120
  end
135
121
 
136
122
  # Returns the latest version of the cookbook and its download link.
137
123
  #
138
124
  # @return [String]
139
125
  def latest_version(name)
140
- response = get("cookbooks/#{name}")
126
+ body = connection.get("cookbooks/#{name}")
141
127
 
142
128
  # Artifactory responds with a 200 and blank body for unknown cookbooks.
143
- if response.status == 200 && response.body.to_s == ""
144
- response.env.status = 404
145
- end
129
+ raise CookbookNotFound.new(name, nil, "at `#{api_uri}'") if body.nil?
146
130
 
147
- case response.status
148
- when (200..299)
149
- self.class.version_from_uri response.body["latest_version"]
150
- when 404
151
- raise CookbookNotFound.new(name, nil, "at `#{api_uri}'")
152
- else
153
- raise CommunitySiteError.new(api_uri, "the latest version of '#{name}'")
154
- end
131
+ self.class.version_from_uri body["latest_version"]
132
+ rescue Berkshelf::APIClient::ServiceNotFound
133
+ raise CookbookNotFound.new(name, nil, "at `#{api_uri}'")
134
+ rescue
135
+ raise CommunitySiteError.new(api_uri, "the latest version of '#{name}'")
155
136
  end
156
137
 
157
138
  # @param [String] name
158
139
  #
159
140
  # @return [Array]
160
141
  def versions(name)
161
- response = get("cookbooks/#{name}")
142
+ body = connection.get("cookbooks/#{name}")
162
143
 
163
- case response.status
164
- when (200..299)
165
- response.body["versions"].collect do |version_uri|
166
- self.class.version_from_uri(version_uri)
167
- end
168
- when 404
169
- raise CookbookNotFound.new(name, nil, "at `#{api_uri}'")
170
- else
171
- raise CommunitySiteError.new(api_uri, "versions of '#{name}'")
144
+ # Artifactory responds with a 200 and blank body for unknown cookbooks.
145
+ raise CookbookNotFound.new(name, nil, "at `#{api_uri}'") if body.nil?
146
+
147
+ body["versions"].collect do |version_uri|
148
+ self.class.version_from_uri(version_uri)
172
149
  end
150
+
151
+ rescue Berkshelf::APIClient::ServiceNotFound
152
+ raise CookbookNotFound.new(name, nil, "at `#{api_uri}'")
153
+ rescue
154
+ raise CommunitySiteError.new(api_uri, "versions of '#{name}'")
173
155
  end
174
156
 
175
157
  # @param [String] name
@@ -191,12 +173,9 @@ module Berkshelf
191
173
  def stream(target)
192
174
  local = Tempfile.new("community-rest-stream")
193
175
  local.binmode
194
-
195
- Retryable.retryable(tries: retries, on: Faraday::Error::ConnectionFailed, sleep: retry_interval) do
196
- get(target, nil, streaming_file: local)
176
+ Retryable.retryable(tries: retries, on: Berkshelf::APIClientError, sleep: retry_interval) do
177
+ connection.streaming_request(target, {}, local)
197
178
  end
198
-
199
- local
200
179
  ensure
201
180
  local.close(false) unless local.nil?
202
181
  end
@@ -1,8 +1,18 @@
1
- require "buff/config/json"
1
+ require "mixlib/config"
2
2
  require "openssl"
3
3
 
4
+ # we need this method, but have to inject it into mixlib-config directly
5
+ # to have it available from config contexts
6
+ module Mixlib
7
+ module Config
8
+ def each(&block)
9
+ save(true).each(&block)
10
+ end
11
+ end
12
+ end
13
+
4
14
  module Berkshelf
5
- class Config < Buff::Config::JSON
15
+ class Config
6
16
  class << self
7
17
  # @return [String]
8
18
  def store_location
@@ -16,7 +26,7 @@ module Berkshelf
16
26
 
17
27
  # @return [String]
18
28
  def path
19
- path = File.exists?(local_location) ? local_location : store_location
29
+ path = File.exist?(local_location) ? local_location : store_location
20
30
  File.expand_path(path)
21
31
  end
22
32
 
@@ -33,7 +43,7 @@ module Berkshelf
33
43
  # @return [String, nil]
34
44
  # the contents of the file
35
45
  def file
36
- File.read(path) if File.exists?(path)
46
+ File.read(path) if File.exist?(path)
37
47
  end
38
48
 
39
49
  # Instantiate and return or just return the currently instantiated Berkshelf
@@ -62,130 +72,94 @@ module Berkshelf
62
72
  #
63
73
  # @return [Config]
64
74
  def coerce_ssl
65
- ssl = @instance.ssl
75
+ ssl = @instance[:ssl]
66
76
  ssl[:ca_cert] = OpenSSL::X509::Certificate.new(File.read(ssl[:ca_cert])) if ssl[:ca_cert] && ssl[:ca_cert].is_a?(String)
67
77
  ssl[:client_cert] = OpenSSL::X509::Certificate.new(File.read(ssl[:client_cert])) if ssl[:client_cert] && ssl[:client_cert].is_a?(String)
68
78
  ssl[:client_key] = OpenSSL::PKey::RSA.new(File.read(ssl[:client_key])) if ssl[:client_key] && ssl[:client_key].is_a?(String)
69
79
  @instance
70
80
  end
81
+
82
+ def from_file(path)
83
+ new(path)
84
+ end
85
+
86
+ def from_json(json)
87
+ new.from_json(json)
88
+ end
89
+
90
+ def from_hash(hash)
91
+ new.from_hash(hash)
92
+ end
71
93
  end
72
94
 
95
+ attr_accessor :path
96
+
73
97
  # @param [String] path
74
- # @param [Hash] options
75
- # @see {Buff::Config::JSON}
76
- def initialize(path = self.class.path, options = {})
77
- super(path, options).tap do
78
- # Deprecation
79
- if !vagrant.omnibus.enabled.nil?
80
- Berkshelf.ui.warn "`vagrant.omnibus.enabled' is deprecated and " \
81
- "will be removed in a future release. Please remove the " \
82
- "`enabled' attribute from your Berkshelf config."
83
- end
84
- if !vagrant.vm.box_url.nil?
85
- Berkshelf.ui.warn "`vagrant.vm.box_url' is deprecated and " \
86
- "will be removed in a future release. Please remove the " \
87
- "`box_url' attribute from your Berkshelf config."
98
+ def initialize(path = self.class.path)
99
+ # this is a bit tricky, mixlib-config wants to extend a class and create effectively a global config object while
100
+ # what we want to do is use an instance, so we create an anonymous class and shove it into an instance variable.
101
+ # this is actually similar to what mixlib-config itself does to create config contexts.
102
+ @klass = Class.new
103
+ @klass.extend(Mixlib::Config)
104
+ @klass.extend(BerksConfig)
105
+
106
+ @path = File.expand_path(path)
107
+ @klass.from_file(@path) if File.exist?(@path)
108
+ # yeah, if !File.exist?() you just get back an empty config object
109
+ end
110
+
111
+ def method_missing(method, *args, &block)
112
+ @klass.send(method, *args, &block)
113
+ end
114
+
115
+ module BerksConfig
116
+ def self.extended(base)
117
+ base.class_exec do
118
+ config_strict_mode true
119
+ config_context :api do
120
+ default :timeout, "30"
121
+ end
122
+ config_context :chef do
123
+ default :chef_server_url, Berkshelf.chef_config.chef_server_url
124
+ default :validation_client_name, Berkshelf.chef_config.validation_client_name
125
+ default :validation_key_path, Berkshelf.chef_config.validation_key
126
+ default :client_key, Berkshelf.chef_config.client_key
127
+ default :node_name, Berkshelf.chef_config.node_name
128
+ default :trusted_certs_dir, Berkshelf.chef_config.trusted_certs_dir
129
+ default :artifactory_api_key, Berkshelf.chef_config.artifactory_api_key
130
+ end
131
+ config_context :cookbook do
132
+ default :copyright, Berkshelf.chef_config.cookbook_copyright
133
+ default :email, Berkshelf.chef_config.cookbook_email
134
+ default :license, Berkshelf.chef_config.cookbook_license
135
+ end
136
+ default :allowed_licenses, Array.new
137
+ default :raise_license_exception, false
138
+ config_context :vagrant do
139
+ config_context :vm do
140
+ default :box, "bento/ubuntu-14.04"
141
+ default :forward_port, Hash.new
142
+ default :provision, "chef_solo"
143
+ config_context :omnibus do
144
+ default :version, "latest"
145
+ end
146
+ end
147
+ end
148
+ config_context :ssl do
149
+ default :verify, true
150
+ default :cert_store, false
151
+ default :ca_file, nil
152
+ default :ca_path, nil
153
+ default :ca_cert, nil
154
+ default :client_cert, nil
155
+ default :client_key, nil
156
+ end
157
+ default :github, []
158
+ default :gitlab, []
159
+ # :git, :ssh, or :https
160
+ default :github_protocol, :https
88
161
  end
89
162
  end
90
163
  end
91
-
92
- attribute "api.timeout",
93
- type: String,
94
- default: "30"
95
- attribute "chef.chef_server_url",
96
- type: String,
97
- default: Berkshelf.chef_config.chef_server_url
98
- attribute "chef.validation_client_name",
99
- type: String,
100
- default: Berkshelf.chef_config.validation_client_name
101
- attribute "chef.validation_key_path",
102
- type: String,
103
- default: Berkshelf.chef_config.validation_key
104
- attribute "chef.client_key",
105
- type: String,
106
- default: Berkshelf.chef_config.client_key
107
- attribute "chef.node_name",
108
- type: String,
109
- default: Berkshelf.chef_config.node_name
110
- attribute "chef.trusted_certs_dir",
111
- type: String,
112
- default: Berkshelf.chef_config.trusted_certs_dir
113
- attribute "chef.artifactory_api_key",
114
- type: String,
115
- default: Berkshelf.chef_config.artifactory_api_key
116
- attribute "cookbook.copyright",
117
- type: String,
118
- default: Berkshelf.chef_config.cookbook_copyright
119
- attribute "cookbook.email",
120
- type: String,
121
- default: Berkshelf.chef_config.cookbook_email
122
- attribute "cookbook.license",
123
- type: String,
124
- default: Berkshelf.chef_config.cookbook_license
125
- attribute "allowed_licenses",
126
- type: Array,
127
- default: Array.new
128
- attribute "raise_license_exception",
129
- type: Buff::Boolean,
130
- default: false
131
- attribute "vagrant.vm.box",
132
- type: String,
133
- default: "bento/ubuntu-14.04",
134
- required: true
135
- # @todo Deprecated, remove?
136
- attribute "vagrant.vm.box_url",
137
- type: String,
138
- default: nil
139
- attribute "vagrant.vm.forward_port",
140
- type: Hash,
141
- default: Hash.new
142
- attribute "vagrant.vm.provision",
143
- type: String,
144
- default: "chef_solo"
145
- # @todo Deprecated, remove. There's a really weird tri-state here where
146
- # nil is used to represent an unset value, just FYI
147
- attribute "vagrant.omnibus.enabled",
148
- type: Buff::Boolean,
149
- default: nil
150
- attribute "vagrant.omnibus.version",
151
- type: String,
152
- default: "latest"
153
- attribute "ssl.verify",
154
- type: Buff::Boolean,
155
- default: true,
156
- required: true
157
- attribute "ssl.cert_store",
158
- type: Buff::Boolean,
159
- default: false,
160
- required: false
161
- attribute "ssl.ca_file",
162
- type: String,
163
- default: nil,
164
- required: false
165
- attribute "ssl.ca_path",
166
- type: String,
167
- default: nil,
168
- required: false
169
- attribute "ssl.client_cert",
170
- type: String,
171
- default: nil,
172
- required: false
173
- attribute "ssl.client_key",
174
- type: String,
175
- default: nil,
176
- required: false
177
- attribute "github",
178
- type: Array,
179
- default: [],
180
- required: false
181
- attribute "gitlab",
182
- type: Array,
183
- default: [],
184
- required: false
185
- attribute "github_protocol",
186
- # :git, :ssh, or :https
187
- type: Symbol,
188
- default: :https,
189
- required: false
190
164
  end
191
165
  end
@@ -1,4 +1,5 @@
1
1
  require "fileutils"
2
+ require "chef/exceptions"
2
3
 
3
4
  module Berkshelf
4
5
  class CookbookStore
@@ -67,7 +68,7 @@ module Berkshelf
67
68
  destination = cookbook_path(name, version)
68
69
  FileUtils.mv(path, destination)
69
70
  cookbook(name, version)
70
- rescue => ex
71
+ rescue
71
72
  FileUtils.rm_f(destination)
72
73
  raise
73
74
  end
@@ -108,7 +109,7 @@ module Berkshelf
108
109
 
109
110
  begin
110
111
  CachedCookbook.from_store_path(path)
111
- rescue Ridley::Errors::MissingNameAttribute
112
+ rescue Chef::Exceptions::MetadataNotValid
112
113
  # Skip cached cookbooks that do not have a name attribute.
113
114
  skipped_cookbooks << File.basename(path)
114
115
  next
@@ -7,7 +7,7 @@ class File
7
7
  #
8
8
  # @return [Boolean]
9
9
  def cookbook?(path)
10
- File.exists?(File.join(path, "metadata.json")) || File.exists?(File.join(path, "metadata.rb"))
10
+ File.exist?(File.join(path, "metadata.json")) || File.exist?(File.join(path, "metadata.rb"))
11
11
  end
12
12
  alias_method :chef_cookbook?, :cookbook?
13
13
  end