A question refers to the comment ! And if not Ooopsy

This commit is contained in:
mr
2024-08-30 14:50:48 +02:00
parent db78c70dc3
commit 8180fe5e99
39 changed files with 737 additions and 404 deletions

View File

@@ -3,31 +3,36 @@ package tools
import (
"encoding/json"
"errors"
"fmt"
"cloud.o-forge.io/core/oc-lib/dbs/mongo"
)
var UncatchedError = []error{}
/*
* API is the Health Check API
* it defines the health check methods
*/
var UncatchedError = []error{} // Singleton instance of the api 500 error cache
type State int
// State is an enum that defines the state of the API
const (
ALIVE State = iota
REDUCED_SERVICE
UNPROCESSABLE_ENTITY
DB_FALLOUT
TEAPOT
DEAD
WAITING
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
)
// EnumIndex returns the index of the enum
func (s State) EnumIndex() int {
return int(s)
}
// ToState returns the state from a string
func ToState(str string) State {
for _, s := range []State{ALIVE, REDUCED_SERVICE, UNPROCESSABLE_ENTITY, DB_FALLOUT, TEAPOT, DEAD, WAITING} {
for _, s := range []State{ALIVE, REDUCED_SERVICE, UNPROCESSABLE_ENTITY, DB_FALLOUT, TEAPOT, DEAD} {
if s.String() == str {
return s
}
@@ -35,92 +40,105 @@ func ToState(str string) State {
return DEAD
}
// String returns the string of the enum
func (s State) String() string {
return [...]string{"alive", "reduced service", "unprocessable entity", "database fallout",
"some things boils in here, i'm probably a teapot", "dead", "waiting"}[s]
"some things boils in here, i'm probably a teapot", "dead"}[s]
}
type API struct{}
// GetState returns the state of the API
func (a *API) GetState() (State, int, error) {
// Check if the database is up
err := mongo.MONGOService.TestDB(GetConfig())
if err != nil {
return DB_FALLOUT, 200, err
return DB_FALLOUT, 200, err // If the database is not up, return database fallout
}
err = mongo.MONGOService.TestCollections(GetConfig(), []string{})
err = mongo.MONGOService.TestCollections(GetConfig(), []string{}) // Check if the collections are up
if err != nil {
return UNPROCESSABLE_ENTITY, 200, err
return UNPROCESSABLE_ENTITY, 200, err // If the collections are not up, return unprocessable entity
}
if len(UncatchedError) > 0 {
if len(UncatchedError) > 0 { // If there are uncatched errors, return teapot
errStr := ""
for _, e := range UncatchedError {
errStr += e.Error() + "\n"
}
return TEAPOT, 200, errors.New(errStr)
}
return ALIVE, 200, nil
return ALIVE, 200, nil // If everything is up, return alive
}
// CheckRemotePeer checks the state of a remote peer
func (a *API) CheckRemotePeer(url string) (State, map[string]int) {
// Check if the database is up
caller := NewHTTPCaller(map[string]map[METHOD]string{})
caller := NewHTTPCaller(map[string]map[METHOD]string{}) // Create a new http caller
var resp APIStatusResponse
b, err := caller.CallPost(url, "/status", map[string]interface{}{})
fmt.Println("CheckRemotePeer", b, url, err)
b, err := caller.CallPost(url, "/status", map[string]interface{}{}) // Call the status endpoint of the peer
if err != nil {
return DEAD, map[string]int{}
return DEAD, map[string]int{} // If the peer is not reachable, return dead
}
json.Unmarshal(b, &resp)
fmt.Println("CheckRemotePeer2", b, err)
if resp.Data == nil {
if resp.Data == nil { // If the response is empty, return dead
return DEAD, map[string]int{}
}
new := map[string]int{}
fmt.Println("CheckRemotePeer", resp.Data.Services)
for k, v := range resp.Data.Services {
for k, v := range resp.Data.Services { // Return the services states of the peer
new[k] = ToState(v).EnumIndex()
}
return ToState(resp.Data.State), new
return ToState(resp.Data.State), new // Return the state of the peer & its services states
}
// CheckRemoteAPIs checks the state of remote APIs from your proper OC
func (a *API) CheckRemoteAPIs(urls map[string]string) (State, map[string]string, error) {
// Check if the database is up
new := map[string]string{}
caller := NewHTTPCaller(map[string]map[METHOD]string{})
caller := NewHTTPCaller(map[string]map[METHOD]string{}) // Create a new http caller
code := 0
u := ""
e := ""
for appName, url := range urls {
state := ALIVE
reachable := false
for appName, url := range urls { // Check the state of each remote API in the list
var resp APIStatusResponse
b, err := caller.CallGet(url, "/version/status")
b, err := caller.CallGet(url, "/version/status") // Call the status endpoint of the remote API (standard OC status endpoint)
if err != nil {
return REDUCED_SERVICE, new, err
state = REDUCED_SERVICE // If a remote API is not reachable, return reduced service
continue
}
json.Unmarshal(b, &resp)
if resp.Data == nil {
return DEAD, new, errors.New(url + " -> is DEAD")
if resp.Data == nil { //
state = REDUCED_SERVICE // If the response is empty, return reduced service
continue
}
new[appName] = resp.Data.State
if resp.Data.Code > code {
code = resp.Data.Code
u = url
e += resp.Error
}
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
}
if code > 0 {
return REDUCED_SERVICE, new, errors.New(u + " -> " + e)
state = REDUCED_SERVICE
}
return ALIVE, new, nil
return state, new, nil
}
/* APIStatusResponse is the response of the API status */
type APIStatusResponse struct {
Data *APIStatus `json:"data"`
Error string `json:"error"`
}
/*
* 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
*/
type APIStatus struct {
Code int `json:"code"`
State string `json:"state"`
Services map[string]string `json:"services"`
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)
}

View File

@@ -7,6 +7,7 @@ import (
"github.com/nats-io/nats.go"
)
// NATS Method Enum defines the different methods that can be used to interact with the NATS server
type NATSMethod int
const (
@@ -14,6 +15,7 @@ const (
CREATE
)
// NameToMethod returns the NATSMethod enum value from a string
func NameToMethod(name string) NATSMethod {
for _, v := range [...]NATSMethod{REMOVE, CREATE} {
if strings.Contains(strings.ToLower(v.String()), strings.ToLower(name)) {
@@ -23,20 +25,24 @@ func NameToMethod(name string) NATSMethod {
return -1
}
// GenerateKey generates a key for the NATSMethod usefull for standard key based on data name & method
func (d NATSMethod) GenerateKey(name string) string {
return name + "_" + d.String()
}
// String returns the string of the enum
func (d NATSMethod) String() string {
return [...]string{"remove", "create", "discovery"}[d]
}
type natsCaller struct{}
// NewNATSCaller creates a new instance of the NATS Caller
func NewNATSCaller() *natsCaller {
return &natsCaller{}
}
// SetNATSPub sets a message to the NATS server
func (o *natsCaller) SetNATSPub(dataName string, method NATSMethod, data interface{}) string {
if GetConfig().NATSUrl == "" {
return " -> NATS_SERVER is not set"
@@ -50,9 +56,9 @@ func (o *natsCaller) SetNATSPub(dataName string, method NATSMethod, data interfa
if err != nil {
return " -> " + err.Error()
}
err = nc.Publish(method.GenerateKey(dataName), js)
err = nc.Publish(method.GenerateKey(dataName), js) // Publish the message on the NATS server with a channel name based on the data name (or whatever start) and the method
if err != nil {
return " -> " + err.Error()
return " -> " + err.Error() // Return an error if the message could not be published
}
return ""
}

View File

@@ -7,6 +7,7 @@ import (
"net/http"
)
// HTTP Method Enum defines the different methods that can be used to interact with the HTTP server
type METHOD int
const (
@@ -16,14 +17,17 @@ const (
DELETE
)
// String returns the string of the enum
func (m METHOD) String() string {
return [...]string{"GET", "PUT", "POST", "DELETE"}[m]
}
// EnumIndex returns the index of the enum
func (m METHOD) EnumIndex() int {
return int(m)
}
// ToMethod returns the method from a string
func ToMethod(str string) METHOD {
for _, s := range []METHOD{GET, PUT, POST, DELETE} {
if s.String() == str {
@@ -33,18 +37,20 @@ func ToMethod(str string) METHOD {
return GET
}
var HTTPCallerInstance = &HTTPCaller{}
var HTTPCallerInstance = &HTTPCaller{} // Singleton instance of the HTTPCaller
type HTTPCaller struct {
URLS map[string]map[METHOD]string
URLS map[string]map[METHOD]string // Map of the different methods and their urls
}
// NewHTTPCaller creates a new instance of the HTTP Caller
func NewHTTPCaller(urls map[string]map[METHOD]string) *HTTPCaller {
return &HTTPCaller{
URLS: urls,
URLS: urls, // Set the urls defined in the config & based on the data name type & method
}
}
// CallGet calls the GET method on the HTTP server
func (caller *HTTPCaller) CallGet(url string, subpath string) ([]byte, error) {
resp, err := http.Get(url + subpath)
if err != nil {
@@ -54,6 +60,7 @@ func (caller *HTTPCaller) CallGet(url string, subpath string) ([]byte, error) {
return io.ReadAll(resp.Body)
}
// CallPut calls the DELETE method on the HTTP server
func (caller *HTTPCaller) CallDelete(url string, subpath string) ([]byte, error) {
resp, err := http.NewRequest("DELETE", url+subpath, nil)
if err != nil {
@@ -63,6 +70,7 @@ func (caller *HTTPCaller) CallDelete(url string, subpath string) ([]byte, error)
return io.ReadAll(resp.Body)
}
// CallPost calls the POST method on the HTTP server
func (caller *HTTPCaller) CallPost(url string, subpath string, body map[string]interface{}) ([]byte, error) {
postBody, _ := json.Marshal(body)
responseBody := bytes.NewBuffer(postBody)
@@ -73,3 +81,5 @@ func (caller *HTTPCaller) CallPost(url string, subpath string, body map[string]i
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}
// NO PUT IN HERE TO DANGEROUS TO USE ON A REMOTE SERVER, MAYBE NEEDED IN THE FUTURE