ocular 0.1.16 → 0.1.17
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/README.md +18 -3
- data/lib/ocular/daemon.rb +0 -1
- data/lib/ocular/dsl/etcd.rb +62 -0
- data/lib/ocular/event/eventbase.rb +7 -1
- data/lib/ocular/inputs/cron_input.rb +12 -6
- data/lib/ocular/inputs/http_input.rb +9 -4
- data/lib/ocular/inputs/rabbitmq_input.rb +8 -2
- data/lib/ocular/settings.rb +1 -1
- data/lib/ocular/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f2b13556c63053b9d7f15e8382b3803295edc3d4
|
4
|
+
data.tar.gz: 8862213d31cabfaee4332bcb33bcacf63601e5d7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1be07d88abd000eb2cb6c70b92b9790b33720d8d72554393f8e0f0bc21ac07a43abc8c511b7f807ed98c40a728f46bbc7613a7d0fd73e9994104a59a0089d91d
|
7
|
+
data.tar.gz: bb7edaf144bac5bc7433505b6c05b23e9027f27656c8bbd32a257fff9a319cafc3395947e499831ebdba9a8236b3f1b21ff7bd7a5c020527ad30b14433201c4d
|
data/README.md
CHANGED
@@ -5,11 +5,26 @@ Ocular allows to easily create small scripts which are triggered from multiple d
|
|
5
5
|
|
6
6
|
The goal is that a new script could be written really quickly to automate a previously manual infrastructure maintenance job instead of doing the manual job yet another time. Scripts are written in Ruby with a simple Ocular DSL which allows the script to easily respond to multitude different events.
|
7
7
|
|
8
|
-
|
8
|
+
Currently implemented event sources:
|
9
9
|
- HTTP calls
|
10
10
|
- RabbitMQ messages
|
11
|
+
- Timers
|
12
|
+
|
13
|
+
Planned event sources:
|
11
14
|
- SQS/SNS messages
|
12
|
-
- Graphite item triggers
|
13
15
|
- Zabbix alerts
|
14
|
-
-
|
16
|
+
- NATS broadcast messages
|
17
|
+
- salt-stack event bus
|
18
|
+
|
19
|
+
Currently implemented clients to external services (ie. what you can easily do once your event has been triggered):
|
20
|
+
- MySQL
|
21
|
+
- etcd
|
22
|
+
- RabbitMQ
|
23
|
+
- AWS/EC2
|
24
|
+
- Kafka (for logging)
|
25
|
+
- SSH for remote execution
|
15
26
|
|
27
|
+
Planned clients to external services:
|
28
|
+
- Graphite
|
29
|
+
- Zabbix configuration
|
30
|
+
- salt-stack remote state execution
|
data/lib/ocular/daemon.rb
CHANGED
data/lib/ocular/dsl/etcd.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'logger'
|
2
2
|
require 'etcd'
|
3
|
+
require 'pp'
|
3
4
|
|
4
5
|
class Ocular
|
5
6
|
module DSL
|
@@ -29,6 +30,67 @@ class Ocular
|
|
29
30
|
return @@__etcd_instance
|
30
31
|
end
|
31
32
|
|
33
|
+
def ttl_lock(key, ttl:10)
|
34
|
+
id = @run_id
|
35
|
+
if !id
|
36
|
+
id = Process.pid.to_s
|
37
|
+
Socket.ip_address_list.each do |addr|
|
38
|
+
next if addr.ip_address == "127.0.0.1"
|
39
|
+
next if addr.ip_address.start_with?("::1")
|
40
|
+
next if addr.ip_address.start_with?("fe80::1")
|
41
|
+
id += "-" + addr.ip_address
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
client = etcd()
|
46
|
+
|
47
|
+
current_lock = locked?(key)
|
48
|
+
|
49
|
+
if current_lock != id and current_lock != nil
|
50
|
+
return nil # Somebody else has lock, can't lock by ourself
|
51
|
+
end
|
52
|
+
|
53
|
+
if current_lock == id and current_lock != nil
|
54
|
+
begin
|
55
|
+
client.test_and_set("/ocular/locks/#{key}", value: id, prevValue: id, ttl: ttl)
|
56
|
+
return id
|
57
|
+
rescue ::Etcd::NodeExist => e
|
58
|
+
return nil
|
59
|
+
end
|
60
|
+
|
61
|
+
else
|
62
|
+
begin
|
63
|
+
client.create("/ocular/locks/#{key}", value: id, ttl: ttl)
|
64
|
+
return id
|
65
|
+
rescue ::Etcd::NodeExist => e
|
66
|
+
return nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def unlock(key)
|
72
|
+
client = etcd()
|
73
|
+
begin
|
74
|
+
client.delete("/ocular/locks/#{key}")
|
75
|
+
rescue ::Etcd::KeyNotFound
|
76
|
+
|
77
|
+
end
|
78
|
+
return true
|
79
|
+
end
|
80
|
+
|
81
|
+
def locked?(key)
|
82
|
+
client = etcd()
|
83
|
+
begin
|
84
|
+
ret = client.get("/ocular/locks/#{key}")
|
85
|
+
if ret.node and ret.node.expiration == nil
|
86
|
+
warn("Key #{key} has been locked permanently with value '#{ret.node.value}'!")
|
87
|
+
end
|
88
|
+
return ret.node.value # Key is locked
|
89
|
+
rescue ::Etcd::KeyNotFound => e
|
90
|
+
return nil
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
32
94
|
end
|
33
95
|
|
34
96
|
end
|
@@ -46,7 +46,13 @@ class Ocular
|
|
46
46
|
r.error = error
|
47
47
|
end
|
48
48
|
|
49
|
-
|
49
|
+
begin
|
50
|
+
Marshal.dump(r, writer)
|
51
|
+
rescue TypeError => e
|
52
|
+
::Ocular.logger.error "TypeError when trying to marshal event handler return value. Reason: #{e}"
|
53
|
+
::Ocular.logger.error "This is usually because you forgot that the last function return value is returned from the handler function. The current type of the returned value is #{r.response.class}. Please make sure your handler returns a proper value which doesn't reference any sockets or otherwise complex unserializable objects."
|
54
|
+
::Ocular.logger.error "The value of the returned value is :#{r.response.pretty_inspect}"
|
55
|
+
end
|
50
56
|
writer.close
|
51
57
|
end
|
52
58
|
writer.close
|
@@ -29,22 +29,28 @@ class Ocular
|
|
29
29
|
attr_reader :cron_enabled
|
30
30
|
|
31
31
|
def initialize(settings_factory)
|
32
|
-
settings = settings_factory
|
32
|
+
settings = settings_factory.fetch(:cron, {})
|
33
33
|
@cron_enabled = true
|
34
34
|
|
35
|
+
pp settings
|
35
36
|
if settings[:lock]
|
36
37
|
@cron_enabled = false
|
37
38
|
end
|
38
39
|
|
40
|
+
if settings[:enabled] == false
|
41
|
+
::Ocular.logger.info "Cron is disabled from settings"
|
42
|
+
@cron_enabled = false
|
43
|
+
end
|
44
|
+
|
39
45
|
@scheduler = ::Rufus::Scheduler.new
|
40
46
|
::Ocular.logger.debug "Starting Rufus cron scheduler"
|
41
47
|
|
42
|
-
if settings[:lock]
|
48
|
+
if settings[:lock] and @cron_enabled == true
|
49
|
+
Ocular.logger.debug "Enabling cron locking at pid #{Process.pid.to_s}"
|
43
50
|
@scheduler.every(settings[:lock_delay] || "10s", :overlap => false) do
|
44
|
-
|
51
|
+
c = Class.new.extend(Ocular::DSL::Etcd)
|
45
52
|
|
46
|
-
|
47
|
-
pp ret
|
53
|
+
@cron_enabled = c.ttl_lock("cron_input", ttl:20)
|
48
54
|
end
|
49
55
|
end
|
50
56
|
end
|
@@ -59,7 +65,7 @@ class Ocular
|
|
59
65
|
|
60
66
|
def disable()
|
61
67
|
@cron_enabled = false
|
62
|
-
end
|
68
|
+
end
|
63
69
|
|
64
70
|
def enable()
|
65
71
|
@cron_enabled = true
|
@@ -445,7 +445,9 @@ class Ocular
|
|
445
445
|
context.params = indifferent_params(context.request.params)
|
446
446
|
|
447
447
|
context.response['Content-Type'] = nil
|
448
|
-
invoke(context)
|
448
|
+
invoke(context) do |context|
|
449
|
+
dispatch(context)
|
450
|
+
end
|
449
451
|
|
450
452
|
unless context.response['Content-Type']
|
451
453
|
context.response['Content-Type'] = "text/html"
|
@@ -469,7 +471,6 @@ class Ocular
|
|
469
471
|
|
470
472
|
def invoke(context)
|
471
473
|
res = catch(:halt) { yield(context) }
|
472
|
-
|
473
474
|
if Array === res and Fixnum === res.first
|
474
475
|
res = res.dup
|
475
476
|
status(context, res.shift)
|
@@ -488,7 +489,11 @@ class Ocular
|
|
488
489
|
context.response.status = error.http_status
|
489
490
|
|
490
491
|
if error.respond_to? :to_s
|
491
|
-
|
492
|
+
str = error.to_s
|
493
|
+
if !str.end_with?("\n")
|
494
|
+
str += "\n"
|
495
|
+
end
|
496
|
+
context.response.body = str
|
492
497
|
end
|
493
498
|
else
|
494
499
|
context.response.status = 500
|
@@ -603,7 +608,7 @@ class Ocular
|
|
603
608
|
pattern, keys = compile("/check")
|
604
609
|
|
605
610
|
(@routes["GET"] ||= []) << build_signature(pattern, keys) do |context|
|
606
|
-
[200, "OK"]
|
611
|
+
[200, "OK\n"]
|
607
612
|
end
|
608
613
|
end
|
609
614
|
|
@@ -71,8 +71,14 @@ class Ocular
|
|
71
71
|
context.delivery_info = delivery_info
|
72
72
|
context.metadata = metadata
|
73
73
|
context.payload = payload
|
74
|
-
|
75
|
-
|
74
|
+
begin
|
75
|
+
eventbase.exec(context)
|
76
|
+
ch.acknowledge(delivery_info.delivery_tag, false)
|
77
|
+
rescue
|
78
|
+
sleep 1
|
79
|
+
warn "Error on RabbitMQ event processing on context #{context}"
|
80
|
+
ch.reject(delivery_info.delivery_tag, true)
|
81
|
+
end
|
76
82
|
end
|
77
83
|
|
78
84
|
id = queue + "-" + block.to_s
|
data/lib/ocular/settings.rb
CHANGED
data/lib/ocular/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ocular
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.17
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Juho Mäkinen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-05-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rye
|