snowman-io 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3 -0
  3. data/README.md +4 -6
  4. data/bin/snowman +1 -1
  5. data/lib/config/mongoid.yml +5 -0
  6. data/lib/snowman-io.rb +62 -30
  7. data/lib/snowman-io/aggregate.rb +97 -0
  8. data/lib/snowman-io/api.rb +33 -65
  9. data/lib/snowman-io/api/agent.rb +27 -0
  10. data/lib/snowman-io/api/apps.rb +45 -0
  11. data/lib/snowman-io/api/auth_helpers.rb +76 -0
  12. data/lib/snowman-io/api/checks.rb +59 -0
  13. data/lib/snowman-io/api/extra/meteor.rb +45 -0
  14. data/lib/snowman-io/api/fridge.rb +14 -0
  15. data/lib/snowman-io/api/info.rb +30 -0
  16. data/lib/snowman-io/api/metrics.rb +115 -0
  17. data/lib/snowman-io/api/users.rb +231 -0
  18. data/lib/snowman-io/cli.rb +69 -0
  19. data/lib/snowman-io/launcher.rb +12 -11
  20. data/lib/snowman-io/loop/check_processor.rb +29 -0
  21. data/lib/snowman-io/loop/checks.rb +59 -0
  22. data/lib/snowman-io/loop/main.rb +43 -0
  23. data/lib/snowman-io/loop/ping.rb +25 -0
  24. data/lib/snowman-io/migration.rb +79 -0
  25. data/lib/snowman-io/models/aggregation.rb +15 -0
  26. data/lib/snowman-io/models/app.rb +61 -0
  27. data/lib/snowman-io/models/check.rb +48 -0
  28. data/lib/snowman-io/models/concerns/tokenable.rb +15 -0
  29. data/lib/snowman-io/models/data_point.rb +9 -0
  30. data/lib/snowman-io/models/deleted.rb +9 -0
  31. data/lib/snowman-io/models/following.rb +8 -0
  32. data/lib/snowman-io/models/metric.rb +36 -0
  33. data/lib/snowman-io/models/setting.rb +24 -0
  34. data/lib/snowman-io/models/user.rb +73 -0
  35. data/lib/snowman-io/options.rb +11 -3
  36. data/lib/snowman-io/report_mailer.rb +88 -0
  37. data/lib/snowman-io/reports.rb +16 -0
  38. data/lib/snowman-io/ui/AUTO_GENERATED_FOLDER +2 -0
  39. data/lib/snowman-io/ui/assets/ui-0e39dafcb798020fb855e325931c8451.css +1 -0
  40. data/lib/snowman-io/ui/assets/ui-d30809d0ae0a003d841fa95a352d624b.js +9 -0
  41. data/lib/snowman-io/ui/assets/vendor-7edfd1432c1bbd806306d5583c75b1fc.css +5 -0
  42. data/lib/snowman-io/ui/assets/vendor-c22e2ccc87c9bc7609b95939c308bc7f.js +24 -0
  43. data/lib/snowman-io/{api/public/bootstrap → ui/bootstrap-3.3.1}/css/bootstrap-theme.css +0 -0
  44. data/lib/snowman-io/{api/public/bootstrap → ui/bootstrap-3.3.1}/css/bootstrap-theme.css.map +0 -0
  45. data/lib/snowman-io/{api/public/bootstrap → ui/bootstrap-3.3.1}/css/bootstrap-theme.min.css +0 -0
  46. data/lib/snowman-io/{api/public/bootstrap → ui/bootstrap-3.3.1}/css/bootstrap.css +0 -0
  47. data/lib/snowman-io/{api/public/bootstrap → ui/bootstrap-3.3.1}/css/bootstrap.css.map +0 -0
  48. data/lib/snowman-io/{api/public/bootstrap → ui/bootstrap-3.3.1}/css/bootstrap.min.css +0 -0
  49. data/lib/snowman-io/{api/public/bootstrap → ui/bootstrap-3.3.1}/fonts/glyphicons-halflings-regular.eot +0 -0
  50. data/lib/snowman-io/{api/public/bootstrap → ui/bootstrap-3.3.1}/fonts/glyphicons-halflings-regular.svg +0 -0
  51. data/lib/snowman-io/{api/public/bootstrap → ui/bootstrap-3.3.1}/fonts/glyphicons-halflings-regular.ttf +0 -0
  52. data/lib/snowman-io/{api/public/bootstrap → ui/bootstrap-3.3.1}/fonts/glyphicons-halflings-regular.woff +0 -0
  53. data/lib/snowman-io/{api/public/bootstrap → ui/bootstrap-3.3.1}/js/bootstrap.js +0 -0
  54. data/lib/snowman-io/{api/public/bootstrap → ui/bootstrap-3.3.1}/js/bootstrap.min.js +0 -0
  55. data/lib/snowman-io/{api/public/bootstrap → ui/bootstrap-3.3.1}/js/npm.js +0 -0
  56. data/lib/snowman-io/ui/crossdomain.xml +15 -0
  57. data/lib/snowman-io/ui/fonts/glyphicons-halflings-regular.eot +0 -0
  58. data/lib/snowman-io/ui/fonts/glyphicons-halflings-regular.svg +229 -0
  59. data/lib/snowman-io/ui/fonts/glyphicons-halflings-regular.ttf +0 -0
  60. data/lib/snowman-io/ui/fonts/glyphicons-halflings-regular.woff +0 -0
  61. data/lib/snowman-io/ui/index.html +175 -0
  62. data/lib/snowman-io/ui/robots.txt +2 -0
  63. data/lib/snowman-io/utils.rb +66 -0
  64. data/lib/snowman-io/version.rb +1 -1
  65. data/lib/snowman-io/views/layouts/custom.css +42 -0
  66. data/lib/snowman-io/views/layouts/main.html.erb +38 -0
  67. data/lib/snowman-io/views/layouts/styles.css +264 -0
  68. data/lib/snowman-io/views/layouts/transactional-email-templates-LICENSE +21 -0
  69. data/lib/snowman-io/views/report_mailer/check_triggered.html.erb +16 -0
  70. data/lib/snowman-io/views/report_mailer/checks/_human_last_value_limit.html.erb +2 -0
  71. data/lib/snowman-io/views/report_mailer/checks/_human_prev_day_datapoints_limit.html.erb +2 -0
  72. data/lib/snowman-io/views/report_mailer/daily_report.html.erb +40 -0
  73. data/lib/snowman-io/views/report_mailer/restore_password.html.erb +28 -0
  74. data/lib/snowman-io/views/report_mailer/send_invite.html.erb +32 -0
  75. data/lib/snowman-io/web.rb +28 -0
  76. data/lib/snowman-io/web_server.rb +107 -0
  77. data/snowman-io.gemspec +15 -5
  78. metadata +220 -49
  79. data/lib/snowman-io/api/public/README.md +0 -36
  80. data/lib/snowman-io/api/public/css/normalize.css +0 -406
  81. data/lib/snowman-io/api/public/css/style.css +0 -4
  82. data/lib/snowman-io/api/public/js/app.js +0 -13
  83. data/lib/snowman-io/api/public/js/libs/ember-1.8.1.js +0 -49740
  84. data/lib/snowman-io/api/public/js/libs/handlebars-v1.3.0.js +0 -2746
  85. data/lib/snowman-io/api/public/js/libs/jquery-1.10.2.js +0 -9789
  86. data/lib/snowman-io/api/public/tests/runner.css +0 -14
  87. data/lib/snowman-io/api/public/tests/runner.js +0 -13
  88. data/lib/snowman-io/api/public/tests/tests.js +0 -30
  89. data/lib/snowman-io/api/public/tests/vendor/qunit-1.12.0.css +0 -244
  90. data/lib/snowman-io/api/public/tests/vendor/qunit-1.12.0.js +0 -2212
  91. data/lib/snowman-io/api/views/index.erb +0 -26
  92. data/lib/snowman-io/api/views/layout.erb +0 -24
  93. data/lib/snowman-io/api/views/login.erb +0 -21
  94. data/lib/snowman-io/api/views/unpacking.erb +0 -21
  95. data/lib/snowman-io/check.rb +0 -49
  96. data/lib/snowman-io/check_result.rb +0 -15
  97. data/lib/snowman-io/checks/hosted_graphite.rb +0 -23
  98. data/lib/snowman-io/handler.rb +0 -27
  99. data/lib/snowman-io/notifiers/slack.rb +0 -76
  100. data/lib/snowman-io/processor.rb +0 -32
  101. data/lib/snowman-io/scheduler.rb +0 -42
@@ -1,26 +0,0 @@
1
- <% content_for :head do %>
2
- <meta content="<%= SnowmanIO::VERSION %>" name="snowman-io-version" />
3
- <link rel="stylesheet" href="/css/style.css">
4
- <% end %>
5
-
6
- <script type="text/x-handlebars">
7
- <div class="container">
8
- <h2>
9
- Welcome to SnowmanIO
10
- <small><a href="/logout">logout</a></small>
11
- </h2>
12
-
13
- {{outlet}}
14
- </div>
15
- </script>
16
-
17
- <script type="text/x-handlebars" id="index">
18
- <p>Version: {{model.version}}</p>
19
- </script>
20
-
21
- <script src="/js/libs/jquery-1.10.2.js"></script>
22
- <script src="/js/libs/handlebars-v1.3.0.js"></script>
23
- <script src="/js/libs/ember-1.8.1.js"></script>
24
- <script src="/js/app.js"></script>
25
- <!-- to activate the test runner, add the "?test" query string parameter -->
26
- <script src="/tests/runner.js"></script>
@@ -1,24 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
- <meta name="viewport" content="width=device-width, initial-scale=1">
7
- <title>SnowmanIO: be a little snowy</title>
8
-
9
- <!-- Bootstrap -->
10
- <link href="/bootstrap/css/bootstrap.min.css" rel="stylesheet">
11
-
12
- <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
13
- <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
14
- <!--[if lt IE 9]>
15
- <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
16
- <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
17
- <![endif]-->
18
-
19
- <%= yield_content :head %>
20
- </head>
21
- <body>
22
- <%= yield %>
23
- </body>
24
- </html>
@@ -1,21 +0,0 @@
1
- <div class="container">
2
- <div class="page-header">
3
- <h1>SnowmanIO</h1>
4
- </div>
5
-
6
- <% if @wrong_password_warning %>
7
- <div class="alert alert-danger">
8
- Wrong password.
9
- </div>
10
- <% end %>
11
-
12
- <div class="col-sm-6">
13
- <form action="/login" method="POST" role="form">
14
- <div class="form-group">
15
- <label for=password>Password</label>
16
- <input type=password name=password class="form-control">
17
- </div>
18
- <input type=submit value="Login" class="btn btn-default">
19
- </form>
20
- </div>
21
- </div>
@@ -1,21 +0,0 @@
1
- <div class="container">
2
- <div class="page-header">
3
- <h1>SnowmanIO: Installation</h1>
4
- </div>
5
-
6
- <% if @empty_password_warning %>
7
- <div class="alert alert-danger">
8
- Empty password is not allowed.
9
- </div>
10
- <% end %>
11
-
12
- <div class="col-sm-6">
13
- <form action="/unpacking" method="POST" role="form">
14
- <div class="form-group">
15
- <label for=password>Admin password</label>
16
- <input type=text name=password class="form-control">
17
- </div>
18
- <input type=submit value="Set Admin Password" class="btn btn-primary">
19
- </form>
20
- </div>
21
- </div>
@@ -1,49 +0,0 @@
1
- require 'json'
2
- require 'open-uri'
3
- require "active_support/time"
4
-
5
- require 'snowman-io/notifiers/slack'
6
-
7
- module SnowmanIO
8
- class Check
9
- include Checks::HostedGraphite
10
-
11
- DEFAULT_INTERVAL = 1.minute
12
- class << self
13
- def interval(value = nil)
14
- if value
15
- @interval = value
16
- else
17
- @interval || DEFAULT_INTERVAL
18
- end
19
- end
20
-
21
- def human(value = nil)
22
- if value
23
- @human = value
24
- else
25
- self.name + ": #{@human}"
26
- end
27
- end
28
-
29
- def notifiers
30
- @notifiers ||= [Notifiers::Slack].select { |notifier| notifier.configured? }
31
- end
32
- end
33
-
34
- def perform
35
- if ok?
36
- status = "success"
37
- message = self.class.human + " - OK"
38
- else
39
- status = "failed"
40
- message = self.class.human + " - FAIL"
41
- end
42
- CheckResult.new(self.class, status, message)
43
- end
44
-
45
- def ok?
46
- raise "Implement ok? in check class"
47
- end
48
- end
49
- end
@@ -1,15 +0,0 @@
1
- module SnowmanIO
2
- class CheckResult < Struct.new(:check, :status, :message)
3
- def check_name
4
- check.name
5
- end
6
-
7
- def fail?
8
- status == "failed"
9
- end
10
-
11
- def serialize
12
- JSON.dump({ status: status, message: message })
13
- end
14
- end
15
- end
@@ -1,23 +0,0 @@
1
- module SnowmanIO
2
- module Checks
3
- # Gets last value from Hosted Graphite metric (https://www.hostedgraphite.com/)
4
- module HostedGraphite
5
- protected
6
-
7
- def get_hg_value(metric, options = {})
8
- access_key = ENV["HG_KEY"]
9
- return nil unless access_key
10
- base_url = "https://www.hostedgraphite.com#{access_key}/graphite/render"
11
- from = options[:from] || "-10mins"
12
- url = base_url + "?format=json&target=#{URI.escape metric}&from=#{from}"
13
- handle = open(url)
14
- raw_data = JSON.parse(handle.gets)
15
- raw = raw_data.first
16
- datapoints = raw['datapoints'].delete_if { |v| v.first.nil? }
17
- if datapoints.last
18
- datapoints.last.first
19
- end
20
- end
21
- end
22
- end
23
- end
@@ -1,27 +0,0 @@
1
- module SnowmanIO
2
- # Handler process result from checks, saves them
3
- # to redis and fires notifications if needed.
4
- class Handler
5
- include Celluloid
6
-
7
- def handle(result)
8
- history_key = "history:#{result.check_name.underscore}"
9
- SnowmanIO.redis.rpush(history_key, result.serialize)
10
- history = SnowmanIO.redis.lrange(history_key, -4, -1).map { |result| JSON.load(result) }
11
- previous_status_failed = if history.size < 4
12
- false
13
- else
14
- ["failed", "exception"].include?(history.shift["status"])
15
- end
16
- if !previous_status_failed && history.size >= 3 && history.all? { |result| result["status"] == "failed" || result["status"] == "exception" }
17
- notify_fail(result)
18
- end
19
- end
20
-
21
- private
22
-
23
- def notify_fail(result)
24
- result.check.notifiers.each { |notifier| notifier.pool.async.notify(result) }
25
- end
26
- end
27
- end
@@ -1,76 +0,0 @@
1
- module SnowmanIO
2
- module Notifiers
3
- class Slack
4
- include Celluloid
5
- class << self
6
- def webhook_url
7
- ENV["SLACK_WEBHOOK_URL"]
8
- end
9
-
10
- def channel
11
- ENV["SLACK_CHANNEL"]
12
- end
13
-
14
- def bot_name
15
- ENV["SLACK_BOT_NAME"]
16
- end
17
-
18
- def configured?
19
- webhook_url.present?
20
- end
21
- end
22
-
23
- def notify(result)
24
- return unless self.class.configured?
25
- post_data(result.message)
26
- end
27
-
28
- private
29
-
30
- def configuration
31
- {
32
- :webhook_url => self.class.webhook_url,
33
- :bot_name => self.class.bot_name || "snowman-io",
34
- :channel => self.class.channel
35
- }
36
- end
37
-
38
- def post_data(message)
39
- uri = URI(configuration[:webhook_url])
40
- http = Net::HTTP.new(uri.host, uri.port)
41
- http.use_ssl = true
42
-
43
- req = Net::HTTP::Post.new("#{uri.path}?#{uri.query}")
44
- req.body = payload(message).to_json
45
-
46
- response = http.request(req)
47
- verify_response(response)
48
- end
49
-
50
- def slack_uri(token)
51
- url = "https://#{team_name}.slack.com/services/hooks/incoming-webhook?token=#{token}"
52
- URI(url)
53
- end
54
-
55
- def verify_response(response)
56
- case response
57
- when Net::HTTPSuccess
58
- true
59
- else
60
- raise response.error!
61
- end
62
- end
63
-
64
- # TODO: include :icon_url
65
- def payload(text)
66
- {
67
- :username => configuration[:bot_name],
68
- :attachments => [{
69
- :text => text,
70
- :color => "#FF0000"
71
- }]
72
- }.tap { |payload| payload[:channel] = channel if configuration[:channel] }
73
- end
74
- end
75
- end
76
- end
@@ -1,32 +0,0 @@
1
- module SnowmanIO
2
- # Processor initiated by Scheduler, executes check and notifies
3
- # Scheduler about the result of the check.
4
- class Processor
5
- include Celluloid
6
-
7
- def initialize(scheduler)
8
- @scheduler = scheduler
9
- end
10
-
11
- # TODO: logging can be extracted in some kind of middleware
12
- def process(check)
13
- begin
14
- SnowmanIO.logger.info("Processing check #{check.human}, started at #{Time.now}")
15
- result = check.new.perform
16
- rescue Exception => e
17
- result = result_from_exception(check, e)
18
- raise
19
- ensure
20
- SnowmanIO.logger.info("Processing check #{check.human}, finished at #{Time.now}")
21
- @scheduler.processor_done(current_actor, result)
22
- end
23
- end
24
-
25
- private
26
-
27
- def result_from_exception(check, e)
28
- message = "Check #{check.human} was interruppted by exception: #{e.class}: #{e.message}"
29
- CheckResult.new(check, "exception", message)
30
- end
31
- end
32
- end
@@ -1,42 +0,0 @@
1
- require "snowman-io/processor"
2
-
3
- module SnowmanIO
4
- # Scheduler schedules execution of checks.
5
- class Scheduler
6
- include Celluloid
7
- trap_exit :processor_died
8
-
9
- attr_accessor :handler
10
-
11
- def initialize(checks)
12
- @time = {}
13
- checks.each do |check|
14
- @time[check] = Time.now + check.interval
15
- end
16
- end
17
-
18
- def start
19
- every(1) { schedule_checks }
20
- end
21
-
22
- def processor_done(processor, result)
23
- @handler.async.handle(result)
24
- processor.terminate
25
- end
26
-
27
- private
28
-
29
- def schedule_checks
30
- @time.each do |check, time|
31
- if time <= Time.now
32
- Processor.new_link(current_actor).async.process(check)
33
- @time[check] = Time.now + check.interval
34
- end
35
- end
36
- end
37
-
38
- def processor_died(_actor, _reason)
39
- # TODO
40
- end
41
- end
42
- end