berkshelf 6.3.4 → 7.0.0

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