oc-lib/models/peer/peer_cache.go

146 lines
5.3 KiB
Go
Raw Normal View History

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"
)
/*
* 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 {
2024-08-23 09:53:37 +02:00
Method string `json:"method" bson:"method"`
Url string `json:"url" bson:"url"`
Body map[string]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
}
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
}
// urlFormat formats the URL of the peer with the data type API function
2024-10-02 10:45:52 +02:00
func (p *PeerCache) urlFormat(url string, dt tools.DataType) string {
// 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 := ""
if strings.Contains(url, "localhost") {
localhost = "localhost"
}
if strings.Contains(url, "127.0.0.1") {
localhost = "127.0.0.1"
}
if localhost != "" {
r := regexp.MustCompile("(" + localhost + ":[0-9]+)")
2024-08-22 15:18:59 +02:00
t := r.FindString(url)
if t != "" {
url = strings.Replace(url, t, dt.API()+":8080/oc", -1)
2024-10-07 16:01:11 +02:00
} else {
url = strings.ReplaceAll(url, localhost, dt.API()+":8080/oc")
2024-08-22 15:18:59 +02:00
}
2024-10-07 14:39:02 +02:00
} else {
url = url + "/" + dt.API()
2024-08-22 15:18:59 +02:00
}
return url
}
// checkPeerStatus checks the status of a peer
2024-08-23 10:01:37 +02:00
func (p *PeerCache) checkPeerStatus(peerID string, appName string, caller *tools.HTTPCaller) (*Peer, bool) {
2024-08-21 15:46:16 +02:00
api := tools.API{}
access := NewShallowAccessor()
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
}
2024-10-02 10:45:52 +02:00
methods := caller.URLS[tools.PEER] // Get the methods url of the peer
2024-08-21 16:03:56 +02:00
if methods == nil {
2024-08-23 09:53:37 +02:00
return res.(*Peer), false
2024-08-21 16:03:56 +02:00
}
meth := methods[tools.POST] // Get the POST method to check status
2024-08-21 16:03:56 +02:00
if meth == "" {
2024-08-23 09:53:37 +02:00
return res.(*Peer), false
2024-08-21 16:03:56 +02:00
}
2024-10-16 17:15:21 +02:00
url := p.urlFormat(res.(*Peer).Url, tools.PEER) + meth // Format the URL
2024-10-07 08:44:34 +02:00
fmt.Println("Checking peer status on", url, "...")
2024-11-06 15:32:55 +01:00
state, services := api.CheckRemotePeer(url)
fmt.Println("Checking peer status on", url, state, services) // Check the status of the peer
res.(*Peer).ServicesState = services // Update the services states of the peer
access.UpdateOne(res, peerID) // Update the peer in the db
return res.(*Peer), state != tools.DEAD && services[appName] == 0 // Return the peer and its status
2024-08-13 14:33:26 +02:00
}
// LaunchPeerExecution launches an execution on a peer
2024-08-23 09:53:37 +02:00
func (p *PeerCache) LaunchPeerExecution(peerID string, dataID string,
2024-10-02 10:45:52 +02:00
dt tools.DataType, method tools.METHOD, body map[string]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 == "" {
2024-08-13 14:33:26 +02:00
return nil, errors.New("no path found")
}
2024-10-07 11:37:37 +02:00
meth := methods[method] // Get the method url to execute
meth = strings.ReplaceAll(meth, ":id", dataID) // Replace the id in the url 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
// Check the status of the peer
2024-11-19 10:08:46 +01:00
if mypeer, ok := p.checkPeerStatus(peerID, dt.API(), caller); !ok && mypeer != nil {
// 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(),
2024-10-07 14:39:02 +02:00
Url: p.urlFormat((mypeer.Url)+meth, dt),
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)
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")
}
// If the peer is reachable, launch the execution
url = p.urlFormat((mypeer.Url)+meth, dt) // Format the URL
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-10-07 15:12:44 +02:00
fmt.Println("URL exec", url)
return nil, p.exec(url, method, body, caller) // Execute the method
2024-08-23 23:00:57 +02:00
}
// exec executes the method on the peer
2024-08-23 23:00:57 +02:00
func (p *PeerCache) exec(url string, method tools.METHOD, body map[string]interface{}, caller *tools.HTTPCaller) error {
var b []byte
var err error
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
}
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
}
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
}
var m map[string]interface{}
json.Unmarshal(b, &m)
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
}