mirror of
https://github.com/akyaiy/GoSally-mvp.git
synced 2026-01-07 01:32:25 +00:00
Refactor core configuration and UUID handling
- Changed UUIDLength type from byte to int in core/config/consts.go - Introduced MetaDir constant in core/config/consts.go - Added corestate package with initial state management and UUID handling - Implemented GetNodeUUID and SetNodeUUID functions for UUID file management - Created RunManager and RunFileManager for runtime directory management - Updated GeneralServer to use new configuration structure - Removed deprecated init package and replaced with main entry point - Added color utility functions for logging - Enhanced UUID generation functions in utils package - Updated update logic to handle new configuration structure - Added routines for cleaning temporary runtime directories - Introduced response formatting for API responses
This commit is contained in:
22
core/corestate/corestate.go
Normal file
22
core/corestate/corestate.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package corestate
|
||||
|
||||
type Stage string
|
||||
|
||||
const (
|
||||
StageNotReady Stage = "init"
|
||||
StagePreInit Stage = "pre-init"
|
||||
StagePostInit Stage = "post-init"
|
||||
StageReady Stage = "event"
|
||||
)
|
||||
|
||||
const (
|
||||
StringsNone string = "none"
|
||||
)
|
||||
|
||||
func NewCorestate(o *CoreState) *CoreState {
|
||||
// TODO: create a convenient interface for creating a state
|
||||
// if !utils.IsFullyInitialized(o) {
|
||||
// return nil, fmt.Errorf("CoreState is not fully initialized")
|
||||
// }
|
||||
return o
|
||||
}
|
||||
80
core/corestate/node_uuid.go
Normal file
80
core/corestate/node_uuid.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package corestate
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/akyaiy/GoSally-mvp/core/config"
|
||||
"github.com/akyaiy/GoSally-mvp/core/utils"
|
||||
)
|
||||
|
||||
// GetNodeUUID outputs the correct uuid from the file at the path specified in the arguments.
|
||||
// If the uuid is not correct or is not exist, an empty string and an error will be returned.
|
||||
// The path to the identifier must contain the path to the "uuid" directory,
|
||||
// not the file with the identifier itself, for example: "uuid/data"
|
||||
func GetNodeUUID(metaInfPath string) (string, error) {
|
||||
uuid, err := readNodeUUIDRaw(filepath.Join(metaInfPath, "data"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hex.EncodeToString(uuid[:]), nil
|
||||
}
|
||||
|
||||
func readNodeUUIDRaw(p string) ([]byte, error) {
|
||||
data, err := os.ReadFile(p)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
if len(data) != config.GetInternalConsts().GetUUIDLength() {
|
||||
return data, errors.New("decoded UUID length mismatch")
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// SetNodeUUID sets the identifier to the given path.
|
||||
// The function replaces the identifier's associated directory with all its contents.
|
||||
func SetNodeUUID(metaInfPath string) error {
|
||||
if !strings.HasSuffix(metaInfPath, "uuid") {
|
||||
return errors.New("invalid meta/uuid path")
|
||||
}
|
||||
info, err := os.Stat(metaInfPath)
|
||||
if err == nil && info.IsDir() {
|
||||
err = os.RemoveAll(metaInfPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.MkdirAll(metaInfPath, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dataPath := filepath.Join(metaInfPath, "data")
|
||||
|
||||
uuidStr, err := utils.NewUUID32Raw()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.WriteFile(dataPath, uuidStr[:], 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
readmePath := filepath.Join(metaInfPath, "README.txt")
|
||||
readmeContent := ` - - - - ! STRICTLY FORBIDDEN TO MODIFY THIS DIRECTORY ! - - - -
|
||||
This directory contains the unique node identifier stored in the file named data.
|
||||
This identifier is critical for correct node recognition both locally and across the network.
|
||||
Any modification, deletion, or tampering with this directory may lead to permanent loss of identity, data corruption, or network conflicts.
|
||||
Proceed at your own risk. You have been warned.`
|
||||
err = os.WriteFile(readmePath, []byte(readmeContent), 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
93
core/corestate/run_file_manager.go
Normal file
93
core/corestate/run_file_manager.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package corestate
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (r *RunManager) File(index string) RunFileManagerContract {
|
||||
value, ok := (*r.indexedPaths)[index]
|
||||
if !ok {
|
||||
err := r.indexPaths()
|
||||
if err != nil {
|
||||
return &RunFileManager{
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
value, ok = (*r.indexedPaths)[index]
|
||||
if !ok {
|
||||
return &RunFileManager{
|
||||
err: fmt.Errorf("cannot detect file under index %s", index),
|
||||
}
|
||||
}
|
||||
}
|
||||
return &RunFileManager{
|
||||
indexedPath: value,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RunFileManager) Open() (*os.File, error) {
|
||||
if r.err != nil {
|
||||
return nil, r.err
|
||||
}
|
||||
file, err := os.OpenFile(r.indexedPath, os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.file = file
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func (r *RunFileManager) Close() error {
|
||||
return r.file.Close()
|
||||
}
|
||||
|
||||
func (r *RunFileManager) Watch(ctx context.Context, callback func()) error {
|
||||
if r.err != nil {
|
||||
return r.err
|
||||
}
|
||||
if r.file == nil {
|
||||
return fmt.Errorf("file is not opened")
|
||||
}
|
||||
|
||||
info, err := r.file.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
origStat := info.Sys().(*syscall.Stat_t)
|
||||
origIno := origStat.Ino
|
||||
origModTime := info.ModTime()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
newInfo, err := os.Stat(r.indexedPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
callback()
|
||||
return
|
||||
}
|
||||
} else {
|
||||
newStat := newInfo.Sys().(*syscall.Stat_t)
|
||||
if newStat.Ino != origIno {
|
||||
callback()
|
||||
return
|
||||
}
|
||||
if !newInfo.ModTime().Equal(origModTime) {
|
||||
callback()
|
||||
return
|
||||
}
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
115
core/corestate/run_manager.go
Normal file
115
core/corestate/run_manager.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package corestate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/akyaiy/GoSally-mvp/core/utils"
|
||||
)
|
||||
|
||||
func NewRM() *RunManager {
|
||||
return &RunManager{
|
||||
indexedPaths: func() *map[string]string { m := make(map[string]string); return &m }(),
|
||||
created: false,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CoreState) RuntimeDir() RunManagerContract {
|
||||
return c.RM
|
||||
}
|
||||
|
||||
// Create creates a temp directory
|
||||
func (r *RunManager) Create(uuid32 string) (string, error) {
|
||||
if r.created {
|
||||
return r.runDir, fmt.Errorf("runtime directory is already created")
|
||||
}
|
||||
path, err := os.MkdirTemp("", fmt.Sprintf("*-%s-%s", uuid32, "gosally-runtime"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
r.runDir = path
|
||||
r.created = true
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func (r *RunManager) Clean() error {
|
||||
return utils.CleanTempRuntimes(r.runDir)
|
||||
}
|
||||
|
||||
// Quite dangerous and goofy.
|
||||
// TODO: implement a better variant of runDir indexing on the second stage of initialization
|
||||
func (r *RunManager) Toggle() string {
|
||||
r.runDir = filepath.Dir(os.Args[0])
|
||||
r.created = true
|
||||
return r.runDir
|
||||
}
|
||||
|
||||
func (r *RunManager) Get(index string) (string, error) {
|
||||
if !r.created {
|
||||
return "", fmt.Errorf("runtime directory is not created")
|
||||
}
|
||||
if r.indexedPaths == nil {
|
||||
err := r.indexPaths()
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
if r.indexedPaths == nil {
|
||||
return "", fmt.Errorf("indexedPaths is nil")
|
||||
}
|
||||
value, ok := (*r.indexedPaths)[index]
|
||||
if !ok {
|
||||
err := r.indexPaths()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
value, ok = (*r.indexedPaths)[index]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("cannot detect file under index %s", index)
|
||||
}
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (r *RunManager) Set(index string) error {
|
||||
if !r.created {
|
||||
return fmt.Errorf("runtime directory is not created")
|
||||
}
|
||||
fullPath := filepath.Join(r.runDir, index)
|
||||
|
||||
dir := filepath.Dir(fullPath)
|
||||
err := os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(fullPath, os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if r.indexedPaths == nil {
|
||||
err = r.indexPaths()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
(*r.indexedPaths)[index] = fullPath
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *RunManager) indexPaths() error {
|
||||
if !r.created {
|
||||
return fmt.Errorf("runtime directory is not created")
|
||||
}
|
||||
i, err := utils.IndexPaths(r.runDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.indexedPaths = i
|
||||
return nil
|
||||
}
|
||||
62
core/corestate/types.go
Normal file
62
core/corestate/types.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package corestate
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
)
|
||||
|
||||
// CoreStateContract is interface for CoreState.
|
||||
// CoreState is a structure that contains the basic meta-information vital to the node.
|
||||
// The interface contains functionality for working with the Runtime directory and its files,
|
||||
// and access to low-level logging in stdout
|
||||
type CoreStateContract interface {
|
||||
RuntimeDir() RunManagerContract
|
||||
}
|
||||
|
||||
type CoreState struct {
|
||||
UUID32 string
|
||||
UUID32DirName string
|
||||
|
||||
StartTimestampUnix int64
|
||||
|
||||
NodeBinName string
|
||||
NodeVersion string
|
||||
|
||||
Stage Stage
|
||||
|
||||
NodePath string
|
||||
MetaDir string
|
||||
RunDir string
|
||||
|
||||
RM *RunManager
|
||||
}
|
||||
|
||||
type RunManagerContract interface {
|
||||
Get(index string) (string, error)
|
||||
|
||||
// Set recursively creates a file in runDir
|
||||
Set(index string) error
|
||||
|
||||
File(index string) RunFileManagerContract
|
||||
|
||||
indexPaths() error
|
||||
}
|
||||
|
||||
type RunManager struct {
|
||||
created bool
|
||||
runDir string
|
||||
// I obviously keep it with a pointer because it makes me feel calmer
|
||||
indexedPaths *map[string]string
|
||||
}
|
||||
|
||||
type RunFileManagerContract interface {
|
||||
Open() (*os.File, error)
|
||||
Close() error
|
||||
Watch(ctx context.Context, callback func()) error
|
||||
}
|
||||
|
||||
type RunFileManager struct {
|
||||
err error
|
||||
indexedPath string
|
||||
file *os.File
|
||||
}
|
||||
Reference in New Issue
Block a user