2024-08-13 14:33:26 +02:00
package peer
import (
"encoding/json"
"errors"
"fmt"
2024-08-22 10:19:14 +02:00
"regexp"
2024-08-13 14:33:26 +02:00
"strings"
"cloud.o-forge.io/core/oc-lib/tools"
)
2024-08-30 14:50:48 +02:00
/ *
* PeerExecution is a struct that represents an execution on a peer
* it defines the execution data
* /
2024-08-13 14:33:26 +02:00
type PeerExecution struct {
2025-01-22 14:53:42 +01:00
Method string ` json:"method" bson:"method" `
Url string ` json:"url" bson:"url" `
Body interface { } ` json:"body" bson:"body" `
DataType int ` json:"data_type" bson:"data_type" `
DataID string ` json:"data_id" bson:"data_id" `
2024-08-13 14:33:26 +02:00
}
2024-08-30 14:50:48 +02:00
var cache = & PeerCache { } // Singleton instance of the peer cache
// PeerCache is a struct that represents a peer cache
2024-08-13 14:33:26 +02:00
type PeerCache struct {
Executions [ ] * PeerExecution
}
2024-08-30 14:50:48 +02:00
// urlFormat formats the URL of the peer with the data type API function
2025-03-05 11:54:14 +01:00
func ( p * PeerCache ) urlFormat ( hostUrl string , dt tools . DataType ) string {
2024-08-30 14:50:48 +02:00
// localhost is replaced by the local peer URL
// because localhost must collide on a web request security protocol
2024-10-07 15:07:06 +02:00
localhost := ""
2025-03-05 11:54:14 +01:00
if strings . Contains ( hostUrl , "localhost" ) {
2024-10-07 15:07:06 +02:00
localhost = "localhost"
}
2025-03-05 11:54:14 +01:00
if strings . Contains ( hostUrl , "127.0.0.1" ) {
2024-10-07 15:07:06 +02:00
localhost = "127.0.0.1"
}
if localhost != "" {
r := regexp . MustCompile ( "(" + localhost + ":[0-9]+)" )
2025-03-05 11:54:14 +01:00
t := r . FindString ( hostUrl )
2024-08-22 15:18:59 +02:00
if t != "" {
2025-03-05 11:54:14 +01:00
hostUrl = strings . Replace ( hostUrl , t , dt . API ( ) + ":8080/oc" , - 1 )
2024-10-07 16:01:11 +02:00
} else {
2025-03-05 11:54:14 +01:00
hostUrl = strings . ReplaceAll ( hostUrl , localhost , dt . API ( ) + ":8080/oc" )
2024-08-22 15:18:59 +02:00
}
2024-10-07 14:39:02 +02:00
} else {
2025-03-05 11:54:14 +01:00
hostUrl = hostUrl + "/" + strings . ReplaceAll ( dt . API ( ) , "oc-" , "" )
2024-08-22 15:18:59 +02:00
}
2025-03-05 11:54:14 +01:00
return hostUrl
2024-08-22 15:18:59 +02:00
}
2024-08-30 14:50:48 +02:00
// checkPeerStatus checks the status of a peer
2025-03-03 10:32:52 +01:00
func ( p * PeerCache ) checkPeerStatus ( peerID string , appName string ) ( * Peer , bool ) {
2024-08-21 15:46:16 +02:00
api := tools . API { }
2024-12-12 16:25:47 +01:00
access := NewShallowAccessor ( )
2025-01-23 09:06:22 +01:00
res , code , _ := access . LoadOne ( peerID ) // Load the peer from db
if code != 200 { // no peer no party
2024-08-23 09:53:37 +02:00
return nil , false
2024-08-21 15:46:16 +02:00
}
2025-03-03 10:32:52 +01:00
url := p . urlFormat ( res . ( * Peer ) . Url , tools . PEER ) + "/status" // Format the URL
2024-11-06 15:32:55 +01:00
state , services := api . CheckRemotePeer ( url )
2025-01-23 09:06:22 +01:00
res . ( * Peer ) . ServicesState = services // Update the services states of the peer
access . UpdateOne ( res , peerID ) // Update the peer in the db
2024-08-30 14:50:48 +02:00
return res . ( * Peer ) , state != tools . DEAD && services [ appName ] == 0 // Return the peer and its status
2024-08-13 14:33:26 +02:00
}
2024-08-30 14:50:48 +02:00
// LaunchPeerExecution launches an execution on a peer
2025-03-11 12:03:35 +01:00
// The method contacts the path described by : peer.Url + datatype path (from enums) + replacement of id by dataID
2024-08-23 09:53:37 +02:00
func ( p * PeerCache ) LaunchPeerExecution ( peerID string , dataID string ,
2025-01-22 14:53:42 +01:00
dt tools . DataType , method tools . METHOD , body interface { } , caller * tools . HTTPCaller ) ( * PeerExecution , error ) {
2024-10-07 11:37:37 +02:00
fmt . Println ( "Launching peer execution on" , caller . URLS , dt , method )
2024-10-02 10:45:52 +02:00
methods := caller . URLS [ dt ] // Get the methods url of the data type
2024-10-07 11:37:37 +02:00
if m , ok := methods [ method ] ; ! ok || m == "" {
2025-03-11 16:48:05 +01:00
return nil , errors . New ( "Requested method " + method . String ( ) + " not declared in HTTPCaller" )
2024-08-13 14:33:26 +02:00
}
2025-03-11 12:03:35 +01:00
path := methods [ method ] // Get the path corresponding to the action we want to execute
path = strings . ReplaceAll ( path , ":id" , dataID ) // Replace the id in the path in case of a DELETE / UPDATE method (it's a standard naming in OC)
2024-08-23 09:53:37 +02:00
url := ""
2024-10-07 16:01:11 +02:00
2024-08-30 14:50:48 +02:00
// Check the status of the peer
2025-03-03 10:32:52 +01:00
if mypeer , ok := p . checkPeerStatus ( peerID , dt . API ( ) ) ; ! ok && mypeer != nil {
2024-08-30 14:50:48 +02:00
// If the peer is not reachable, add the execution to the failed executions list
2024-08-23 23:00:57 +02:00
pexec := & PeerExecution {
Method : method . String ( ) ,
2025-03-11 12:03:35 +01:00
Url : p . urlFormat ( ( mypeer . Url ) , dt ) + path , // the url is constitued of : host URL + resource path + action path (ex : mypeer.com/datacenter/resourcetype/path/to/action)
2024-08-23 23:00:57 +02:00
Body : body ,
DataType : dt . EnumIndex ( ) ,
DataID : dataID ,
}
2024-08-23 09:53:37 +02:00
mypeer . AddExecution ( * pexec )
2024-12-12 16:25:47 +01:00
NewShallowAccessor ( ) . UpdateOne ( mypeer , peerID ) // Update the peer in the db
2024-08-22 12:29:38 +02:00
return nil , errors . New ( "peer is not reachable" )
2024-08-23 09:53:37 +02:00
} else {
2024-11-19 10:08:46 +01:00
if mypeer == nil {
return nil , errors . New ( "peer not found" )
}
2024-08-30 14:50:48 +02:00
// If the peer is reachable, launch the execution
2025-03-11 12:03:35 +01:00
url = p . urlFormat ( ( mypeer . Url ) , dt ) + path // Format the URL
2024-12-12 16:25:47 +01:00
tmp := mypeer . FailedExecution // Get the failed executions list
mypeer . FailedExecution = [ ] PeerExecution { } // Reset the failed executions list
NewShallowAccessor ( ) . UpdateOne ( mypeer , peerID ) // Update the peer in the db
for _ , v := range tmp { // Retry the failed executions
2024-08-23 23:00:57 +02:00
go p . exec ( v . Url , tools . ToMethod ( v . Method ) , v . Body , caller )
2024-08-23 09:53:37 +02:00
}
2024-08-13 14:33:26 +02:00
}
2024-08-30 14:50:48 +02:00
return nil , p . exec ( url , method , body , caller ) // Execute the method
2024-08-23 23:00:57 +02:00
}
2024-08-30 14:50:48 +02:00
// exec executes the method on the peer
2025-01-22 14:53:42 +01:00
func ( p * PeerCache ) exec ( url string , method tools . METHOD , body interface { } , caller * tools . HTTPCaller ) error {
2024-08-23 23:00:57 +02:00
var b [ ] byte
var err error
2024-08-30 14:50:48 +02:00
if method == tools . POST { // Execute the POST method if it's a POST method
2024-08-22 16:28:21 +02:00
b , err = caller . CallPost ( url , "" , body )
2024-08-13 14:33:26 +02:00
}
2024-08-30 14:50:48 +02:00
if method == tools . GET { // Execute the GET method if it's a GET method
2024-08-22 16:28:21 +02:00
b , err = caller . CallGet ( url , "" )
2024-08-13 14:33:26 +02:00
}
2024-08-30 14:50:48 +02:00
if method == tools . DELETE { // Execute the DELETE method if it's a DELETE method
2024-08-22 16:28:21 +02:00
b , err = caller . CallDelete ( url , "" )
2024-08-13 14:33:26 +02:00
}
2025-01-20 15:35:09 +01:00
if err != nil {
return err
}
2024-08-13 14:33:26 +02:00
var m map [ string ] interface { }
2025-01-20 15:35:09 +01:00
err = json . Unmarshal ( b , & m )
2024-08-13 14:33:26 +02:00
if err != nil {
2024-08-23 23:00:57 +02:00
return err
2024-08-13 14:33:26 +02:00
}
2024-10-07 16:47:03 +02:00
if e , ok := m [ "error" ] ; ok && e != "<nil>" && e != "" { // Check if there is an error in the response
2024-08-23 23:00:57 +02:00
return errors . New ( fmt . Sprintf ( "%v" , m [ "error" ] ) )
2024-08-13 14:33:26 +02:00
}
2024-09-23 10:34:08 +02:00
return nil
2024-08-13 14:33:26 +02:00
}