berkshelf 3.0.0.beta3 → 3.0.0.beta4

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +1 -0
  4. data/CHANGELOG.md +12 -2
  5. data/CONTRIBUTING.md +53 -53
  6. data/Gemfile +1 -1
  7. data/Guardfile +12 -6
  8. data/PLUGINS.md +2 -2
  9. data/README.md +7 -11
  10. data/berkshelf.gemspec +1 -1
  11. data/features/commands/apply.feature +15 -4
  12. data/features/commands/cookbook.feature +1 -1
  13. data/features/commands/install.feature +85 -15
  14. data/features/commands/list.feature +35 -2
  15. data/features/commands/outdated.feature +3 -3
  16. data/features/commands/package.feature +1 -0
  17. data/features/commands/show.feature +3 -3
  18. data/features/commands/update.feature +1 -0
  19. data/features/commands/upload.feature +28 -4
  20. data/features/json_formatter.feature +2 -0
  21. data/features/lockfile.feature +1 -0
  22. data/features/support/env.rb +8 -4
  23. data/generator_files/Gemfile.erb +9 -0
  24. data/generator_files/README.md.erb +15 -33
  25. data/generator_files/Vagrantfile.erb +6 -3
  26. data/lib/berkshelf.rb +32 -2
  27. data/lib/berkshelf/berksfile.rb +59 -83
  28. data/lib/berkshelf/cli.rb +15 -67
  29. data/lib/berkshelf/community_rest.rb +18 -2
  30. data/lib/berkshelf/dependency.rb +6 -4
  31. data/lib/berkshelf/downloader.rb +3 -0
  32. data/lib/berkshelf/errors.rb +15 -0
  33. data/lib/berkshelf/formatters.rb +1 -0
  34. data/lib/berkshelf/formatters/human_readable.rb +27 -4
  35. data/lib/berkshelf/formatters/json.rb +21 -5
  36. data/lib/berkshelf/git.rb +4 -0
  37. data/lib/berkshelf/installer.rb +3 -1
  38. data/lib/berkshelf/locations/git_location.rb +22 -10
  39. data/lib/berkshelf/locations/github_location.rb +3 -7
  40. data/lib/berkshelf/locations/path_location.rb +0 -19
  41. data/lib/berkshelf/lockfile.rb +66 -4
  42. data/lib/berkshelf/mixin/logging.rb +1 -1
  43. data/lib/berkshelf/version.rb +1 -1
  44. data/spec/spec_helper.rb +8 -4
  45. data/spec/support/git.rb +9 -9
  46. data/spec/support/mercurial.rb +9 -9
  47. data/spec/unit/berkshelf/api_client_spec.rb +1 -1
  48. data/spec/unit/berkshelf/berksfile_spec.rb +36 -59
  49. data/spec/unit/berkshelf/cli_spec.rb +16 -0
  50. data/spec/unit/berkshelf/config_spec.rb +1 -1
  51. data/spec/unit/berkshelf/cookbook_generator_spec.rb +4 -6
  52. data/spec/unit/berkshelf/locations/git_location_spec.rb +1 -1
  53. data/spec/unit/berkshelf/lockfile_spec.rb +81 -2
  54. data/spec/unit/berkshelf/resolver/graph_spec.rb +1 -1
  55. data/spec/unit/berkshelf/ui_spec.rb +4 -4
  56. metadata +7 -7
  57. data/features/commands/configure.feature +0 -89
@@ -1,24 +1,5 @@
1
1
  module Berkshelf
2
2
  class PathLocation < Location::Base
3
- class << self
4
- # Expand and return a string representation of the given path if it is
5
- # absolute or a path in the users home directory.
6
- #
7
- # Returns the given relative path otherwise.
8
- #
9
- # @param [#to_s] path
10
- #
11
- # @return [String]
12
- def normalize_path(path)
13
- path = path.to_s
14
- if (path[0] == '~') || Pathname.new(path).absolute?
15
- File.expand_path(path)
16
- else
17
- path
18
- end
19
- end
20
- end
21
-
22
3
  set_location_key :path
23
4
  set_valid_options :path, :metadata
24
5
 
@@ -5,6 +5,29 @@ module Berkshelf
5
5
  # when working in teams where the same cookbook versions are desired across
6
6
  # multiple workstations.
7
7
  class Lockfile
8
+ class << self
9
+ # Initialize a Lockfile from the given filepath
10
+ #
11
+ # @param [String] filepath
12
+ # filepath to the lockfile
13
+ def from_file(filepath)
14
+ new(filepath: filepath)
15
+ end
16
+
17
+ # Initialize a Lockfile from the given Berksfile
18
+ #
19
+ # @param [Berkshelf::Berksfile] berksfile
20
+ # the Berksfile associated with the Lockfile
21
+ def from_berksfile(berksfile)
22
+ filepath = File.join(File.dirname(File.expand_path(berksfile.filepath)), Lockfile::DEFAULT_FILENAME)
23
+ new(berksfile: berksfile, filepath: filepath)
24
+ end
25
+ end
26
+
27
+ DEFAULT_FILENAME = "Berksfile.lock"
28
+
29
+ include Berkshelf::Mixin::Logging
30
+
8
31
  # @return [Pathname]
9
32
  # the path to this Lockfile
10
33
  attr_reader :filepath
@@ -17,16 +40,55 @@ module Berkshelf
17
40
  # Lockfile exists, it is automatically loaded. Otherwise, an empty instance is
18
41
  # created and ready for use.
19
42
  #
20
- # @param berksfile [Berkshelf::Berksfile]
43
+ # @option options [String] :filepath
44
+ # filepath to the lockfile
45
+ # @option options [Berkshelf::Berksfile] :berksfile
21
46
  # the Berksfile associated with this Lockfile
22
- def initialize(berksfile)
23
- @berksfile = berksfile
24
- @filepath = File.expand_path("#{berksfile.filepath}.lock")
47
+ def initialize(options = {})
48
+ @filepath = options[:filepath].to_s
49
+ @berksfile = options[:berksfile]
25
50
  @dependencies = {}
26
51
 
27
52
  load! if File.exists?(@filepath)
28
53
  end
29
54
 
55
+ # Resolve this Berksfile and apply the locks found in the generated Berksfile.lock to the
56
+ # target Chef environment
57
+ #
58
+ # @param [String] environment_name
59
+ #
60
+ # @option options [Hash] :ssl_verify (true)
61
+ # Disable/Enable SSL verification during uploads
62
+ #
63
+ # @raise [EnvironmentNotFound]
64
+ # if the target environment was not found
65
+ # @raise [ChefConnectionError]
66
+ # if you are locking cookbooks with an invalid or not-specified client configuration
67
+ def apply(environment_name, options = {})
68
+ Berkshelf.ridley_connection(options) do |conn|
69
+ unless environment = conn.environment.find(environment_name)
70
+ raise EnvironmentNotFound.new(environment_name)
71
+ end
72
+
73
+ environment.cookbook_versions = {}.tap do |cookbook_versions|
74
+ dependencies.each do |dependency|
75
+ if dependency.locked_version.nil?
76
+ # A locked version must be present for each entry. Older versions of the lockfile
77
+ # may have contained dependencies with a special type of location that would attempt
78
+ # to dynamically determine the locked version. This is incorrect and the Lockfile
79
+ # should be regenerated if that is the case.
80
+ raise InvalidLockFile, "Your lockfile contains a dependency without a locked version. This " +
81
+ "may be because you have an old lockfile. Regenerate your lockfile and try again."
82
+ end
83
+
84
+ cookbook_versions[dependency.name] = "= #{dependency.locked_version.to_s}"
85
+ end
86
+ end
87
+
88
+ environment.save
89
+ end
90
+ end
91
+
30
92
  # Load the lockfile from file system.
31
93
  def load!
32
94
  contents = File.read(filepath).strip
@@ -5,7 +5,7 @@ module Berkshelf
5
5
  Berkshelf::Logger
6
6
  end
7
7
 
8
- # Log an exception and it's backtrace to FATAL
8
+ # Log an exception and its backtrace to FATAL
9
9
  #
10
10
  # @param [Exception] ex
11
11
  def log_exception(ex)
@@ -1,3 +1,3 @@
1
1
  module Berkshelf
2
- VERSION = "3.0.0.beta3"
2
+ VERSION = "3.0.0.beta4"
3
3
  end
@@ -1,9 +1,13 @@
1
1
  require 'spork'
2
2
 
3
+ def windows?
4
+ !!(RUBY_PLATFORM =~ /mswin|mingw|windows/)
5
+ end
6
+
3
7
  Spork.prefork do
4
8
  require 'rspec'
5
9
  require 'webmock/rspec'
6
- require 'berkshelf/api/rspec'
10
+ require 'berkshelf/api/rspec' unless windows?
7
11
 
8
12
  Dir['spec/support/**/*.rb'].each { |f| require File.expand_path(f) }
9
13
 
@@ -13,7 +17,7 @@ Spork.prefork do
13
17
  config.include Berkshelf::RSpec::ChefServer
14
18
  config.include Berkshelf::RSpec::Git
15
19
  config.include Berkshelf::RSpec::PathHelpers
16
- config.include Berkshelf::API::RSpec
20
+ config.include Berkshelf::API::RSpec unless windows?
17
21
 
18
22
  config.expect_with :rspec do |c|
19
23
  c.syntax = :expect
@@ -27,7 +31,7 @@ Spork.prefork do
27
31
  config.before(:suite) do
28
32
  WebMock.disable_net_connect!(allow_localhost: true, net_http_connect_on_start: true)
29
33
  Berkshelf::RSpec::ChefServer.start
30
- Berkshelf::API::RSpec::Server.start
34
+ Berkshelf::API::RSpec::Server.start unless windows?
31
35
  Berkshelf.set_format(:null)
32
36
  Berkshelf.ui.mute!
33
37
  end
@@ -41,7 +45,7 @@ Spork.prefork do
41
45
  end
42
46
 
43
47
  config.before(:each) do
44
- Berkshelf::API::RSpec::Server.clear_cache
48
+ Berkshelf::API::RSpec::Server.clear_cache unless windows?
45
49
  clean_tmp_path
46
50
  Berkshelf.initialize_filesystem
47
51
  Berkshelf::CookbookStore.instance.initialize_filesystem
@@ -21,23 +21,23 @@ module Berkshelf
21
21
 
22
22
  Dir.chdir(path) do
23
23
  shell_out "git config receive.denyCurrentBranch ignore"
24
- shell_out "echo '# a change!' >> content_file"
24
+ shell_out "echo \"# a change!\" >> content_file"
25
25
  shell_out "git add ."
26
- shell_out "git commit -am 'A commit.'"
26
+ shell_out "git commit -am \"A commit.\""
27
27
 
28
28
  options[:tags].each do |tag|
29
- shell_out "echo '#{tag}' > content_file"
29
+ shell_out "echo \"#{tag}\" > content_file"
30
30
  shell_out "git add content_file"
31
- shell_out "git commit -am '#{tag} content'"
32
- shell_out "git tag '#{tag}' 2> /dev/null"
31
+ shell_out "git commit -am \"#{tag} content\""
32
+ shell_out "git tag \"#{tag}\""
33
33
  end if options[:tags]
34
34
 
35
35
  options[:branches].each do |branch|
36
- shell_out "git checkout -b #{branch} master 2> /dev/null"
37
- shell_out "echo '#{branch}' > content_file"
36
+ shell_out "git checkout -b #{branch} master"
37
+ shell_out "echo \"#{branch}\" > content_file"
38
38
  shell_out "git add content_file"
39
- shell_out "git commit -am '#{branch} content'"
40
- shell_out "git checkout master 2> /dev/null"
39
+ shell_out "git commit -am \"#{branch} content\""
40
+ shell_out "git checkout master"
41
41
  end if options[:branches]
42
42
  end
43
43
  end
@@ -8,7 +8,7 @@ module Berkshelf
8
8
  include Berkshelf::RSpec::PathHelpers
9
9
 
10
10
  def mercurial_origin_for(repo, options = {})
11
- "file://localhost#{generate_fake_mercurial_remote(repo, options)}"
11
+ File.join("file://localhost", generate_fake_mercurial_remote(repo, options))
12
12
  end
13
13
 
14
14
  def generate_fake_mercurial_remote(uri, options = {})
@@ -19,21 +19,21 @@ module Berkshelf
19
19
  Dir.chdir(repo_path) do
20
20
  ENV['HGUSER'] = 'test_user'
21
21
  shell_out "hg init"
22
- shell_out "echo '# a change!' >> content_file"
22
+ shell_out "echo \"# a change!\" >> content_file"
23
23
  if options[:is_cookbook]
24
- shell_out "echo '#cookbook' >> metadata.rb"
24
+ shell_out "echo \"#cookbook\" >> metadata.rb"
25
25
  end
26
26
  shell_out "hg add ."
27
- shell_out "hg commit -m 'A commit.'"
27
+ shell_out "hg commit -m \"A commit.\""
28
28
  options[:tags].each do |tag|
29
- shell_out "echo '#{tag}' > content_file"
30
- shell_out "hg commit -m '#{tag} content'"
31
- shell_out "hg tag '#{tag}'"
29
+ shell_out "echo \"#{tag}\" > content_file"
30
+ shell_out "hg commit -m \"#{tag} content\""
31
+ shell_out "hg tag \"#{tag}\""
32
32
  end if options.has_key? :tags
33
33
  options[:branches].each do |branch|
34
34
  shell_out "hg branch #{branch}"
35
- shell_out "echo '#{branch}' > content_file"
36
- shell_out "hg commit -m '#{branch} content'"
35
+ shell_out "echo \"#{branch}\" > content_file"
36
+ shell_out "hg commit -m \"#{branch} content\""
37
37
  shell_out "hg up default"
38
38
  end if options.has_key? :branches
39
39
  end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Berkshelf::APIClient do
3
+ describe Berkshelf::APIClient, :api_client do
4
4
  let(:instance) { described_class.new("http://localhost:26210") }
5
5
 
6
6
  describe "#universe" do
@@ -68,7 +68,13 @@ describe Berkshelf::Berksfile do
68
68
  end
69
69
 
70
70
  it 'merges the default options into specified options' do
71
- subject.should_receive(:add_dependency).with(name, constraint, path: '/Users/reset', group: [])
71
+ subject.should_receive(:add_dependency)do |arg_name, arg_constraint, arg_options|
72
+ expect(arg_name).to eq(name)
73
+ expect(arg_constraint).to eq(constraint)
74
+ expect(arg_options[:path]).to match(%r{/Users/reset})
75
+ expect(arg_options[:group]).to eq([])
76
+ end
77
+
72
78
  subject.cookbook(name, constraint, path: '/Users/reset')
73
79
  end
74
80
 
@@ -333,30 +339,25 @@ describe Berkshelf::Berksfile do
333
339
 
334
340
  describe '#retrieve_locked' do
335
341
  let(:lockfile) { double('lockfile', find: locked) }
342
+ let(:dependency) { double('dependency', name: 'bacon') }
336
343
  let(:locked) { double('locked', cached_cookbook: cached, locked_version: '1.0.0') }
337
344
  let(:cached) { double('cached') }
338
345
 
339
346
  before do
340
- subject.stub(:validate_cookbook_names!)
341
347
  subject.stub(:lockfile).and_return(lockfile)
342
348
  end
343
349
 
344
- it 'validates cookbook names' do
345
- expect(subject).to receive(:validate_cookbook_names!).once
346
- subject.retrieve_locked('bacon')
347
- end
348
-
349
- it 'raises an error when the lockfile does not exist' do
350
+ it 'raises an error when the lockfile does not have the source' do
350
351
  lockfile.stub(:find)
351
352
  expect {
352
- subject.retrieve_locked('bacon')
353
- }.to raise_error(Berkshelf::LockfileNotFound)
353
+ subject.retrieve_locked(dependency)
354
+ }.to raise_error(Berkshelf::CookbookNotFound)
354
355
  end
355
356
 
356
357
  it 'raises an error when the cookbook is not downloaded' do
357
- locked.stub(:cached_cookbook)
358
+ locked.stub(:downloaded?).and_return(false)
358
359
  expect {
359
- subject.retrieve_locked('bacon')
360
+ subject.retrieve_locked(dependency)
360
361
  }.to raise_error(Berkshelf::CookbookNotFound)
361
362
  end
362
363
  end
@@ -483,54 +484,30 @@ describe Berkshelf::Berksfile do
483
484
  upload
484
485
  end
485
486
  end
486
- end
487
-
488
- describe "#apply" do
489
- let(:env_name) { 'berkshelf-test' }
490
- let(:server_url) { Berkshelf::RSpec::ChefServer.server_url }
491
- let(:client_name) { 'berkshelf' }
492
- let(:client_key) { fixtures_path.join('../config/berkshelf.pem').to_s }
493
- let(:ridley) { Ridley.new(server_url: server_url, client_name: client_name, client_key: client_key) }
494
487
 
495
- before do
496
- subject.stub(:ridley_connection).and_yield(ridley)
497
- subject.add_dependency('nginx', '>= 0.1.2')
498
- subject.stub(install: nil)
499
- end
500
-
501
- context 'when the chef environment exists' do
502
- let(:dependencies) do
503
- [
504
- double(name: 'nginx', locked_version: '1.2.3'),
505
- double(name: 'artifact', locked_version: '1.4.0')
506
- ]
507
- end
508
-
509
- before do
510
- chef_environment('berkshelf')
511
- subject.lockfile.stub(:dependencies).and_return(dependencies)
512
- end
513
-
514
- it 'installs the Berksfile' do
515
- subject.should_receive(:install)
516
- subject.apply('berkshelf')
488
+ context 'when validate is passed' do
489
+ let(:options) do
490
+ {
491
+ force: false,
492
+ freeze: true,
493
+ validate: false,
494
+ name: "cookbook"
495
+ }
517
496
  end
518
-
519
- it 'applys the locked_versions of the Lockfile dependencies to the given Chef environment' do
520
- subject.apply('berkshelf')
521
-
522
- environment = ::JSON.parse(chef_server.data_store.get(['environments', 'berkshelf']))
523
- expect(environment['cookbook_versions']).to have(2).items
524
- expect(environment['cookbook_versions']['nginx']).to eq('1.2.3')
525
- expect(environment['cookbook_versions']['artifact']).to eq('1.4.0')
497
+ let(:ridley_options) do
498
+ default_ridley_options.merge(
499
+ { server_url: 'http://configured-chef-server/'})
526
500
  end
527
- end
501
+ let(:cookbook) { double('cookbook', cookbook_name: 'cookbook', path: 'path', version: '1.0.0') }
502
+ let(:installed_cookbooks) { [ cookbook ] }
503
+ let(:cookbook_resource) { double('cookbook') }
504
+ let(:conn) { double('conn') }
528
505
 
529
- context 'when the environment does not exist' do
530
- it 'raises an EnvironmentNotFound error' do
531
- expect {
532
- subject.apply(env_name)
533
- }.to raise_error(Berkshelf::EnvironmentNotFound)
506
+ it 'uses the passed in :validate' do
507
+ Ridley.should_receive(:open).with(ridley_options).and_yield(conn)
508
+ conn.should_receive(:cookbook).and_return(cookbook_resource)
509
+ cookbook_resource.should_receive(:upload).with('path', options )
510
+ upload
534
511
  end
535
512
  end
536
513
  end
@@ -539,7 +516,7 @@ describe Berkshelf::Berksfile do
539
516
  context 'when the dependency does not exist' do
540
517
  it 'raises a CookbookNotFound exception' do
541
518
  expect {
542
- subject.package('non-existent', output: '/tmp')
519
+ subject.package('non-existent', output: Dir.tmpdir)
543
520
  }.to raise_error(Berkshelf::CookbookNotFound)
544
521
  end
545
522
  end
@@ -547,7 +524,7 @@ describe Berkshelf::Berksfile do
547
524
  context 'when the dependency exists' do
548
525
  let(:dependency) { double('dependency') }
549
526
  let(:cached) { double('cached', path: '/foo/bar', cookbook_name: 'cookbook') }
550
- let(:options) { { output: '/tmp' } }
527
+ let(:options) { { output: Dir.tmpdir } }
551
528
 
552
529
  before do
553
530
  FileUtils.stub(:cp_r)
@@ -562,7 +539,7 @@ describe Berkshelf::Berksfile do
562
539
  end
563
540
 
564
541
  it 'returns the output path' do
565
- expect(subject.package('non-existent', options)).to eq('/tmp/non-existent.tar.gz')
542
+ expect(subject.package('non-existent', options)).to eq(File.join(Dir.tmpdir, 'non-existent.tar.gz'))
566
543
  end
567
544
  end
568
545
  end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ describe Berkshelf::Cli do
4
+ let(:subject) { described_class.new }
5
+ let(:berksfile) { double('Berksfile') }
6
+ let(:cookbooks) { ['mysql'] }
7
+ describe '#upload' do
8
+ it 'calls to upload with params if passed in cli' do
9
+ Berkshelf::Berksfile.should_receive(:from_file).and_return(berksfile)
10
+ berksfile.should_receive(:upload).with(include(:skip_syntax_check => true, :freeze => false, :cookbooks => cookbooks))
11
+ subject.options[:skip_syntax_check] = true
12
+ subject.options[:no_freeze] = true
13
+ subject.upload('mysql')
14
+ end
15
+ end
16
+ end
@@ -37,7 +37,7 @@ describe Berkshelf::Config do
37
37
  end
38
38
 
39
39
  it "points to a location within it" do
40
- expect(Berkshelf::Config.path).to eq('/tmp/config.json')
40
+ expect(Berkshelf::Config.path).to match(%r{/tmp/config.json})
41
41
  end
42
42
  end
43
43
  end
@@ -39,13 +39,11 @@ describe Berkshelf::CookbookGenerator do
39
39
  contains 'All rights reserved - Do Not Redistribute'
40
40
  end
41
41
  file 'README.md' do
42
- contains 'sparkle_motion cookbook'
43
- contains '======================='
44
- contains '- `toaster` - sparkle_motion needs toaster to brown your bagel.'
45
- contains '#### sparkle_motion::default'
42
+ contains '# sparkle_motion-cookbook'
43
+ contains '### sparkle_motion::default'
46
44
  contains " <td><tt>['sparkle_motion']['bacon']</tt></td>"
47
- contains "Just include `sparkle_motion` in your node's `run_list`:"
48
- contains ' "recipe[sparkle_motion]"'
45
+ contains "Include `sparkle_motion` in your node's `run_list`:"
46
+ contains ' "recipe[sparkle_motion::default]"'
49
47
  contains 'Author:: YOUR_NAME (<YOUR_EMAIL>)'
50
48
  end
51
49
  file 'metadata.rb' do