berkshelf-api 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +17 -0
  5. data/CONTRIBUTING.md +33 -0
  6. data/Gemfile +40 -0
  7. data/Guardfile +20 -0
  8. data/LICENSE +201 -0
  9. data/README.md +37 -0
  10. data/Thorfile +39 -0
  11. data/berkshelf-api.gemspec +35 -0
  12. data/bin/berks-api +5 -0
  13. data/lib/berkshelf-api.rb +1 -0
  14. data/lib/berkshelf/api.rb +25 -0
  15. data/lib/berkshelf/api/application.rb +114 -0
  16. data/lib/berkshelf/api/cache_builder.rb +60 -0
  17. data/lib/berkshelf/api/cache_builder/worker.rb +116 -0
  18. data/lib/berkshelf/api/cache_builder/worker/chef_server.rb +46 -0
  19. data/lib/berkshelf/api/cache_builder/worker/opscode.rb +59 -0
  20. data/lib/berkshelf/api/cache_manager.rb +96 -0
  21. data/lib/berkshelf/api/config.rb +23 -0
  22. data/lib/berkshelf/api/cucumber.rb +11 -0
  23. data/lib/berkshelf/api/dependency_cache.rb +123 -0
  24. data/lib/berkshelf/api/endpoint.rb +17 -0
  25. data/lib/berkshelf/api/endpoint/v1.rb +19 -0
  26. data/lib/berkshelf/api/errors.rb +8 -0
  27. data/lib/berkshelf/api/generic_server.rb +50 -0
  28. data/lib/berkshelf/api/logging.rb +37 -0
  29. data/lib/berkshelf/api/mixin.rb +7 -0
  30. data/lib/berkshelf/api/mixin/services.rb +48 -0
  31. data/lib/berkshelf/api/rack_app.rb +5 -0
  32. data/lib/berkshelf/api/remote_cookbook.rb +3 -0
  33. data/lib/berkshelf/api/rest_gateway.rb +62 -0
  34. data/lib/berkshelf/api/rspec.rb +20 -0
  35. data/lib/berkshelf/api/rspec/server.rb +29 -0
  36. data/lib/berkshelf/api/site_connector.rb +7 -0
  37. data/lib/berkshelf/api/site_connector/opscode.rb +162 -0
  38. data/lib/berkshelf/api/srv_ctl.rb +63 -0
  39. data/lib/berkshelf/api/version.rb +5 -0
  40. data/spec/fixtures/reset.pem +27 -0
  41. data/spec/spec_helper.rb +53 -0
  42. data/spec/support/actor_mocking.rb +7 -0
  43. data/spec/support/chef_server.rb +73 -0
  44. data/spec/unit/berkshelf/api/application_spec.rb +24 -0
  45. data/spec/unit/berkshelf/api/cache_builder/worker/chef_server_spec.rb +59 -0
  46. data/spec/unit/berkshelf/api/cache_builder/worker/opscode_spec.rb +41 -0
  47. data/spec/unit/berkshelf/api/cache_builder/worker_spec.rb +80 -0
  48. data/spec/unit/berkshelf/api/cache_builder_spec.rb +37 -0
  49. data/spec/unit/berkshelf/api/cache_manager_spec.rb +123 -0
  50. data/spec/unit/berkshelf/api/config_spec.rb +24 -0
  51. data/spec/unit/berkshelf/api/dependency_cache_spec.rb +109 -0
  52. data/spec/unit/berkshelf/api/endpoint/v1_spec.rb +18 -0
  53. data/spec/unit/berkshelf/api/logging_spec.rb +28 -0
  54. data/spec/unit/berkshelf/api/mixin/services_spec.rb +68 -0
  55. data/spec/unit/berkshelf/api/rack_app_spec.rb +6 -0
  56. data/spec/unit/berkshelf/api/rest_gateway_spec.rb +26 -0
  57. data/spec/unit/berkshelf/api/site_connector/opscode_spec.rb +85 -0
  58. data/spec/unit/berkshelf/api/srv_ctl_spec.rb +56 -0
  59. metadata +293 -0
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ describe Berkshelf::API::CacheBuilder::Worker::ChefServer do
4
+ describe "ClassMethods" do
5
+ subject { described_class }
6
+ its(:worker_type) { should eql("chef_server") }
7
+ end
8
+
9
+ subject do
10
+ described_class.new(url: "http://localhost:8889", client_name: "reset",
11
+ client_key: fixtures_path.join("reset.pem"))
12
+ end
13
+
14
+ describe "#cookbooks" do
15
+ before do
16
+ chef_cookbook("ruby", "1.0.0")
17
+ chef_cookbook("ruby", "2.0.0")
18
+ chef_cookbook("elixir", "3.0.0")
19
+ chef_cookbook("elixir", "3.0.1")
20
+ end
21
+
22
+ it "returns an array containing an item for each cookbook on the server" do
23
+ expect(subject.cookbooks).to have(4).items
24
+ end
25
+
26
+ it "returns an array of RemoteCookbooks" do
27
+ subject.cookbooks.each do |cookbook|
28
+ expect(cookbook).to be_a(Berkshelf::API::RemoteCookbook)
29
+ end
30
+ end
31
+
32
+ it "each RemoteCookbook is tagged with a location_type matching the worker_type of the builder" do
33
+ subject.cookbooks.each do |cookbook|
34
+ expect(cookbook.location_type).to eql(described_class.worker_type)
35
+ end
36
+ end
37
+ end
38
+
39
+ describe "#build" do
40
+ before do
41
+ Berkshelf::API::CacheManager.start
42
+ chef_cookbook("ruby", "1.0.0")
43
+ chef_cookbook("ruby", "2.0.0")
44
+ chef_cookbook("elixir", "3.0.0")
45
+ chef_cookbook("elixir", "3.0.1")
46
+ end
47
+
48
+ let(:cache) { Berkshelf::API::CacheManager.instance.cache }
49
+
50
+ it "adds each item to the cache" do
51
+ subject.build
52
+ expect(cache).to have_cookbook("ruby", "1.0.0")
53
+ expect(cache).to have_cookbook("ruby", "2.0.0")
54
+ expect(cache).to have_cookbook("elixir", "3.0.0")
55
+ expect(cache).to have_cookbook("elixir", "3.0.1")
56
+ expect(cache.cookbooks).to have(4).items
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe Berkshelf::API::CacheBuilder::Worker::Opscode do
4
+ describe "ClassMethods" do
5
+ subject { described_class }
6
+ its(:worker_type) { should eql("opscode") }
7
+ end
8
+
9
+ let(:cookbooks) { ["chicken", "tuna"] }
10
+ let(:chicken_versions) { ["1.0", "2.0"] }
11
+ let(:tuna_versions) { ["3.0.0", "3.0.1"] }
12
+ let(:connection) do
13
+ connection = double('connection')
14
+ connection.stub(:cookbooks).and_return(cookbooks)
15
+ connection
16
+ end
17
+
18
+ subject do
19
+ Berkshelf::API::CacheManager.start
20
+ described_class.new
21
+ end
22
+
23
+ describe "#cookbooks" do
24
+ let(:location_type) { described_class.worker_type }
25
+ let(:location_path) { Berkshelf::API::SiteConnector::Opscode::V1_API}
26
+
27
+ it "returns an array of RemoteCookbooks described by the server" do
28
+ expected_value = [
29
+ Berkshelf::API::RemoteCookbook.new("chicken", "1.0", location_type, location_path),
30
+ Berkshelf::API::RemoteCookbook.new("chicken", "2.0", location_type, location_path),
31
+ Berkshelf::API::RemoteCookbook.new("tuna", "3.0.0", location_type, location_path),
32
+ Berkshelf::API::RemoteCookbook.new("tuna", "3.0.1", location_type, location_path)
33
+ ]
34
+
35
+ connection.should_receive(:future).with(:versions, "chicken").and_return(double(value: chicken_versions))
36
+ connection.should_receive(:future).with(:versions, "tuna").and_return(double(value: tuna_versions))
37
+ subject.should_receive(:connection).at_least(1).times.and_return(connection)
38
+ expect(subject.cookbooks).to eql(expected_value)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+
3
+ describe Berkshelf::API::CacheBuilder::Worker do
4
+ describe "ClassMethods" do
5
+ describe "::[]" do
6
+ it "returns the class of the registered worker" do
7
+ expect(described_class["opscode"]).to eql(Berkshelf::API::CacheBuilder::Worker::Opscode)
8
+ end
9
+ end
10
+
11
+ describe "::register" do
12
+ it "adds the item to the Hash of types" do
13
+ worker = double('new-worker')
14
+ described_class.register("rspec-2", worker)
15
+ expect(described_class.types["rspec-2"]).to eql(worker)
16
+ end
17
+ end
18
+
19
+ describe "::types" do
20
+ subject { described_class.types }
21
+
22
+ it "returns a Hash" do
23
+ expect(subject).to be_a(Hash)
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ describe Berkshelf::API::CacheBuilder::Worker::Base do
30
+ describe "ClassMethods" do
31
+ describe "::worker_type" do
32
+ let(:klass) { Class.new(described_class) }
33
+
34
+ it "registers the worker type and class to Worker.types" do
35
+ klass.worker_type("rspec")
36
+ expect(Berkshelf::API::CacheBuilder::Worker.types).to include("rspec")
37
+ end
38
+ end
39
+ end
40
+
41
+ let(:cache_manager) { double(:diff => :chicken) }
42
+ subject { described_class.new }
43
+
44
+ describe "#diff" do
45
+ it "should delegate to the cache_manager to calculate the diff" do
46
+ subject.should_receive(:cache_manager).and_return(cache_manager)
47
+ subject.should_receive(:cookbooks).and_return(:cookbooks)
48
+
49
+ expect(subject.diff).to eql(:chicken)
50
+ end
51
+
52
+ it "should memoize the diff to prevent recalculating" do
53
+ subject.should_receive(:cache_manager).exactly(1).times.and_return(cache_manager)
54
+ subject.should_receive(:cookbooks).and_return(:cookbooks)
55
+
56
+ subject.diff
57
+ subject.diff
58
+ end
59
+ end
60
+
61
+ describe "#clear_diff" do
62
+ it "should set the diff to nil" do
63
+ subject.should_receive(:cache_manager).and_return(cache_manager)
64
+ subject.should_receive(:cookbooks).and_return(:cookbooks)
65
+
66
+ subject.diff
67
+ expect(subject.instance_variable_get(:@diff)).to eql(:chicken)
68
+ subject.send(:clear_diff)
69
+ expect(subject.instance_variable_get(:@diff)).to eql(nil)
70
+ end
71
+
72
+ it "memoizes the diff to prevent recalculating" do
73
+ subject.should_receive(:cache_manager).exactly(1).times.and_return(cache_manager)
74
+ subject.should_receive(:cookbooks).and_return(:cookbooks)
75
+
76
+ subject.diff
77
+ subject.diff
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe Berkshelf::API::CacheBuilder do
4
+ let(:instance) { described_class.new }
5
+
6
+ describe "#build" do
7
+ subject(:build) { instance.build }
8
+ let(:workers) { [ double('worker') ] }
9
+ let(:future) { double('future', value: nil) }
10
+
11
+ before { instance.stub(workers: workers) }
12
+
13
+ it "sends a #build message to each worker" do
14
+ workers.each { |worker| worker.should_receive(:future).with(:build).and_return(future) }
15
+ build
16
+ end
17
+ end
18
+
19
+ describe "#workers" do
20
+ subject(:workers) { instance.workers }
21
+
22
+ it "returns an array of workers" do
23
+ expect(workers).to be_a(Array)
24
+ workers.each do |worker|
25
+ expect(worker).to be_a(described_class::Worker::Base)
26
+ end
27
+ end
28
+
29
+ it "has one worker started by default" do
30
+ expect(workers).to have(1).item
31
+ end
32
+
33
+ it "has an opscode worker started by default" do
34
+ expect(workers.first).to be_a(described_class::Worker::Opscode)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,123 @@
1
+ require 'spec_helper'
2
+
3
+ describe Berkshelf::API::CacheManager do
4
+ describe "ClassMethods" do
5
+ describe "::new" do
6
+ subject { described_class.new }
7
+ its(:cache) { should be_empty }
8
+
9
+ context "when a save file exists" do
10
+ before do
11
+ @tempfile = Tempfile.new('berkshelf-api-rspec')
12
+ described_class.stub(:cache_file) { @tempfile.path }
13
+ end
14
+ after { @tempfile.close(true) }
15
+
16
+ it "loads the saved cache" do
17
+ described_class.any_instance.should_receive(:load_save)
18
+ subject
19
+ end
20
+ end
21
+
22
+ context "when a save file does not exist" do
23
+ before { described_class.stub(cache_file: tmp_path.join('does_not_exist').to_s) }
24
+
25
+ it "skips loading of the saved cache" do
26
+ described_class.any_instance.should_not_receive(:load_save)
27
+ subject
28
+ end
29
+ end
30
+ end
31
+
32
+ describe "::start" do
33
+ it "starts and registers a cache manager it with the application" do
34
+ described_class.start
35
+
36
+ expect(Berkshelf::API::Application[:cache_manager]).to be_a(described_class)
37
+ expect(Berkshelf::API::Application[:cache_manager]).to be_alive
38
+ end
39
+ end
40
+ end
41
+
42
+ subject { described_class.new }
43
+
44
+ describe "#add" do
45
+ pending
46
+ end
47
+
48
+ describe "#load_save" do
49
+ pending
50
+ end
51
+
52
+ describe "#remove" do
53
+ pending
54
+ end
55
+
56
+ describe "#clear" do
57
+ it "returns an empty Hash" do
58
+ result = subject.clear
59
+ expect(result).to be_a(Hash)
60
+ expect(result).to be_empty
61
+ end
62
+
63
+ it "empties the cache" do
64
+ subject.clear
65
+ expect(subject.cache).to be_empty
66
+ end
67
+ end
68
+
69
+ describe "#diff" do
70
+ let(:cookbook_one) { Berkshelf::API::RemoteCookbook.new("ruby", "1.2.3", "opscode") }
71
+ let(:cookbook_two) { Berkshelf::API::RemoteCookbook.new("elixir", "2.0.0", "opscode") }
72
+ let(:comparison) { Array.new }
73
+
74
+ before do
75
+ subject.add(cookbook_one, double(dependencies: nil, platforms: nil))
76
+ subject.add(cookbook_two, double(dependencies: nil, platforms: nil))
77
+
78
+ @created, @deleted = @diff = subject.diff(comparison)
79
+ end
80
+
81
+ it "returns two items" do
82
+ expect(@diff).to have(2).items
83
+ end
84
+
85
+ context "when there are created and deleted cookbooks" do
86
+ let(:new_cookbook) { Berkshelf::API::RemoteCookbook.new("ruby", "3.0.0", "opscode") }
87
+ let(:comparison) { [ cookbook_one, new_cookbook ] }
88
+
89
+ it "should return created and deleted cookbooks" do
90
+ expect(@created).to eql([new_cookbook])
91
+ expect(@deleted).to eql([cookbook_two])
92
+ end
93
+ end
94
+
95
+ context "when there are only created cookbooks" do
96
+ let(:new_cookbook) { Berkshelf::API::RemoteCookbook.new("ruby", "3.0.0", "opscode") }
97
+ let(:comparison) { [ cookbook_one, cookbook_two, new_cookbook ] }
98
+
99
+ it "should return only created cookbooks" do
100
+ expect(@created).to eql([new_cookbook])
101
+ expect(@deleted).to be_empty
102
+ end
103
+ end
104
+
105
+ context "when there are only deleted cookbooks" do
106
+ let(:comparison) { [ cookbook_one ] }
107
+
108
+ it "should return only deleted cookbooks" do
109
+ expect(@created).to be_empty
110
+ expect(@deleted).to eql([cookbook_two])
111
+ end
112
+ end
113
+
114
+ context "when there are no differences" do
115
+ let(:comparison) { [ cookbook_one, cookbook_two ] }
116
+
117
+ it "should return empty arrays" do
118
+ expect(@created).to be_empty
119
+ expect(@deleted).to be_empty
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe Berkshelf::API::Config do
4
+ describe "ClassMethods" do
5
+ describe "::default_path" do
6
+ it "returns a String" do
7
+ expect(described_class.default_path).to be_a(String)
8
+ end
9
+ end
10
+ end
11
+
12
+ describe "default config" do
13
+ subject { described_class.new }
14
+
15
+ it "has a endpoint configured" do
16
+ expect(subject.endpoints).to have(1).item
17
+ end
18
+
19
+ it "has the Opscode community site as an endpoint" do
20
+ expect(subject.endpoints.first.type).to eql("opscode")
21
+ expect(subject.endpoints.first.options[:url]).to eql("http://cookbooks.opscode.com/api/v1")
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,109 @@
1
+ require 'spec_helper'
2
+
3
+ describe Berkshelf::API::DependencyCache do
4
+ describe "ClassMethods" do
5
+ describe "::from_file" do
6
+ let(:filepath) { @tempfile.path }
7
+ before { @tempfile = Tempfile.new('berkshelf-api-rspec') }
8
+ after { @tempfile.close(true) }
9
+
10
+ subject { described_class.from_file(filepath) }
11
+
12
+ context "when the file contains valid json" do
13
+ before do
14
+ @tempfile.write(JSON.generate({brooke: "winsor"}))
15
+ @tempfile.flush
16
+ end
17
+
18
+ it "returns an instance of DependencyCache" do
19
+ expect(subject).to be_a(described_class)
20
+ end
21
+ end
22
+
23
+ context "when the filepath does not exist" do
24
+ let(:filepath) { nil }
25
+
26
+ it "raises a SaveNotFoundError" do
27
+ expect { subject }.to raise_error(Berkshelf::API::SaveNotFoundError)
28
+ end
29
+ end
30
+
31
+ context "when the file does not contain valid JSON" do
32
+ let(:filepath) { @tempfile.path }
33
+ before do
34
+ @tempfile.write("asdfasdfasdf")
35
+ @tempfile.flush
36
+ end
37
+
38
+ it "raises an InvalidSaveError" do
39
+ expect { subject }.to raise_error(Berkshelf::API::InvalidSaveError)
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ let(:chicken) do
46
+ { "1.0" =>
47
+ {
48
+ dependencies: { "tuna" => "= 3.0.0" },
49
+ platforms: { "centos" => ">= 0.0.0" }
50
+ }
51
+ }
52
+ end
53
+ let(:tuna) do
54
+ { "3.0.0" =>
55
+ {
56
+ dependencies: { },
57
+ platforms: { "centos" => ">= 0.0.0" }
58
+ }
59
+ }
60
+ end
61
+ let(:contents) do
62
+ {
63
+ "chicken" => chicken,
64
+ "tuna" => tuna,
65
+ }
66
+ end
67
+
68
+ subject { described_class.new(contents) }
69
+
70
+ describe "#cookbooks" do
71
+ it "should return a list of RemoteCookbooks" do
72
+ expected_value = [
73
+ Berkshelf::API::RemoteCookbook.new("chicken", "1.0"),
74
+ Berkshelf::API::RemoteCookbook.new("tuna", "3.0.0")
75
+ ]
76
+
77
+ expect(subject.cookbooks).to eql(expected_value)
78
+ end
79
+ end
80
+
81
+ describe "#add" do
82
+ let(:cookbook) { Berkshelf::API::RemoteCookbook.new("ruby", "1.2.3", "opscode") }
83
+ before { subject.clear }
84
+
85
+ it "adds items to the cache" do
86
+ subject.add(cookbook, double(platforms: nil, dependencies: nil))
87
+ expect(subject.to_hash).to have(1).item
88
+ end
89
+ end
90
+
91
+ describe "#save" do
92
+ let(:path) { tmp_path.join('cerch.json') }
93
+
94
+ it "saves the contents of the cache as json to the given path" do
95
+ subject.save(path)
96
+ expect(File.exist?(path)).to be_true
97
+ end
98
+ end
99
+
100
+ describe "#clear" do
101
+ let(:cookbook) { Berkshelf::API::RemoteCookbook.new("ruby", "1.2.3", "opscode") }
102
+ before { subject.add(cookbook, double(platforms: nil, dependencies: nil)) }
103
+
104
+ it "empties items added to the cache" do
105
+ subject.clear
106
+ expect(subject).to be_empty
107
+ end
108
+ end
109
+ end