flapjack 0.9.3 → 0.9.4

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -25,7 +25,5 @@ tmp/spec*
25
25
  tmp/profiles
26
26
  flapjack-*.gem
27
27
  _site/*
28
- .rbenv-version
29
- .ruby-version
30
28
  .rvmrc
31
29
  artifacts/*
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 1.9.3-p125
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  ## Flapjack Changelog
2
2
 
3
+ # 0.9.4 - 2014-09-21
4
+ - Bug: crash in pagerduty gateway when failing entities have no id #593 (@jessereynolds)
5
+ - Bug: bugfix for updating rule data without contact_ids (no-issue) (@ali-graham)
6
+ - Bug: set key expiry properly for scheduled maintenance spanning current time #634 (@ali-graham)
7
+
3
8
  # 0.9.3 - 2014-07-28
4
9
  - Bug: fix outage reports in jsonapi c8e09f7 (@ali-graham)
5
10
  - Bug: fix maintenance reports in jsonapi 234ce48 (@ali-graham)
@@ -30,10 +30,23 @@ module Flapjack
30
30
  def self.for_event_id(event_id, options = {})
31
31
  raise "Redis connection not set" unless redis = options[:redis]
32
32
  entity_name, check_name = event_id.split(':', 2)
33
- create_entity = options[:create_entity]
34
33
  logger = options[:logger]
34
+ if logger
35
+ logger.debug("Flapjack::Data::EntityCheck#self.for_event_id called for event_id: #{event_id}")
36
+ logger.debug("Flapjack::Data::EntityCheck#self.for_event_id found entity_name: #{entity_name}, check_name: #{check_name}")
37
+ end
38
+ create_entity = options[:create_entity]
35
39
  entity = Flapjack::Data::Entity.find_by_name(entity_name,
36
40
  :create => create_entity, :logger => logger, :redis => redis)
41
+ if logger
42
+ logger.debug("Flapjack::Data::EntityCheck#self.for_event_id found entity by name(#{entity_name}): #{entity.inspect}")
43
+ end
44
+ unless entity
45
+ if logger
46
+ logger.error("entity can't be instantiated for #{entity_name}!")
47
+ end
48
+ return nil
49
+ end
37
50
  self.new(entity, check_name, :logger => logger, :redis => redis)
38
51
  end
39
52
 
@@ -114,12 +127,13 @@ module Flapjack
114
127
 
115
128
  def self.unacknowledged_failing(options = {})
116
129
  raise "Redis connection not set" unless redis = options[:redis]
130
+ logger = options[:logger]
117
131
 
118
132
  redis.zrange('failed_checks', '0', '-1').reject {|entity_check|
119
133
  redis.exists(entity_check + ':unscheduled_maintenance')
120
134
  }.collect {|entity_check|
121
- Flapjack::Data::EntityCheck.for_event_id(entity_check, :redis => redis)
122
- }
135
+ Flapjack::Data::EntityCheck.for_event_id(entity_check, :redis => redis, :logger => logger)
136
+ }.compact
123
137
  end
124
138
 
125
139
  def self.conflate_to_keys(entity_checks_hash)
@@ -315,8 +329,11 @@ module Flapjack
315
329
  # if multiple scheduled maintenances found, find the end_time furthest in the future
316
330
  most_futuristic = current_sched_ms.max {|sm| sm[:end_time] }
317
331
  start_time = most_futuristic[:start_time]
318
- duration = most_futuristic[:duration]
319
- @redis.setex("#{@key}:scheduled_maintenance", duration.to_i, start_time)
332
+
333
+ duration = most_futuristic[:end_time] - current_time
334
+ if duration > 0
335
+ @redis.setex("#{@key}:scheduled_maintenance", duration.to_i, start_time)
336
+ end
320
337
  end
321
338
 
322
339
  # TODO allow summary to be changed as part of the termination
@@ -165,7 +165,9 @@ module Flapjack
165
165
  logger.debug(params.inspect)
166
166
 
167
167
  rule = find_rule(params[:id])
168
- contact = find_contact(rule.contact_id)
168
+
169
+ # previous bug may have led to rule's contact_id being wiped :(
170
+ contact = find_contact(rule.contact_id || params[:contact_id])
169
171
 
170
172
  rule_data = hashify(:entities, :regex_entities, :tags, :regex_tags,
171
173
  :unknown_media, :warning_media, :critical_media, :time_restrictions,
@@ -178,7 +178,7 @@ module Flapjack
178
178
  def find_pagerduty_acknowledgements
179
179
  @logger.debug("looking for acks in pagerduty for unack'd problems")
180
180
 
181
- unacknowledged_failing_checks = Flapjack::Data::EntityCheck.unacknowledged_failing(:redis => @redis)
181
+ unacknowledged_failing_checks = Flapjack::Data::EntityCheck.unacknowledged_failing(:redis => @redis, :logger => @logger)
182
182
 
183
183
  @logger.debug "found unacknowledged failing checks as follows: " + unacknowledged_failing_checks.join(', ')
184
184
 
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  module Flapjack
4
- VERSION = "0.9.3"
4
+ VERSION = "0.9.4"
5
5
  end
6
6
 
@@ -233,7 +233,6 @@ describe Flapjack::Data::EntityCheck, :redis => true do
233
233
  expect(duration).to eq(half_an_hour)
234
234
  end
235
235
 
236
- # TODO this should probably enforce that it starts in the future
237
236
  it "creates a scheduled maintenance period covering the current time" do
238
237
  t = Time.now.to_i
239
238
  ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
@@ -255,6 +254,11 @@ describe Flapjack::Data::EntityCheck, :redis => true do
255
254
  expect(duration).not_to be_nil
256
255
  expect(duration).to be_a(Float)
257
256
  expect(duration).to eq(2 * (60 * 60))
257
+
258
+ ttl = @redis.ttl("#{name}:#{check}:scheduled_maintenance")
259
+ expect(ttl).to be_within(20).of(60 * 60) # 20-second margin of error,
260
+ # should be large enough for a slow machine running the test -- duration
261
+ # is considered to start at the begininng of the period
258
262
  end
259
263
 
260
264
  it "removes a scheduled maintenance period for a future time" do
@@ -318,6 +318,29 @@ describe 'Flapjack::Gateways::API::ContactMethods', :sinatra => true, :logger =>
318
318
  expect(last_response).to be_forbidden
319
319
  end
320
320
 
321
+ it 'uses the contact_id param if the rule being updated lacks that data' do
322
+ nr_nil = double(Flapjack::Data::NotificationRule, :id => '1', :contact_id => nil)
323
+
324
+ expect(Flapjack::Data::Contact).to receive(:find_by_id).
325
+ with(contact.id, {:redis => redis, :logger => @logger}).and_return(contact)
326
+ expect(nr_nil).to receive(:to_json).and_return('"rule_1"')
327
+ expect(Flapjack::Data::NotificationRule).to receive(:find_by_id).
328
+ with(nr_nil.id, {:redis => redis, :logger => @logger}).and_return(nr_nil)
329
+
330
+ # symbolize the keys
331
+ notification_rule_data_sym = notification_rule_data.inject({}){|memo,(k,v)|
332
+ memo[k.to_sym] = v; memo
333
+ }
334
+ notification_rule_data_sym.delete(:contact_id)
335
+
336
+ expect(nr_nil).to receive(:update).with(notification_rule_data_sym, :logger => @logger).and_return(nil)
337
+
338
+ aput "/notification_rules/#{nr_nil.id}", notification_rule_data.to_json,
339
+ {'CONTENT_TYPE' => 'application/json'}
340
+ expect(last_response).to be_ok
341
+ expect(last_response.body).to eq('"rule_1"')
342
+ end
343
+
321
344
  # DELETE /notification_rules/RULE_ID
322
345
  it "deletes a notification rule" do
323
346
  expect(notification_rule).to receive(:contact_id).and_return(contact.id)
metadata CHANGED
@@ -1,7 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flapjack
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.3
4
+ version: 0.9.4
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Lindsay Holmwood
@@ -10,314 +11,358 @@ authors:
10
11
  autorequire:
11
12
  bindir: bin
12
13
  cert_chain: []
13
- date: 2014-07-28 00:00:00.000000000 Z
14
+ date: 2014-09-22 00:00:00.000000000 Z
14
15
  dependencies:
15
16
  - !ruby/object:Gem::Dependency
16
17
  name: dante
17
18
  requirement: !ruby/object:Gem::Requirement
19
+ none: false
18
20
  requirements:
19
- - - ">="
21
+ - - ! '>='
20
22
  - !ruby/object:Gem::Version
21
23
  version: '0'
22
24
  type: :runtime
23
25
  prerelease: false
24
26
  version_requirements: !ruby/object:Gem::Requirement
27
+ none: false
25
28
  requirements:
26
- - - ">="
29
+ - - ! '>='
27
30
  - !ruby/object:Gem::Version
28
31
  version: '0'
29
32
  - !ruby/object:Gem::Dependency
30
33
  name: oj
31
34
  requirement: !ruby/object:Gem::Requirement
35
+ none: false
32
36
  requirements:
33
- - - ">="
37
+ - - ! '>='
34
38
  - !ruby/object:Gem::Version
35
39
  version: 2.9.0
36
40
  type: :runtime
37
41
  prerelease: false
38
42
  version_requirements: !ruby/object:Gem::Requirement
43
+ none: false
39
44
  requirements:
40
- - - ">="
45
+ - - ! '>='
41
46
  - !ruby/object:Gem::Version
42
47
  version: 2.9.0
43
48
  - !ruby/object:Gem::Dependency
44
49
  name: eventmachine
45
50
  requirement: !ruby/object:Gem::Requirement
51
+ none: false
46
52
  requirements:
47
- - - "~>"
53
+ - - ~>
48
54
  - !ruby/object:Gem::Version
49
55
  version: 1.0.0
50
56
  type: :runtime
51
57
  prerelease: false
52
58
  version_requirements: !ruby/object:Gem::Requirement
59
+ none: false
53
60
  requirements:
54
- - - "~>"
61
+ - - ~>
55
62
  - !ruby/object:Gem::Version
56
63
  version: 1.0.0
57
64
  - !ruby/object:Gem::Dependency
58
65
  name: hiredis
59
66
  requirement: !ruby/object:Gem::Requirement
67
+ none: false
60
68
  requirements:
61
- - - ">="
69
+ - - ! '>='
62
70
  - !ruby/object:Gem::Version
63
71
  version: '0'
64
72
  type: :runtime
65
73
  prerelease: false
66
74
  version_requirements: !ruby/object:Gem::Requirement
75
+ none: false
67
76
  requirements:
68
- - - ">="
77
+ - - ! '>='
69
78
  - !ruby/object:Gem::Version
70
79
  version: '0'
71
80
  - !ruby/object:Gem::Dependency
72
81
  name: em-synchrony
73
82
  requirement: !ruby/object:Gem::Requirement
83
+ none: false
74
84
  requirements:
75
- - - "~>"
85
+ - - ~>
76
86
  - !ruby/object:Gem::Version
77
87
  version: 1.0.2
78
88
  type: :runtime
79
89
  prerelease: false
80
90
  version_requirements: !ruby/object:Gem::Requirement
91
+ none: false
81
92
  requirements:
82
- - - "~>"
93
+ - - ~>
83
94
  - !ruby/object:Gem::Version
84
95
  version: 1.0.2
85
96
  - !ruby/object:Gem::Dependency
86
97
  name: em-http-request
87
98
  requirement: !ruby/object:Gem::Requirement
99
+ none: false
88
100
  requirements:
89
- - - ">="
101
+ - - ! '>='
90
102
  - !ruby/object:Gem::Version
91
103
  version: '0'
92
104
  type: :runtime
93
105
  prerelease: false
94
106
  version_requirements: !ruby/object:Gem::Requirement
107
+ none: false
95
108
  requirements:
96
- - - ">="
109
+ - - ! '>='
97
110
  - !ruby/object:Gem::Version
98
111
  version: '0'
99
112
  - !ruby/object:Gem::Dependency
100
113
  name: redis
101
114
  requirement: !ruby/object:Gem::Requirement
115
+ none: false
102
116
  requirements:
103
- - - "~>"
117
+ - - ~>
104
118
  - !ruby/object:Gem::Version
105
119
  version: 3.0.6
106
120
  type: :runtime
107
121
  prerelease: false
108
122
  version_requirements: !ruby/object:Gem::Requirement
123
+ none: false
109
124
  requirements:
110
- - - "~>"
125
+ - - ~>
111
126
  - !ruby/object:Gem::Version
112
127
  version: 3.0.6
113
128
  - !ruby/object:Gem::Dependency
114
129
  name: em-resque
115
130
  requirement: !ruby/object:Gem::Requirement
131
+ none: false
116
132
  requirements:
117
- - - ">="
133
+ - - ! '>='
118
134
  - !ruby/object:Gem::Version
119
135
  version: '0'
120
136
  type: :runtime
121
137
  prerelease: false
122
138
  version_requirements: !ruby/object:Gem::Requirement
139
+ none: false
123
140
  requirements:
124
- - - ">="
141
+ - - ! '>='
125
142
  - !ruby/object:Gem::Version
126
143
  version: '0'
127
144
  - !ruby/object:Gem::Dependency
128
145
  name: resque
129
146
  requirement: !ruby/object:Gem::Requirement
147
+ none: false
130
148
  requirements:
131
- - - "~>"
149
+ - - ~>
132
150
  - !ruby/object:Gem::Version
133
151
  version: 1.23.0
134
152
  type: :runtime
135
153
  prerelease: false
136
154
  version_requirements: !ruby/object:Gem::Requirement
155
+ none: false
137
156
  requirements:
138
- - - "~>"
157
+ - - ~>
139
158
  - !ruby/object:Gem::Version
140
159
  version: 1.23.0
141
160
  - !ruby/object:Gem::Dependency
142
161
  name: sinatra
143
162
  requirement: !ruby/object:Gem::Requirement
163
+ none: false
144
164
  requirements:
145
- - - ">="
165
+ - - ! '>='
146
166
  - !ruby/object:Gem::Version
147
167
  version: '0'
148
168
  type: :runtime
149
169
  prerelease: false
150
170
  version_requirements: !ruby/object:Gem::Requirement
171
+ none: false
151
172
  requirements:
152
- - - ">="
173
+ - - ! '>='
153
174
  - !ruby/object:Gem::Version
154
175
  version: '0'
155
176
  - !ruby/object:Gem::Dependency
156
177
  name: rack-fiber_pool
157
178
  requirement: !ruby/object:Gem::Requirement
179
+ none: false
158
180
  requirements:
159
- - - ">="
181
+ - - ! '>='
160
182
  - !ruby/object:Gem::Version
161
183
  version: '0'
162
184
  type: :runtime
163
185
  prerelease: false
164
186
  version_requirements: !ruby/object:Gem::Requirement
187
+ none: false
165
188
  requirements:
166
- - - ">="
189
+ - - ! '>='
167
190
  - !ruby/object:Gem::Version
168
191
  version: '0'
169
192
  - !ruby/object:Gem::Dependency
170
193
  name: thin
171
194
  requirement: !ruby/object:Gem::Requirement
195
+ none: false
172
196
  requirements:
173
- - - "~>"
197
+ - - ~>
174
198
  - !ruby/object:Gem::Version
175
199
  version: 1.6.1
176
200
  type: :runtime
177
201
  prerelease: false
178
202
  version_requirements: !ruby/object:Gem::Requirement
203
+ none: false
179
204
  requirements:
180
- - - "~>"
205
+ - - ~>
181
206
  - !ruby/object:Gem::Version
182
207
  version: 1.6.1
183
208
  - !ruby/object:Gem::Dependency
184
209
  name: mail
185
210
  requirement: !ruby/object:Gem::Requirement
211
+ none: false
186
212
  requirements:
187
- - - ">="
213
+ - - ! '>='
188
214
  - !ruby/object:Gem::Version
189
215
  version: '0'
190
216
  type: :runtime
191
217
  prerelease: false
192
218
  version_requirements: !ruby/object:Gem::Requirement
219
+ none: false
193
220
  requirements:
194
- - - ">="
221
+ - - ! '>='
195
222
  - !ruby/object:Gem::Version
196
223
  version: '0'
197
224
  - !ruby/object:Gem::Dependency
198
225
  name: blather
199
226
  requirement: !ruby/object:Gem::Requirement
227
+ none: false
200
228
  requirements:
201
- - - "~>"
229
+ - - ~>
202
230
  - !ruby/object:Gem::Version
203
231
  version: 0.8.3
204
232
  type: :runtime
205
233
  prerelease: false
206
234
  version_requirements: !ruby/object:Gem::Requirement
235
+ none: false
207
236
  requirements:
208
- - - "~>"
237
+ - - ~>
209
238
  - !ruby/object:Gem::Version
210
239
  version: 0.8.3
211
240
  - !ruby/object:Gem::Dependency
212
241
  name: chronic
213
242
  requirement: !ruby/object:Gem::Requirement
243
+ none: false
214
244
  requirements:
215
- - - ">="
245
+ - - ! '>='
216
246
  - !ruby/object:Gem::Version
217
247
  version: '0'
218
248
  type: :runtime
219
249
  prerelease: false
220
250
  version_requirements: !ruby/object:Gem::Requirement
251
+ none: false
221
252
  requirements:
222
- - - ">="
253
+ - - ! '>='
223
254
  - !ruby/object:Gem::Version
224
255
  version: '0'
225
256
  - !ruby/object:Gem::Dependency
226
257
  name: chronic_duration
227
258
  requirement: !ruby/object:Gem::Requirement
259
+ none: false
228
260
  requirements:
229
- - - ">="
261
+ - - ! '>='
230
262
  - !ruby/object:Gem::Version
231
263
  version: '0'
232
264
  type: :runtime
233
265
  prerelease: false
234
266
  version_requirements: !ruby/object:Gem::Requirement
267
+ none: false
235
268
  requirements:
236
- - - ">="
269
+ - - ! '>='
237
270
  - !ruby/object:Gem::Version
238
271
  version: '0'
239
272
  - !ruby/object:Gem::Dependency
240
273
  name: activesupport
241
274
  requirement: !ruby/object:Gem::Requirement
275
+ none: false
242
276
  requirements:
243
- - - "~>"
277
+ - - ~>
244
278
  - !ruby/object:Gem::Version
245
279
  version: 3.2.14
246
280
  type: :runtime
247
281
  prerelease: false
248
282
  version_requirements: !ruby/object:Gem::Requirement
283
+ none: false
249
284
  requirements:
250
- - - "~>"
285
+ - - ~>
251
286
  - !ruby/object:Gem::Version
252
287
  version: 3.2.14
253
288
  - !ruby/object:Gem::Dependency
254
289
  name: ice_cube
255
290
  requirement: !ruby/object:Gem::Requirement
291
+ none: false
256
292
  requirements:
257
- - - ">="
293
+ - - ! '>='
258
294
  - !ruby/object:Gem::Version
259
295
  version: '0'
260
296
  type: :runtime
261
297
  prerelease: false
262
298
  version_requirements: !ruby/object:Gem::Requirement
299
+ none: false
263
300
  requirements:
264
- - - ">="
301
+ - - ! '>='
265
302
  - !ruby/object:Gem::Version
266
303
  version: '0'
267
304
  - !ruby/object:Gem::Dependency
268
305
  name: tzinfo
269
306
  requirement: !ruby/object:Gem::Requirement
307
+ none: false
270
308
  requirements:
271
- - - "~>"
309
+ - - ~>
272
310
  - !ruby/object:Gem::Version
273
311
  version: 1.0.1
274
312
  type: :runtime
275
313
  prerelease: false
276
314
  version_requirements: !ruby/object:Gem::Requirement
315
+ none: false
277
316
  requirements:
278
- - - "~>"
317
+ - - ~>
279
318
  - !ruby/object:Gem::Version
280
319
  version: 1.0.1
281
320
  - !ruby/object:Gem::Dependency
282
321
  name: tzinfo-data
283
322
  requirement: !ruby/object:Gem::Requirement
323
+ none: false
284
324
  requirements:
285
- - - ">="
325
+ - - ! '>='
286
326
  - !ruby/object:Gem::Version
287
327
  version: '0'
288
328
  type: :runtime
289
329
  prerelease: false
290
330
  version_requirements: !ruby/object:Gem::Requirement
331
+ none: false
291
332
  requirements:
292
- - - ">="
333
+ - - ! '>='
293
334
  - !ruby/object:Gem::Version
294
335
  version: '0'
295
336
  - !ruby/object:Gem::Dependency
296
337
  name: rbtrace
297
338
  requirement: !ruby/object:Gem::Requirement
339
+ none: false
298
340
  requirements:
299
- - - ">="
341
+ - - ! '>='
300
342
  - !ruby/object:Gem::Version
301
343
  version: '0'
302
344
  type: :runtime
303
345
  prerelease: false
304
346
  version_requirements: !ruby/object:Gem::Requirement
347
+ none: false
305
348
  requirements:
306
- - - ">="
349
+ - - ! '>='
307
350
  - !ruby/object:Gem::Version
308
351
  version: '0'
309
352
  - !ruby/object:Gem::Dependency
310
353
  name: rake
311
354
  requirement: !ruby/object:Gem::Requirement
355
+ none: false
312
356
  requirements:
313
- - - ">="
357
+ - - ! '>='
314
358
  - !ruby/object:Gem::Version
315
359
  version: '0'
316
360
  type: :runtime
317
361
  prerelease: false
318
362
  version_requirements: !ruby/object:Gem::Requirement
363
+ none: false
319
364
  requirements:
320
- - - ">="
365
+ - - ! '>='
321
366
  - !ruby/object:Gem::Version
322
367
  version: '0'
323
368
  description: Flapjack is a distributed monitoring notification system that provides
@@ -336,10 +381,11 @@ executables:
336
381
  extensions: []
337
382
  extra_rdoc_files: []
338
383
  files:
339
- - ".gitignore"
340
- - ".gitmodules"
341
- - ".rspec"
342
- - ".travis.yml"
384
+ - .gitignore
385
+ - .gitmodules
386
+ - .rspec
387
+ - .ruby-version
388
+ - .travis.yml
343
389
  - CHANGELOG.md
344
390
  - CONTRIBUTING.md
345
391
  - Gemfile
@@ -575,26 +621,33 @@ files:
575
621
  homepage: http://flapjack.io/
576
622
  licenses:
577
623
  - MIT
578
- metadata: {}
579
624
  post_install_message:
580
625
  rdoc_options: []
581
626
  require_paths:
582
627
  - lib
583
628
  required_ruby_version: !ruby/object:Gem::Requirement
629
+ none: false
584
630
  requirements:
585
- - - ">="
631
+ - - ! '>='
586
632
  - !ruby/object:Gem::Version
587
633
  version: '0'
634
+ segments:
635
+ - 0
636
+ hash: 1556922316348718500
588
637
  required_rubygems_version: !ruby/object:Gem::Requirement
638
+ none: false
589
639
  requirements:
590
- - - ">="
640
+ - - ! '>='
591
641
  - !ruby/object:Gem::Version
592
642
  version: '0'
643
+ segments:
644
+ - 0
645
+ hash: 1556922316348718500
593
646
  requirements: []
594
647
  rubyforge_project:
595
- rubygems_version: 2.2.2
648
+ rubygems_version: 1.8.23
596
649
  signing_key:
597
- specification_version: 4
650
+ specification_version: 3
598
651
  summary: Intelligent, scalable, distributed monitoring notification system.
599
652
  test_files:
600
653
  - features/cli.feature
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: 0d000bd7c6fe6166ad1c537c91fe4c53ddc85d9b
4
- data.tar.gz: 678ebf7c7d42fbe6041d28df1748c8e607674d0a
5
- SHA512:
6
- metadata.gz: a0b577918c1f294d4c7619e5e97f025cd8b5d9bf7e1b144efdcedfc58f0d05e188a8394cd9b51b334e82cf5ff7a508ff20316a02c26e60b140426b3ef633d804
7
- data.tar.gz: 5acab492158136c9a110435c53c07480b8ea390beaea33ae31336b397611e0f576f270940639f9737907dd90e7261f6484a9629681303f111f1e448bf2540f83