Skip to content

Commit

Permalink
BUGFIX: pass metadata to locking APIs (#138)
Browse files Browse the repository at this point in the history
* add failing tests for locks not passing auth info

* make lock APIs usable with authentication
  • Loading branch information
forestgagnon authored Dec 1, 2021
1 parent d47a9a8 commit c5d673c
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 16 deletions.
4 changes: 2 additions & 2 deletions lib/etcdv3/lock.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ def initialize(hostname, credentials, timeout, metadata = {})

def lock(name, lease_id, timeout: nil)
request = V3lockpb::LockRequest.new(name: name, lease: lease_id)
@stub.lock(request, deadline: deadline(timeout))
@stub.lock(request, metadata: @metadata, deadline: deadline(timeout))
end

def unlock(key, timeout: nil)
request = V3lockpb::UnlockRequest.new(key: key)
@stub.unlock(request, deadline: deadline(timeout))
@stub.unlock(request, metadata: @metadata, deadline: deadline(timeout))
end

private
Expand Down
4 changes: 2 additions & 2 deletions lib/etcdv3/namespace/lock.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ def initialize(hostname, credentials, timeout, namespace, metadata = {})
def lock(name, lease_id, timeout: nil)
name = prepend_prefix(@namespace, name)
request = V3lockpb::LockRequest.new(name: name, lease: lease_id)
resp = @stub.lock(request, deadline: deadline(timeout))
resp = @stub.lock(request, metadata: @metadata, deadline: deadline(timeout))
strip_prefix_from_lock(@namespace, resp)
end

def unlock(key, timeout: nil)
key = prepend_prefix(@namespace, key)
request = V3lockpb::UnlockRequest.new(key: key)
@stub.unlock(request, deadline: deadline(timeout))
@stub.unlock(request, metadata: @metadata, deadline: deadline(timeout))
end

private
Expand Down
24 changes: 19 additions & 5 deletions spec/etcdv3/lock_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,28 @@
#it_should_behave_like "a method with a GRPC timeout", described_class, :lock, :lock, 'foo'

describe '#lock' do
let(:lease_id) { lease_stub.lease_grant(10)['ID'] }
subject { stub.lock('foo', lease_id) }
it { is_expected.to be_an_instance_of(V3lockpb::LockResponse) }
it 'returns a response' do
lease_id = lease_stub.lease_grant(10)['ID']

expect(stub.lock('example1', lease_id)).to be_an_instance_of(V3lockpb::LockResponse)
end

it 'passes metadata correctly' do
lease_id = lease_stub.lease_grant(10)['ID']
stub = expect_metadata_passthrough(described_class, :lock, :lock)
stub.lock('example2', lease_id)
end
end

describe '#unlock' do
subject { stub.unlock('foo') }
it { is_expected.to be_an_instance_of(V3lockpb::UnlockResponse) }
it 'returns a response' do
expect(stub.unlock('example3')).to be_an_instance_of(V3lockpb::UnlockResponse)
end

it 'passes metadata correctly' do
stub = expect_metadata_passthrough(described_class, :unlock, :unlock)
stub.unlock('example4')
end
end
end
end
33 changes: 26 additions & 7 deletions spec/etcdv3/namespace/lock_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,41 @@

# Locking is not implemented in etcd v3.1.X
unless $instance.version < Gem::Version.new("3.2.0")
describe Etcdv3::Lock do
describe Etcdv3::Namespace::Lock do
let(:stub) { local_namespace_stub(Etcdv3::Namespace::Lock, 1, '/namespace/') }
let(:lease_stub) { local_stub(Etcdv3::Lease, 1) }

it_should_behave_like "a method with a GRPC timeout", described_class, :unlock, :unlock, 'foo'
# NOTE: this was running duplicate tests against Etcdv3::Lock before, but it
# doesn't work with Etcdv3::Namespace::Lock
#
# it_should_behave_like "a method with a GRPC timeout", described_class, :unlock, :unlock, 'foo'

# it_should_behave_like "a method with a GRPC timeout", described_class, :lock, :lock, 'foo'

describe '#lock' do
let(:lease_id) { lease_stub.lease_grant(10)['ID'] }
subject { stub.lock('foo', lease_id) }
it { is_expected.to be_an_instance_of(V3lockpb::LockResponse) }
it 'returns a response' do
lease_id = lease_stub.lease_grant(10)['ID']
expect(stub.lock('example1', lease_id)).to(
be_an_instance_of(V3lockpb::LockResponse)
)
end

it 'passes metadata correctly' do
lease_id = lease_stub.lease_grant(10)['ID']
stub = expect_metadata_passthrough_namespace(described_class, :lock, :lock, '/namespace/')
stub.lock('example2', lease_id)
end
end

describe '#unlock' do
subject { stub.unlock('foo') }
it { is_expected.to be_an_instance_of(V3lockpb::UnlockResponse) }
it 'returns a response' do
expect(stub.unlock('example3')).to be_an_instance_of(V3lockpb::UnlockResponse)
end

it 'passes metadata correctly' do
stub = expect_metadata_passthrough_namespace(described_class, :unlock, :unlock, '/namespace/')
stub.unlock('example4')
end
end
end
end
8 changes: 8 additions & 0 deletions spec/helpers/connections.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,18 @@ def local_stub(interface, timeout=nil)
interface.new(local_url, :this_channel_is_insecure, timeout, {})
end

def local_stub_with_metadata(interface, timeout: nil, metadata: {})
interface.new(local_url, :this_channel_is_insecure, timeout, metadata)
end

def local_namespace_stub(interface, timeout=nil, namespace)
interface.new(local_url, :this_channel_is_insecure, timeout, namespace, {})
end

def local_namespace_stub_with_metadata(interface, timeout: nil, namespace:, metadata: {})
interface.new(local_url, :this_channel_is_insecure, timeout, namespace, metadata)
end

def local_url
"127.0.0.1:#{port}"
end
Expand Down
21 changes: 21 additions & 0 deletions spec/helpers/metadata_passthrough.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module Helpers
module MetadataPassthrough
include Connections

def expect_metadata_passthrough(stub_class, method_name, expectation_target)
metadata = { user: "foo", password: "bar" }
handler = local_stub_with_metadata(stub_class, metadata: metadata, timeout: 1)
inner_stub = handler.instance_variable_get("@stub")
expect(inner_stub).to receive(expectation_target).with(anything, hash_including(metadata: metadata)).and_call_original
return handler
end

def expect_metadata_passthrough_namespace(stub_class, method_name, expectation_target, namespace)
metadata = { user: "foo", password: "bar" }
handler = local_namespace_stub_with_metadata(stub_class, metadata: metadata, timeout: 1, namespace: namespace)
inner_stub = handler.instance_variable_get("@stub")
expect(inner_stub).to receive(expectation_target).with(anything, hash_including(metadata: metadata)).and_call_original
return handler
end
end
end
2 changes: 2 additions & 0 deletions spec/helpers/test_instance.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
require 'socket'
require 'timeout'
require 'helpers/connections'
require 'helpers/metadata_passthrough'

module Helpers
class TestInstance
include Helpers::Connections
include Helpers::MetadataPassthrough

class InvalidVersionException < StandardError; end
class PortInUseException < StandardError; end
Expand Down
2 changes: 2 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@
require 'etcdv3'
require 'helpers/test_instance'
require 'helpers/connections'
require 'helpers/metadata_passthrough'
require 'helpers/shared_examples_for_timeout'

$instance = Helpers::TestInstance.new

RSpec.configure do |config|
config.include(Helpers::Connections)
config.include(Helpers::MetadataPassthrough)

config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
Expand Down

0 comments on commit c5d673c

Please sign in to comment.