auto-consul 0.0.1
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.
- data/Gemfile +12 -0
- data/Gemfile.lock +36 -0
- data/LICENSE +21 -0
- data/README.md +132 -0
- data/bin/auto-consul +110 -0
- data/lib/auto-consul/cluster.rb +53 -0
- data/lib/auto-consul/local.rb +78 -0
- data/lib/auto-consul/providers/s3.rb +108 -0
- data/lib/auto-consul/run_state/cli.rb +61 -0
- data/lib/auto-consul/run_state.rb +5 -0
- data/lib/auto-consul/runner.rb +56 -0
- data/lib/auto-consul.rb +8 -0
- data/spec/cluster_spec.rb +100 -0
- data/spec/local_state_spec.rb +110 -0
- data/spec/run_state_cli_provider_spec.rb +82 -0
- data/spec/runner_spec.rb +75 -0
- data/spec/s3_registry_provider_spec.rb +239 -0
- data/spec/spec-helper.rb +43 -0
- metadata +128 -0
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'spec-helper'
|
2
|
+
|
3
|
+
describe AutoConsul::Cluster do
|
4
|
+
let(:uri) { "s3://some-bucket-#{double.object_id}/some/prefix/#{double.object_id}" }
|
5
|
+
let(:servers_uri) { "#{uri}/servers" }
|
6
|
+
let(:agents_uri) { "#{uri}/agents" }
|
7
|
+
let(:registry_lookup) { AutoConsul::Cluster.should_receive(:get_provider_for_uri) }
|
8
|
+
|
9
|
+
subject { AutoConsul::Cluster.new uri }
|
10
|
+
|
11
|
+
it 'should get provider for */servers' do
|
12
|
+
registry_lookup.once.with(servers_uri).and_return(provider = double("S3Provider"))
|
13
|
+
expect(subject.servers).to equal(provider)
|
14
|
+
expect(subject.servers).to equal(provider)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should get provider for */agents' do
|
18
|
+
registry_lookup.once.with(agents_uri).and_return(provider = double("S3Provider"))
|
19
|
+
subject.agents
|
20
|
+
expect(subject.agents).to equal(provider)
|
21
|
+
expect(subject.agents).to equal(provider)
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'set_mode!' do
|
25
|
+
let(:agents) { [] }
|
26
|
+
let(:servers) { [] }
|
27
|
+
let(:expiry) { double }
|
28
|
+
let(:local_state) { double }
|
29
|
+
|
30
|
+
before do
|
31
|
+
subject.stub(:agents).and_return(agents_reg = double)
|
32
|
+
agents_reg.stub(:members).with(expiry).and_return agents
|
33
|
+
subject.stub(:servers).and_return(servers_reg = double)
|
34
|
+
servers_reg.stub(:members).with(expiry).and_return servers
|
35
|
+
end
|
36
|
+
|
37
|
+
describe 'with no active servers' do
|
38
|
+
before do
|
39
|
+
# Default desired servers of 1.
|
40
|
+
local_state.should_receive(:set_server!)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should set_server! with no server count specified' do
|
44
|
+
subject.set_mode! local_state, expiry
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should set_server! with a server count specified' do
|
48
|
+
subject.set_mode! local_state, expiry, 2
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe 'without enough active servers' do
|
53
|
+
before do
|
54
|
+
local_state.should_receive(:set_server!).with
|
55
|
+
local_state.should_not_receive(:set_agent!)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should set_server! given 1 server but wanting 3' do
|
59
|
+
servers << double
|
60
|
+
subject.set_mode! local_state, expiry, 3
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should set_server! given 3 servers but wanting 4' do
|
64
|
+
servers << double
|
65
|
+
servers << double
|
66
|
+
servers << double
|
67
|
+
subject.set_mode! local_state, expiry, 4
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe 'with enough active servers' do
|
72
|
+
before do
|
73
|
+
servers << double
|
74
|
+
local_state.should_receive(:set_agent!).with
|
75
|
+
local_state.should_not_receive(:set_server!)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should set_agent! given 1 server, wanting default' do
|
79
|
+
subject.set_mode! local_state, expiry
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should set_agent! given 1 server, wanting 1' do
|
83
|
+
subject.set_mode! local_state, expiry, 1
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'should set_agent! given 3 servers, wanting 3' do
|
87
|
+
servers << double
|
88
|
+
servers << double
|
89
|
+
subject.set_mode! local_state, expiry, 3
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should set_agent! given 3 servers, wanting 2' do
|
93
|
+
servers << double
|
94
|
+
servers << double
|
95
|
+
subject.set_mode! local_state, expiry, 2
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'spec-helper'
|
2
|
+
|
3
|
+
shared_examples_for 'a server' do
|
4
|
+
it 'should use "PATH/server" for data path' do
|
5
|
+
subject.data_path.should == File.join(tempdir.path 'server')
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'should be true for server?' do
|
9
|
+
subject.should be_server
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should be false for agent?' do
|
13
|
+
subject.should_not be_agent
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
shared_examples_for 'an agent' do
|
18
|
+
it 'should use "PATH/agent" for data path' do
|
19
|
+
subject.data_path.should == File.join(tempdir.path 'agent')
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should be false for server?' do
|
23
|
+
subject.should_not be_server
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should be true for agent?' do
|
27
|
+
subject.should be_agent
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
tempdir_context AutoConsul::Local do
|
32
|
+
subject { AutoConsul::Local.bind_to_path path }
|
33
|
+
|
34
|
+
let(:path) { tempdir.path }
|
35
|
+
|
36
|
+
context 'given a non-existent path' do
|
37
|
+
let(:path) { tempdir.path 'faux', 'state', 'directory' }
|
38
|
+
|
39
|
+
it 'should create the desired path' do
|
40
|
+
File.directory?(subject.path).should be_true
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should use "PATH/mode" for mode_path' do
|
45
|
+
subject.mode_path.should == File.join(path, 'mode')
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'set to server mode' do
|
49
|
+
before do
|
50
|
+
subject.set_server!
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should enter "server" in mode file' do
|
54
|
+
File.open(tempdir.path('mode'), 'r').read.should == 'server'
|
55
|
+
end
|
56
|
+
|
57
|
+
it_should_behave_like 'a server'
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'set to agent mode' do
|
61
|
+
before do
|
62
|
+
subject.set_agent!
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should enter "agent" in mode file' do
|
66
|
+
File.open(tempdir.path('mode'), 'r').read.should == 'agent'
|
67
|
+
end
|
68
|
+
|
69
|
+
it_should_behave_like 'an agent'
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'given a server mode file' do
|
73
|
+
before do
|
74
|
+
File.open(File.join(path, 'mode'), 'w') {|f| f.write 'server'}
|
75
|
+
end
|
76
|
+
|
77
|
+
it_should_behave_like 'a server'
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'given an agent mode file' do
|
81
|
+
before do
|
82
|
+
File.open(File.join(path, 'mode'), 'w') {|f| f.write 'agent'}
|
83
|
+
end
|
84
|
+
|
85
|
+
it_should_behave_like 'an agent'
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'given a bogus mode file' do
|
89
|
+
before do
|
90
|
+
File.open(File.join(path, 'mode'), 'w') {|f| f.write 'shmerver'}
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should have a nil mode' do
|
94
|
+
subject.mode.should be_nil
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'should be false for server?' do
|
98
|
+
subject.should_not be_server
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'should be false for agent?' do
|
102
|
+
subject.should_not be_agent
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'should have a nil data_path' do
|
106
|
+
subject.data_path.should be_nil
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'spec-helper'
|
2
|
+
|
3
|
+
def mock_output name
|
4
|
+
File.open(File.join(File.dirname(__FILE__), name), 'r') do |f|
|
5
|
+
f.read
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
shared_examples_for 'a running cli agent' do
|
10
|
+
it 'should be running' do
|
11
|
+
expect(subject).to be_running
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should be a running cli agent' do
|
15
|
+
expect(subject).to be_agent
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe AutoConsul::RunState::CLIProvider do
|
20
|
+
subject do
|
21
|
+
AutoConsul::RunState::CLIProvider.new
|
22
|
+
end
|
23
|
+
|
24
|
+
let :consul_fail do
|
25
|
+
c = subject.should_receive(:system).with do |cmd, opts|
|
26
|
+
cmd.should == 'consul info'
|
27
|
+
end
|
28
|
+
c.and_return false
|
29
|
+
c
|
30
|
+
end
|
31
|
+
|
32
|
+
let :consul_call do
|
33
|
+
subject.should_receive(:system) do |cmd, opts|
|
34
|
+
cmd.should == 'consul info'
|
35
|
+
opts[:out].write(output)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'with no running consul' do
|
40
|
+
before do
|
41
|
+
consul_fail
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should not be running' do
|
45
|
+
expect(subject).not_to be_running
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should not be a running cli agent' do
|
49
|
+
expect(subject).not_to be_agent
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should not be a server' do
|
53
|
+
expect(subject).not_to be_server
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe 'with running consul' do
|
58
|
+
before do
|
59
|
+
consul_call
|
60
|
+
end
|
61
|
+
|
62
|
+
describe 'as normal agent' do
|
63
|
+
let(:output) { mock_output 'agent-output.txt' }
|
64
|
+
|
65
|
+
it_should_behave_like 'a running cli agent'
|
66
|
+
|
67
|
+
it 'should not be a server' do
|
68
|
+
expect(subject).not_to be_server
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe 'as server' do
|
73
|
+
let(:output) { mock_output 'server-output.txt' }
|
74
|
+
|
75
|
+
it_should_behave_like 'a running cli agent'
|
76
|
+
|
77
|
+
it 'should be a server' do
|
78
|
+
expect(subject).to be_server
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/spec/runner_spec.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'spec-helper'
|
2
|
+
|
3
|
+
shared_examples_for 'a consul agent run' do |method_name, registry_name, join_flag, args|
|
4
|
+
it 'properly launches consul agent' do
|
5
|
+
members = []
|
6
|
+
members << member if join_flag
|
7
|
+
|
8
|
+
registry.should_receive(registry_name).with.and_return(reg = double)
|
9
|
+
reg.should_receive(:members).with(expiry).and_return(members)
|
10
|
+
|
11
|
+
expected_args = (['consul', 'agent', '-bind', ip, '-data-dir', data_dir, '-node', identity] + args).collect do |e|
|
12
|
+
if e.instance_of? Symbol
|
13
|
+
send e
|
14
|
+
else
|
15
|
+
e
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
AutoConsul::Runner.should_receive(:spawn).with(*expected_args).and_return(agent_pid = double)
|
20
|
+
|
21
|
+
# consul info retries to verify that it's running.
|
22
|
+
AutoConsul::Runner.should_receive(:system).with('consul', 'info').and_return(false, false, true)
|
23
|
+
|
24
|
+
AutoConsul::Runner.should_receive(:sleep).with(2)
|
25
|
+
AutoConsul::Runner.should_receive(:sleep).with(4)
|
26
|
+
AutoConsul::Runner.should_receive(:sleep).with(6)
|
27
|
+
|
28
|
+
if join_flag
|
29
|
+
AutoConsul::Runner.should_receive(:system).with('consul', 'join', remote_ip).and_return(true)
|
30
|
+
end
|
31
|
+
|
32
|
+
Process.should_receive(:wait).with(agent_pid)
|
33
|
+
|
34
|
+
callable = AutoConsul::Runner.method(method_name)
|
35
|
+
callable.call(identity, ip, expiry, local_state, registry)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe AutoConsul::Runner do
|
40
|
+
let(:ip) { "192.168.50.101" }
|
41
|
+
let(:remote_ip) { "192.168.50.102" }
|
42
|
+
let(:member) { double("ClusterMember", :identity => 'foo', :time => double, :data => remote_ip) }
|
43
|
+
let(:agents_list) { [] }
|
44
|
+
let(:servers_list) { [] }
|
45
|
+
let(:identity) { "id-#{double.object_id}" }
|
46
|
+
let(:data_dir) { "/var/lib/consul/#{double.object_id}" }
|
47
|
+
let(:local_state) { double("FileSystemState", :data_path => data_dir) }
|
48
|
+
let(:registry) { double("Registry", :agents => double("S3Provider"),
|
49
|
+
:servers => double("S3Provider")) }
|
50
|
+
|
51
|
+
let(:expiry) { 120.to_i }
|
52
|
+
|
53
|
+
before do
|
54
|
+
registry.agents.stub(:agents).with(expiry).and_return(agents_list)
|
55
|
+
registry.servers.stub(:servers).with(expiry).and_return(servers_list)
|
56
|
+
end
|
57
|
+
|
58
|
+
describe :run_agent! do
|
59
|
+
it_behaves_like 'a consul agent run', :run_agent!, :agents, true, []
|
60
|
+
end
|
61
|
+
|
62
|
+
describe :run_server! do
|
63
|
+
describe 'with empty server registry' do
|
64
|
+
# consul agent -bind 192.168.50.100 -data-dir /opt/consul/server/data -node vagrant-server -server -bootstrap
|
65
|
+
it_behaves_like 'a consul agent run', :run_server!, :servers, false, ['-server', '-bootstrap']
|
66
|
+
end
|
67
|
+
|
68
|
+
describe 'with other servers in registry' do
|
69
|
+
# consul agent -bind 192.168.50.100 -data-dir /opt/consul/server/data -node vagrant-server -server
|
70
|
+
# consul join some_ip
|
71
|
+
|
72
|
+
it_behaves_like 'a consul agent run', :run_server!, :servers, true, ['-server']
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,239 @@
|
|
1
|
+
require 'spec-helper'
|
2
|
+
|
3
|
+
shared_examples_for 'a heartbeat emitter' do
|
4
|
+
it 'should write a TIMESTAMP-IDENTIFIER entry under the key prefix with the payload as data.' do
|
5
|
+
objects.should_receive(:[]).with(File.join(path, "#{stamp}-#{identity}")).and_return(obj = double)
|
6
|
+
obj.should_receive(:write).with(payload = "Some payload data #{double.to_s}")
|
7
|
+
subject.heartbeat! identity, payload, expiry
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
shared_examples_for 'has valid members' do |locator_pairs|
|
12
|
+
let :member_pairs do
|
13
|
+
locator_pairs.collect do |time_sym, identifier|
|
14
|
+
[send(time_sym), identifier]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'gets appropriate identifier and timestamp per member' do
|
19
|
+
m = subject.members(expiry).collect {|mem| [mem.time, mem.identifier]}
|
20
|
+
m.should == member_pairs
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'does not read object data if not requested' do
|
24
|
+
member_pairs.each do |pair|
|
25
|
+
s3_cache[pair].should_not_receive(:read)
|
26
|
+
end
|
27
|
+
|
28
|
+
subject.members(expiry).each {|mem| :amazing}
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'exposes object data through the :data method' do
|
32
|
+
m = subject.members(expiry).collect {|mem| mem.data}
|
33
|
+
m.should == member_pairs.collect {|p| s3_cache[p].read}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe AutoConsul::Cluster::Registry::S3Provider do
|
38
|
+
let(:provider) { AutoConsul::Cluster::Registry::S3Provider }
|
39
|
+
let(:bucket) { "bucket-#{self.object_id.to_s}" }
|
40
|
+
let(:path) { "#{self.object_id.to_s}/foo/bar" }
|
41
|
+
let(:uri) { "s3://#{bucket}/#{path}" }
|
42
|
+
|
43
|
+
context 'retrieved via AutoConsul::Cluster.get_provider_for_uri' do
|
44
|
+
it 'should be the provider for an s3:// URL' do
|
45
|
+
AutoConsul::Cluster.get_provider_for_uri(uri).should be_a provider
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should be instantiated with the uri string as URI instance' do
|
49
|
+
provider.should_receive(:new).with(URI(uri))
|
50
|
+
AutoConsul::Cluster.get_provider_for_uri(uri)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe 'given a URI instance' do
|
55
|
+
subject { provider.new URI(uri) }
|
56
|
+
|
57
|
+
it 'should get the bucket name from the URI host' do
|
58
|
+
subject.bucket_name.should == bucket
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should get the key prefix from the URI path' do
|
62
|
+
subject.key_prefix.should == path
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should get the bucket object using the bucket name and the AWS SDK' do
|
66
|
+
AWS::S3.stub(:new).and_return(s3 = double)
|
67
|
+
s3.should_receive(:buckets).with.and_return(buckets = double)
|
68
|
+
buckets.should_receive(:[]).with(bucket).and_return(bucket_object = double)
|
69
|
+
subject.bucket.should == bucket_object
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'with a bucket' do
|
73
|
+
let(:bucket_object) { double }
|
74
|
+
let(:objects) { double }
|
75
|
+
let(:time) do
|
76
|
+
t = Time.now.utc
|
77
|
+
Time.utc t.year, t.month, t.day, t.hour, t.min, t.sec, 0
|
78
|
+
end
|
79
|
+
let(:expiry) { 120.to_i }
|
80
|
+
let(:check_time) { time - expiry }
|
81
|
+
|
82
|
+
before do
|
83
|
+
subject.stub(:bucket).with.and_return(bucket_object)
|
84
|
+
bucket_object.stub(:objects).with.and_return(objects)
|
85
|
+
end
|
86
|
+
|
87
|
+
describe 'the heartbeat method' do
|
88
|
+
let(:identity) { "#{self.object_id.to_s}-identifier" }
|
89
|
+
let(:stamp) { time.dup.utc.strftime('%Y%m%d%H%M%S') }
|
90
|
+
|
91
|
+
before do
|
92
|
+
t = time
|
93
|
+
Time.stub(:now).with.and_return(t)
|
94
|
+
end
|
95
|
+
|
96
|
+
describe 'with no expiry' do
|
97
|
+
let(:expiry) { nil }
|
98
|
+
|
99
|
+
before do
|
100
|
+
objects.should_not_receive(:with_prefix)
|
101
|
+
bucket.should_not_receive(:delete_if)
|
102
|
+
objects.should_not_receive(:delete_if)
|
103
|
+
bucket.should_not_receive(:delete)
|
104
|
+
objects.should_not_receive(:delete)
|
105
|
+
end
|
106
|
+
|
107
|
+
it_should_behave_like 'a heartbeat emitter'
|
108
|
+
end
|
109
|
+
|
110
|
+
describe 'with an expiry' do
|
111
|
+
let(:expiry) { 145.to_i }
|
112
|
+
let(:check_time) { time.dup.utc - expiry }
|
113
|
+
|
114
|
+
let :pre_expiration do
|
115
|
+
double "S3Object", :key => File.join(path, "#{(check_time - 1).strftime('%Y%m%d%H%M%S')}-foo")
|
116
|
+
end
|
117
|
+
|
118
|
+
let :on_expiration do
|
119
|
+
double "S3Object", :key => File.join(path, "#{check_time.strftime('%Y%m%d%H%M%S')}-bar")
|
120
|
+
end
|
121
|
+
|
122
|
+
let :post_expiration do
|
123
|
+
double "S3Object", :key => File.join(path, "#{(check_time + 1).strftime('%Y%m%d%H%M%S')}-baz")
|
124
|
+
end
|
125
|
+
|
126
|
+
before do
|
127
|
+
objects.should_receive(:with_prefix).with(path).and_return(with_pre = double)
|
128
|
+
with_pre.should_receive(:delete_if) do |&block|
|
129
|
+
block.call(pre_expiration).should be_true
|
130
|
+
block.call(post_expiration).should be_false
|
131
|
+
block.call(on_expiration).should be_true
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
it_should_behave_like 'a heartbeat emitter'
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe 'and no heartbeats' do
|
140
|
+
before do
|
141
|
+
objects.should_receive(:with_prefix).with(path).and_return(collection = double)
|
142
|
+
# Doesn't yield, and thus is "empty".
|
143
|
+
collection.should_receive(:each).and_return(collection)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe 'and heartbeats' do
|
148
|
+
let(:s3_cache) { {} }
|
149
|
+
let :deletes do
|
150
|
+
key_source.find_all {|pair| pair[0] <= check_time}.collect do |pair|
|
151
|
+
s3_cache[pair]
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
before do
|
156
|
+
with_pre = objects.should_receive(:with_prefix).with(path).and_return(with_prefix_each = double)
|
157
|
+
with_prefix_each = with_prefix_each.should_receive(:each).with
|
158
|
+
key_source.inject(with_prefix_each) do |o, pair|
|
159
|
+
s3_cache[pair] = double("S3Object",
|
160
|
+
:key => File.join(path, "#{pair[0].strftime('%Y%m%d%H%M%S')}-#{pair[1]}"),
|
161
|
+
:read => pair[1])
|
162
|
+
o.and_yield(s3_cache[pair])
|
163
|
+
end
|
164
|
+
|
165
|
+
with_prefix_each.and_return(with_prefix_each) if key_source.size < 1
|
166
|
+
end
|
167
|
+
|
168
|
+
before do
|
169
|
+
if deletes.size > 0
|
170
|
+
objects.should_receive(:delete).with(deletes)
|
171
|
+
else
|
172
|
+
objects.should_not_receive(:delete)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
describe 'past expiration' do
|
177
|
+
let(:earliest) { (check_time - 10).utc }
|
178
|
+
let(:early) { (check_time - 5).utc }
|
179
|
+
let(:max_time) { check_time.dup.utc }
|
180
|
+
|
181
|
+
let :key_source do
|
182
|
+
[[earliest, 'earliest'],
|
183
|
+
[early, 'early'],
|
184
|
+
[max_time, 'expiry']]
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'should have an empty members list' do
|
188
|
+
subject.members(expiry).should == []
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
describe 'that are live' do
|
193
|
+
let(:valid_early) { (check_time + 1).utc }
|
194
|
+
let(:valid_late) { (time - 1).utc }
|
195
|
+
|
196
|
+
describe 'only' do
|
197
|
+
let :key_source do
|
198
|
+
[[valid_early, 'both'],
|
199
|
+
[valid_early, 'early_only'],
|
200
|
+
[valid_late, 'both'],
|
201
|
+
[valid_late, 'late_only']]
|
202
|
+
end
|
203
|
+
|
204
|
+
it_should_behave_like 'has valid members', [
|
205
|
+
[:valid_early, 'early_only'],
|
206
|
+
[:valid_late, 'both'],
|
207
|
+
[:valid_late, 'late_only']]
|
208
|
+
end
|
209
|
+
|
210
|
+
describe 'mixed with expired heartbeats' do
|
211
|
+
let(:expired_early) { (check_time - 10).utc }
|
212
|
+
let(:expired_late) { check_time.dup.utc }
|
213
|
+
|
214
|
+
let :key_source do
|
215
|
+
[[expired_early, 'expired_and_valid'],
|
216
|
+
[expired_early, 'expired_early'],
|
217
|
+
[expired_late, 'expired_late'],
|
218
|
+
[expired_late, 'check_time_and_valid'],
|
219
|
+
[valid_early, 'both'],
|
220
|
+
[valid_early, 'early_only'],
|
221
|
+
[valid_early, 'expired_and_valid'],
|
222
|
+
[valid_late, 'both'],
|
223
|
+
[valid_late, 'check_time_and_valid'],
|
224
|
+
[valid_late, 'late_only']]
|
225
|
+
end
|
226
|
+
|
227
|
+
it_should_behave_like 'has valid members', [
|
228
|
+
[:valid_early, 'early_only'],
|
229
|
+
[:valid_early, 'expired_and_valid'],
|
230
|
+
[:valid_late, 'both'],
|
231
|
+
[:valid_late, 'check_time_and_valid'],
|
232
|
+
[:valid_late, 'late_only']]
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
data/spec/spec-helper.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'tempfile'
|
4
|
+
require 'auto-consul'
|
5
|
+
|
6
|
+
module AutoConsulTest
|
7
|
+
# A wrapper for a temporary directory.
|
8
|
+
#
|
9
|
+
# While initialization will create a temporary directory, it is the
|
10
|
+
# caller's responsibility to clean things up.
|
11
|
+
class Tempdir
|
12
|
+
def initialize
|
13
|
+
@path = File.expand_path(Dir.mktmpdir)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Helper that joins `paths` to the temporary directory for easy
|
17
|
+
# temporary-directory-internal path generation.
|
18
|
+
#
|
19
|
+
# Call it with no arguments to get the temporary directory itself.
|
20
|
+
def path(*paths)
|
21
|
+
if paths.size > 0
|
22
|
+
File.join @path, *paths
|
23
|
+
else
|
24
|
+
@path
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module RSpec::Core::DSL
|
31
|
+
def tempdir_context(*args, &block)
|
32
|
+
describe(*args) do
|
33
|
+
let(:tempdir) { AutoConsulTest::Tempdir.new }
|
34
|
+
|
35
|
+
after do
|
36
|
+
::FileUtils.remove_entry tempdir.path
|
37
|
+
end
|
38
|
+
|
39
|
+
instance_eval(&block)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|