lock_manager 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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