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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0a235cfae8f73825fbc5b738d4f2adeac031f1b2
4
- data.tar.gz: 1440406b654d29de1949e76b4f04f80b3117df91
3
+ metadata.gz: 6182864c1d1e43eda6276e9459037dd7c0aaf176
4
+ data.tar.gz: 6b6eb4cc2823147881e540080ddc930c2d942dcc
5
5
  SHA512:
6
- metadata.gz: 884bc3e33046cf124843fc424b22d51f5affff5063886d690243a510b3b4d3927988c95906cbd1dfac82de04486e78ec64188631585655de741b8115a66a28b2
7
- data.tar.gz: 6b77f05acfb5f783121bf345c0d93c7c1b96f64af4555cd372999a0071d141ff1a8f226dde7aa9d176a5e5a122cae1eb213a5937f36e021d72ae748ec4483967
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
@@ -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.0
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