lock_manager 0.1.0 → 0.1.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.
- checksums.yaml +4 -4
- data/lib/lock_manager/connection.rb +31 -0
- data/lib/lock_manager/redis_connection.rb +45 -0
- data/lib/lock_manager/worker.rb +97 -0
- data/lib/lock_manager.rb +36 -0
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6182864c1d1e43eda6276e9459037dd7c0aaf176
|
|
4
|
+
data.tar.gz: 6b6eb4cc2823147881e540080ddc930c2d942dcc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9bdb3020bb25b41f471c102864f86a93eefb4948ccf204d19d16b155be91c4a84fbaa9deb53ac65be109fd4b3e1f918e446c091ce5ab3f9542f03b93e6767f6b
|
|
7
|
+
data.tar.gz: 0fe35dfea4dd4c5768e32f665b9859ab89f7973bfb14d288fb5c04dd50db4a2a02b49aab49c15e12bb0b819f215d8b1c951de25f2ad5b1286fd5b1dd787d6577
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
class LockManager
|
|
2
|
+
class Connection
|
|
3
|
+
attr_reader :options
|
|
4
|
+
|
|
5
|
+
def self.connection_class(type)
|
|
6
|
+
case type.to_s
|
|
7
|
+
when 'redis'
|
|
8
|
+
require 'lock_manager/redis_connection'
|
|
9
|
+
LockManager::RedisConnection
|
|
10
|
+
else
|
|
11
|
+
fail ArgumentError, "Unknown connection type: #{type}"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def initialize(options = {})
|
|
16
|
+
@options = options
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def write(key, value)
|
|
20
|
+
fail "Not implemented: write(#{key}, #{value})"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def read(key)
|
|
24
|
+
fail "Not implemented: read(#{key})"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def remove(key)
|
|
28
|
+
fail "Not implemented: remove(#{key})"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require 'redis'
|
|
2
|
+
|
|
3
|
+
class LockManager
|
|
4
|
+
class RedisConnection < LockManager::Connection
|
|
5
|
+
attr_reader :server, :port
|
|
6
|
+
|
|
7
|
+
def initialize(options = {})
|
|
8
|
+
super
|
|
9
|
+
fail ArgumentError, ':server option is mandatory for redis connections' unless options[:server]
|
|
10
|
+
@server = options[:server]
|
|
11
|
+
@port = options[:port]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def read(host)
|
|
15
|
+
handle.get key_from_host(host)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def write_if_not_exists(host, value)
|
|
19
|
+
handle.setnx(key_from_host(host), value)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def write(host, value)
|
|
23
|
+
handle.set(key_from_host(host), value)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def remove(host)
|
|
27
|
+
handle.del key_from_host(host)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def key_from_host(host)
|
|
31
|
+
"node_lock_#{host}"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def handle
|
|
35
|
+
@handle ||= connect_redis(server, port)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Create a connection to redis.
|
|
39
|
+
def connect_redis(redis_server, redis_port = 6379)
|
|
40
|
+
redis = Redis.new(host: redis_server, port: redis_port)
|
|
41
|
+
redis.ping
|
|
42
|
+
redis
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
require 'lock_manager/connection'
|
|
2
|
+
require 'resolv'
|
|
3
|
+
class LockManager
|
|
4
|
+
class Worker
|
|
5
|
+
attr_reader :connection, :host, :user
|
|
6
|
+
|
|
7
|
+
def initialize(connection, host)
|
|
8
|
+
@connection = connection
|
|
9
|
+
if host =~ Regexp.union(Resolv::IPv4::Regex, Resolv::IPv6::Regex)
|
|
10
|
+
fail ArgumentError, 'Please use a DNS name rather than an IP address.'
|
|
11
|
+
else
|
|
12
|
+
# Using the shortname of the host has the downside of being unable to
|
|
13
|
+
# lock two hosts with the same shortname and different domains.
|
|
14
|
+
# However, since the majority of users interact with shortname only,
|
|
15
|
+
# we're using shortname to normalize and prevent the system from
|
|
16
|
+
# allowing a lock on aixbuilder1 if aixbuilder1.delivery.puppetlabs.net
|
|
17
|
+
# is locked.
|
|
18
|
+
@host = host.split('.')[0]
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def lock(user, reason = nil)
|
|
23
|
+
lock_contents = {
|
|
24
|
+
user: user,
|
|
25
|
+
time: Time.now.to_s,
|
|
26
|
+
reason: reason
|
|
27
|
+
}
|
|
28
|
+
r = connection.write_if_not_exists host, lock_contents.to_json
|
|
29
|
+
log "#{host} already locked." if r == false
|
|
30
|
+
r
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def lock!(user, reason = nil)
|
|
34
|
+
lock_contents = {
|
|
35
|
+
user: user,
|
|
36
|
+
time: Time.now.to_s,
|
|
37
|
+
reason: reason
|
|
38
|
+
}
|
|
39
|
+
r = connection.write host, lock_contents.to_json
|
|
40
|
+
r == 'OK'
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Boolean to figure out if a host is locked.
|
|
44
|
+
#
|
|
45
|
+
# @return [Bool] whether or not the host is locked.
|
|
46
|
+
def locked?
|
|
47
|
+
!!connection.read(host)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def unlock(user)
|
|
51
|
+
if !locked?
|
|
52
|
+
log "Refusing to unlock. No lock exists on #{host}."
|
|
53
|
+
false
|
|
54
|
+
elsif user == lock_user
|
|
55
|
+
unlock!
|
|
56
|
+
else
|
|
57
|
+
log "Refusing to unlock. Lock on #{host} is owned by #{lock_user}."
|
|
58
|
+
false
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def unlock!
|
|
63
|
+
connection.remove(host) > 0
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def lock_user
|
|
67
|
+
lock_data = connection.read host
|
|
68
|
+
return false unless lock_data
|
|
69
|
+
result = JSON.parse lock_data
|
|
70
|
+
result['user']
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def polling_lock(user, reason = nil)
|
|
74
|
+
sleep_duration = 1
|
|
75
|
+
loop do
|
|
76
|
+
if locked?
|
|
77
|
+
log "#{host} is locked..."
|
|
78
|
+
log "waiting #{sleep_duration} seconds."
|
|
79
|
+
sleep sleep_duration
|
|
80
|
+
sleep_duration *= 2
|
|
81
|
+
else
|
|
82
|
+
break
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
lock(user, reason)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def show
|
|
89
|
+
data = connection.read(host)
|
|
90
|
+
data ? JSON.parse(data) : nil
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def log(message)
|
|
94
|
+
warn message
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
data/lib/lock_manager.rb
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
require 'lock_manager/worker'
|
|
3
|
+
require 'lock_manager/connection'
|
|
4
|
+
|
|
5
|
+
class LockManager
|
|
6
|
+
attr_reader :options
|
|
7
|
+
|
|
8
|
+
def initialize(options = {})
|
|
9
|
+
@options = options
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def lock(host, user, reason = nil)
|
|
13
|
+
LockManager::Worker.new(connection, host).lock(user, reason)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def polling_lock(host, user, reason = nil)
|
|
17
|
+
LockManager::Worker.new(connection, host).polling_lock(user, reason)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def unlock(host, user)
|
|
21
|
+
LockManager::Worker.new(connection, host).unlock(user)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def locked?(host)
|
|
25
|
+
LockManager::Worker.new(connection, host).locked?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def show(host)
|
|
29
|
+
LockManager::Worker.new(connection, host).show
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def connection
|
|
33
|
+
fail ArgumentError, ':type option is required' unless options[:type]
|
|
34
|
+
@connection ||= LockManager::Connection.connection_class(options[:type]).new(options)
|
|
35
|
+
end
|
|
36
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lock_manager
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Puppet Labs
|
|
@@ -61,6 +61,10 @@ extra_rdoc_files: []
|
|
|
61
61
|
files:
|
|
62
62
|
- LICENSE
|
|
63
63
|
- README.md
|
|
64
|
+
- lib/lock_manager.rb
|
|
65
|
+
- lib/lock_manager/connection.rb
|
|
66
|
+
- lib/lock_manager/redis_connection.rb
|
|
67
|
+
- lib/lock_manager/worker.rb
|
|
64
68
|
- test/test_helper.rb
|
|
65
69
|
- test/test_lock.rb
|
|
66
70
|
- test/test_poll.rb
|