mirror of
https://github.com/fankes/beszel.git
synced 2025-10-19 17:59:28 +08:00
change agent file structure
This commit is contained in:
177
agent/types.go
177
agent/types.go
@@ -1,177 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
type SystemData struct {
|
|
||||||
Stats *SystemStats `json:"stats"`
|
|
||||||
Info *SystemInfo `json:"info"`
|
|
||||||
Containers []*ContainerStats `json:"container"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SystemInfo struct {
|
|
||||||
Cores int `json:"c"`
|
|
||||||
Threads int `json:"t"`
|
|
||||||
CpuModel string `json:"m"`
|
|
||||||
// Os string `json:"o"`
|
|
||||||
Uptime uint64 `json:"u"`
|
|
||||||
Cpu float64 `json:"cpu"`
|
|
||||||
MemPct float64 `json:"mp"`
|
|
||||||
DiskPct float64 `json:"dp"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SystemStats struct {
|
|
||||||
Cpu float64 `json:"cpu"`
|
|
||||||
Mem float64 `json:"m"`
|
|
||||||
MemUsed float64 `json:"mu"`
|
|
||||||
MemPct float64 `json:"mp"`
|
|
||||||
MemBuffCache float64 `json:"mb"`
|
|
||||||
Swap float64 `json:"s"`
|
|
||||||
SwapUsed float64 `json:"su"`
|
|
||||||
Disk float64 `json:"d"`
|
|
||||||
DiskUsed float64 `json:"du"`
|
|
||||||
DiskPct float64 `json:"dp"`
|
|
||||||
DiskRead float64 `json:"dr"`
|
|
||||||
DiskWrite float64 `json:"dw"`
|
|
||||||
NetworkSent float64 `json:"ns"`
|
|
||||||
NetworkRecv float64 `json:"nr"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ContainerStats struct {
|
|
||||||
Name string `json:"n"`
|
|
||||||
Cpu float64 `json:"c"`
|
|
||||||
Mem float64 `json:"m"`
|
|
||||||
NetworkSent float64 `json:"ns"`
|
|
||||||
NetworkRecv float64 `json:"nr"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Container struct {
|
|
||||||
Id string
|
|
||||||
IdShort string
|
|
||||||
Names []string
|
|
||||||
Status string
|
|
||||||
// Image string
|
|
||||||
// ImageID string
|
|
||||||
// Command string
|
|
||||||
// Created int64
|
|
||||||
// Ports []Port
|
|
||||||
// SizeRw int64 `json:",omitempty"`
|
|
||||||
// SizeRootFs int64 `json:",omitempty"`
|
|
||||||
// Labels map[string]string
|
|
||||||
// State string
|
|
||||||
// HostConfig struct {
|
|
||||||
// NetworkMode string `json:",omitempty"`
|
|
||||||
// Annotations map[string]string `json:",omitempty"`
|
|
||||||
// }
|
|
||||||
// NetworkSettings *SummaryNetworkSettings
|
|
||||||
// Mounts []MountPoint
|
|
||||||
}
|
|
||||||
|
|
||||||
type CStats struct {
|
|
||||||
// Common stats
|
|
||||||
// Read time.Time `json:"read"`
|
|
||||||
// PreRead time.Time `json:"preread"`
|
|
||||||
|
|
||||||
// Linux specific stats, not populated on Windows.
|
|
||||||
// PidsStats PidsStats `json:"pids_stats,omitempty"`
|
|
||||||
// BlkioStats BlkioStats `json:"blkio_stats,omitempty"`
|
|
||||||
|
|
||||||
// Windows specific stats, not populated on Linux.
|
|
||||||
// NumProcs uint32 `json:"num_procs"`
|
|
||||||
// StorageStats StorageStats `json:"storage_stats,omitempty"`
|
|
||||||
// Networks request version >=1.21
|
|
||||||
Networks map[string]NetworkStats
|
|
||||||
|
|
||||||
// Shared stats
|
|
||||||
CPUStats CPUStats `json:"cpu_stats,omitempty"`
|
|
||||||
// PreCPUStats CPUStats `json:"precpu_stats,omitempty"` // "Pre"="Previous"
|
|
||||||
MemoryStats MemoryStats `json:"memory_stats,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CPUStats struct {
|
|
||||||
// CPU Usage. Linux and Windows.
|
|
||||||
CPUUsage CPUUsage `json:"cpu_usage"`
|
|
||||||
|
|
||||||
// System Usage. Linux only.
|
|
||||||
SystemUsage uint64 `json:"system_cpu_usage,omitempty"`
|
|
||||||
|
|
||||||
// Online CPUs. Linux only.
|
|
||||||
// OnlineCPUs uint32 `json:"online_cpus,omitempty"`
|
|
||||||
|
|
||||||
// Throttling Data. Linux only.
|
|
||||||
// ThrottlingData ThrottlingData `json:"throttling_data,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CPUUsage struct {
|
|
||||||
// Total CPU time consumed.
|
|
||||||
// Units: nanoseconds (Linux)
|
|
||||||
// Units: 100's of nanoseconds (Windows)
|
|
||||||
TotalUsage uint64 `json:"total_usage"`
|
|
||||||
|
|
||||||
// Total CPU time consumed per core (Linux). Not used on Windows.
|
|
||||||
// Units: nanoseconds.
|
|
||||||
// PercpuUsage []uint64 `json:"percpu_usage,omitempty"`
|
|
||||||
|
|
||||||
// Time spent by tasks of the cgroup in kernel mode (Linux).
|
|
||||||
// Time spent by all container processes in kernel mode (Windows).
|
|
||||||
// Units: nanoseconds (Linux).
|
|
||||||
// Units: 100's of nanoseconds (Windows). Not populated for Hyper-V Containers.
|
|
||||||
// UsageInKernelmode uint64 `json:"usage_in_kernelmode"`
|
|
||||||
|
|
||||||
// Time spent by tasks of the cgroup in user mode (Linux).
|
|
||||||
// Time spent by all container processes in user mode (Windows).
|
|
||||||
// Units: nanoseconds (Linux).
|
|
||||||
// Units: 100's of nanoseconds (Windows). Not populated for Hyper-V Containers
|
|
||||||
// UsageInUsermode uint64 `json:"usage_in_usermode"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type MemoryStats struct {
|
|
||||||
|
|
||||||
// current res_counter usage for memory
|
|
||||||
Usage uint64 `json:"usage,omitempty"`
|
|
||||||
Cache uint64 `json:"cache,omitempty"`
|
|
||||||
// maximum usage ever recorded.
|
|
||||||
// MaxUsage uint64 `json:"max_usage,omitempty"`
|
|
||||||
// TODO(vishh): Export these as stronger types.
|
|
||||||
// all the stats exported via memory.stat.
|
|
||||||
Stats map[string]uint64 `json:"stats,omitempty"`
|
|
||||||
// number of times memory usage hits limits.
|
|
||||||
// Failcnt uint64 `json:"failcnt,omitempty"`
|
|
||||||
// Limit uint64 `json:"limit,omitempty"`
|
|
||||||
|
|
||||||
// // committed bytes
|
|
||||||
// Commit uint64 `json:"commitbytes,omitempty"`
|
|
||||||
// // peak committed bytes
|
|
||||||
// CommitPeak uint64 `json:"commitpeakbytes,omitempty"`
|
|
||||||
// // private working set
|
|
||||||
// PrivateWorkingSet uint64 `json:"privateworkingset,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type NetworkStats struct {
|
|
||||||
// Bytes received. Windows and Linux.
|
|
||||||
RxBytes uint64 `json:"rx_bytes"`
|
|
||||||
// Bytes sent. Windows and Linux.
|
|
||||||
TxBytes uint64 `json:"tx_bytes"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type DiskIoStats struct {
|
|
||||||
Read uint64
|
|
||||||
Write uint64
|
|
||||||
Time time.Time
|
|
||||||
Filesystem string
|
|
||||||
}
|
|
||||||
|
|
||||||
type NetIoStats struct {
|
|
||||||
BytesRecv uint64
|
|
||||||
BytesSent uint64
|
|
||||||
Time time.Time
|
|
||||||
Name string
|
|
||||||
}
|
|
||||||
|
|
||||||
type PrevContainerStats struct {
|
|
||||||
Cpu [2]uint64
|
|
||||||
Net struct {
|
|
||||||
Sent uint64
|
|
||||||
Recv uint64
|
|
||||||
Time time.Time
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,54 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/blang/semver"
|
|
||||||
"github.com/rhysd/go-github-selfupdate/selfupdate"
|
|
||||||
)
|
|
||||||
|
|
||||||
func updateBeszel() {
|
|
||||||
var latest *selfupdate.Release
|
|
||||||
var found bool
|
|
||||||
var err error
|
|
||||||
currentVersion := semver.MustParse(Version)
|
|
||||||
fmt.Println("beszel-agent", currentVersion)
|
|
||||||
fmt.Println("Checking for updates...")
|
|
||||||
updater, _ := selfupdate.NewUpdater(selfupdate.Config{
|
|
||||||
Filters: []string{"beszel-agent"},
|
|
||||||
})
|
|
||||||
latest, found, err = updater.DetectLatest("henrygd/beszel")
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Error checking for updates:", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
fmt.Println("No updates found")
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("Latest version:", latest.Version)
|
|
||||||
|
|
||||||
if latest.Version.LTE(currentVersion) {
|
|
||||||
fmt.Println("You are up to date")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var binaryPath string
|
|
||||||
fmt.Printf("Updating from %s to %s...\n", currentVersion, latest.Version)
|
|
||||||
binaryPath, err = os.Executable()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Error getting binary path:", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
err = selfupdate.UpdateTo(latest.AssetURL, binaryPath)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Please try rerunning with sudo. Error:", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
fmt.Printf("Successfully updated to %s\n\n%s\n", latest.Version, strings.TrimSpace(latest.ReleaseNotes))
|
|
||||||
}
|
|
@@ -1 +1,48 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"beszel"
|
||||||
|
"beszel/internal/agent"
|
||||||
|
"beszel/internal/update"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// handle flags / subcommands
|
||||||
|
if len(os.Args) > 1 {
|
||||||
|
switch os.Args[1] {
|
||||||
|
case "-v":
|
||||||
|
fmt.Println(beszel.AppName+"-agent", beszel.Version)
|
||||||
|
case "update":
|
||||||
|
update.UpdateBeszelAgent()
|
||||||
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
var pubKey []byte
|
||||||
|
if pubKeyEnv, exists := os.LookupEnv("KEY"); exists {
|
||||||
|
pubKey = []byte(pubKeyEnv)
|
||||||
|
} else {
|
||||||
|
log.Fatal("KEY environment variable is not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
var port string
|
||||||
|
|
||||||
|
if p, exists := os.LookupEnv("PORT"); exists {
|
||||||
|
// allow passing an address in the form of "127.0.0.1:45876"
|
||||||
|
if !strings.Contains(port, ":") {
|
||||||
|
port = ":" + port
|
||||||
|
}
|
||||||
|
port = p
|
||||||
|
} else {
|
||||||
|
port = ":45876"
|
||||||
|
}
|
||||||
|
|
||||||
|
a := agent.NewAgent(pubKey, port)
|
||||||
|
|
||||||
|
a.Run()
|
||||||
|
|
||||||
|
}
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
package main
|
package agent
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"beszel/internal/entities/container"
|
||||||
|
"beszel/internal/entities/system"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -15,38 +17,49 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
sshServer "github.com/gliderlabs/ssh"
|
|
||||||
|
|
||||||
"github.com/shirou/gopsutil/v4/cpu"
|
"github.com/shirou/gopsutil/v4/cpu"
|
||||||
"github.com/shirou/gopsutil/v4/disk"
|
"github.com/shirou/gopsutil/v4/disk"
|
||||||
"github.com/shirou/gopsutil/v4/host"
|
"github.com/shirou/gopsutil/v4/host"
|
||||||
"github.com/shirou/gopsutil/v4/mem"
|
"github.com/shirou/gopsutil/v4/mem"
|
||||||
|
|
||||||
|
sshServer "github.com/gliderlabs/ssh"
|
||||||
psutilNet "github.com/shirou/gopsutil/v4/net"
|
psutilNet "github.com/shirou/gopsutil/v4/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Version = "0.1.2"
|
var Version = "0.1.2"
|
||||||
|
|
||||||
var containerStatsMap = make(map[string]*PrevContainerStats)
|
var containerStatsMap = make(map[string]*container.PrevContainerStats)
|
||||||
var containerStatsMutex = &sync.Mutex{}
|
var containerStatsMutex = &sync.Mutex{}
|
||||||
|
|
||||||
var sem = make(chan struct{}, 15)
|
type Agent struct {
|
||||||
|
port string
|
||||||
func acquireSemaphore() {
|
pubKey []byte
|
||||||
sem <- struct{}{}
|
sem chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func releaseSemaphore() {
|
func NewAgent(pubKey []byte, port string) *Agent {
|
||||||
<-sem
|
return &Agent{
|
||||||
|
pubKey: pubKey,
|
||||||
|
sem: make(chan struct{}, 15),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var diskIoStats = DiskIoStats{
|
func (a *Agent) acquireSemaphore() {
|
||||||
|
a.sem <- struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Agent) releaseSemaphore() {
|
||||||
|
<-a.sem
|
||||||
|
}
|
||||||
|
|
||||||
|
var diskIoStats = system.DiskIoStats{
|
||||||
Read: 0,
|
Read: 0,
|
||||||
Write: 0,
|
Write: 0,
|
||||||
Time: time.Now(),
|
Time: time.Now(),
|
||||||
Filesystem: "",
|
Filesystem: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
var netIoStats = NetIoStats{
|
var netIoStats = system.NetIoStats{
|
||||||
BytesRecv: 0,
|
BytesRecv: 0,
|
||||||
BytesSent: 0,
|
BytesSent: 0,
|
||||||
Time: time.Now(),
|
Time: time.Now(),
|
||||||
@@ -56,8 +69,8 @@ var netIoStats = NetIoStats{
|
|||||||
// client for docker engine api
|
// client for docker engine api
|
||||||
var dockerClient = newDockerClient()
|
var dockerClient = newDockerClient()
|
||||||
|
|
||||||
func getSystemStats() (*SystemInfo, *SystemStats) {
|
func getSystemStats() (*system.SystemInfo, *system.SystemStats) {
|
||||||
systemStats := &SystemStats{}
|
systemStats := &system.SystemStats{}
|
||||||
|
|
||||||
// cpu percent
|
// cpu percent
|
||||||
cpuPct, err := cpu.Percent(0, false)
|
cpuPct, err := cpu.Percent(0, false)
|
||||||
@@ -124,7 +137,7 @@ func getSystemStats() (*SystemInfo, *SystemStats) {
|
|||||||
netIoStats.Time = time.Now()
|
netIoStats.Time = time.Now()
|
||||||
}
|
}
|
||||||
|
|
||||||
systemInfo := &SystemInfo{
|
systemInfo := &system.SystemInfo{
|
||||||
Cpu: systemStats.Cpu,
|
Cpu: systemStats.Cpu,
|
||||||
MemPct: systemStats.MemPct,
|
MemPct: systemStats.MemPct,
|
||||||
DiskPct: systemStats.DiskPct,
|
DiskPct: systemStats.DiskPct,
|
||||||
@@ -150,21 +163,21 @@ func getSystemStats() (*SystemInfo, *SystemStats) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDockerStats() ([]*ContainerStats, error) {
|
func (a *Agent) getDockerStats() ([]*container.ContainerStats, error) {
|
||||||
resp, err := dockerClient.Get("http://localhost/containers/json")
|
resp, err := dockerClient.Get("http://localhost/containers/json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
closeIdleConnections(err)
|
closeIdleConnections(err)
|
||||||
return []*ContainerStats{}, err
|
return []*container.ContainerStats{}, err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
var containers []*Container
|
var containers []*container.Container
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&containers); err != nil {
|
if err := json.NewDecoder(resp.Body).Decode(&containers); err != nil {
|
||||||
log.Printf("Error decoding containers: %+v\n", err)
|
log.Printf("Error decoding containers: %+v\n", err)
|
||||||
return []*ContainerStats{}, err
|
return []*container.ContainerStats{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
containerStats := make([]*ContainerStats, 0, len(containers))
|
containerStats := make([]*container.ContainerStats, 0, len(containers))
|
||||||
|
|
||||||
// store valid ids to clean up old container ids from map
|
// store valid ids to clean up old container ids from map
|
||||||
validIds := make(map[string]struct{}, len(containers))
|
validIds := make(map[string]struct{}, len(containers))
|
||||||
@@ -183,7 +196,7 @@ func getDockerStats() ([]*ContainerStats, error) {
|
|||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
cstats, err := getContainerStats(ctr)
|
cstats, err := a.getContainerStats(ctr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Check if the error is a network timeout
|
// Check if the error is a network timeout
|
||||||
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
|
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
|
||||||
@@ -194,7 +207,7 @@ func getDockerStats() ([]*ContainerStats, error) {
|
|||||||
deleteContainerStatsSync(ctr.IdShort)
|
deleteContainerStatsSync(ctr.IdShort)
|
||||||
}
|
}
|
||||||
// retry once
|
// retry once
|
||||||
cstats, err = getContainerStats(ctr)
|
cstats, err = a.getContainerStats(ctr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error getting container stats: %+v\n", err)
|
log.Printf("Error getting container stats: %+v\n", err)
|
||||||
return
|
return
|
||||||
@@ -216,17 +229,17 @@ func getDockerStats() ([]*ContainerStats, error) {
|
|||||||
return containerStats, nil
|
return containerStats, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getContainerStats(ctr *Container) (*ContainerStats, error) {
|
func (a *Agent) getContainerStats(ctr *container.Container) (*container.ContainerStats, error) {
|
||||||
// use semaphore to limit concurrency
|
// use semaphore to limit concurrency
|
||||||
acquireSemaphore()
|
a.acquireSemaphore()
|
||||||
defer releaseSemaphore()
|
defer a.releaseSemaphore()
|
||||||
resp, err := dockerClient.Get("http://localhost/containers/" + ctr.IdShort + "/stats?stream=0&one-shot=1")
|
resp, err := dockerClient.Get("http://localhost/containers/" + ctr.IdShort + "/stats?stream=0&one-shot=1")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &ContainerStats{}, err
|
return &container.ContainerStats{}, err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
var statsJson CStats
|
var statsJson system.CStats
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&statsJson); err != nil {
|
if err := json.NewDecoder(resp.Body).Decode(&statsJson); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -246,7 +259,7 @@ func getContainerStats(ctr *Container) (*ContainerStats, error) {
|
|||||||
// add empty values if they doesn't exist in map
|
// add empty values if they doesn't exist in map
|
||||||
stats, initialized := containerStatsMap[ctr.IdShort]
|
stats, initialized := containerStatsMap[ctr.IdShort]
|
||||||
if !initialized {
|
if !initialized {
|
||||||
stats = &PrevContainerStats{}
|
stats = &container.PrevContainerStats{}
|
||||||
containerStatsMap[ctr.IdShort] = stats
|
containerStatsMap[ctr.IdShort] = stats
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,7 +268,7 @@ func getContainerStats(ctr *Container) (*ContainerStats, error) {
|
|||||||
systemDelta := statsJson.CPUStats.SystemUsage - stats.Cpu[1]
|
systemDelta := statsJson.CPUStats.SystemUsage - stats.Cpu[1]
|
||||||
cpuPct := float64(cpuDelta) / float64(systemDelta) * 100
|
cpuPct := float64(cpuDelta) / float64(systemDelta) * 100
|
||||||
if cpuPct > 100 {
|
if cpuPct > 100 {
|
||||||
return &ContainerStats{}, fmt.Errorf("%s cpu pct greater than 100: %+v", name, cpuPct)
|
return &container.ContainerStats{}, fmt.Errorf("%s cpu pct greater than 100: %+v", name, cpuPct)
|
||||||
}
|
}
|
||||||
stats.Cpu = [2]uint64{statsJson.CPUStats.CPUUsage.TotalUsage, statsJson.CPUStats.SystemUsage}
|
stats.Cpu = [2]uint64{statsJson.CPUStats.CPUUsage.TotalUsage, statsJson.CPUStats.SystemUsage}
|
||||||
|
|
||||||
@@ -277,7 +290,7 @@ func getContainerStats(ctr *Container) (*ContainerStats, error) {
|
|||||||
stats.Net.Recv = total_recv
|
stats.Net.Recv = total_recv
|
||||||
stats.Net.Time = time.Now()
|
stats.Net.Time = time.Now()
|
||||||
|
|
||||||
cStats := &ContainerStats{
|
cStats := &container.ContainerStats{
|
||||||
Name: name,
|
Name: name,
|
||||||
Cpu: twoDecimals(cpuPct),
|
Cpu: twoDecimals(cpuPct),
|
||||||
Mem: bytesToMegabytes(float64(usedMemory)),
|
Mem: bytesToMegabytes(float64(usedMemory)),
|
||||||
@@ -294,14 +307,14 @@ func deleteContainerStatsSync(id string) {
|
|||||||
delete(containerStatsMap, id)
|
delete(containerStatsMap, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func gatherStats() *SystemData {
|
func (a *Agent) gatherStats() *system.SystemData {
|
||||||
systemInfo, systemStats := getSystemStats()
|
systemInfo, systemStats := getSystemStats()
|
||||||
stats := &SystemData{
|
stats := &system.SystemData{
|
||||||
Stats: systemStats,
|
Stats: systemStats,
|
||||||
Info: systemInfo,
|
Info: systemInfo,
|
||||||
Containers: []*ContainerStats{},
|
Containers: []*container.ContainerStats{},
|
||||||
}
|
}
|
||||||
containerStats, err := getDockerStats()
|
containerStats, err := a.getDockerStats()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
stats.Containers = containerStats
|
stats.Containers = containerStats
|
||||||
}
|
}
|
||||||
@@ -309,9 +322,9 @@ func gatherStats() *SystemData {
|
|||||||
return stats
|
return stats
|
||||||
}
|
}
|
||||||
|
|
||||||
func startServer(addr string, pubKey []byte) {
|
func (a *Agent) startServer(addr string, pubKey []byte) {
|
||||||
sshServer.Handle(func(s sshServer.Session) {
|
sshServer.Handle(func(s sshServer.Session) {
|
||||||
stats := gatherStats()
|
stats := a.gatherStats()
|
||||||
var jsonStats []byte
|
var jsonStats []byte
|
||||||
jsonStats, _ = json.Marshal(stats)
|
jsonStats, _ = json.Marshal(stats)
|
||||||
io.WriteString(s, string(jsonStats))
|
io.WriteString(s, string(jsonStats))
|
||||||
@@ -330,24 +343,7 @@ func startServer(addr string, pubKey []byte) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func (a *Agent) Run() {
|
||||||
// handle flags / subcommands
|
|
||||||
if len(os.Args) > 1 {
|
|
||||||
switch os.Args[1] {
|
|
||||||
case "-v":
|
|
||||||
fmt.Println("beszel-agent", Version)
|
|
||||||
case "update":
|
|
||||||
updateBeszel()
|
|
||||||
}
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
var pubKey []byte
|
|
||||||
if pubKeyEnv, exists := os.LookupEnv("KEY"); exists {
|
|
||||||
pubKey = []byte(pubKeyEnv)
|
|
||||||
} else {
|
|
||||||
log.Fatal("KEY environment variable is not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
if filesystem, exists := os.LookupEnv("FILESYSTEM"); exists {
|
if filesystem, exists := os.LookupEnv("FILESYSTEM"); exists {
|
||||||
diskIoStats.Filesystem = filesystem
|
diskIoStats.Filesystem = filesystem
|
||||||
@@ -358,15 +354,7 @@ func main() {
|
|||||||
initializeDiskIoStats()
|
initializeDiskIoStats()
|
||||||
initializeNetIoStats()
|
initializeNetIoStats()
|
||||||
|
|
||||||
if port, exists := os.LookupEnv("PORT"); exists {
|
a.startServer(a.port, a.pubKey)
|
||||||
// allow passing an address in the form of "127.0.0.1:45876"
|
|
||||||
if !strings.Contains(port, ":") {
|
|
||||||
port = ":" + port
|
|
||||||
}
|
|
||||||
startServer(port, pubKey)
|
|
||||||
} else {
|
|
||||||
startServer(":45876", pubKey)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func bytesToMegabytes(b float64) float64 {
|
func bytesToMegabytes(b float64) float64 {
|
@@ -1,5 +1,29 @@
|
|||||||
package container
|
package container
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Container struct {
|
||||||
|
Id string
|
||||||
|
IdShort string
|
||||||
|
Names []string
|
||||||
|
Status string
|
||||||
|
// Image string
|
||||||
|
// ImageID string
|
||||||
|
// Command string
|
||||||
|
// Created int64
|
||||||
|
// Ports []Port
|
||||||
|
// SizeRw int64 `json:",omitempty"`
|
||||||
|
// SizeRootFs int64 `json:",omitempty"`
|
||||||
|
// Labels map[string]string
|
||||||
|
// State string
|
||||||
|
// HostConfig struct {
|
||||||
|
// NetworkMode string `json:",omitempty"`
|
||||||
|
// Annotations map[string]string `json:",omitempty"`
|
||||||
|
// }
|
||||||
|
// NetworkSettings *SummaryNetworkSettings
|
||||||
|
// Mounts []MountPoint
|
||||||
|
}
|
||||||
|
|
||||||
type ContainerStats struct {
|
type ContainerStats struct {
|
||||||
Name string `json:"n"`
|
Name string `json:"n"`
|
||||||
Cpu float64 `json:"c"`
|
Cpu float64 `json:"c"`
|
||||||
@@ -7,3 +31,12 @@ type ContainerStats struct {
|
|||||||
NetworkSent float64 `json:"ns"`
|
NetworkSent float64 `json:"ns"`
|
||||||
NetworkRecv float64 `json:"nr"`
|
NetworkRecv float64 `json:"nr"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PrevContainerStats struct {
|
||||||
|
Cpu [2]uint64
|
||||||
|
Net struct {
|
||||||
|
Sent uint64
|
||||||
|
Recv uint64
|
||||||
|
Time time.Time
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
package system
|
package system
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
type SystemStats struct {
|
type SystemStats struct {
|
||||||
Cpu float64 `json:"cpu"`
|
Cpu float64 `json:"cpu"`
|
||||||
Mem float64 `json:"m"`
|
Mem float64 `json:"m"`
|
||||||
@@ -16,3 +18,104 @@ type SystemStats struct {
|
|||||||
NetworkSent float64 `json:"ns"`
|
NetworkSent float64 `json:"ns"`
|
||||||
NetworkRecv float64 `json:"nr"`
|
NetworkRecv float64 `json:"nr"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DiskIoStats struct {
|
||||||
|
Read uint64
|
||||||
|
Write uint64
|
||||||
|
Time time.Time
|
||||||
|
Filesystem string
|
||||||
|
}
|
||||||
|
|
||||||
|
type NetIoStats struct {
|
||||||
|
BytesRecv uint64
|
||||||
|
BytesSent uint64
|
||||||
|
Time time.Time
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CPUStats struct {
|
||||||
|
// CPU Usage. Linux and Windows.
|
||||||
|
CPUUsage CPUUsage `json:"cpu_usage"`
|
||||||
|
|
||||||
|
// System Usage. Linux only.
|
||||||
|
SystemUsage uint64 `json:"system_cpu_usage,omitempty"`
|
||||||
|
|
||||||
|
// Online CPUs. Linux only.
|
||||||
|
// OnlineCPUs uint32 `json:"online_cpus,omitempty"`
|
||||||
|
|
||||||
|
// Throttling Data. Linux only.
|
||||||
|
// ThrottlingData ThrottlingData `json:"throttling_data,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CPUUsage struct {
|
||||||
|
// Total CPU time consumed.
|
||||||
|
// Units: nanoseconds (Linux)
|
||||||
|
// Units: 100's of nanoseconds (Windows)
|
||||||
|
TotalUsage uint64 `json:"total_usage"`
|
||||||
|
|
||||||
|
// Total CPU time consumed per core (Linux). Not used on Windows.
|
||||||
|
// Units: nanoseconds.
|
||||||
|
// PercpuUsage []uint64 `json:"percpu_usage,omitempty"`
|
||||||
|
|
||||||
|
// Time spent by tasks of the cgroup in kernel mode (Linux).
|
||||||
|
// Time spent by all container processes in kernel mode (Windows).
|
||||||
|
// Units: nanoseconds (Linux).
|
||||||
|
// Units: 100's of nanoseconds (Windows). Not populated for Hyper-V Containers.
|
||||||
|
// UsageInKernelmode uint64 `json:"usage_in_kernelmode"`
|
||||||
|
|
||||||
|
// Time spent by tasks of the cgroup in user mode (Linux).
|
||||||
|
// Time spent by all container processes in user mode (Windows).
|
||||||
|
// Units: nanoseconds (Linux).
|
||||||
|
// Units: 100's of nanoseconds (Windows). Not populated for Hyper-V Containers
|
||||||
|
// UsageInUsermode uint64 `json:"usage_in_usermode"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CStats struct {
|
||||||
|
// Common stats
|
||||||
|
// Read time.Time `json:"read"`
|
||||||
|
// PreRead time.Time `json:"preread"`
|
||||||
|
|
||||||
|
// Linux specific stats, not populated on Windows.
|
||||||
|
// PidsStats PidsStats `json:"pids_stats,omitempty"`
|
||||||
|
// BlkioStats BlkioStats `json:"blkio_stats,omitempty"`
|
||||||
|
|
||||||
|
// Windows specific stats, not populated on Linux.
|
||||||
|
// NumProcs uint32 `json:"num_procs"`
|
||||||
|
// StorageStats StorageStats `json:"storage_stats,omitempty"`
|
||||||
|
// Networks request version >=1.21
|
||||||
|
Networks map[string]NetworkStats
|
||||||
|
|
||||||
|
// Shared stats
|
||||||
|
CPUStats CPUStats `json:"cpu_stats,omitempty"`
|
||||||
|
// PreCPUStats CPUStats `json:"precpu_stats,omitempty"` // "Pre"="Previous"
|
||||||
|
MemoryStats MemoryStats `json:"memory_stats,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MemoryStats struct {
|
||||||
|
|
||||||
|
// current res_counter usage for memory
|
||||||
|
Usage uint64 `json:"usage,omitempty"`
|
||||||
|
Cache uint64 `json:"cache,omitempty"`
|
||||||
|
// maximum usage ever recorded.
|
||||||
|
// MaxUsage uint64 `json:"max_usage,omitempty"`
|
||||||
|
// TODO(vishh): Export these as stronger types.
|
||||||
|
// all the stats exported via memory.stat.
|
||||||
|
Stats map[string]uint64 `json:"stats,omitempty"`
|
||||||
|
// number of times memory usage hits limits.
|
||||||
|
// Failcnt uint64 `json:"failcnt,omitempty"`
|
||||||
|
// Limit uint64 `json:"limit,omitempty"`
|
||||||
|
|
||||||
|
// // committed bytes
|
||||||
|
// Commit uint64 `json:"commitbytes,omitempty"`
|
||||||
|
// // peak committed bytes
|
||||||
|
// CommitPeak uint64 `json:"commitpeakbytes,omitempty"`
|
||||||
|
// // private working set
|
||||||
|
// PrivateWorkingSet uint64 `json:"privateworkingset,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NetworkStats struct {
|
||||||
|
// Bytes received. Windows and Linux.
|
||||||
|
RxBytes uint64 `json:"rx_bytes"`
|
||||||
|
// Bytes sent. Windows and Linux.
|
||||||
|
TxBytes uint64 `json:"tx_bytes"`
|
||||||
|
}
|
||||||
|
@@ -14,7 +14,7 @@ type SystemInfo struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SystemData struct {
|
type SystemData struct {
|
||||||
Stats SystemStats `json:"stats"`
|
Stats *SystemStats `json:"stats"`
|
||||||
Info SystemInfo `json:"info"`
|
Info *SystemInfo `json:"info"`
|
||||||
Containers []container.ContainerStats `json:"container"`
|
Containers []*container.ContainerStats `json:"container"`
|
||||||
}
|
}
|
||||||
|
@@ -54,3 +54,47 @@ func UpdateBeszel(cmd *cobra.Command, args []string) {
|
|||||||
}
|
}
|
||||||
fmt.Printf("Successfully updated to %s\n\n%s\n", latest.Version, strings.TrimSpace(latest.ReleaseNotes))
|
fmt.Printf("Successfully updated to %s\n\n%s\n", latest.Version, strings.TrimSpace(latest.ReleaseNotes))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func UpdateBeszelAgent() {
|
||||||
|
var latest *selfupdate.Release
|
||||||
|
var found bool
|
||||||
|
var err error
|
||||||
|
currentVersion := semver.MustParse(beszel.Version)
|
||||||
|
fmt.Println("beszel-agent", currentVersion)
|
||||||
|
fmt.Println("Checking for updates...")
|
||||||
|
updater, _ := selfupdate.NewUpdater(selfupdate.Config{
|
||||||
|
Filters: []string{"beszel-agent"},
|
||||||
|
})
|
||||||
|
latest, found, err = updater.DetectLatest("henrygd/beszel")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error checking for updates:", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
fmt.Println("No updates found")
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Latest version:", latest.Version)
|
||||||
|
|
||||||
|
if latest.Version.LTE(currentVersion) {
|
||||||
|
fmt.Println("You are up to date")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var binaryPath string
|
||||||
|
fmt.Printf("Updating from %s to %s...\n", currentVersion, latest.Version)
|
||||||
|
binaryPath, err = os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error getting binary path:", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
err = selfupdate.UpdateTo(latest.AssetURL, binaryPath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Please try rerunning with sudo. Error:", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Printf("Successfully updated to %s\n\n%s\n", latest.Version, strings.TrimSpace(latest.ReleaseNotes))
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user