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:
alex
2025-07-09 01:21:34 +03:00
parent 8d01314ded
commit 9919f77c90
31 changed files with 1186 additions and 275 deletions

View 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
}

View 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
}

View 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
}

View 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
View 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
}