2018-12-04 05:57:11 +00:00
// Statping
2018-08-16 06:22:20 +00:00
// Copyright (C) 2018. Hunter Long and the project contributors
// Written by Hunter Long <info@socialeck.com> and the project contributors
//
2018-12-04 04:17:29 +00:00
// https://github.com/hunterlong/statping
2018-08-16 06:22:20 +00:00
//
// The licenses for most software and other practical works are designed
// to take away your freedom to share and change the works. By contrast,
// the GNU General Public License is intended to guarantee your freedom to
// share and change all versions of a program--to make sure it remains free
// software for all its users.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
2018-06-30 00:57:05 +00:00
package core
2018-06-22 06:56:44 +00:00
import (
"fmt"
2018-10-06 09:50:39 +00:00
"github.com/ararog/timeago"
2018-12-04 04:17:29 +00:00
"github.com/hunterlong/statping/types"
"github.com/hunterlong/statping/utils"
2018-12-06 19:03:55 +00:00
"sort"
2018-06-22 06:56:44 +00:00
"time"
)
2018-10-07 22:38:56 +00:00
type Checkin struct {
2018-08-20 07:20:05 +00:00
* types . Checkin
}
2018-06-30 00:57:05 +00:00
2018-11-21 03:39:40 +00:00
type CheckinHit struct {
2018-10-03 10:47:32 +00:00
* types . CheckinHit
}
2018-12-06 19:03:55 +00:00
// Select returns a *types.Checkin
func ( c * Checkin ) Select ( ) * types . Checkin {
return c . Checkin
}
2018-10-07 22:38:56 +00:00
// Routine for checking if the last Checkin was within its interval
func ( c * Checkin ) Routine ( ) {
if c . Last ( ) == nil {
return
}
reCheck := c . Period ( )
CheckinLoop :
for {
select {
case <- c . Running :
2018-10-08 04:00:57 +00:00
utils . Log ( 1 , fmt . Sprintf ( "Stopping checkin routine: %v" , c . Name ) )
2018-12-06 19:03:55 +00:00
c . Failing = false
2018-10-07 22:38:56 +00:00
break CheckinLoop
case <- time . After ( reCheck ) :
2018-10-08 04:00:57 +00:00
utils . Log ( 1 , fmt . Sprintf ( "Checkin %v is expected at %v, checking every %v" , c . Name , utils . FormatDuration ( c . Expected ( ) ) , utils . FormatDuration ( c . Period ( ) ) ) )
2018-10-07 22:38:56 +00:00
if c . Expected ( ) . Seconds ( ) <= 0 {
2018-10-08 04:00:57 +00:00
issue := fmt . Sprintf ( "Checkin %v is failing, no request since %v" , c . Name , c . Last ( ) . CreatedAt )
2018-10-07 22:38:56 +00:00
utils . Log ( 3 , issue )
c . CreateFailure ( )
}
reCheck = c . Period ( )
}
continue
}
}
// String will return a Checkin API string
func ( c * Checkin ) String ( ) string {
2018-10-02 07:26:49 +00:00
return c . ApiKey
2018-06-30 00:57:05 +00:00
}
2018-10-07 22:38:56 +00:00
// ReturnCheckin converts *types.Checking to *core.Checkin
func ReturnCheckin ( c * types . Checkin ) * Checkin {
return & Checkin { Checkin : c }
2018-08-20 07:20:05 +00:00
}
2018-10-07 04:48:33 +00:00
// ReturnCheckinHit converts *types.checkinHit to *core.checkinHit
2018-11-21 03:39:40 +00:00
func ReturnCheckinHit ( c * types . CheckinHit ) * CheckinHit {
return & CheckinHit { CheckinHit : c }
2018-10-03 10:47:32 +00:00
}
2018-10-07 22:38:56 +00:00
func ( c * Checkin ) Service ( ) * Service {
2018-12-06 19:03:55 +00:00
return SelectService ( c . ServiceId )
2018-10-07 22:38:56 +00:00
}
func ( c * Checkin ) CreateFailure ( ) ( int64 , error ) {
service := c . Service ( )
2018-12-06 19:03:55 +00:00
c . Failing = true
fail := & Failure { & types . Failure {
2018-10-07 22:38:56 +00:00
Issue : fmt . Sprintf ( "Checkin %v was not reported %v ago, it expects a request every %v" , c . Name , utils . FormatDuration ( c . Expected ( ) ) , utils . FormatDuration ( c . Period ( ) ) ) ,
Method : "checkin" ,
MethodId : c . Id ,
Service : service . Id ,
2018-12-06 19:03:55 +00:00
Checkin : c . Id ,
PingTime : c . Expected ( ) . Seconds ( ) ,
} }
2018-10-07 22:38:56 +00:00
row := failuresDB ( ) . Create ( & fail )
2018-12-06 19:03:55 +00:00
sort . Sort ( types . FailSort ( c . Failures ) )
c . Failures = append ( c . Failures , fail )
if len ( c . Failures ) > limitedFailures {
c . Failures = c . Failures [ 1 : ]
}
2018-10-07 22:38:56 +00:00
return fail . Id , row . Error
}
2018-12-06 19:03:55 +00:00
// LimitedHits will return the last amount of successful hits from a checkin
func ( c * Checkin ) LimitedHits ( amount int64 ) [ ] * types . CheckinHit {
var hits [ ] * types . CheckinHit
checkinHitsDB ( ) . Where ( "checkin = ?" , c . Id ) . Order ( "id desc" ) . Limit ( amount ) . Find ( & hits )
return hits
}
2018-11-16 16:42:49 +00:00
// AllCheckins returns all checkin in system
func AllCheckins ( ) [ ] * Checkin {
var checkins [ ] * Checkin
checkinDB ( ) . Find ( & checkins )
return checkins
}
2018-10-07 22:38:56 +00:00
// SelectCheckin will find a Checkin based on the API supplied
func SelectCheckin ( api string ) * Checkin {
2018-12-06 19:03:55 +00:00
for _ , s := range Services ( ) {
for _ , c := range s . Select ( ) . Checkins {
if c . Select ( ) . ApiKey == api {
return c . ( * Checkin )
}
}
}
return nil
2018-10-07 22:38:56 +00:00
}
// Period will return the duration of the Checkin interval
func ( c * Checkin ) Period ( ) time . Duration {
2018-10-07 04:48:33 +00:00
duration , _ := time . ParseDuration ( fmt . Sprintf ( "%vs" , c . Interval ) )
2018-10-04 08:18:55 +00:00
return duration
}
2018-10-07 22:38:56 +00:00
// Grace will return the duration of the Checkin Grace Period (after service hasn't responded, wait a bit for a response)
func ( c * Checkin ) Grace ( ) time . Duration {
2018-10-07 04:48:33 +00:00
duration , _ := time . ParseDuration ( fmt . Sprintf ( "%vs" , c . GracePeriod ) )
2018-10-04 08:18:55 +00:00
return duration
}
2018-10-07 22:38:56 +00:00
// Expected returns the duration of when the serviec should receive a Checkin
func ( c * Checkin ) Expected ( ) time . Duration {
2018-10-07 04:48:33 +00:00
last := c . Last ( ) . CreatedAt
2018-10-04 08:18:55 +00:00
now := time . Now ( )
lastDir := now . Sub ( last )
2018-10-07 04:48:33 +00:00
sub := time . Duration ( c . Period ( ) - lastDir )
2018-10-04 08:18:55 +00:00
return sub
}
2018-10-07 22:38:56 +00:00
// Last returns the last checkinHit for a Checkin
2018-11-21 03:39:40 +00:00
func ( c * Checkin ) Last ( ) * CheckinHit {
var hit CheckinHit
2018-10-07 04:48:33 +00:00
checkinHitsDB ( ) . Where ( "checkin = ?" , c . Id ) . Last ( & hit )
2018-10-07 22:38:56 +00:00
return & hit
2018-06-22 06:56:44 +00:00
}
2018-10-07 22:38:56 +00:00
func ( c * Checkin ) Link ( ) string {
return fmt . Sprintf ( "%v/checkin/%v" , CoreApp . Domain , c . ApiKey )
}
2018-11-21 03:39:40 +00:00
// AllHits returns all of the CheckinHits for a given Checkin
func ( c * Checkin ) AllHits ( ) [ ] * types . CheckinHit {
var checkins [ ] * types . CheckinHit
2018-10-07 04:48:33 +00:00
checkinHitsDB ( ) . Where ( "checkin = ?" , c . Id ) . Order ( "id DESC" ) . Find ( & checkins )
2018-10-03 10:47:32 +00:00
return checkins
}
2018-12-06 19:03:55 +00:00
// Hits returns all of the CheckinHits for a given Checkin
func ( c * Checkin ) LimitedFailures ( amount int64 ) [ ] types . FailureInterface {
var failures [ ] * Failure
var failInterfaces [ ] types . FailureInterface
col := failuresDB ( ) . Where ( "checkin = ?" , c . Id ) . Where ( "method = 'checkin'" ) . Limit ( amount ) . Order ( "id desc" )
col . Find ( & failures )
for _ , f := range failures {
failInterfaces = append ( failInterfaces , f )
}
return failInterfaces
}
2018-11-21 03:39:40 +00:00
// Hits returns all of the CheckinHits for a given Checkin
func ( c * Checkin ) AllFailures ( ) [ ] * types . Failure {
var failures [ ] * types . Failure
col := failuresDB ( ) . Where ( "checkin = ?" , c . Id ) . Where ( "method = 'checkin'" ) . Order ( "id desc" )
col . Find ( & failures )
return failures
}
2018-10-07 22:38:56 +00:00
// Create will create a new Checkin
func ( c * Checkin ) Delete ( ) error {
c . Close ( )
2019-01-10 16:47:01 +00:00
i := c . index ( )
service := c . Service ( )
slice := service . Checkins
service . Checkins = append ( slice [ : i ] , slice [ i + 1 : ] ... )
2018-10-07 22:38:56 +00:00
row := checkinDB ( ) . Delete ( & c )
return row . Error
}
2019-01-10 16:47:01 +00:00
// index returns a checkin index int for updating the *checkin.Service slice
func ( c * Checkin ) index ( ) int {
for k , checkin := range c . Service ( ) . Checkins {
if c . Id == checkin . Select ( ) . Id {
return k
}
}
return 0
}
2018-10-07 22:38:56 +00:00
// Create will create a new Checkin
func ( c * Checkin ) Create ( ) ( int64 , error ) {
2018-10-07 04:48:33 +00:00
c . ApiKey = utils . RandomString ( 7 )
row := checkinDB ( ) . Create ( & c )
2018-10-06 03:35:53 +00:00
if row . Error != nil {
utils . Log ( 2 , row . Error )
return 0 , row . Error
2018-10-04 08:18:55 +00:00
}
2018-12-06 19:03:55 +00:00
service := SelectService ( c . ServiceId )
service . Checkins = append ( service . Checkins , c )
c . Start ( )
go c . Routine ( )
2018-10-07 04:48:33 +00:00
return c . Id , row . Error
2018-10-06 03:35:53 +00:00
}
2018-10-07 22:38:56 +00:00
// Update will update a Checkin
func ( c * Checkin ) Update ( ) ( int64 , error ) {
2018-11-21 04:29:21 +00:00
row := checkinDB ( ) . Update ( & c )
2018-10-05 04:28:38 +00:00
if row . Error != nil {
2018-09-05 10:54:57 +00:00
utils . Log ( 2 , row . Error )
return 0 , row . Error
2018-06-22 06:56:44 +00:00
}
2018-10-07 05:04:06 +00:00
return c . Id , row . Error
2018-06-22 06:56:44 +00:00
}
2018-10-07 04:48:33 +00:00
// Create will create a new successful checkinHit
2018-11-21 03:39:40 +00:00
func ( c * CheckinHit ) Create ( ) ( int64 , error ) {
2018-10-07 05:04:06 +00:00
if c . CreatedAt . IsZero ( ) {
c . CreatedAt = time . Now ( )
2018-10-04 08:18:55 +00:00
}
2018-11-21 04:29:21 +00:00
row := checkinHitsDB ( ) . Create ( & c )
2018-10-05 06:04:26 +00:00
if row . Error != nil {
2018-10-03 10:47:32 +00:00
utils . Log ( 2 , row . Error )
return 0 , row . Error
}
2018-10-07 05:04:06 +00:00
return c . Id , row . Error
2018-10-03 10:47:32 +00:00
}
2018-10-07 04:48:33 +00:00
// Ago returns the duration of time between now and the last successful checkinHit
2018-11-21 03:39:40 +00:00
func ( c * CheckinHit ) Ago ( ) string {
2018-10-07 05:04:06 +00:00
got , _ := timeago . TimeAgoWithTime ( time . Now ( ) , c . CreatedAt )
2018-10-06 09:50:39 +00:00
return got
}
2018-10-07 22:38:56 +00:00
// RecheckCheckinFailure will check if a Service Checkin has been reported yet
func ( c * Checkin ) RecheckCheckinFailure ( guard chan struct { } ) {
2018-10-02 07:26:49 +00:00
between := time . Now ( ) . Sub ( time . Now ( ) ) . Seconds ( )
2018-06-22 06:56:44 +00:00
if between > float64 ( c . Interval ) {
fmt . Println ( "rechecking every 15 seconds!" )
time . Sleep ( 15 * time . Second )
guard <- struct { } { }
c . RecheckCheckinFailure ( guard )
} else {
fmt . Println ( "i recovered!!" )
}
<- guard
}