ridley 1.0.3 → 1.1.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.
@@ -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