ridley 1.0.3 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1 +1 @@
1
- 1.9.3-p392
1
+ 1.9.3-p429
@@ -1,4 +1,5 @@
1
1
  require 'shellwords'
2
+ require 'buff/shell_out'
2
3
 
3
4
  module Ridley::Chef
4
5
  class Cookbook
@@ -67,8 +68,8 @@ module Ridley::Chef
67
68
  end
68
69
  end
69
70
 
71
+ include Buff::ShellOut
70
72
  include Ridley::Logging
71
- include Ridley::Mixin::ShellOut
72
73
  include Ridley::Mixin::Checksum
73
74
 
74
75
  attr_reader :cookbook_path
@@ -85,7 +85,9 @@ module Ridley
85
85
  #
86
86
  # @return [Object]
87
87
  def reload
88
- mass_assign(resource.find(self)._attributes_)
88
+ new_attributes = resource.find(self)._attributes_
89
+ @_attributes_ = nil
90
+ mass_assign(new_attributes)
89
91
  self
90
92
  end
91
93
 
@@ -30,10 +30,13 @@ module Ridley
30
30
  alias_method :default_attributes, :default
31
31
  alias_method :override_attributes, :override
32
32
 
33
- alias_method :normal_attributes=, :normal=
34
- alias_method :automatic_attributes=, :automatic=
35
- alias_method :default_attributes=, :default=
36
- alias_method :override_attributes=, :override=
33
+ # A merged hash containing a deep merge of all of the attributes respecting the node attribute
34
+ # precedence level.
35
+ #
36
+ # @return [hashie::Mash]
37
+ def chef_attributes
38
+ default.merge(normal.merge(override.merge(automatic)))
39
+ end
37
40
 
38
41
  # Set a node level normal attribute given the dotted path representation of the Chef
39
42
  # attribute and value.
@@ -65,6 +65,9 @@ module Ridley
65
65
  class SandboxCommitError < RidleyError; end
66
66
  class PermissionDenied < RidleyError; end
67
67
 
68
+ class SandboxUploadError < RidleyError; end
69
+ class ChecksumMismatch < RidleyError; end
70
+
68
71
  class HTTPError < RidleyError
69
72
  class << self
70
73
  def fabricate(env)
@@ -81,11 +81,7 @@ module Ridley
81
81
  end
82
82
  end
83
83
  ensure
84
- begin
85
- command_uploaders.map(&:cleanup)
86
- rescue ::WinRM::WinRMHTTPTransportError => ex
87
- log.info "Error cleaning up leftover Powershell scripts on some hosts"
88
- end
84
+ command_uploaders.map(&:cleanup)
89
85
  end
90
86
 
91
87
  # Returns the command if it does not break the WinRM command length
@@ -4,35 +4,30 @@ module Ridley
4
4
  class << self
5
5
  # Return the checksum of the contents of the file at the given filepath
6
6
  #
7
- # @param [String] path
8
- # file to checksum
7
+ # @param [String] io
8
+ # a filepath or an IO
9
+ # @param [Digest::Base] digest
9
10
  #
10
11
  # @return [String]
11
12
  # the binary checksum of the contents of the file
12
- def checksum(path)
13
- File.open(path, 'rb') { |f| checksum_io(f, Digest::MD5.new) }
13
+ def checksum(io, digest = Digest::MD5.new)
14
+ io = io.dup
15
+ while chunk = io.read(1024 * 8)
16
+ digest.update(chunk)
17
+ end
18
+ digest.hexdigest
14
19
  end
15
20
 
16
21
  # Return a base64 encoded checksum of the contents of the given file. This is the expected
17
22
  # format of sandbox checksums given to the Chef Server.
18
23
  #
19
- # @param [String] path
20
- #
21
- # @return [String]
22
- # a base64 encoded checksum
23
- def checksum64(path)
24
- Base64.encode64([checksum(path)].pack("H*")).strip
25
- end
26
-
27
24
  # @param [String] io
28
- # @param [Object] digest
25
+ # a filepath or an IO
29
26
  #
30
27
  # @return [String]
31
- def checksum_io(io, digest)
32
- while chunk = io.read(1024 * 8)
33
- digest.update(chunk)
34
- end
35
- digest.hexdigest
28
+ # a base64 encoded checksum
29
+ def checksum64(io)
30
+ Base64.encode64([checksum(io)].pack("H*")).strip
36
31
  end
37
32
  end
38
33
 
@@ -53,22 +48,33 @@ module Ridley
53
48
  # @param [Ridley::SandboxObject] sandbox
54
49
  # @param [String] chk_id
55
50
  # checksum of the file being uploaded
56
- # @param [String] path
57
- # path to the file to upload
51
+ # @param [String, #read] file
52
+ # a filepath or an IO
53
+ #
54
+ # @raise [Errors::ChecksumMismatch]
55
+ # if the given file does not match the expected checksum
58
56
  #
59
57
  # @return [Hash, nil]
60
- def upload(sandbox, chk_id, path)
58
+ def upload(sandbox, chk_id, file)
61
59
  checksum = sandbox.checksum(chk_id)
62
60
 
63
61
  unless checksum[:needs_upload]
64
62
  return nil
65
63
  end
66
64
 
67
- headers = {
65
+ io = file.respond_to?(:read) ? file : File.new(file, 'rb')
66
+ calculated_checksum = self.class.checksum64(io)
67
+ expected_checksum = Base64.encode64([chk_id].pack('H*')).strip
68
+
69
+ unless calculated_checksum == expected_checksum
70
+ raise Errors::ChecksumMismatch,
71
+ "Error uploading #{chk_id}. Expected #{expected_checksum} but got #{calculated_checksum}"
72
+ end
73
+
74
+ headers = {
68
75
  'Content-Type' => 'application/x-binary',
69
- 'content-md5' => self.class.checksum64(path)
76
+ 'content-md5' => calculated_checksum
70
77
  }
71
- contents = File.open(path, 'rb') { |f| f.read }
72
78
 
73
79
  url = URI(checksum[:url])
74
80
  upload_path = url.path
@@ -86,7 +92,7 @@ module Ridley
86
92
  c.response :follow_redirects
87
93
  c.request :chef_auth, self.client_name, self.client_key
88
94
  c.adapter :net_http
89
- end.put(upload_path, contents, headers)
95
+ end.put(upload_path, io.read, headers)
90
96
  rescue Ridley::Errors::HTTPError => ex
91
97
  abort(ex)
92
98
  end
@@ -1,3 +1,3 @@
1
1
  module Ridley
2
- VERSION = "1.0.3"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -18,6 +18,7 @@ Gem::Specification.new do |s|
18
18
  s.required_ruby_version = ">= 1.9.1"
19
19
 
20
20
  s.add_runtime_dependency 'addressable'
21
+ s.add_runtime_dependency 'buff-shell_out', '~> 0.1'
21
22
  s.add_runtime_dependency 'chozo', '>= 0.6.0'
22
23
  s.add_runtime_dependency 'celluloid', '~> 0.14.0'
23
24
  s.add_runtime_dependency 'celluloid-io', '~> 0.14.0'
@@ -8,8 +8,8 @@ describe "Sandbox API operations", type: "acceptance" do
8
8
 
9
9
  let(:checksums) do
10
10
  [
11
- Ridley::SandboxUploader.checksum(fixtures_path.join("recipe_one.rb")),
12
- Ridley::SandboxUploader.checksum(fixtures_path.join("recipe_two.rb"))
11
+ Ridley::SandboxUploader.checksum(File.open(fixtures_path.join("recipe_one.rb"))),
12
+ Ridley::SandboxUploader.checksum(File.open(fixtures_path.join("recipe_two.rb")))
13
13
  ]
14
14
  end
15
15
 
@@ -142,21 +142,28 @@ describe Ridley::ChefObject do
142
142
  end
143
143
 
144
144
  describe "#reload" do
145
- let(:updated_subject) { double('updated_subject', _attributes_: { fake_attribute: "some_value" }) }
145
+ let(:updated_subject) { double('updated_subject', _attributes_: { one: "val" }) }
146
146
 
147
147
  before(:each) do
148
- subject.class.attribute(:fake_attribute)
149
- resource.should_receive(:find).with(subject).and_return(updated_subject)
148
+ subject.class.attribute(:one)
149
+ subject.class.attribute(:two)
150
+ resource.stub(:find).with(subject).and_return(updated_subject)
150
151
  end
151
152
 
152
153
  it "returns itself" do
153
154
  subject.reload.should eql(subject)
154
155
  end
155
156
 
156
- it "sets the attributes of self to include those of the reloaded object" do
157
+ it "sets the attributes of self to equal those of the updated object" do
157
158
  subject.reload
158
159
 
159
- subject.get_attribute(:fake_attribute).should eql("some_value")
160
+ subject.get_attribute(:one).should eql("val")
161
+ end
162
+
163
+ it "does not include attributes not set by the updated object" do
164
+ subject.two = "other"
165
+ subject.reload
166
+ expect(subject.two).to be_nil
160
167
  end
161
168
  end
162
169
 
@@ -5,6 +5,55 @@ describe Ridley::NodeObject do
5
5
  let(:instance) { described_class.new(resource) }
6
6
  subject { instance }
7
7
 
8
+ describe "#chef_attributes" do
9
+ subject { instance.chef_attributes }
10
+
11
+ it "returns a Hashie::Mash" do
12
+ expect(subject).to be_a(Hashie::Mash)
13
+ end
14
+
15
+ it "includes default attributes" do
16
+ instance.default = default = { default: { one: "val", two: "val" } }
17
+ expect(subject.to_hash).to include(default)
18
+ end
19
+
20
+ it "includes normal attributes" do
21
+ instance.normal = normal = { normal: { one: "new", two: "val" } }
22
+ expect(subject.to_hash).to include(normal)
23
+ end
24
+
25
+ it "includes override attributes" do
26
+ instance.override = override = { override: { one: "new", two: "val" } }
27
+ expect(subject.to_hash).to include(override)
28
+ end
29
+
30
+ it "includes automatic attributes" do
31
+ instance.automatic = automatic = { automatic: { one: "new", two: "val" } }
32
+ expect(subject.to_hash).to include(automatic)
33
+ end
34
+
35
+ it "overrides default attributes with normal attributes" do
36
+ instance.default = default = { one: "old", two: "old" }
37
+ instance.normal = normal = { one: "new" }
38
+ expect(subject[:one]).to eql("new")
39
+ expect(subject[:two]).to eql("old")
40
+ end
41
+
42
+ it "overrides normal attributes with override attributes" do
43
+ instance.normal = normal = { one: "old", two: "old" }
44
+ instance.override = override = { one: "new" }
45
+ expect(subject[:one]).to eql("new")
46
+ expect(subject[:two]).to eql("old")
47
+ end
48
+
49
+ it "overrides override attributes with automatic attributes" do
50
+ instance.override = override = { one: "old", two: "old" }
51
+ instance.automatic = automatic = { one: "new" }
52
+ expect(subject[:one]).to eql("new")
53
+ expect(subject[:two]).to eql("old")
54
+ end
55
+ end
56
+
8
57
  describe "#set_chef_attribute" do
9
58
  it "sets an normal node attribute at the nested path" do
10
59
  subject.set_chef_attribute('deep.nested.item', true)
@@ -1,6 +1,24 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Ridley::SandboxUploader do
4
+ describe "ClassMethods" do
5
+ subject { described_class }
6
+
7
+ describe "::checksum" do
8
+ let(:io) { StringIO.new("some long string") }
9
+ subject { described_class.checksum(io) }
10
+
11
+ it { should eq("2fb66bbfb88cdf9e07a3f1d1dfad71ab") }
12
+ end
13
+
14
+ describe "::checksum64" do
15
+ let(:io) { StringIO.new("some long string") }
16
+ subject { described_class.checksum64(io) }
17
+
18
+ it { should eq("L7Zrv7iM354Ho/HR361xqw==") }
19
+ end
20
+ end
21
+
4
22
  let(:client_name) { "reset" }
5
23
  let(:client_key) { fixtures_path.join('reset.pem') }
6
24
  let(:connection) do
@@ -22,35 +40,14 @@ describe Ridley::SandboxUploader do
22
40
 
23
41
  let(:sandbox) { Ridley::SandboxObject.new(resource, checksums: checksums) }
24
42
 
25
- describe "ClassMethods" do
26
- subject { described_class }
27
-
28
- describe "::checksum" do
29
- subject { described_class.checksum(path) }
30
-
31
- let(:path) { fixtures_path.join('reset.pem') }
32
-
33
- it { should eq("a0608f1eb43ee4cca510bf95f8d209f7") }
34
- end
35
-
36
- describe "::checksum64" do
37
- subject { described_class.checksum64(path) }
38
-
39
- let(:path) { fixtures_path.join('reset.pem') }
40
-
41
- it { should eq("oGCPHrQ+5MylEL+V+NIJ9w==") }
42
- end
43
- end
44
-
45
43
  subject { described_class.new(client_name, client_key, {}) }
46
44
 
47
45
  describe "#upload" do
48
- let(:chk_id) { "oGCPHrQ+5MylEL+V+NIJ9w==" }
46
+ let(:chk_id) { "a0608f1eb43ee4cca510bf95f8d209f7" }
49
47
  let(:path) { fixtures_path.join('reset.pem').to_s }
48
+ let(:different_path) { fixtures_path.join('recipe_one.rb').to_s }
50
49
 
51
- before do
52
- connection.stub(foss?: false)
53
- end
50
+ before { connection.stub(foss?: false) }
54
51
 
55
52
  context "when the checksum needs uploading" do
56
53
  let(:checksums) do
@@ -67,6 +64,10 @@ describe Ridley::SandboxUploader do
67
64
 
68
65
  subject.upload(sandbox, chk_id, path)
69
66
  end
67
+
68
+ it "raises an exception when the calcuated checksum does not match the expected checksum" do
69
+ expect { subject.upload(sandbox, chk_id, different_path) }.to raise_error(Ridley::Errors::ChecksumMismatch)
70
+ end
70
71
  end
71
72
 
72
73
  context "when the checksum doesn't need uploading" do
@@ -78,10 +79,6 @@ describe Ridley::SandboxUploader do
78
79
  }
79
80
  end
80
81
 
81
- let(:sandbox) do
82
- Ridley::SandboxObject.new(double, checksums: checksums)
83
- end
84
-
85
82
  it "returns nil" do
86
83
  subject.upload(sandbox, chk_id, path).should be_nil
87
84
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ridley
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-06-29 00:00:00.000000000 Z
13
+ date: 2013-06-12 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: addressable
@@ -28,6 +28,22 @@ dependencies:
28
28
  - - ! '>='
29
29
  - !ruby/object:Gem::Version
30
30
  version: '0'
31
+ - !ruby/object:Gem::Dependency
32
+ name: buff-shell_out
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ~>
37
+ - !ruby/object:Gem::Version
38
+ version: '0.1'
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ version: '0.1'
31
47
  - !ruby/object:Gem::Dependency
32
48
  name: chozo
33
49
  requirement: !ruby/object:Gem::Requirement
@@ -280,7 +296,6 @@ files:
280
296
  - lib/ridley/middleware/retry.rb
281
297
  - lib/ridley/mixin.rb
282
298
  - lib/ridley/mixin/checksum.rb
283
- - lib/ridley/mixin/shell_out.rb
284
299
  - lib/ridley/resource.rb
285
300
  - lib/ridley/resources.rb
286
301
  - lib/ridley/resources/client_resource.rb
@@ -356,7 +371,6 @@ files:
356
371
  - spec/unit/ridley/middleware/chef_response_spec.rb
357
372
  - spec/unit/ridley/middleware/gzip_spec.rb
358
373
  - spec/unit/ridley/middleware/parse_json_spec.rb
359
- - spec/unit/ridley/mixin/shell_out_spec.rb
360
374
  - spec/unit/ridley/resource_spec.rb
361
375
  - spec/unit/ridley/resources/client_resource_spec.rb
362
376
  - spec/unit/ridley/resources/cookbook_resource_spec.rb
@@ -390,7 +404,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
390
404
  version: '0'
391
405
  segments:
392
406
  - 0
393
- hash: -1974631482888934392
407
+ hash: -1556301606897048958
394
408
  requirements: []
395
409
  rubyforge_project:
396
410
  rubygems_version: 1.8.23
@@ -457,7 +471,6 @@ test_files:
457
471
  - spec/unit/ridley/middleware/chef_response_spec.rb
458
472
  - spec/unit/ridley/middleware/gzip_spec.rb
459
473
  - spec/unit/ridley/middleware/parse_json_spec.rb
460
- - spec/unit/ridley/mixin/shell_out_spec.rb
461
474
  - spec/unit/ridley/resource_spec.rb
462
475
  - spec/unit/ridley/resources/client_resource_spec.rb
463
476
  - spec/unit/ridley/resources/cookbook_resource_spec.rb
@@ -1,84 +0,0 @@
1
- module Ridley
2
- module Mixin
3
- # A Ruby platform agnostic way of executing shell commands on the local system
4
- module ShellOut
5
- class Response
6
- # @return [Fixnum]
7
- attr_reader :exitstatus
8
- # @return [String]
9
- attr_reader :stdout
10
- # @return [String]
11
- attr_reader :stderr
12
-
13
- # @param [Fixnum] exitstatus
14
- # @param [String] stdout
15
- # @param [String] stderr
16
- def initialize(exitstatus, stdout, stderr)
17
- @exitstatus = exitstatus
18
- @stdout = stdout
19
- @stderr = stderr
20
- end
21
-
22
- def success?
23
- exitstatus == 0
24
- end
25
-
26
- def error?
27
- !success?
28
- end
29
- alias_method :failure?, :error?
30
- end
31
-
32
- include Chozo::RubyEngine
33
- extend self
34
-
35
- # Executes the given shell command on the local system
36
- #
37
- # @param [String] command
38
- # The command to execute
39
- #
40
- # @return [ShellOut::Response]
41
- def shell_out(command)
42
- process_status, out, err = jruby? ? jruby_out(command) : mri_out(command)
43
- Response.new(process_status, out, err)
44
- end
45
-
46
- private
47
-
48
- # @param [String] command
49
- # The command to execute
50
- def mri_out(command)
51
- out, err = Tempfile.new('ridley.shell_out.stdout'), Tempfile.new('ridley.shell_out.stderr')
52
-
53
- begin
54
- pid = Process.spawn(command, out: out.to_i, err: err.to_i)
55
- pid, status = Process.waitpid2(pid)
56
- exitstatus = status.exitstatus
57
- rescue Errno::ENOENT => ex
58
- exitstatus = 127
59
- out.write("")
60
- err.write("command not found: #{command}")
61
- end
62
-
63
- out.close
64
- err.close
65
-
66
- [ exitstatus, File.read(out), File.read(err) ]
67
- end
68
-
69
- # @param [String] command
70
- # The command to execute
71
- def jruby_out(command)
72
- out, err = StringIO.new, StringIO.new
73
- $stdout, $stderr = out, err
74
- system(command)
75
-
76
- out.rewind
77
- err.rewind
78
- [ $?, out.read, err.read ]
79
- ensure
80
- $stdout, $stderr = STDOUT, STDERR
81
- end
82
- end
83
- end
84
- end
@@ -1,37 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Ridley::Mixin::ShellOut do
4
- describe "shell_out" do
5
- let(:command) { "ls" }
6
-
7
- subject(:result) { described_class.shell_out(command) }
8
-
9
- it "returns a ShellOut::Response" do
10
- expect(result).to be_a(described_class::Response)
11
- end
12
-
13
- its(:stdout) { should be_a(String) }
14
- its(:stderr) { should be_a(String) }
15
- its(:exitstatus) { should be_a(Fixnum) }
16
- its(:success?) { should be_true }
17
- its(:error?) { should be_false }
18
-
19
- context "when on MRI" do
20
- before { described_class.stub(jruby?: false) }
21
-
22
- it "delegates to #mri_out" do
23
- described_class.should_receive(:mri_out).with(command)
24
- result
25
- end
26
- end
27
-
28
- context "when on JRuby" do
29
- before { described_class.stub(jruby?: true) }
30
-
31
- it "delegates to #jruby_out" do
32
- described_class.should_receive(:jruby_out).with(command)
33
- result
34
- end
35
- end
36
- end
37
- end