diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index bf17c80..0789cc0 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -56,22 +56,54 @@ jobs: with: go-version: '1.24.x' - - name: Installing Agent Dependencies - run: cd agent && go get + - name: Installing Dependencies + run: go mod tidy - name: Vet Agent - run: cd agent && go vet + run: go vet ./agent + + - name: Create build directory + run: mkdir -p build - name: Build Agent - run: cd agent && go build -o build/agent main.go + run: go build -o build/agent agent/main.go - name: Verify Binary - run: file agent/build/agent + run: file build/agent - name: Success run: echo "✅ All Agent checks passed." + relay-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '1.24.x' + + - name: Installing Dependencies + run: go mod tidy + + - name: Vet Relay + run: go vet ./relay + + - name: Create build directory + run: mkdir -p build + + - name: Build Relay + run: go build -o build/relay relay/main.go + + - name: Verify Binary + run: file build/relay + + - name: Success + run: echo "✅ All Relay checks passed." + + master-check: runs-on: ubuntu-latest steps: @@ -82,17 +114,20 @@ jobs: with: go-version: '1.24.x' - - name: Installing Master Dependencies - run: cd master && go get + - name: Installing Dependencies + run: go mod tidy - name: Vet Master - run: cd master && go vet - + run: go vet ./master + + - name: Create build directory + run: mkdir -p build + - name: Build Master - run: cd master && go build -o build/master main.go + run: go build -o build/master master/main.go - name: Verify Binary - run: file master/build/master + run: file build/master - name: Success run: echo "✅ All Master checks passed." diff --git a/agent/config.yaml b/agent/config.yaml index 4508d38..44e3434 100644 --- a/agent/config.yaml +++ b/agent/config.yaml @@ -1,9 +1,6 @@ -server_url: "http://localhost:8080/report" +relay_url: "http://localhost:8080/report" report_interval: 1 #(seconds) -thresholds: - cpu_percentage: 80.0 - memory_percentage: 90.0 - +wavelenght_duration: 1 #(seconds) diff --git a/agent/go.mod b/agent/go.mod deleted file mode 100644 index 01e26f5..0000000 --- a/agent/go.mod +++ /dev/null @@ -1,17 +0,0 @@ -module agent - -go 1.24.5 - -require github.com/shirou/gopsutil/v4 v4.25.6 - -require ( - github.com/ebitengine/purego v0.8.4 // indirect - github.com/go-ole/go-ole v1.3.0 // indirect - github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect - github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect - github.com/tklauser/go-sysconf v0.3.15 // indirect - github.com/tklauser/numcpus v0.10.0 // indirect - github.com/yusufpapurcu/wmi v1.2.4 // indirect - golang.org/x/sys v0.34.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) diff --git a/agent/main.go b/agent/main.go index 90b8bd2..614f4f2 100644 --- a/agent/main.go +++ b/agent/main.go @@ -11,37 +11,17 @@ import ( "github.com/shirou/gopsutil/v4/mem" "gopkg.in/yaml.v3" + "net/http" "time" - "net/http" + "github.com/Coder-Harshit/RefleXSys/common" ) -type signal struct { - HostID string `json:"host_id"` - Hostname string `json:"host_name"` - TotalMem uint64 `json:"total_memory"` - UsedMem uint64 `json:"used_memory"` - UsedMemPercentage float64 `json:"used_memory_percentage"` - CPUPercentage float64 `json:"cpu_used_percentage"` - Timestamp time.Time `json:"timestamp"` -} - -type Thresholds struct { - CPUPercentage float64 `yaml:"cpu_percentage"` - UsedMemPercentage float64 `yaml:"memory_percentage"` -} - -type Config struct { - ServerURL string `yaml:"server_url"` - ReportInterval int `yaml:"report_interval"` - Thresholds Thresholds `yaml:"thresholds"` -} - func main() { conf, err := loadConfig() errorCheck(err, "Config Failed to load") - url := conf.ServerURL + url := conf.RelayURL hid, err := host.HostID() errorCheck(err, "[HostID] Object creation Issue!") @@ -58,7 +38,7 @@ func main() { cpupr, err := cpu.Percent(time.Second, false) errorCheck(err, "[CPUPercentage] Object creation Issue!") - signal := signal{ + signal := common.Signal{ HostID: hid, Hostname: info.Hostname, TotalMem: vmem.Total, @@ -108,13 +88,13 @@ func errorCheck(err error, msg string) { } } -func loadConfig() (*Config, error) { +func loadConfig() (*common.AgentConfig, error) { data, err := os.ReadFile("config.yaml") errorCheck(err, "Error Reading File") - var config Config + var config common.AgentConfig if len(data) == 0 { // empty config file ... create a default one and read it - config.ServerURL = "http://localhost:8080/report" + config.RelayURL = "http://localhost:8080/report" config.ReportInterval = 1 config.Thresholds.CPUPercentage = 80.0 config.Thresholds.UsedMemPercentage = 90.0 diff --git a/common/types.go b/common/types.go new file mode 100644 index 0000000..444f636 --- /dev/null +++ b/common/types.go @@ -0,0 +1,38 @@ +package common + +import ( + "time" +) + +type Signal struct { + HostID string `json:"host_id"` + Hostname string `json:"host_name"` + TotalMem uint64 `json:"total_memory"` + UsedMem uint64 `json:"used_memory"` + UsedMemPercentage float64 `json:"used_memory_percentage"` + CPUPercentage float64 `json:"cpu_used_percentage"` + Timestamp time.Time `json:"timestamp"` +} + +type ServerConfig struct { + Port uint16 `yaml:"port"` + LogLevel string `yaml:"logging_level"` +} + +type Thresholds struct { + CPUPercentage float64 `yaml:"cpu_percentage"` + UsedMemPercentage float64 `yaml:"memory_percentage"` +} + +type AgentConfig struct { + RelayURL string `yaml:"relay_url"` + ReportInterval int `yaml:"report_interval"` + Thresholds Thresholds `yaml:"thresholds"` +} + +type RelayConfig struct { + Port uint16 `yaml:"port"` + LogLevel string `yaml:"logging_level"` + FlushInterval int `yaml:"flush_interval"` + MasterUrl string `yaml:"master_url"` +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..4f24ca6 --- /dev/null +++ b/go.mod @@ -0,0 +1,19 @@ +module github.com/Coder-Harshit/RefleXSys + +go 1.24.6 + +require ( + github.com/shirou/gopsutil/v4 v4.25.7 + gopkg.in/yaml.v3 v3.0.1 +) + +require ( + github.com/ebitengine/purego v0.8.4 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/tklauser/go-sysconf v0.3.15 // indirect + github.com/tklauser/numcpus v0.10.0 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect + golang.org/x/sys v0.34.0 // indirect +) diff --git a/agent/go.sum b/go.sum similarity index 67% rename from agent/go.sum rename to go.sum index 27b2fbb..818ded3 100644 --- a/agent/go.sum +++ b/go.sum @@ -2,19 +2,19 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw= github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= -github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 h1:PpXWgLPs+Fqr325bN2FD2ISlRRztXibcX6e8f5FR5Dc= -github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= -github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs= -github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/shirou/gopsutil/v4 v4.25.7 h1:bNb2JuqKuAu3tRlPv5piSmBZyMfecwQ+t/ILq+1JqVM= +github.com/shirou/gopsutil/v4 v4.25.7/go.mod h1:XV/egmwJtd3ZQjBpJVY5kndsiOO4IRqy9TQnmm6VP7U= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4= @@ -25,9 +25,10 @@ github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/master/config.yaml b/master/config.yaml index b9e5e44..c07685f 100644 --- a/master/config.yaml +++ b/master/config.yaml @@ -1,5 +1,8 @@ -port: 8080 +port: 9090 # Levels-> { "info" , "warn" , "debug" } logging_level: "info" +addons: [ + +] \ No newline at end of file diff --git a/master/go.mod b/master/go.mod deleted file mode 100644 index a9e06fc..0000000 --- a/master/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module master - -go 1.24.5 - -require gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/master/go.sum b/master/go.sum deleted file mode 100644 index 4bc0337..0000000 --- a/master/go.sum +++ /dev/null @@ -1,3 +0,0 @@ -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/master/main.go b/master/main.go index eb71e7e..977f080 100644 --- a/master/main.go +++ b/master/main.go @@ -6,59 +6,48 @@ import ( "log" "net/http" "os" - "time" + "github.com/Coder-Harshit/RefleXSys/common" "gopkg.in/yaml.v3" ) -type signal struct { - HostID string `json:"host_id"` - Hostname string `json:"host_name"` - TotalMem uint64 `json:"total_memory"` - UsedMem uint64 `json:"used_memory"` - UsedMemPercentage float64 `json:"used_memory_percentage"` - CPUPercentage float64 `json:"cpu_used_percentage"` - Timestamp time.Time `json:"timestamp"` -} - -type Config struct { - Port uint16 `yaml:"port"` - LogLevel string `yaml:"logging_level"` -} - func main() { conf, err := loadConfig() if err != nil { fmt.Printf("error reading file: %v", err) } - fmt.Println("Server Listing on Port :", conf.Port, conf.LogLevel) + fmt.Println("Master Server Listing on Port :", conf.Port, conf.LogLevel) http.HandleFunc("/report", displayReport) log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", conf.Port), nil)) } func displayReport(respw http.ResponseWriter, req *http.Request) { if req.Method == "POST" { - // fmt.Println("Got Signal!") - var sig signal - if err := json.NewDecoder(req.Body).Decode(&sig); err != nil { + var signals []common.Signal + if err := json.NewDecoder(req.Body).Decode(&signals); err != nil { http.Error(respw, "Invalid JSON", http.StatusBadRequest) return } - fmt.Printf("[RECEIVED] Host: %s | CPU: %.2f%% | RAM: %.2f%% | Time: %v\n", - sig.Hostname, sig.CPUPercentage, sig.UsedMemPercentage, sig.Timestamp) + fmt.Printf("[RECEIVED BATCH] %d signals\n", len(signals)) + for _, sig := range signals { + fmt.Printf("%+v\n", sig) + } respw.WriteHeader(http.StatusOK) + } else { + http.Error(respw, "Method Not Allowed", http.StatusMethodNotAllowed) + return } } -func loadConfig() (*Config, error) { +func loadConfig() (*common.ServerConfig, error) { data, err := os.ReadFile("config.yaml") if err != nil { return nil, fmt.Errorf("error reading file: %v", err) } - var config Config + var config common.ServerConfig if len(data) == 0 { // empty config file ... create a default one and read it - config.Port = 8080 + config.Port = 9090 config.LogLevel = "info" _, err = yaml.Marshal(&config) diff --git a/relay/config.yaml b/relay/config.yaml new file mode 100644 index 0000000..5c06c8d --- /dev/null +++ b/relay/config.yaml @@ -0,0 +1,8 @@ +port: 8080 + +# Levels-> { "info" , "warn" , "debug" } +logging_level: "info" + +flush_interval: 5 # in seconds + +master_url: "http://localhost:9090/report" \ No newline at end of file diff --git a/relay/main.go b/relay/main.go new file mode 100644 index 0000000..ec6285c --- /dev/null +++ b/relay/main.go @@ -0,0 +1,120 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "log" + "net/http" + "os" + "sync" + "time" + + "github.com/Coder-Harshit/RefleXSys/common" + "gopkg.in/yaml.v3" +) + +type SignalBuffer struct { + sync.Mutex + signals []common.Signal +} + +var buffer SignalBuffer + +func main() { + conf, err := loadConfig() + if err != nil { + fmt.Printf("error reading file: %v", err) + } + fmt.Println("Relay Server Listing on Port :", conf.Port, conf.LogLevel) + http.HandleFunc("/report", displayReport) + go flushing(time.Duration(conf.FlushInterval)*time.Second, func(s []common.Signal) { + if len(s) == 0 { + return + } + jsonBatch, _ := json.Marshal(s) + resp, err := http.Post(conf.MasterUrl, "application/json", bytes.NewReader(jsonBatch)) + if err != nil { + fmt.Println("Error encountered while informing MASTER!") + return + } + resp.Body.Close() + + }) + // go flushing(time.Duration(conf.FlushInterval)*time.Second, conf.MasterUrl) + log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", conf.Port), nil)) +} + +func displayReport(respw http.ResponseWriter, req *http.Request) { + if req.Method == "POST" { + var sig common.Signal + if err := json.NewDecoder(req.Body).Decode(&sig); err != nil { + http.Error(respw, "Invalid JSON", http.StatusBadRequest) + return + } + fmt.Printf("[RECEIVED] %+v\n", sig) + // fmt.Printf("buffer.signals: %v\n", buffer.signals) + + buffer.Lock() + buffer.signals = append(buffer.signals, sig) + buffer.Unlock() + + respw.WriteHeader(http.StatusOK) + } else { + http.Error(respw, "Method Not Allowed", http.StatusMethodNotAllowed) + return + } +} + +func flushing(interval time.Duration, sendFun func([]common.Signal)) { + // func flushing(interval time.Duration, masterUrl string) { + // To flush the buffer signals via the sendFun function + for { + // always running loop + + time.Sleep(interval) + buffer.Lock() + batch := buffer.signals + buffer.signals = nil + buffer.Unlock() + + if len(batch) > 0 { + sendFun(batch) + // informMaster(masterUrl, batch) + } + + } +} + +func informMaster(url string, batch []common.Signal) { + if len(batch) == 0 { + return + } + jsonBatch, _ := json.Marshal(batch) + http.Post(url, "application/json", bytes.NewReader(jsonBatch)) +} + +func loadConfig() (*common.RelayConfig, error) { + data, err := os.ReadFile("config.yaml") + if err != nil { + return nil, fmt.Errorf("error reading file: %v", err) + } + var config common.RelayConfig + if len(data) == 0 { + // empty config file ... create a default one and read it + config.Port = 8080 + config.LogLevel = "info" + config.FlushInterval = 5 + + _, err = yaml.Marshal(&config) + if err != nil { + return nil, fmt.Errorf("error creating default config file: %v", err) + } + } else { + err = yaml.Unmarshal([]byte(data), &config) + if err != nil { + return nil, fmt.Errorf("error reading file: %v", err) + } + } + return &config, nil +}