2024-08-21 10:58:24 +02:00
package tools
import (
2024-08-21 15:32:10 +02:00
"encoding/json"
2024-08-21 11:23:07 +02:00
"errors"
2024-11-06 15:42:06 +01:00
"fmt"
2024-10-17 14:14:04 +02:00
"strings"
2024-08-21 11:23:07 +02:00
2024-09-04 10:53:12 +02:00
"cloud.o-forge.io/core/oc-lib/config"
2024-08-21 10:58:24 +02:00
"cloud.o-forge.io/core/oc-lib/dbs/mongo"
2024-10-17 13:53:57 +02:00
beego "github.com/beego/beego/v2/server/web"
2024-08-21 10:58:24 +02:00
)
2024-08-30 14:50:48 +02:00
/ *
* API is the Health Check API
* it defines the health check methods
* /
var UncatchedError = [ ] error { } // Singleton instance of the api 500 error cache
2024-08-21 10:58:24 +02:00
type State int
2024-08-30 14:50:48 +02:00
// State is an enum that defines the state of the API
2024-08-21 10:58:24 +02:00
const (
2024-08-30 14:50:48 +02:00
ALIVE State = iota
REDUCED_SERVICE // occurs when some services are down
UNPROCESSABLE_ENTITY // occurs when the database is up but the collections are not
DB_FALLOUT // occurs when the database is down
TEAPOT // well some things boils in here, i'm probably a teapot, occurs when uncatched errors are present (it's fun)
DEAD // occurs when the peer is dead
2024-08-21 10:58:24 +02:00
)
2024-08-30 14:50:48 +02:00
// EnumIndex returns the index of the enum
2024-08-23 09:01:28 +02:00
func ( s State ) EnumIndex ( ) int {
return int ( s )
}
2024-08-30 14:50:48 +02:00
// ToState returns the state from a string
2024-08-21 15:46:16 +02:00
func ToState ( str string ) State {
2024-08-30 14:50:48 +02:00
for _ , s := range [ ] State { ALIVE , REDUCED_SERVICE , UNPROCESSABLE_ENTITY , DB_FALLOUT , TEAPOT , DEAD } {
2024-08-21 15:46:16 +02:00
if s . String ( ) == str {
return s
}
}
return DEAD
}
2024-08-30 14:50:48 +02:00
// String returns the string of the enum
2024-08-21 10:58:24 +02:00
func ( s State ) String ( ) string {
return [ ... ] string { "alive" , "reduced service" , "unprocessable entity" , "database fallout" ,
2024-08-30 14:50:48 +02:00
"some things boils in here, i'm probably a teapot" , "dead" } [ s ]
2024-08-21 10:58:24 +02:00
}
2024-08-22 13:11:21 +02:00
type API struct { }
2024-08-21 10:58:24 +02:00
2024-10-17 16:05:00 +02:00
func ( a * API ) Discovered ( infos [ ] * beego . ControllerInfo ) {
2024-10-17 14:14:04 +02:00
respondToDiscovery := func ( m map [ string ] interface { } ) {
if len ( m ) == 0 {
2024-10-17 16:05:00 +02:00
a . SubscribeRouter ( infos )
2024-10-17 14:14:04 +02:00
}
}
a . ListenRouter ( respondToDiscovery )
2024-10-17 16:05:00 +02:00
a . SubscribeRouter ( infos )
2024-10-17 14:14:04 +02:00
}
2024-08-30 14:50:48 +02:00
// GetState returns the state of the API
2024-08-21 11:23:07 +02:00
func ( a * API ) GetState ( ) ( State , int , error ) {
2024-08-21 10:58:24 +02:00
// Check if the database is up
2024-09-04 10:53:12 +02:00
err := mongo . MONGOService . TestDB ( config . GetConfig ( ) )
2024-08-21 10:58:24 +02:00
if err != nil {
2024-08-30 14:50:48 +02:00
return DB_FALLOUT , 200 , err // If the database is not up, return database fallout
2024-08-21 10:58:24 +02:00
}
2024-09-04 10:53:12 +02:00
err = mongo . MONGOService . TestCollections ( config . GetConfig ( ) , [ ] string { } ) // Check if the collections are up
2024-08-21 10:58:24 +02:00
if err != nil {
2024-08-30 14:50:48 +02:00
return UNPROCESSABLE_ENTITY , 200 , err // If the collections are not up, return unprocessable entity
2024-08-21 10:58:24 +02:00
}
2024-08-30 14:50:48 +02:00
if len ( UncatchedError ) > 0 { // If there are uncatched errors, return teapot
2024-08-21 11:23:07 +02:00
errStr := ""
for _ , e := range UncatchedError {
errStr += e . Error ( ) + "\n"
}
return TEAPOT , 200 , errors . New ( errStr )
2024-08-21 10:58:24 +02:00
}
2024-08-30 14:50:48 +02:00
return ALIVE , 200 , nil // If everything is up, return alive
2024-08-21 10:58:24 +02:00
}
2024-08-21 11:28:13 +02:00
2024-10-17 13:53:57 +02:00
func ( a * API ) ListenRouter ( exec func ( msg map [ string ] interface { } ) ) {
2024-10-17 14:14:04 +02:00
go NewNATSCaller ( ) . ListenNats ( DISCOVERY . GenerateKey ( "api" ) , exec )
2024-10-17 13:53:57 +02:00
}
2024-10-17 16:04:07 +02:00
func ( a * API ) SubscribeRouter ( infos [ ] * beego . ControllerInfo ) {
2024-10-17 13:53:57 +02:00
nats := NewNATSCaller ( )
2024-11-20 10:39:20 +01:00
discovery := map [ string ] [ ] string { }
2024-10-17 16:04:07 +02:00
for _ , info := range infos {
2024-11-20 10:39:20 +01:00
path := strings . ReplaceAll ( info . GetPattern ( ) , "/oc/" , "/" + strings . ReplaceAll ( config . GetAppName ( ) , "oc-" , "" ) )
2024-10-22 14:27:52 +02:00
for k , v := range info . GetMethod ( ) {
2024-11-20 10:39:20 +01:00
if discovery [ path ] == nil {
discovery [ path ] = [ ] string { }
}
2024-10-22 14:27:52 +02:00
if strings . Contains ( strings . ToLower ( v ) , "internal" ) {
2024-11-20 10:39:20 +01:00
discovery [ path ] = append ( discovery [ path ] , "INTERNAL" + k )
2024-10-22 14:27:52 +02:00
} else {
2024-11-20 10:39:20 +01:00
discovery [ path ] = append ( discovery [ path ] , k )
2024-10-22 14:27:52 +02:00
}
2024-10-17 13:53:57 +02:00
}
}
nats . SetNATSPub ( "api" , DISCOVERY , discovery )
}
2024-08-30 14:50:48 +02:00
// CheckRemotePeer checks the state of a remote peer
2024-08-23 09:01:28 +02:00
func ( a * API ) CheckRemotePeer ( url string ) ( State , map [ string ] int ) {
2024-08-21 15:46:16 +02:00
// Check if the database is up
2024-10-02 10:45:52 +02:00
caller := NewHTTPCaller ( map [ DataType ] map [ METHOD ] string { } ) // Create a new http caller
2024-08-21 15:46:16 +02:00
var resp APIStatusResponse
2024-10-07 12:02:33 +02:00
b , err := caller . CallPost ( url , "" , map [ string ] interface { } { } ) // Call the status endpoint of the peer
2024-08-21 15:46:16 +02:00
if err != nil {
2024-08-30 14:50:48 +02:00
return DEAD , map [ string ] int { } // If the peer is not reachable, return dead
2024-08-21 15:46:16 +02:00
}
json . Unmarshal ( b , & resp )
2024-08-30 14:50:48 +02:00
if resp . Data == nil { // If the response is empty, return dead
2024-08-23 09:01:28 +02:00
return DEAD , map [ string ] int { }
}
new := map [ string ] int { }
2024-08-30 14:50:48 +02:00
for k , v := range resp . Data . Services { // Return the services states of the peer
2024-08-23 09:01:28 +02:00
new [ k ] = ToState ( v ) . EnumIndex ( )
2024-08-22 13:19:08 +02:00
}
2024-08-30 14:50:48 +02:00
return ToState ( resp . Data . State ) , new // Return the state of the peer & its services states
2024-08-21 15:46:16 +02:00
}
2024-08-30 14:50:48 +02:00
// CheckRemoteAPIs checks the state of remote APIs from your proper OC
2024-10-15 10:23:07 +02:00
func ( a * API ) CheckRemoteAPIs ( apis [ ] DataType ) ( State , map [ string ] string , error ) {
2024-08-21 11:28:13 +02:00
// Check if the database is up
2024-08-23 11:13:01 +02:00
new := map [ string ] string { }
2024-10-02 10:45:52 +02:00
caller := NewHTTPCaller ( map [ DataType ] map [ METHOD ] string { } ) // Create a new http caller
2024-08-23 15:43:12 +02:00
code := 0
e := ""
2024-08-30 14:50:48 +02:00
state := ALIVE
reachable := false
2024-10-15 10:23:07 +02:00
for _ , api := range apis { // Check the state of each remote API in the list
2024-08-21 15:32:10 +02:00
var resp APIStatusResponse
2024-11-06 16:09:56 +01:00
b , err := caller . CallGet ( "http://" + api . API ( ) + ":8080" , "/oc/version/status" ) // Call the status endpoint of the remote API (standard OC status endpoint)
2024-08-21 11:28:13 +02:00
if err != nil {
2024-08-30 14:50:48 +02:00
state = REDUCED_SERVICE // If a remote API is not reachable, return reduced service
continue
2024-08-21 11:28:13 +02:00
}
2024-08-21 15:32:10 +02:00
json . Unmarshal ( b , & resp )
2024-11-06 15:42:06 +01:00
fmt . Println ( string ( b ) )
2024-08-30 14:50:48 +02:00
if resp . Data == nil { //
state = REDUCED_SERVICE // If the response is empty, return reduced service
continue
2024-08-23 09:01:28 +02:00
}
2024-10-15 10:23:07 +02:00
new [ api . String ( ) ] = resp . Data . State
2024-08-23 15:43:12 +02:00
if resp . Data . Code > code {
code = resp . Data . Code
e += resp . Error
2024-08-21 15:32:10 +02:00
}
2024-08-30 14:50:48 +02:00
reachable = true // If the remote API is reachable, set reachable to true cause we are not dead
}
if ! reachable {
state = DEAD // If no remote API is reachable, return dead, nobody is alive
2024-08-21 11:28:13 +02:00
}
2024-08-23 15:43:12 +02:00
if code > 0 {
2024-08-30 14:50:48 +02:00
state = REDUCED_SERVICE
2024-08-23 15:43:12 +02:00
}
2024-08-30 14:50:48 +02:00
return state , new , nil
2024-08-21 11:28:13 +02:00
}
2024-08-21 15:32:10 +02:00
2024-08-30 14:50:48 +02:00
/* APIStatusResponse is the response of the API status */
2024-08-21 15:32:10 +02:00
type APIStatusResponse struct {
Data * APIStatus ` json:"data" `
Error string ` json:"error" `
}
2024-08-30 14:50:48 +02:00
/ *
* APIStatus is the status of the API
* it defines the state of the API
* Code is the status code , where 0 is ALIVE , 1 is REDUCED_SERVICE , 2 is UNPROCESSABLE_ENTITY , 3 is DB_FALLOUT , 4 is TEAPOT , 5 is DEAD
* /
2024-08-21 15:32:10 +02:00
type APIStatus struct {
2024-08-30 14:50:48 +02:00
Code int ` json:"code" ` // Code is the status code, where 0 is ALIVE, 1 is REDUCED_SERVICE, 2 is UNPROCESSABLE_ENTITY, 3 is DB_FALLOUT, 4 is TEAPOT, 5 is DEAD
State string ` json:"state" ` // State is the state of the API (status shows as a string) (alive, reduced service, unprocessable entity, database fallout, some things boils in here, i'm probably a teapot, dead)
Services map [ string ] string ` json:"services" ` // Services is the state of the services of the API (status shows as a string) (alive, reduced service, unprocessable entity, database fallout, some things boils in here, i'm probably a teapot, dead)
2024-08-21 15:32:10 +02:00
}