Merge pull request #1148 from moonstream-to/revert-1147-revert-1145-fix-nb-access

Fix nodebalancer CLI and removed autogen accesses 2
nb-multi
Sergei Sumarokov 2024-12-13 13:11:09 +03:00 zatwierdzone przez GitHub
commit 1de7b7fa96
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
12 zmienionych plików z 760 dodań i 615 usunięć

Wyświetl plik

@ -3,37 +3,36 @@
## Installation ## Installation
- Prepare environment variables, according to `sample.env`. - Prepare environment variables, according to `sample.env`.
- Build application - Build an application
```bash ```bash
go build -o nodebalancer . go build -o nodebalancer .
``` ```
## Work with nodebalancer ## CLI
**IMPORTANT** Do not use flag `-debug` in production. **IMPORTANT** Do not use flag `-debug` in production.
### add-access Node balancer access manipulation requires an administration token to create and modify resources within the Bugout moonstream application.
### add new access
Add new access for user: Add new access for user:
```bash ```bash
nodebalancer add-access \ ./nodebalancer access add \
--user-id "<user_uuid>" \ --access-token "<bugout_access_token>"
--access-id "<access_uuid>" \
--name "Access name" \ --name "Access name" \
--description "Description of access" \ --description "Description of access"
--extended-methods false \
--blockchain--access true
``` ```
### delete-access ### delete access
Delete user access: Delete user access:
```bash ```bash
nodebalancer delete-access \ ./nodebalancer access delete \
--user-id "<user_uuid>" \ --access-token "<bugout_access_token>"
--access-id "<access_uuid>" --access-id "<access_uuid>"
``` ```
@ -42,10 +41,10 @@ If `access-id` not specified, all user accesses will be deleted.
### users ### users
```bash ```bash
nodebalancer users | jq . ./nodebalancer access list --access-token "<bugout_access_token>" | jq .
``` ```
This command will return a list of bugout resources of registered users to access node balancer with their `crawlers/app/project` (in our project we will call it `crawlers`). This command will return a list of bugout resources of registered users to access node balancer.
```json ```json
[ [
@ -72,7 +71,7 @@ This command will return a list of bugout resources of registered users to acces
### server ### server
```bash ```bash
nodebalancer server -host 0.0.0.0 -port 8544 -healthcheck ./nodebalancer server --host 0.0.0.0 --port 8544 --healthcheck
``` ```
Flag `--healthcheck` will execute background process to ping-pong available nodes to keep their status and current block number. Flag `--healthcheck` will execute background process to ping-pong available nodes to keep their status and current block number.

Wyświetl plik

@ -1,10 +1,15 @@
package main package main
import ( import (
"encoding/json"
"fmt"
"log" "log"
"reflect" "reflect"
"sync" "sync"
"time" "time"
"github.com/bugout-dev/bugout-go/pkg/brood"
"github.com/google/uuid"
) )
var ( var (
@ -55,7 +60,7 @@ func (ca *ClientAccess) CheckClientCallPeriodLimits(tsNow int64) bool {
} }
} else { } else {
// Client period should be refreshed // Client period should be refreshed
if stateCLI.enableDebugFlag { if NB_ENABLE_DEBUG {
log.Printf("Refresh client's period_start_ts with time.now() and reset calls_per_period") log.Printf("Refresh client's period_start_ts with time.now() and reset calls_per_period")
} }
ca.ClientResourceData.CallsPerPeriod = 0 ca.ClientResourceData.CallsPerPeriod = 0
@ -188,3 +193,111 @@ func (cpool *ClientPool) CleanInactiveClientNodes() int {
return cnt return cnt
} }
// Creates new Bugout resource according to nodebalancer type to grant user or application access to call JSON RPC nodes
func AddNewAccess(accessToken, accessId, userId, name, description string, blockchainAccess, extendedMethods bool, periodDuration, maxCallsPerPeriod uint) (*ClientAccess, error) {
if userId == "" {
userId = NB_CONTROLLER_USER_ID
} else {
_, findErr := bugoutClient.Brood.FindUser(
accessToken,
map[string]string{
"user_id": userId,
"application_id": MOONSTREAM_APPLICATION_ID,
},
)
if findErr != nil {
return nil, fmt.Errorf("user does not exists, err: %v", findErr)
}
}
if accessId == "" {
accessId = uuid.NewString()
}
proposedClientResourceData := ClientResourceData{
AccessID: accessId,
UserID: userId,
Name: name,
Description: description,
BlockchainAccess: blockchainAccess,
ExtendedMethods: extendedMethods,
PeriodDuration: int64(periodDuration),
PeriodStartTs: int64(time.Now().Unix()),
MaxCallsPerPeriod: int64(maxCallsPerPeriod),
CallsPerPeriod: 0,
Type: BUGOUT_RESOURCE_TYPE_NODEBALANCER_ACCESS,
}
resource, err := bugoutClient.Brood.CreateResource(accessToken, MOONSTREAM_APPLICATION_ID, proposedClientResourceData)
if err != nil {
return nil, fmt.Errorf("unable to create user access, err: %v", err)
}
resourceData, err := json.Marshal(resource.ResourceData)
if err != nil {
return nil, fmt.Errorf("unable to encode resource %s data interface to json, err: %v", resource.Id, err)
}
var newUserAccess ClientAccess
err = json.Unmarshal(resourceData, &newUserAccess)
if err != nil {
return nil, fmt.Errorf("unable to decode resource %s data json to structure, err: %v", resource.Id, err)
}
newUserAccess.ResourceID = resource.Id
return &newUserAccess, nil
}
// Share access represented as Brood resource with new holder. Mostly used to share with nodebalancer application user
func ShareAccess(accessToken, resourceId, userId, holderType string, permissions []string) (*brood.ResourceHolders, error) {
resourceHolderPermissions, holdErr := bugoutClient.Brood.AddResourceHolderPermissions(
accessToken, resourceId, brood.ResourceHolder{
Id: userId,
HolderType: holderType,
Permissions: permissions,
},
)
if holdErr != nil {
return nil, fmt.Errorf("unable to grant permissions to user with ID %s at resource with ID %s, err: %v", userId, resourceId, holdErr)
}
return &resourceHolderPermissions, nil
}
// Get resource with nodebalancer access type
func GetResources(accessToken, accessId, userId string) (*brood.Resources, error) {
queryParameters := map[string]string{
"type": BUGOUT_RESOURCE_TYPE_NODEBALANCER_ACCESS,
}
if userId != "" {
queryParameters["user_id"] = userId
}
if accessId != "" {
queryParameters["access_id"] = accessId
}
resources, getResErr := bugoutClient.Brood.GetResources(accessToken, MOONSTREAM_APPLICATION_ID, queryParameters)
if getResErr != nil {
return nil, fmt.Errorf("unable to get Bugout resources, err: %v", getResErr)
}
return &resources, nil
}
// Parse Brood resource to nodebalancer client access representation
func ParseResourceDataToClientAccess(resource brood.Resource) (*ClientAccess, error) {
resourceData, marErr := json.Marshal(resource.ResourceData)
if marErr != nil {
return nil, fmt.Errorf("unable to encode resource %s data interface to json, err: %v", resource.Id, marErr)
}
var clientAccess ClientAccess
clientAccess.ResourceID = resource.Id
unmarErr := json.Unmarshal(resourceData, &clientAccess.ClientResourceData)
if unmarErr != nil {
return nil, fmt.Errorf("unable to decode resource %s data json to structure, err: %v", resource.Id, unmarErr)
}
return &clientAccess, nil
}

Wyświetl plik

@ -23,6 +23,8 @@ var (
supportedBlockchains map[string]bool supportedBlockchains map[string]bool
bugoutClient *bugout.BugoutClient
// Bugout client // Bugout client
// TODO(kompotkot): Find out why it cuts out the port // TODO(kompotkot): Find out why it cuts out the port
BUGOUT_BROOD_URL = "https://auth.bugout.dev" BUGOUT_BROOD_URL = "https://auth.bugout.dev"
@ -32,11 +34,14 @@ var (
// Bugout and application configuration // Bugout and application configuration
BUGOUT_AUTH_CALL_TIMEOUT = time.Second * 5 BUGOUT_AUTH_CALL_TIMEOUT = time.Second * 5
MOONSTREAM_APPLICATION_ID = os.Getenv("MOONSTREAM_APPLICATION_ID") MOONSTREAM_APPLICATION_ID = os.Getenv("MOONSTREAM_APPLICATION_ID")
NB_CONTROLLER_USER_ID = os.Getenv("NB_CONTROLLER_USER_ID")
NB_CONTROLLER_TOKEN = os.Getenv("NB_CONTROLLER_TOKEN") NB_CONTROLLER_TOKEN = os.Getenv("NB_CONTROLLER_TOKEN")
NB_CONTROLLER_ACCESS_ID = os.Getenv("NB_CONTROLLER_ACCESS_ID") NB_CONTROLLER_ACCESS_ID = os.Getenv("NB_CONTROLLER_ACCESS_ID")
MOONSTREAM_CORS_ALLOWED_ORIGINS = os.Getenv("MOONSTREAM_CORS_ALLOWED_ORIGINS") MOONSTREAM_CORS_ALLOWED_ORIGINS = os.Getenv("MOONSTREAM_CORS_ALLOWED_ORIGINS")
CORS_WHITELIST_MAP = make(map[string]bool) CORS_WHITELIST_MAP = make(map[string]bool)
NB_ENABLE_DEBUG = false
NB_CONNECTION_RETRIES = 2 NB_CONNECTION_RETRIES = 2
NB_CONNECTION_RETRIES_INTERVAL = time.Millisecond * 10 NB_CONNECTION_RETRIES_INTERVAL = time.Millisecond * 10
NB_HEALTH_CHECK_INTERVAL = os.Getenv("NB_HEALTH_CHECK_INTERVAL") NB_HEALTH_CHECK_INTERVAL = os.Getenv("NB_HEALTH_CHECK_INTERVAL")
@ -64,15 +69,15 @@ var (
DEFAULT_AUTOGENERATED_MAX_CALLS_PER_PERIOD = int64(1000) DEFAULT_AUTOGENERATED_MAX_CALLS_PER_PERIOD = int64(1000)
) )
func CreateBugoutClient() (bugout.BugoutClient, error) { func CreateBugoutClient() (*bugout.BugoutClient, error) {
bugoutTimeoutSeconds, err := strconv.Atoi(NB_BUGOUT_TIMEOUT_SECONDS_RAW) bugoutTimeoutSeconds, err := strconv.Atoi(NB_BUGOUT_TIMEOUT_SECONDS_RAW)
if err != nil { if err != nil {
return bugout.BugoutClient{}, fmt.Errorf("unable to parse environment variable as integer: %v", err) return nil, fmt.Errorf("unable to parse environment variable as integer: %v", err)
} }
NB_BUGOUT_TIMEOUT_SECONDS := time.Duration(bugoutTimeoutSeconds) * time.Second NB_BUGOUT_TIMEOUT_SECONDS := time.Duration(bugoutTimeoutSeconds) * time.Second
broodClient := bugout.ClientBrood(BUGOUT_BROOD_URL, NB_BUGOUT_TIMEOUT_SECONDS) bugoutClient := bugout.ClientBrood(BUGOUT_BROOD_URL, NB_BUGOUT_TIMEOUT_SECONDS)
return broodClient, nil return &bugoutClient, nil
} }
func CheckEnvVarSet() { func CheckEnvVarSet() {

Wyświetl plik

@ -1,5 +1,13 @@
package main package main
import (
"log"
"os"
)
func main() { func main() {
cli() app := NodebalancerAppCli()
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
} }

Wyświetl plik

@ -236,50 +236,12 @@ func fetchClientAccessFromResources(accessID, authorizationToken string, tsNow i
if len(resources.Resources) == 0 { if len(resources.Resources) == 0 {
if authorizationToken != "" { if authorizationToken != "" {
// Generate new autogenerated access resource with default parameters and grant user permissions to work with it // Generate new autogenerated access resource with default parameters and grant user permissions to work with it
user, err := bugoutClient.Brood.GetUser(authorizationToken) // TODO(kompotkot): Not working because of permissions models changed at Brood layer
if err != nil { return nil, fmt.Errorf("unsupported authentication method")
log.Printf("Unable to get user, err: %v", err)
return nil, fmt.Errorf("unable to find user with provided authorization token")
}
newResource, err := bugoutClient.Brood.CreateResource(
NB_CONTROLLER_TOKEN, MOONSTREAM_APPLICATION_ID, ClientResourceData{
UserID: user.Id,
AccessID: uuid.New().String(),
Name: user.Username,
Description: "Autogenerated access ID",
BlockchainAccess: true,
ExtendedMethods: false,
PeriodDuration: DEFAULT_AUTOGENERATED_PERIOD_DURATION,
PeriodStartTs: tsNow,
MaxCallsPerPeriod: DEFAULT_AUTOGENERATED_MAX_CALLS_PER_PERIOD,
CallsPerPeriod: 0,
Type: BUGOUT_RESOURCE_TYPE_NODEBALANCER_ACCESS,
},
)
if err != nil {
log.Printf("Unable to create resource with autogenerated access for user with ID %s, err: %v", user.Id, err)
return nil, fmt.Errorf("unable to create resource with autogenerated access for user")
}
resourceHolderPermissions, err := bugoutClient.Brood.AddResourceHolderPermissions(
NB_CONTROLLER_TOKEN, newResource.Id, brood.ResourceHolder{
Id: user.Id,
HolderType: "user",
Permissions: DEFAULT_AUTOGENERATED_USER_PERMISSIONS,
},
)
if err != nil {
log.Printf("Unable to grant permissions to user with ID %s at resource with ID %s, err: %v", newResource.Id, user.Id, err)
return nil, fmt.Errorf("unable to create resource with autogenerated access for user")
}
log.Printf("Created new resource with ID %s with autogenerated access for user with ID %s", resourceHolderPermissions.ResourceId, user.Id)
resources.Resources = append(resources.Resources, newResource)
} else { } else {
return nil, fmt.Errorf("there are no provided access identifier") return nil, fmt.Errorf("there are no provided access identifier")
} }
} else if len(resources.Resources) > 1 { } else if len(resources.Resources) > 1 {
// TODO(kompotkot): Write support of multiple resources, be careful, because NB_CONTROLLER has several resources // TODO(kompotkot): Write support of multiple resources, be careful, because NB_CONTROLLER has several resources
return nil, fmt.Errorf("there are no provided access identifier") return nil, fmt.Errorf("there are no provided access identifier")
@ -479,7 +441,7 @@ func logMiddleware(next http.Handler) http.Handler {
} }
} }
if stateCLI.enableDebugFlag { if NB_ENABLE_DEBUG {
if r.URL.RawQuery != "" { if r.URL.RawQuery != "" {
logStr += fmt.Sprintf(" %s", r.URL.RawQuery) logStr += fmt.Sprintf(" %s", r.URL.RawQuery)
} }
@ -526,14 +488,14 @@ func accessMiddleware(next http.Handler) http.Handler {
// If access id does not belong to internal crawlers, then check cache or find it in Bugout resources // If access id does not belong to internal crawlers, then check cache or find it in Bugout resources
if accessID != "" && accessID == NB_CONTROLLER_ACCESS_ID { if accessID != "" && accessID == NB_CONTROLLER_ACCESS_ID {
if stateCLI.enableDebugFlag { if NB_ENABLE_DEBUG {
log.Printf("Access ID belongs to internal usage for user with ID %s", currentClientAccess.ClientResourceData.UserID) log.Printf("Access ID belongs to internal usage for user with ID %s", currentClientAccess.ClientResourceData.UserID)
} }
currentClientAccess = internalUsageAccess currentClientAccess = internalUsageAccess
currentClientAccess.LastAccessTs = tsNow currentClientAccess.LastAccessTs = tsNow
currentClientAccess.requestedDataSource = requestedDataSource currentClientAccess.requestedDataSource = requestedDataSource
} else if accessID != "" && accessCache.isAccessIdInCache(accessID) { } else if accessID != "" && accessCache.isAccessIdInCache(accessID) {
if stateCLI.enableDebugFlag { if NB_ENABLE_DEBUG {
log.Printf("Access ID found in cache for user with ID %s", currentClientAccess.ClientResourceData.UserID) log.Printf("Access ID found in cache for user with ID %s", currentClientAccess.ClientResourceData.UserID)
} }
currentClientAccess = *accessCache.accessIds[accessID] currentClientAccess = *accessCache.accessIds[accessID]
@ -545,7 +507,7 @@ func accessMiddleware(next http.Handler) http.Handler {
currentClientAccess.requestedDataSource = requestedDataSource currentClientAccess.requestedDataSource = requestedDataSource
accessCache.UpdateAccessAtCache(accessID, authorizationToken, requestedDataSource, tsNow) accessCache.UpdateAccessAtCache(accessID, authorizationToken, requestedDataSource, tsNow)
} else if accessID == "" && accessCache.isAuthorizationTokenInCache(authorizationToken) { } else if accessID == "" && accessCache.isAuthorizationTokenInCache(authorizationToken) {
if stateCLI.enableDebugFlag { if NB_ENABLE_DEBUG {
log.Printf("Client connected with Authorization token") log.Printf("Client connected with Authorization token")
} }
currentClientAccess = *accessCache.authorizationTokens[authorizationToken] currentClientAccess = *accessCache.authorizationTokens[authorizationToken]
@ -557,7 +519,7 @@ func accessMiddleware(next http.Handler) http.Handler {
currentClientAccess.requestedDataSource = requestedDataSource currentClientAccess.requestedDataSource = requestedDataSource
accessCache.UpdateAccessAtCache(accessID, authorizationToken, requestedDataSource, tsNow) accessCache.UpdateAccessAtCache(accessID, authorizationToken, requestedDataSource, tsNow)
} else { } else {
if stateCLI.enableDebugFlag { if NB_ENABLE_DEBUG {
log.Printf("No access identity found in cache, looking at Brood resources") log.Printf("No access identity found in cache, looking at Brood resources")
} }
@ -581,7 +543,7 @@ func accessMiddleware(next http.Handler) http.Handler {
if authorizationToken != "" && accessCache.isAccessIdInCache(currentClientAccess.ClientResourceData.AccessID) { if authorizationToken != "" && accessCache.isAccessIdInCache(currentClientAccess.ClientResourceData.AccessID) {
accessCache.authorizationTokens[authorizationToken] = accessCache.accessIds[currentClientAccess.ClientResourceData.AccessID] accessCache.authorizationTokens[authorizationToken] = accessCache.accessIds[currentClientAccess.ClientResourceData.AccessID]
} else { } else {
if stateCLI.enableDebugFlag { if NB_ENABLE_DEBUG {
log.Printf("Adding new access identifier in cache") log.Printf("Adding new access identifier in cache")
} }
err := accessCache.AddAccessToCache(currentClientAccess, tsNow) err := accessCache.AddAccessToCache(currentClientAccess, tsNow)

Wyświetl plik

@ -5,13 +5,11 @@ package main
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"log" "log"
"net/http" "net/http"
"net/http/httputil" "net/http/httputil"
"net/url" "net/url"
"os"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -107,7 +105,7 @@ func proxyErrorHandler(proxy *httputil.ReverseProxy, url *url.URL) {
} }
} }
func Server() { func Server(configPath, listeningHostAddr, listeningPort string, enableHealthCheck bool) error {
// Create Access ID cache // Create Access ID cache
CreateAccessCache() CreateAccessCache()
@ -117,47 +115,26 @@ func Server() {
consent := humbug.CreateHumbugConsent(humbug.True) consent := humbug.CreateHumbugConsent(humbug.True)
reporter, err = humbug.CreateHumbugReporter(consent, "moonstream-node-balancer", sessionID, HUMBUG_REPORTER_NB_TOKEN) reporter, err = humbug.CreateHumbugReporter(consent, "moonstream-node-balancer", sessionID, HUMBUG_REPORTER_NB_TOKEN)
if err != nil { if err != nil {
fmt.Printf("Invalid Humbug Crash configuration, err: %v\n", err) return fmt.Errorf("invalid Humbug Crash configuration, err: %v", err)
os.Exit(1)
} }
// Record system information // Record system information
reporter.Publish(humbug.SystemReport()) reporter.Publish(humbug.SystemReport())
// Fetch access id for internal usage (crawlers, infrastructure, etc) // Fetch access id for internal usage (crawlers, infrastructure, etc)
resources, err := bugoutClient.Brood.GetResources( resources, getErr := GetResources(NB_CONTROLLER_TOKEN, NB_CONTROLLER_ACCESS_ID, "")
NB_CONTROLLER_TOKEN, if getErr != nil {
MOONSTREAM_APPLICATION_ID, return fmt.Errorf("unable to get user with provided access identifier, err: %v", getErr)
map[string]string{"access_id": NB_CONTROLLER_ACCESS_ID},
)
if err != nil {
fmt.Printf("Unable to get user with provided access identifier, err: %v\n", err)
os.Exit(1)
} }
if len(resources.Resources) == 1 { if len(resources.Resources) == 1 {
resourceData, err := json.Marshal(resources.Resources[0].ResourceData) clientAccess, parseErr := ParseResourceDataToClientAccess(resources.Resources[0])
if err != nil { if parseErr != nil {
fmt.Printf("Unable to encode resource data interface to json, err: %v\n", err) return parseErr
os.Exit(1)
}
var clientResourceData ClientResourceData
err = json.Unmarshal(resourceData, &clientResourceData)
if err != nil {
fmt.Printf("Unable to decode resource data json to structure, err: %v\n", err)
os.Exit(1)
}
internalUsageAccess = ClientAccess{
ClientResourceData: ClientResourceData{
UserID: clientResourceData.UserID,
AccessID: clientResourceData.AccessID,
Name: clientResourceData.Name,
Description: clientResourceData.Description,
BlockchainAccess: clientResourceData.BlockchainAccess,
ExtendedMethods: clientResourceData.ExtendedMethods,
},
} }
internalUsageAccess = *clientAccess
log.Printf( log.Printf(
"Internal crawlers access set, resource id: %s, blockchain access: %t, extended methods: %t", "Internal crawlers access set, resource id: %s, blockchain access: %t, extended methods: %t",
resources.Resources[0].Id, clientResourceData.BlockchainAccess, clientResourceData.ExtendedMethods, resources.Resources[0].Id, internalUsageAccess.ClientResourceData.BlockchainAccess, internalUsageAccess.ClientResourceData.ExtendedMethods,
) )
} else if len(resources.Resources) == 0 { } else if len(resources.Resources) == 0 {
@ -173,15 +150,13 @@ func Server() {
} }
fmt.Printf("There are no provided NB_CONTROLLER_ACCESS_ID records in Brood resources. Using provided with environment variable or randomly generated\n") fmt.Printf("There are no provided NB_CONTROLLER_ACCESS_ID records in Brood resources. Using provided with environment variable or randomly generated\n")
} else { } else {
fmt.Printf("User with provided access identifier has wrong number of resources: %d\n", len(resources.Resources)) return fmt.Errorf("user with provided access identifier has wrong number of resources: %d\n", len(resources.Resources))
os.Exit(1)
} }
// Fill NodeConfigList with initial nodes from environment variables // Fill NodeConfigList with initial nodes from environment variables
err = LoadConfig(stateCLI.configPathFlag) err = LoadConfig(configPath)
if err != nil { if err != nil {
fmt.Println(err) return err
os.Exit(1)
} }
supportedBlockchains = make(map[string]bool) supportedBlockchains = make(map[string]bool)
@ -189,8 +164,7 @@ func Server() {
for i, nodeConfig := range nodeConfigs { for i, nodeConfig := range nodeConfigs {
endpoint, err := url.Parse(nodeConfig.Endpoint) endpoint, err := url.Parse(nodeConfig.Endpoint)
if err != nil { if err != nil {
fmt.Println(err) return err
os.Exit(1)
} }
// Append to supported blockchain set // Append to supported blockchain set
@ -251,7 +225,7 @@ func Server() {
commonHandler = panicMiddleware(commonHandler) commonHandler = panicMiddleware(commonHandler)
server := http.Server{ server := http.Server{
Addr: fmt.Sprintf("%s:%s", stateCLI.listeningAddrFlag, stateCLI.listeningPortFlag), Addr: fmt.Sprintf("%s:%s", listeningHostAddr, listeningPort),
Handler: commonHandler, Handler: commonHandler,
ReadTimeout: 40 * time.Second, ReadTimeout: 40 * time.Second,
WriteTimeout: 40 * time.Second, WriteTimeout: 40 * time.Second,
@ -259,17 +233,18 @@ func Server() {
// Start node health checking and current block fetching // Start node health checking and current block fetching
blockchainPool.HealthCheck() blockchainPool.HealthCheck()
if stateCLI.enableHealthCheckFlag { if enableHealthCheck {
go initHealthCheck(stateCLI.enableDebugFlag) go initHealthCheck(NB_ENABLE_DEBUG)
} }
// Start access id cache cleaning // Start access id cache cleaning
go initCacheCleaning(stateCLI.enableDebugFlag) go initCacheCleaning(NB_ENABLE_DEBUG)
log.Printf("Starting node load balancer HTTP server at %s:%s", stateCLI.listeningAddrFlag, stateCLI.listeningPortFlag) log.Printf("Starting node load balancer HTTP server at %s:%s", listeningHostAddr, listeningPort)
err = server.ListenAndServe() err = server.ListenAndServe()
if err != nil { if err != nil {
fmt.Printf("Failed to start server listener, err: %v\n", err) return fmt.Errorf("failed to start server listener, err: %v", err)
os.Exit(1)
} }
return nil
} }

Wyświetl plik

@ -1,3 +1,3 @@
package main package main
var NB_VERSION = "0.2.7" var NB_VERSION = "0.2.8"

Wyświetl plik

@ -10,10 +10,10 @@ EnvironmentFile=/home/ubuntu/nodebalancer-secrets/app.env
Restart=on-failure Restart=on-failure
RestartSec=15s RestartSec=15s
ExecStart=/home/ubuntu/api/nodebalancer/nodebalancer server \ ExecStart=/home/ubuntu/api/nodebalancer/nodebalancer server \
-host "${AWS_LOCAL_IPV4}" \ --host "${AWS_LOCAL_IPV4}" \
-port 8544 \ --port 8544 \
-healthcheck \ --healthcheck \
-config /home/ubuntu/.nodebalancer/config.json --config /home/ubuntu/.nodebalancer/config.json
SyslogIdentifier=nodebalancer SyslogIdentifier=nodebalancer
[Install] [Install]

Wyświetl plik

@ -3,7 +3,14 @@ module github.com/bugout-dev/moonstream/nodes/node_balancer
go 1.17 go 1.17
require ( require (
github.com/bugout-dev/bugout-go v0.4.3 github.com/bugout-dev/bugout-go v0.4.6
github.com/bugout-dev/humbug/go v0.0.0-20211206230955-57607cd2d205 github.com/bugout-dev/humbug/go v0.0.0-20230713220619-2cd74a2b36d7
github.com/google/uuid v1.3.0 github.com/google/uuid v1.6.0
github.com/urfave/cli/v2 v2.27.5
)
require (
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
) )

Wyświetl plik

@ -12,6 +12,7 @@ cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2k
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@ -23,10 +24,10 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/bugout-dev/bugout-go v0.4.3 h1:sTwMgNDZR8mK0BkPVfXIsFWZAkL8ePKr5Xmj35A2O3o= github.com/bugout-dev/bugout-go v0.4.6 h1:HaXoVNVZYqd6BaPwlQGhWKBYdGc2lhF3BRxIgyL+1SY=
github.com/bugout-dev/bugout-go v0.4.3/go.mod h1:P4+788iHtt/32u2wIaRTaiXTWpvSVBYxZ01qQ8N7eB8= github.com/bugout-dev/bugout-go v0.4.6/go.mod h1:P4+788iHtt/32u2wIaRTaiXTWpvSVBYxZ01qQ8N7eB8=
github.com/bugout-dev/humbug/go v0.0.0-20211206230955-57607cd2d205 h1:UQ7XGjvoOVKGRIuTFXgqGtU/UgMOk8+ikpoHWrWefjQ= github.com/bugout-dev/humbug/go v0.0.0-20230713220619-2cd74a2b36d7 h1:Mn6t3HO056/++m5UESl/06FdSxz84S1p7pfQA+NZwVo=
github.com/bugout-dev/humbug/go v0.0.0-20211206230955-57607cd2d205/go.mod h1:U/NXHfc3tzGeQz+xVfpifXdPZi7p6VV8xdP/4ZKeWJU= github.com/bugout-dev/humbug/go v0.0.0-20230713220619-2cd74a2b36d7/go.mod h1:U/NXHfc3tzGeQz+xVfpifXdPZi7p6VV8xdP/4ZKeWJU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
@ -35,6 +36,8 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
@ -65,8 +68,8 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
@ -142,6 +145,8 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
@ -163,7 +168,11 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
@ -282,6 +291,7 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

Wyświetl plik

@ -3,8 +3,9 @@ export BUGOUT_BROOD_URL="https://auth.bugout.dev"
export NB_BUGOUT_TIMEOUT_SECONDS=15 export NB_BUGOUT_TIMEOUT_SECONDS=15
export MOONSTREAM_APPLICATION_ID="<application_id_to_controll_access>" export MOONSTREAM_APPLICATION_ID="<application_id_to_controll_access>"
export MOONSTREAM_CORS_ALLOWED_ORIGINS="http://localhost:3000,https://moonstream.to,https://portal.moonstream.to" export MOONSTREAM_CORS_ALLOWED_ORIGINS="http://localhost:3000,https://moonstream.to,https://portal.moonstream.to"
export NB_CONTROLLER_TOKEN="<token_of_controller_user>" export NB_CONTROLLER_USER_ID="<bugout_id_of_nodebalancer_user>"
export NB_CONTROLLER_ACCESS_ID="<controller_access_id_for_internal_usage>" export NB_CONTROLLER_TOKEN="<token_of_nodebalancer_user>"
export NB_CONTROLLER_ACCESS_ID="<nodebalancer_access_id_for_internal_usage>"
# Error humbug reporter # Error humbug reporter
export HUMBUG_REPORTER_NODE_BALANCER_TOKEN="<bugout_humbug_token_for_crash_reports>" export HUMBUG_REPORTER_NODE_BALANCER_TOKEN="<bugout_humbug_token_for_crash_reports>"