ferris-bueller 0.0.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bd02010a5690c0772a134273d476c24e292d8b15
4
+ data.tar.gz: b0e2d65aaa1b7bd1546c9681c322b4429e4a0f65
5
+ SHA512:
6
+ metadata.gz: 41280791c72f6d5b32861eea7d9c588e533b76dce2485925f498b8a88e5a33dc89f3e959dedc46897350cf215924ce6ed7357598f220d66e8abd355dc8f61391
7
+ data.tar.gz: ccdbaefb94e9c4ac5418857913c8db8c862c020f6e22c0c588226bf862313b8285fd565f9e7e4a971739b9f47fa2a11b78835eedc62b8c6d004c8c42877e142e
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2015 Sean Clemmer and Blue Jeans Network
2
+
3
+ Permission to use, copy, modify, and/or distribute this software for any
4
+ purpose with or without fee is hereby granted, provided that the above
5
+ copyright notice and this permission notice appear in all copies.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
8
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
9
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
10
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
12
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13
+ PERFORMANCE OF THIS SOFTWARE.
data/Readme.md ADDED
@@ -0,0 +1,3 @@
1
+ # Ferris
2
+
3
+ TODO
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.0
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require 'ferris-bueller'
3
+ FerrisBueller::Main.start
@@ -0,0 +1,37 @@
1
+ module FerrisBueller
2
+ module Constants
3
+ RETRY_DELAY = 5
4
+
5
+ RESOLVED_TRANSITIONS = %w[ 51 ]
6
+
7
+ RESOLVED_STATE = /resolve/i
8
+
9
+ CLOSED_TRANSITIONS = %w[ 61 71 ]
10
+
11
+ CLOSED_STATE = /close/i
12
+
13
+ SEVERITIES = {
14
+ 1 => '10480',
15
+ 2 => '10481',
16
+ 3 => '10482',
17
+ 4 => '10483',
18
+ 5 => '10484'
19
+ }
20
+
21
+ SHOW_FIELDS = {
22
+ 'summary' => 'Summary',
23
+ 'description' => 'Description',
24
+ 'customfield_11250' => 'Severity',
25
+ 'customfield_11251' => 'Impact Started',
26
+ 'customfield_11252' => 'Impact Ended',
27
+ 'customfield_11253' => 'Reported By',
28
+ 'customfield_11254' => 'Services Affected',
29
+ 'customfield_11255' => 'Cause',
30
+ 'status' => 'Status',
31
+ 'created' => 'Created',
32
+ 'updated' => 'Updated'
33
+ }
34
+
35
+ SEVERITY_FIELD = SHOW_FIELDS.key('Severity')
36
+ end
37
+ end
@@ -0,0 +1,170 @@
1
+ require 'thin/logging'
2
+ require 'queryparams'
3
+
4
+ require_relative 'constants'
5
+ require_relative 'web'
6
+
7
+
8
+ module FerrisBueller
9
+ module Helpers
10
+ include Constants
11
+
12
+
13
+ def store ; @store end
14
+
15
+
16
+ def start_your_day_off
17
+ Web.set :environment, options.environment
18
+ Web.set :port, options.port
19
+ Web.set :bind, options.bind
20
+ Web.set :store, @store
21
+ Web.set :logger, log
22
+
23
+ if log.level >= ::Logger::DEBUG
24
+ Web.set :raise_errors, true
25
+ Web.set :dump_errors, true
26
+ Web.set :show_exceptions, true
27
+ Web.set :logging, ::Logger::DEBUG
28
+ end
29
+
30
+ Thin::Logging.logger = log
31
+
32
+ Web.run!
33
+ end
34
+
35
+
36
+ def go_refresh_jira_users
37
+ Thread.new do
38
+ loop do
39
+ refresh_jira_users
40
+ sleep options.user_refresh
41
+ end
42
+ end
43
+ end
44
+
45
+
46
+ def go_refresh_jira_members
47
+ Thread.new do
48
+ loop do
49
+ refresh_jira_members
50
+ sleep options.member_refresh
51
+ end
52
+ end
53
+ end
54
+
55
+
56
+ def go_refresh_jira_incidents
57
+ Thread.new do
58
+ loop do
59
+ refresh_jira_incidents
60
+ sleep options.incident_refresh
61
+ end
62
+ end
63
+ end
64
+
65
+
66
+ def refresh_jira_users
67
+ data = jira_request 'user/assignable/search', \
68
+ project: options.jira_project,
69
+ startAt: 0,
70
+ maxResults: 1_000_000
71
+
72
+ users = data.inject({}) do |h, user|
73
+ h[user['name']] = {
74
+ key: user['key'],
75
+ nick: user['name'],
76
+ name: user['displayName'],
77
+ email: user['emailAddress']
78
+ } ; h
79
+ end
80
+
81
+ store['jira_users'] = users
82
+
83
+ rescue StandardError => e
84
+ log.error \
85
+ error: 'could not refresh users',
86
+ event: 'exception',
87
+ exception: e.inspect,
88
+ class: e.class,
89
+ message: e.message,
90
+ backtrace: e.backtrace,
91
+ remediation: 'pausing breifly before retrying'
92
+ sleep RETRY_DELAY
93
+ retry
94
+ end
95
+
96
+
97
+ def refresh_jira_members
98
+ data = jira_request 'group', \
99
+ groupname: options.jira_group,
100
+ expand: 'users'
101
+
102
+ user_names = data['users']['items'].map { |u| u['displayName'] }
103
+ store['jira_members'] = user_names
104
+
105
+ rescue StandardError => e
106
+ log.error \
107
+ error: 'could not refresh members',
108
+ event: 'exception',
109
+ exception: e.inspect,
110
+ class: e.class,
111
+ message: e.message,
112
+ backtrace: e.backtrace,
113
+ remediation: 'pausing breifly before retrying'
114
+ sleep RETRY_DELAY
115
+ retry
116
+ end
117
+
118
+
119
+ def refresh_jira_incidents
120
+ data = jira_request 'search', \
121
+ jql: "project = #{options.jira_project} ORDER BY created ASC, priority DESC",
122
+ fields: SHOW_FIELDS.keys.join(','),
123
+ startAt: 0,
124
+ maxResults: 1_000_000
125
+
126
+ store['jira_incidents'] = data['issues'].map do |i|
127
+ i['num'] = i['key'].split('-', 2).last ; i
128
+ end
129
+
130
+ rescue StandardError => e
131
+ log.error \
132
+ error: 'could not refresh incidents',
133
+ event: 'exception',
134
+ exception: e.inspect,
135
+ class: e.class,
136
+ message: e.message,
137
+ backtrace: e.backtrace,
138
+ remediation: 'pausing breifly before retrying'
139
+ sleep RETRY_DELAY
140
+ retry
141
+ end
142
+
143
+
144
+ def jira_request path, params
145
+ api_url = File.join options.jira_url, 'rest/api/latest', path
146
+ log.trace \
147
+ event: 'jira request',
148
+ path: path,
149
+ params: params,
150
+ api_url: api_url
151
+ encoded_params = QueryParams.encode params
152
+ uri = URI(api_url + '?' + encoded_params)
153
+ http = Net::HTTP.new uri.host, uri.port
154
+ req = Net::HTTP::Get.new uri
155
+ req.use_ssl if uri.scheme == 'https'
156
+ req.basic_auth options.jira_user, options.jira_pass
157
+ req['Content-Type'] = 'application/json'
158
+ req['Accept'] = 'application/json'
159
+ resp = http.request req
160
+ log.debug \
161
+ event: 'jira request responded',
162
+ path: path,
163
+ params: params,
164
+ api_url: api_url,
165
+ response: resp
166
+ JSON.parse resp.body
167
+ end
168
+
169
+ end
170
+ end
@@ -0,0 +1,113 @@
1
+ require_relative 'mjolnir'
2
+ require_relative 'metadata'
3
+ require_relative 'helpers'
4
+
5
+
6
+ module FerrisBueller
7
+ class Main < Mjolnir
8
+ include Helpers
9
+
10
+
11
+ desc 'version', 'Show application version'
12
+ def version
13
+ puts VERSION
14
+ end
15
+
16
+
17
+ desc 'art', 'Show application art'
18
+ def art
19
+ puts "\n%s\n" % ART
20
+ end
21
+
22
+
23
+ desc 'start', 'Start Bender HipChat bot and Web server'
24
+ option :token, \
25
+ type: :string,
26
+ aliases: %w[ -t ],
27
+ desc: 'Set Slack verificatino token',
28
+ required: true,
29
+ default: ENV['FERRIS_BUELLER_TOKEN']
30
+ option :database, \
31
+ type: :string,
32
+ aliases: %w[ -d ],
33
+ desc: 'Set path to application database',
34
+ required: true,
35
+ default: ENV['FERRIS_BUELLER_DATABASE']
36
+ option :jira_user, \
37
+ type: :string,
38
+ aliases: %w[ -u ],
39
+ desc: 'Set JIRA username',
40
+ required: true,
41
+ default: ENV['FERRIS_BUELLER_JIRA_USER']
42
+ option :jira_pass, \
43
+ type: :string,
44
+ aliases: %w[ -p ],
45
+ desc: 'Set JIRA password',
46
+ required: true,
47
+ default: ENV['FERRIS_BUELLER_JIRA_PASS']
48
+ option :jira_url, \
49
+ type: :string,
50
+ aliases: %w[ -j ],
51
+ desc: 'Set JIRA base URL',
52
+ required: true,
53
+ default: ENV['FERRIS_BUELLER_JIRA_URL']
54
+ option :jira_project, \
55
+ type: :string,
56
+ aliases: %w[ -o ],
57
+ desc: 'Set JIRA project',
58
+ required: true,
59
+ default: ENV['FERRIS_BUELLER_JIRA_PROJECT']
60
+ option :jira_group, \
61
+ type: :string,
62
+ aliases: %w[ -g ],
63
+ desc: 'Set JIRA group for write mode',
64
+ required: true,
65
+ default: ENV['FERRIS_BUELLER_JIRA_GROUP']
66
+ option :jira_type, \
67
+ type: :string,
68
+ aliases: %w[ -e ],
69
+ desc: 'Set JIRA issue type',
70
+ required: true,
71
+ default: ENV['FERRIS_BUELLER_JIRA_TYPE']
72
+ option :user_refresh, \
73
+ type: :numeric,
74
+ aliases: %w[ -r ],
75
+ desc: 'Set JIRA user refresh rate',
76
+ default: (ENV['FERRIS_BUELLER_USER_REFRESH'] || 90)
77
+ option :incident_refresh, \
78
+ type: :numeric,
79
+ aliases: %w[ -i ],
80
+ desc: 'Set JIRA incident refresh rate',
81
+ default: (ENV['FERRIS_BUELLER_INCIDENT_REFRESH'] || 5)
82
+ option :member_refresh, \
83
+ type: :numeric,
84
+ aliases: %w[ -g ],
85
+ desc: 'Set JIRA group membership refresh rate',
86
+ default: (ENV['FERRIS_BUELLER_MEMBER_REFRESH'] || 60)
87
+ option :environment, \
88
+ type: :string,
89
+ aliases: %w[ -m ],
90
+ desc: 'Set environment for Web app',
91
+ default: (ENV['FERRIS_BUELLER_ENVIRONMENT'] || 'development')
92
+ option :port, \
93
+ type: :numeric,
94
+ aliases: %w[ -n ],
95
+ desc: 'Set port for Web app',
96
+ default: (ENV['FERRIS_BUELLER_PORT'] || 8080)
97
+ option :bind, \
98
+ type: :string,
99
+ aliases: %w[ -b ],
100
+ desc: 'Set bind for Web app',
101
+ default: (ENV['FERRIS_BUELLER_BIND'] || '0.0.0.0')
102
+ include_common_options
103
+ def start
104
+ log.info 'starting...'
105
+ @store = {}
106
+ go_refresh_jira_users
107
+ go_refresh_jira_members
108
+ go_refresh_jira_incidents
109
+ start_your_day_off
110
+ end
111
+
112
+ end
113
+ end
@@ -0,0 +1,43 @@
1
+ module FerrisBueller
2
+
3
+ # We use a VERSION file to tie into our build pipeline
4
+ VERSION = File.read(File.join(File.dirname(__FILE__), '..', '..', 'VERSION')).strip
5
+
6
+ # We don't really do all that much, be humble
7
+ SUMMARY = 'Successor to Bender, based on Ferris'
8
+
9
+ # Like the MIT license, but even simpler
10
+ LICENSE = 'ISC'
11
+
12
+ # Where you should look first
13
+ HOMEPAGE = 'https://github.com/sczizzo/ferris-bueller'
14
+
15
+ # Your benevolent dictator for life
16
+ AUTHOR = 'Sean Clemmer'
17
+
18
+ # Turn here to strangle your dictator
19
+ EMAIL = 'sclemmer@bluejeans.com'
20
+
21
+ # Bundled extensions
22
+ TRAVELING_RUBY_BUCKET = 'http://d6r77u77i8pq3.cloudfront.net'
23
+ TRAVELING_RUBY_VERSION = '20150715-2.2.2'
24
+ EM_VERSION = '1.0.4'
25
+ FFI_VERSION = '1.9.6'
26
+ JSON_VERSION = '1.8.2'
27
+ THIN_VERSION = '1.6.3'
28
+
29
+ # Every project deserves its own ASCII art
30
+ ART = <<-'EOART' % VERSION
31
+
32
+
33
+
34
+ .--. . .
35
+ | ) | |
36
+ |--: .-. . . | | .-. .--.
37
+ | )(.-' | | | |(.-' |
38
+ '--' `--'`--`-`-`-`--''
39
+
40
+
41
+
42
+ EOART
43
+ end
@@ -0,0 +1,57 @@
1
+ require 'thor'
2
+ require 'slog'
3
+
4
+
5
+ module FerrisBueller
6
+
7
+ # Thor's hammer! Like Thor with better logging
8
+ class Mjolnir < Thor
9
+
10
+ # Common options for Thor commands
11
+ COMMON_OPTIONS = {
12
+ log: {
13
+ type: :string,
14
+ aliases: %w[ -l ],
15
+ desc: 'Log to file instead of STDOUT',
16
+ default: ENV['FERRIS_BUELLER_LOG'] || nil
17
+ },
18
+ debug: {
19
+ type: :boolean,
20
+ aliases: %w[ -v ],
21
+ desc: 'Enable DEBUG-level logging',
22
+ default: ENV['FERRIS_BUELLER_DEBUG'] || false
23
+ },
24
+ trace: {
25
+ type: :boolean,
26
+ aliases: %w[ -z ],
27
+ desc: 'Enable TRACE-level logging (overrides DEBUG)',
28
+ default: ENV['FERRIS_BUELLER_TRACE'] || false
29
+ }
30
+ }
31
+
32
+ # Decorate Thor commands with the options above
33
+ def self.include_common_options
34
+ COMMON_OPTIONS.each do |name, spec|
35
+ option name, spec
36
+ end
37
+ end
38
+
39
+
40
+ no_commands do
41
+
42
+ # Construct a Logger given the command-line options
43
+ def log
44
+ return @logger if defined? @logger
45
+ device = options.log || $stdout
46
+ colorize, prettify = false, false
47
+ colorize, prettify = true, true if device.tty? rescue false
48
+ @logger = Slog.new \
49
+ out: device, colorize: colorize, prettify: prettify
50
+ @logger.level = :debug if options.debug?
51
+ @logger.level = :trace if options.trace?
52
+ @logger
53
+ end
54
+
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,47 @@
1
+ require 'json'
2
+
3
+ require 'sinatra/base'
4
+
5
+ require_relative 'metadata'
6
+
7
+ Thread.abort_on_exception = true
8
+
9
+
10
+ module FerrisBueller
11
+ class Web < Sinatra::Application
12
+ def log ; settings.logger end
13
+ def store ; settings.store end
14
+
15
+
16
+ get '/' do
17
+ log.info event: 'get /'
18
+ content_type :text
19
+ 'Sup.'
20
+ end
21
+
22
+
23
+ get '/v' do
24
+ log.info event: 'get /v'
25
+ content_type :text
26
+ VERSION
27
+ end
28
+
29
+
30
+ get '/s' do
31
+ log.info event: 'get /s'
32
+ content_type :json
33
+ store.to_json
34
+ end
35
+
36
+
37
+ post '/' do
38
+ request.body.rewind
39
+ body = request.body.read
40
+ log.info event: 'post /', params: params, body: body
41
+ # TODO
42
+ content_type :json
43
+ { params: params, body: body }.to_json
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,2 @@
1
+ require_relative 'ferris-bueller/metadata'
2
+ require_relative 'ferris-bueller/main'
metadata ADDED
@@ -0,0 +1,181 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ferris-bueller
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Sean Clemmer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-01-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: slog
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: thor
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.19'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.19'
41
+ - !ruby/object:Gem::Dependency
42
+ name: queryparams
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.0.3
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.0.3
55
+ - !ruby/object:Gem::Dependency
56
+ name: fuzzy-string-match
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.9.7
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.9.7
69
+ - !ruby/object:Gem::Dependency
70
+ name: sinatra
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.4'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.4'
83
+ - !ruby/object:Gem::Dependency
84
+ name: eventmachine
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '='
88
+ - !ruby/object:Gem::Version
89
+ version: 1.0.4
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '='
95
+ - !ruby/object:Gem::Version
96
+ version: 1.0.4
97
+ - !ruby/object:Gem::Dependency
98
+ name: thin
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '='
102
+ - !ruby/object:Gem::Version
103
+ version: 1.6.3
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '='
109
+ - !ruby/object:Gem::Version
110
+ version: 1.6.3
111
+ - !ruby/object:Gem::Dependency
112
+ name: json
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '='
116
+ - !ruby/object:Gem::Version
117
+ version: 1.8.2
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '='
123
+ - !ruby/object:Gem::Version
124
+ version: 1.8.2
125
+ - !ruby/object:Gem::Dependency
126
+ name: ffi
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '='
130
+ - !ruby/object:Gem::Version
131
+ version: 1.9.6
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '='
137
+ - !ruby/object:Gem::Version
138
+ version: 1.9.6
139
+ description: Successor to Bender, based on Ferris.
140
+ email: sclemmer@bluejeans.com
141
+ executables: []
142
+ extensions: []
143
+ extra_rdoc_files: []
144
+ files:
145
+ - LICENSE
146
+ - Readme.md
147
+ - VERSION
148
+ - bin/ferris-bueller
149
+ - lib/ferris-bueller.rb
150
+ - lib/ferris-bueller/constants.rb
151
+ - lib/ferris-bueller/helpers.rb
152
+ - lib/ferris-bueller/main.rb
153
+ - lib/ferris-bueller/metadata.rb
154
+ - lib/ferris-bueller/mjolnir.rb
155
+ - lib/ferris-bueller/web.rb
156
+ homepage: https://github.com/sczizzo/ferris-bueller
157
+ licenses:
158
+ - ISC
159
+ metadata: {}
160
+ post_install_message:
161
+ rdoc_options: []
162
+ require_paths:
163
+ - lib
164
+ required_ruby_version: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - ">="
167
+ - !ruby/object:Gem::Version
168
+ version: '0'
169
+ required_rubygems_version: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ requirements: []
175
+ rubyforge_project:
176
+ rubygems_version: 2.4.5.1
177
+ signing_key:
178
+ specification_version: 4
179
+ summary: Successor to Bender, based on Ferris
180
+ test_files: []
181
+ has_rdoc: