eye-patch 0.0.4 → 0.0.5

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: 6ccc9b1afad17fb6c48380c118b5b98fef372f38
4
- data.tar.gz: caf7686b488014a72041764a33d546cd862048e2
3
+ metadata.gz: 2b5844cb0bcc8406207a5838cf37b7c508096423
4
+ data.tar.gz: cdeffbc2afbfce475d4680ff91db40a9ec09ff6b
5
5
  SHA512:
6
- metadata.gz: cc8f68ecfb3fe72e52227190422287b9b379c958421640b0bad27626816fb546b933400b21b7fe65dfb6eb938c68e21261a6e64d9877f6f4b82bcec145a699e5
7
- data.tar.gz: 780c1b16f806ca7222fb2936ff0ed759230fcc9745d80bb46bff115264b8bebec1fb8a4e7def2e49e7a6fb8c04603014b7ed0a847f11f50d4f19daf40676b485
6
+ metadata.gz: 8c6eaabb46e382d2e04eb2f41286cab9f218d0b7a32c837ad057da6b55c838f0a9241abc726514fe63c5d32837abc3e39dd766499e3be80a5f20d0c003f84275
7
+ data.tar.gz: abc4cb6a750ceacb722d4be06d537858b2339d7d5c07c57b2abb2e306d83cccc65658e9f3443c61eb0e578974333b30676842bb37a002792b54e6fe8693f4cb2
data/eye-patch.gemspec CHANGED
@@ -21,9 +21,10 @@ Gem::Specification.new do |spec|
21
21
  spec.test_files = spec.files.grep(%r{^test/})
22
22
  spec.require_paths = ["lib"]
23
23
 
24
- spec.add_development_dependency "bundler", "~> 1.3"
25
- spec.add_development_dependency "rake"
26
-
27
24
  spec.add_dependency "eye"
28
25
  spec.add_dependency "chronic_duration"
26
+
27
+ spec.add_development_dependency "bundler", "~> 1.3"
28
+ spec.add_development_dependency "rake"
29
+ spec.add_development_dependency "mocha"
29
30
  end
data/lib/eye/patch.rb CHANGED
@@ -15,11 +15,11 @@ module Eye::Patch
15
15
  require "eye/patch/version"
16
16
 
17
17
  def self.parse(filename)
18
- settings = Eye::Patch::Settings.new(filename)
18
+ settings = Settings.new(filename)
19
19
 
20
- config = Eye::Config.new(
21
- Eye::Patch::Config.new(settings),
22
- Eye::Patch::Application.new(settings))
20
+ config = ::Eye::Config.new(
21
+ Config.new(settings),
22
+ Application.new(settings))
23
23
  config.validate!
24
24
 
25
25
  config
@@ -1,73 +1,49 @@
1
- class Eye::Patch::Application < Hash
1
+ require_relative "group_set"
2
+ require_relative "option_set"
2
3
 
3
- def initialize(settings)
4
- super()
5
- @settings = settings
6
- self[settings[:name].to_s] = parse
7
- end
4
+ module Eye::Patch
8
5
 
9
- private
6
+ class Application < Hash
10
7
 
11
- def parse
12
- @config = {}
8
+ def initialize(settings)
9
+ super()
10
+ @settings = settings
11
+ self[settings[:name]] = parse
12
+ end
13
13
 
14
- parse_configuration
15
- parse_processes
14
+ private
16
15
 
17
- @config
18
- end
16
+ def parse
17
+ parse_configuration
18
+ parse_processes
19
19
 
20
- def root_settings
21
- @root_settings ||= (@settings[:application] || {}).merge(
22
- notify: parse_notifications,
23
- triggers: parse_triggers,
24
- checks: parse_checks )
25
- end
26
-
27
- def parse_notifications
28
- Array(@settings[:notifications]).each_with_object({}) do |notify, monitors|
29
- monitors[notify[:name].to_s] = notify[:level].to_sym
20
+ @config
30
21
  end
31
- end
32
22
 
33
- def parse_triggers
34
- Array(@settings[:triggers]).each_with_object({}) do |trigger, triggers|
35
- trigger_data = Eye::Trigger.name_and_class(trigger[:name].to_sym)
36
- triggers[trigger_data[:name]] = trigger[:config].merge(type: trigger_data[:type])
23
+ def parse_configuration
24
+ @config = (@settings[:application] || {}).merge(
25
+ name: @settings[:name],
26
+ notify: notifications,
27
+ triggers: triggers,
28
+ checks: checks )
37
29
  end
38
- end
39
30
 
40
- def parse_checks
41
- Array(@settings[:checks]).each_with_object({}) do |check, checks|
42
- check_data = Eye::Checker.name_and_class(check[:name].to_sym)
43
- checks[check_data[:name]] = check[:config].merge(type: check_data[:type])
31
+ def parse_processes
32
+ @config[:groups] = GroupSet.new(@config, @settings[:processes])
44
33
  end
45
- end
46
-
47
- def parse_configuration
48
- @config.merge!(root_settings)
49
- end
50
-
51
- def parse_processes
52
- @config[:groups] = {}
53
34
 
54
- Array(@settings[:processes]).group_by{ |item| item[:group] }.each do |group_name, items|
55
- name = group_name || "__default__"
56
- parse_group(name, items)
35
+ def notifications
36
+ Array(@settings[:notifications]).each_with_object({}) do |notify, monitors|
37
+ monitors[notify[:name]] = notify[:level].to_sym
38
+ end
57
39
  end
58
- end
59
40
 
60
- def parse_group(name, processes)
61
- @config[:groups][name] = root_settings.merge(
62
- application: @settings[:name].to_s,
63
- processes: parse_process_list(name, processes))
64
- end
41
+ def triggers
42
+ OptionSet.new(Eye::Trigger, @settings[:triggers])
43
+ end
65
44
 
66
- def parse_process_list(group_name, processes)
67
- processes.each_with_object({}) do |process, process_map|
68
- process_map[process[:name].to_s] = root_settings.merge(process[:config]).merge(
69
- name: process[:name].to_s,
70
- group: group_name )
45
+ def checks
46
+ OptionSet.new(Eye::Checker, @settings[:checks])
71
47
  end
72
48
  end
73
49
  end
@@ -0,0 +1,31 @@
1
+ Capistrano::Configuration.instance.load do
2
+
3
+ _cset(:eye_default_hooks) { true }
4
+ _cset(:eye_config) { "config/eye.yml" }
5
+ _cset(:eye_bin) { "bundle exec eye-patch" }
6
+ _cset(:eye_roles) { :app }
7
+
8
+ if fetch(:eye_default_hooks)
9
+ after "deploy:stop", "eye:stop"
10
+ after "deploy:start", "eye:start"
11
+ before "deploy:restart", "eye:restart"
12
+ end
13
+
14
+ namespace :eye do
15
+
16
+ desc "Start eye with the desired configuration file"
17
+ task :start, roles: -> { fetch(:eye_roles) } do
18
+ run "cd #{current_path} && #{fetch(:eye_bin)} l #{fetch(:eye_config)}"
19
+ end
20
+
21
+ desc "Stop eye and all of its monitored tasks"
22
+ task :stop, roles: -> { fetch(:eye_roles) } do
23
+ run "cd #{current_path} && #{fetch(:eye_bin)} stop all && #{fetch(:eye_bin)} q"
24
+ end
25
+
26
+ desc "Restart all tasks monitored by eye"
27
+ task :restart, roles: -> { fetch(:eye_roles) } do
28
+ run "cd #{current_path} && #{fetch(:eye_bin)} r all"
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,26 @@
1
+ require_relative "process_set"
2
+
3
+ module Eye::Patch
4
+
5
+ class GroupSet < Hash
6
+
7
+ def initialize(application, processes)
8
+ @application = application
9
+
10
+ Array(processes).group_by{ |item| item[:group] }.each do |group_name, items|
11
+ name = group_name || "__default__"
12
+ parse_group(name, items)
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def parse_group(name, processes)
19
+ self[name] = @application.merge(
20
+ name: name,
21
+ application: @application[:name])
22
+
23
+ self[name][:processes] = ProcessSet.new(self[name], processes)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,11 @@
1
+ module Eye::Patch
2
+ class OptionSet < Hash
3
+
4
+ def initialize(option_class, options)
5
+ Array(options).each do |option|
6
+ option_data = option_class.name_and_class(option[:name].to_sym)
7
+ self[option_data[:name]] = option[:config].merge(type: option_data[:type])
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,42 @@
1
+ module Eye::Patch
2
+
3
+ class ProcessSet < Hash
4
+
5
+ def initialize(group, processes)
6
+ @group = group
7
+
8
+ Array(processes).each do |process|
9
+ parse_process(process)
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def parse_process(process)
16
+ if process[:count]
17
+ parse_process_cluster(process)
18
+ else
19
+ parse_single_process(process[:name], process[:config])
20
+ end
21
+ end
22
+
23
+ def parse_process_cluster(process)
24
+ process[:count].times do |index|
25
+ name = "#{process[:name]}-#{index}"
26
+ parse_single_process(name, indexed_config(process[:config], index))
27
+ end
28
+ end
29
+
30
+ def parse_single_process(name, config)
31
+ self[name] = @group.merge(config).merge(
32
+ name: name,
33
+ group: @group[:name] )
34
+ end
35
+
36
+ def indexed_config(config, index)
37
+ config.each_with_object({}) do |(key, value), result|
38
+ result[key] = value.is_a?(String) ? value.gsub("{ID}", index.to_s) : value
39
+ end
40
+ end
41
+ end
42
+ end
@@ -1,34 +1,36 @@
1
- require "chronic_duration"
1
+ require_relative "value_parser"
2
2
 
3
- class Eye::Patch::Settings
3
+ module Eye::Patch
4
4
 
5
- def initialize(filename)
6
- @settings = YAML.load(File.open(filename))
7
- end
5
+ class Settings
8
6
 
9
- def [](value)
10
- parsed[value]
11
- end
7
+ def initialize(filename)
8
+ @settings = YAML.load(File.open(filename))
9
+ end
12
10
 
13
- private
11
+ def [](value)
12
+ parsed[value]
13
+ end
14
14
 
15
- def parsed
16
- @parsed ||= parse(@settings)
17
- end
15
+ private
16
+
17
+ def parsed
18
+ @parsed ||= parse(@settings)
19
+ end
18
20
 
19
- def parse(item)
20
- case item
21
- when Hash
22
- item.each_with_object({}) do |(key, val), result|
23
- result[key.to_sym] = parse(val)
21
+ def parse(item)
22
+ case item
23
+ when Hash
24
+ item.each_with_object({}) do |(key, val), result|
25
+ result[key.to_sym] = parse(val)
26
+ end
27
+ when Array
28
+ item.map { |val| parse(val) }
29
+ when String
30
+ ValueParser.parse(item)
31
+ else
32
+ item
24
33
  end
25
- when Array
26
- item.map { |val| parse(val) }
27
- when String
28
- # Assume that we should parse any time-like values
29
- item =~ /\b(hour|second|minute)s?\b/ ? ChronicDuration.parse(item) : item
30
- else
31
- item
32
34
  end
33
35
  end
34
36
  end
@@ -0,0 +1,32 @@
1
+ require "chronic_duration"
2
+
3
+ module Eye::Patch
4
+
5
+ class ValueParser
6
+
7
+ TIME_MATCHER = / (?<duration>(week|day|hour|minute|second)s?)( |\Z)/.freeze
8
+ SIZE_MATCHER = / (?<size>(tera|giga|mega|kilo)?bytes?)( |\Z)/.freeze
9
+ MATCHERS = {
10
+ time: TIME_MATCHER,
11
+ size: SIZE_MATCHER }.freeze
12
+
13
+ def self.parse(value)
14
+ result = MATCHERS.detect do |match_type, matcher|
15
+ break send(:"parse_#{match_type}", value) if value.match(matcher)
16
+ end
17
+
18
+ result || value
19
+ end
20
+
21
+ private
22
+
23
+ def self.parse_time(value)
24
+ ChronicDuration.parse(value)
25
+ end
26
+
27
+ def self.parse_size(value)
28
+ unit = value.match(SIZE_MATCHER)[:size]
29
+ value.gsub(/[^\d.]/, "").to_f.send(unit)
30
+ end
31
+ end
32
+ end
@@ -1,5 +1,5 @@
1
1
  module Eye
2
2
  module Patch
3
- VERSION = "0.0.4"
3
+ VERSION = "0.0.5"
4
4
  end
5
5
  end
@@ -0,0 +1,51 @@
1
+ name: Test Application
2
+
3
+ config:
4
+ logger: /path/to/log/eye.log
5
+
6
+ application:
7
+ working_dir: /path/to/application
8
+
9
+ notifications:
10
+ - name: monitoring
11
+ type: jabber
12
+ level: info
13
+ contact: test+admin@example.com
14
+ config:
15
+ from: test+notifier@example.com
16
+
17
+ triggers:
18
+ - name: flapping
19
+ config:
20
+ times: 10
21
+ within: 1 minute
22
+
23
+ checks:
24
+ - name: memory
25
+ config:
26
+ times: 3
27
+ every: 20 seconds
28
+ below: 1.5 gigabytes
29
+
30
+ processes:
31
+ - name: my-process
32
+ config:
33
+ start_timeout: 25 seconds
34
+ start_command: bundle exec my-process
35
+ pid_file: tmp/pids/my-process.pid
36
+ - name: first-grouped-process
37
+ group: my-group
38
+ config:
39
+ start_command: bundle exec grouped-process
40
+ pid_file: tmp/pids/first-grouped-process.pid
41
+ - name: second-grouped-process
42
+ group: my-group
43
+ config:
44
+ start_command: bundle exec grouped-process
45
+ pid_file: tmp/pids/second-grouped-process.pid
46
+ - name: clustered-process
47
+ group: cluster
48
+ count: 2
49
+ config:
50
+ start_command: bundle exec clustered-process
51
+ pid_file: "tmp/pids/clustered-process-{ID}.pid"
@@ -0,0 +1,22 @@
1
+ require_relative "../../../test_helper"
2
+
3
+ describe Eye::Patch::ValueParser do
4
+
5
+ it "parses time values" do
6
+ assert_equal 2.weeks, Eye::Patch::ValueParser.parse("2 weeks")
7
+ assert_equal 1.5.hours, Eye::Patch::ValueParser.parse("1.5 hours")
8
+ assert_equal 50.minutes, Eye::Patch::ValueParser.parse("50 minutes")
9
+ assert_equal 3.seconds, Eye::Patch::ValueParser.parse("3 seconds")
10
+ end
11
+
12
+ it "parses size values" do
13
+ assert_equal 3.2.gigabytes, Eye::Patch::ValueParser.parse("3.2 gigabytes")
14
+ assert_equal 2.4.megabytes, Eye::Patch::ValueParser.parse("2.4 megabytes")
15
+ assert_equal 1.kilobyte, Eye::Patch::ValueParser.parse("1 kilobyte")
16
+ end
17
+
18
+ it "uses strict word boundary characters" do
19
+ assert_equal "second-thing", Eye::Patch::ValueParser.parse("second-thing")
20
+ assert_equal "minutes", Eye::Patch::ValueParser.parse("minutes")
21
+ end
22
+ end
@@ -0,0 +1,8 @@
1
+ require_relative "../../../test_helper"
2
+
3
+ describe Eye::Patch do
4
+
5
+ it "defines a version" do
6
+ assert Eye::Patch::VERSION
7
+ end
8
+ end
@@ -0,0 +1,83 @@
1
+ require_relative "../../test_helper"
2
+
3
+ describe Eye::Patch do
4
+ before do
5
+ @fixture = File.expand_path(File.join(File.dirname(__FILE__), %w[.. .. fixtures test.yml]))
6
+ @original = YAML.load(File.open(@fixture))
7
+ end
8
+
9
+ describe ".parse" do
10
+ before do
11
+ Eye::Config.any_instance.stubs(:validate!)
12
+ @parsed = Eye::Patch.parse(@fixture)
13
+
14
+ @settings = @parsed.settings
15
+ @applications = @parsed.applications
16
+ @application = @applications.values.first
17
+ end
18
+
19
+ it "returns an Eye::Config" do
20
+ assert_kind_of Eye::Config, @parsed
21
+ end
22
+
23
+ it "gives the application an appropriate name" do
24
+ assert_equal @original["name"], @applications.keys.first
25
+ assert_equal @original["name"], @application[:name]
26
+ end
27
+
28
+ it "parses contacts for notification" do
29
+ notification = @original["notifications"].first
30
+
31
+ assert_equal notification["config"]["from"], @settings[notification["type"].to_sym][:from]
32
+ assert_equal notification["type"].to_sym, @settings[:contacts][notification["name"]][:type]
33
+ assert_equal notification["level"].to_sym, @application[:notify][notification["name"]]
34
+ end
35
+
36
+ it "parses triggers" do
37
+ trigger = @original["triggers"].first
38
+ parsed_trigger = @application[:triggers][trigger["name"].to_sym]
39
+
40
+ assert_equal trigger["config"]["times"], parsed_trigger[:times]
41
+ assert_equal Eye::Patch::ValueParser.parse(trigger["config"]["within"]), parsed_trigger[:within]
42
+ end
43
+
44
+ it "parses checks" do
45
+ check = @original["checks"].first
46
+ parsed_check = @application[:checks][check["name"].to_sym]
47
+
48
+ assert_equal check["config"]["times"], parsed_check[:times]
49
+ assert_equal Eye::Patch::ValueParser.parse(check["config"]["every"]), parsed_check[:every]
50
+ assert_equal Eye::Patch::ValueParser.parse(check["config"]["below"]), parsed_check[:below]
51
+ end
52
+
53
+ it "splits processes into groups" do
54
+ grouped_processes = @original["processes"].select { |process| process["group"] && !process["count"] }
55
+ grouped_processes.each do |process|
56
+ assert_equal process["group"], @application[:groups][process["group"]][:processes][process["name"]][:group]
57
+ end
58
+ end
59
+
60
+ it "puts ungrouped processes into the __default__ group" do
61
+ lone_processes = @original["processes"].reject { |process| process["group"] }
62
+ lone_processes.each do |process|
63
+ assert_equal "__default__", @application[:groups]["__default__"][:processes][process["name"]][:group]
64
+ end
65
+ end
66
+
67
+ it "creates process clusters" do
68
+ process = @original["processes"].detect { |process| process["count"] }
69
+ process["count"].times do |index|
70
+ name = "#{process["name"]}-#{index}"
71
+ parsed_process = @application[:groups][process["group"]][:processes][name]
72
+
73
+ assert_equal process["group"], parsed_process[:group]
74
+ assert_equal process["config"]["pid_file"].gsub("{ID}", index.to_s), parsed_process[:pid_file]
75
+ end
76
+ end
77
+
78
+ it "passes application configurations down to processes" do
79
+ process = @application[:groups]["__default__"][:processes].values.first
80
+ assert_equal @application[:triggers], process[:triggers]
81
+ end
82
+ end
83
+ end
data/test/test_helper.rb CHANGED
@@ -1 +1,5 @@
1
- require 'minitest/autorun'
1
+ require "minitest/autorun"
2
+ require "minitest/unit"
3
+ require "mocha/mini_test"
4
+
5
+ require File.expand_path("../../lib/eye/patch.rb", __FILE__)
metadata CHANGED
@@ -1,37 +1,37 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eye-patch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Horner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-02-02 00:00:00.000000000 Z
11
+ date: 2014-02-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: bundler
14
+ name: eye
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - '>='
18
18
  - !ruby/object:Gem::Version
19
- version: '1.3'
20
- type: :development
19
+ version: '0'
20
+ type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - '>='
25
25
  - !ruby/object:Gem::Version
26
- version: '1.3'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
28
+ name: chronic_duration
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - '>='
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
- type: :development
34
+ type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
@@ -39,13 +39,27 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: eye
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '1.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '1.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
59
  - - '>='
46
60
  - !ruby/object:Gem::Version
47
61
  version: '0'
48
- type: :runtime
62
+ type: :development
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
@@ -53,13 +67,13 @@ dependencies:
53
67
  - !ruby/object:Gem::Version
54
68
  version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
- name: chronic_duration
70
+ name: mocha
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
73
  - - '>='
60
74
  - !ruby/object:Gem::Version
61
75
  version: '0'
62
- type: :runtime
76
+ type: :development
63
77
  prerelease: false
64
78
  version_requirements: !ruby/object:Gem::Requirement
65
79
  requirements:
@@ -86,10 +100,19 @@ files:
86
100
  - lib/eye/notify/ses.rb
87
101
  - lib/eye/patch.rb
88
102
  - lib/eye/patch/application.rb
103
+ - lib/eye/patch/capistrano.rb
89
104
  - lib/eye/patch/config.rb
105
+ - lib/eye/patch/group_set.rb
106
+ - lib/eye/patch/option_set.rb
90
107
  - lib/eye/patch/overrides.rb
108
+ - lib/eye/patch/process_set.rb
91
109
  - lib/eye/patch/settings.rb
110
+ - lib/eye/patch/value_parser.rb
92
111
  - lib/eye/patch/version.rb
112
+ - test/fixtures/test.yml
113
+ - test/lib/eye/patch/value_parser_test.rb
114
+ - test/lib/eye/patch/version_test.rb
115
+ - test/lib/eye/patch_test.rb
93
116
  - test/test_helper.rb
94
117
  homepage: https://github.com/tablexi/eye-patch
95
118
  licenses:
@@ -117,4 +140,8 @@ specification_version: 4
117
140
  summary: Eye::Patch abstracts out the Eye DSL to allow you to load your configuration
118
141
  from a structured YAML file, rather than relying on Eye's built-in DSL.
119
142
  test_files:
143
+ - test/fixtures/test.yml
144
+ - test/lib/eye/patch/value_parser_test.rb
145
+ - test/lib/eye/patch/version_test.rb
146
+ - test/lib/eye/patch_test.rb
120
147
  - test/test_helper.rb