commit cbbbf623fbf5d5151c28325426973165e53a9443 Author: MatBureau <86561676+MatBureau@users.noreply.github.com> Date: Wed Apr 1 14:02:01 2026 +0200 premier commit diff --git a/buildup/Dockerfile b/buildup/Dockerfile new file mode 100644 index 0000000..27a165b --- /dev/null +++ b/buildup/Dockerfile @@ -0,0 +1,24 @@ +# Image golang pour la compilation +FROM golang:1.25 AS builder + +# Copier les fichiers dont ont a besoin +COPY src /go/src +COPY Makefile /go + +# Compilation +RUN make build + +# Image finale +FROM busybox + +# Installer le logiciel compilé +COPY src/www /www +COPY src/.env / +COPY src/servers.yaml / +COPY --from=builder /go/bin/buildup /buildup + +# Partages +EXPOSE 80 + +# Démarrage +ENTRYPOINT [ "/buildup" ] \ No newline at end of file diff --git a/buildup/Makefile b/buildup/Makefile new file mode 100644 index 0000000..3e06f83 --- /dev/null +++ b/buildup/Makefile @@ -0,0 +1,38 @@ +# Variables +COMPOSE ?= docker compose +DOCKER_DIR ?= docker +COMPOSE_FILE ?= docker-compose.yml + +run: + @cd src ; go run *.go + +build: + @cd src ; go mod tidy ; go build -o ../bin/buildup *.go + +clean: + @rm ./bin/buildup + +image: + @docker build -t buildup:latest . + +start: + @docker run -d -p 80:80 --name buildup buildup:latest + +stop: + @docker rm -f buildup + +# Démarrage stack +up: + @$(COMPOSE) --project-directory $(DOCKER_DIR) -f $(DOCKER_DIR)/$(COMPOSE_FILE) up -d + +# Arrêt de la stack +down: + @$(COMPOSE) --project-directory $(DOCKER_DIR) -f $(DOCKER_DIR)/$(COMPOSE_FILE) down + +# Journaux de la stack +logs: + @$(COMPOSE) --project-directory $(DOCKER_DIR) -f $(DOCKER_DIR)/$(COMPOSE_FILE) logs -f + +# Liste des processus de la stack +ps: + @$(COMPOSE) --project-directory $(DOCKER_DIR) -f $(DOCKER_DIR)/$(COMPOSE_FILE) ps \ No newline at end of file diff --git a/buildup/docker/.env b/buildup/docker/.env new file mode 100644 index 0000000..0c09c0f --- /dev/null +++ b/buildup/docker/.env @@ -0,0 +1,11 @@ +# Gestion InfluxDB +DOCKER_INFLUXDB_INIT_MODE=setup +DOCKER_INFLUXDB_INIT_USERNAME=admin +DOCKER_INFLUXDB_INIT_PASSWORD=SuperSecret +DOCKER_INFLUXDB_INIT_ORG=myorg +DOCKER_INFLUXDB_INIT_BUCKET=metrics +DOCKER_INFLUXDB_INIT_RETENTION=30d +DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=admin-token +# Gestion Grafana +GF_SECURITY_ADMIN_USER="admin" +GF_SECURITY_ADMIN_PASSWORD="SuperSecret" \ No newline at end of file diff --git a/buildup/docker/docker-compose.yml b/buildup/docker/docker-compose.yml new file mode 100644 index 0000000..d61b6ab --- /dev/null +++ b/buildup/docker/docker-compose.yml @@ -0,0 +1,54 @@ +# Warning : 사이트 이름을 정하는 것을 잊지 마세요! +services: + # 웹 프런트엔드 + traefik: + image: traefik:v3.6 + container_name: front + restart: unless-stopped + command: + - "--providers.docker=true" + - "--entrypoints.http.address=:80" + ports: + - 80:80 + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + labels: + - "traefik.enable=true" + # InfluxDB 데이터베이스 + influxdb: + image: influxdb:2.8.0 + container_name: influxdb + user: "1000:1000" + environment: + DOCKER_INFLUXDB_INIT_MODE: + DOCKER_INFLUXDB_INIT_USERNAME: + DOCKER_INFLUXDB_INIT_PASSWORD: + DOCKER_INFLUXDB_INIT_ORG: + DOCKER_INFLUXDB_INIT_BUCKET: + DOCKER_INFLUXDB_INIT_RETENTION: + DOCKER_INFLUXDB_INIT_ADMIN_TOKEN: + volumes: + - ./influxdb2/data:/var/lib/influxdb2 + - ./influxdb2/config:/etc/influxdb2 + restart: unless-stopped + labels: + - "traefik.http.routers.influxdb.rule=Host(`influx.test`)" # 사이트 이름 + - "traefik.http.services.influxdb.loadbalancer.server.port=8086" + # 그래픽 렌더링 서비스 + grafana: + image: grafana/grafana:main + container_name: grafana + user: "1000:1000" + restart: always + depends_on: + - influxdb + environment: + - GF_SECURITY_ADMIN_USER + - GF_SECURITY_ADMIN_PASSWORD + ports: + - 3000:3000 + volumes: + - ./grafana/datas:/var/lib/grafana:rw + labels: + - "traefik.http.routers.grafana.rule=Host(`grafana.test`)" # 사이트 이름 + - "traefik.http.services.grafana.loadbalancer.server.port=3000" \ No newline at end of file diff --git a/buildup/src/.env b/buildup/src/.env new file mode 100644 index 0000000..0249029 --- /dev/null +++ b/buildup/src/.env @@ -0,0 +1,5 @@ +# Gestion InfluxDB +INFLUXDB_TOKEN="admin-token" +INFLUXDB_URL="http://influx.test" +INFLUXDB_ORG="myorg" +INFLUXDB_BUCKET="metrics" diff --git a/buildup/src/config.go b/buildup/src/config.go new file mode 100644 index 0000000..fb09326 --- /dev/null +++ b/buildup/src/config.go @@ -0,0 +1,25 @@ +package main + +import ( + "os" + + "gopkg.in/yaml.v3" +) + +type Config struct { + Servers []string `yaml:"servers"` +} + +func LoadConfig(path string) (*Config, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, err + } + + var cfg Config + if err := yaml.Unmarshal(data, &cfg); err != nil { + return nil, err + } + + return &cfg, nil +} diff --git a/buildup/src/cpu/cpu.go b/buildup/src/cpu/cpu.go new file mode 100644 index 0000000..d5f90f9 --- /dev/null +++ b/buildup/src/cpu/cpu.go @@ -0,0 +1,51 @@ +package cpu + +import ( + "github.com/shirou/gopsutil/v4/cpu" +) + +type CPUCore struct { + Info cpu.InfoStat `json:"info"` + Usage float64 `json:"usage_percent"` + Times *cpu.TimesStat `json:"times,omitempty"` +} + +type CPUInfo struct { + Cores []CPUCore `json:"cores"` +} + +func ReadCPU() (*CPUInfo, error) { + infos, err := cpu.Info() + if err != nil { + return nil, err + } + + usage, err := cpu.Percent(0, true) // usage par core + if err != nil { + return nil, err + } + + times, err := cpu.Times(true) // stats par core + if err != nil { + return nil, err + } + + out := make([]CPUCore, 0, len(infos)) + for i, info := range infos { + core := CPUCore{ + Info: info, + } + + if i < len(usage) { + core.Usage = usage[i] + } + if i < len(times) { + tmp := times[i] // adresse stable + core.Times = &tmp + } + + out = append(out, core) + } + + return &CPUInfo{Cores: out}, nil +} diff --git a/buildup/src/disk/disk.go b/buildup/src/disk/disk.go new file mode 100644 index 0000000..9e7ad98 --- /dev/null +++ b/buildup/src/disk/disk.go @@ -0,0 +1,38 @@ +package disk + +import ( + "github.com/shirou/gopsutil/v4/disk" +) + +type DiskFS struct { + Partition disk.PartitionStat `json:"partition"` + Usage *disk.UsageStat `json:"usage,omitempty"` +} + +func ReadDisk() (*[]DiskFS, error) { + parts, err := disk.Partitions(true) // true = toutes les partitions + if err != nil { + return nil, err + } + + var out []DiskFS + + for _, p := range parts { + fs := DiskFS{Partition: p} + + // Usage peut échouer (ex: pseudo-fs, permissions) + if u, err := disk.Usage(p.Mountpoint); err == nil { + fs.Usage = u + } + + // Exclusion des FS spécifique au système + switch fs.Partition.Fstype { + case "none", "proc", "tmpfs", "overlay", "sysfs", "cgroup2", "mqueue", "nsfs": + // ignoré + default: + out = append(out, fs) + } + } + + return &out, nil +} diff --git a/buildup/src/go.mod b/buildup/src/go.mod new file mode 100644 index 0000000..4d9851e --- /dev/null +++ b/buildup/src/go.mod @@ -0,0 +1,26 @@ +module github.com/evoliatis/buildup + +go 1.25.6 + +require ( + github.com/influxdata/influxdb-client-go/v2 v2.14.0 + github.com/joho/godotenv v1.5.1 + github.com/shirou/gopsutil/v4 v4.26.1 + gopkg.in/yaml.v3 v3.0.1 +) + +require ( + github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect + github.com/ebitengine/purego v0.9.1 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect + github.com/google/uuid v1.3.1 // indirect + github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/oapi-codegen/runtime v1.0.0 // indirect + github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/tklauser/go-sysconf v0.3.16 // indirect + github.com/tklauser/numcpus v0.11.0 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect + golang.org/x/net v0.23.0 // indirect + golang.org/x/sys v0.40.0 // indirect +) diff --git a/buildup/src/go.sum b/buildup/src/go.sum new file mode 100644 index 0000000..c2925e2 --- /dev/null +++ b/buildup/src/go.sum @@ -0,0 +1,55 @@ +github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= +github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= +github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= +github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A= +github.com/ebitengine/purego v0.9.1/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/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/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/influxdata/influxdb-client-go/v2 v2.14.0 h1:AjbBfJuq+QoaXNcrova8smSjwJdUHnwvfjMF71M1iI4= +github.com/influxdata/influxdb-client-go/v2 v2.14.0/go.mod h1:Ahpm3QXKMJslpXl3IftVLVezreAUtBOTZssDrjZEFHI= +github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 h1:W9WBk7wlPfJLvMCdtV4zPulc4uCPrlywQOmbFOhgQNU= +github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= +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/oapi-codegen/runtime v1.0.0 h1:P4rqFX5fMFWqRzY9M/3YF9+aPSPPB06IzP2P7oOxrWo= +github.com/oapi-codegen/runtime v1.0.0/go.mod h1:LmCUMQuPB4M/nLXilQXhHw+BLZdDb18B34OO356yJ/A= +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.26.1 h1:TOkEyriIXk2HX9d4isZJtbjXbEjf5qyKPAzbzY0JWSo= +github.com/shirou/gopsutil/v4 v4.26.1/go.mod h1:medLI9/UNAb0dOI9Q3/7yWSqKkj00u+1tgY8nvv41pc= +github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= +github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= +github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= +github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +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.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +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/buildup/src/goroutines/cpu.go b/buildup/src/goroutines/cpu.go new file mode 100644 index 0000000..377eb9b --- /dev/null +++ b/buildup/src/goroutines/cpu.go @@ -0,0 +1,80 @@ +package goroutines + +import ( + "context" + "encoding/json" + "log" + "net/http" + "time" + + "github.com/evoliatis/buildup/cpu" + influxdb2 "github.com/influxdata/influxdb-client-go/v2" +) + +func GoCPU(server, INFLUXDB_URL, INFLUXDB_ORG, INFLUXDB_BUCKET, INFLUXDB_TOKEN string, tick int) { + + // Connexion + client := influxdb2.NewClient(INFLUXDB_URL, INFLUXDB_TOKEN) + defer client.Close() + + writeAPI := client.WriteAPIBlocking(INFLUXDB_ORG, INFLUXDB_BUCKET) + + ticker := time.NewTicker(time.Duration(tick) * time.Second) + defer ticker.Stop() + + for range ticker.C { + // Récupération des valeurs + reqUrl := "http://" + server + "/cpu" + resp, err := http.Get(reqUrl) + if err != nil { + log.Println("Erreur de communication : " + reqUrl) + continue + } + defer resp.Body.Close() + + var cpu cpu.CPUInfo + if err := json.NewDecoder(resp.Body).Decode(&cpu); err != nil { + log.Println("Impossible de récupérer : " + reqUrl) + continue + } + + for _, c := range cpu.Cores { + tags := map[string]string{ // tags (ex: host, env…) + "cpu": c.Times.CPU, // ex "cpu0" + "vendor": c.Info.VendorID, // utile pour multi-host + "model": c.Info.ModelName, + "host": server, + } + fields := map[string]interface{}{ + "cpu": c.Usage, + + // Tous les champs TimesStat (numériques) + "user": c.Times.User, + "system": c.Times.System, + "idle": c.Times.Idle, + "nice": c.Times.Nice, + "iowait": c.Times.Iowait, + "irq": c.Times.Irq, + "softirq": c.Times.Softirq, + "steal": c.Times.Steal, + "guest": c.Times.Guest, + "guest_nice": c.Times.GuestNice, + } + + // ---- point ---- + p := influxdb2.NewPoint( + "cpu", + tags, + fields, + time.Now(), + ) + + // Ecriture + if err := writeAPI.WritePoint(context.Background(), p); err != nil { + log.Printf("write failed: %v \n", err) + } + } + + log.Println("CPU written to InfluxDB :" + reqUrl) + } +} diff --git a/buildup/src/goroutines/disk.go b/buildup/src/goroutines/disk.go new file mode 100644 index 0000000..617e47d --- /dev/null +++ b/buildup/src/goroutines/disk.go @@ -0,0 +1,76 @@ +package goroutines + +import ( + "context" + "encoding/json" + "log" + "net/http" + "time" + + "github.com/evoliatis/buildup/disk" + influxdb2 "github.com/influxdata/influxdb-client-go/v2" +) + +func GoDisk(server, INFLUXDB_URL, INFLUXDB_ORG, INFLUXDB_BUCKET, INFLUXDB_TOKEN string, tick int) { + + // Connexion + client := influxdb2.NewClient(INFLUXDB_URL, INFLUXDB_TOKEN) + defer client.Close() + + writeAPI := client.WriteAPIBlocking(INFLUXDB_ORG, INFLUXDB_BUCKET) + + ticker := time.NewTicker(time.Duration(tick) * time.Second) + defer ticker.Stop() + + for range ticker.C { + // Récupération des valeurs + reqUrl := "http://" + server + "/disk" + resp, err := http.Get(reqUrl) + if err != nil { + log.Println("Erreur de communication : " + reqUrl) + continue + } + defer resp.Body.Close() + + var disks []disk.DiskFS + if err := json.NewDecoder(resp.Body).Decode(&disks); err != nil { + log.Println("Impossible de récupérer : " + reqUrl) + continue + } + + for _, disk := range disks { + tags := map[string]string{ // tags (ex: host, env…) + "host": server, + "device": disk.Partition.Device, + "mountpoint": disk.Partition.Mountpoint, + "fstype": disk.Usage.Fstype, + } + fields := map[string]interface{}{ + // Tous les champs TimesStat (numériques) + "total": disk.Usage.Total, + "free": disk.Usage.Free, + "used": disk.Usage.Used, + "usedpcent": disk.Usage.UsedPercent, + "inodestotal": disk.Usage.InodesTotal, + "inodesused": disk.Usage.InodesUsed, + "inodesfree": disk.Usage.InodesFree, + "inodesusedpcent": disk.Usage.InodesUsedPercent, + } + + // ---- point ---- + p := influxdb2.NewPoint( + "disk", + tags, + fields, + time.Now(), + ) + + // Ecriture + if err := writeAPI.WritePoint(context.Background(), p); err != nil { + log.Printf("write failed: %v \n", err) + } + } + + log.Println("(", len(disks), ") Disks written to InfluxDB :"+reqUrl) + } +} diff --git a/buildup/src/goroutines/load.go b/buildup/src/goroutines/load.go new file mode 100644 index 0000000..cd12f8f --- /dev/null +++ b/buildup/src/goroutines/load.go @@ -0,0 +1,62 @@ +package goroutines + +import ( + "context" + "encoding/json" + "log" + "net/http" + "time" + + "github.com/evoliatis/buildup/load" + influxdb2 "github.com/influxdata/influxdb-client-go/v2" +) + +func GoLoad(server, INFLUXDB_URL, INFLUXDB_ORG, INFLUXDB_BUCKET, INFLUXDB_TOKEN string, tick int) { + + // Connexion + client := influxdb2.NewClient(INFLUXDB_URL, INFLUXDB_TOKEN) + defer client.Close() + + writeAPI := client.WriteAPIBlocking(INFLUXDB_ORG, INFLUXDB_BUCKET) + + ticker := time.NewTicker(time.Duration(tick) * time.Second) + defer ticker.Stop() + + for range ticker.C { + // Récupération des valeurs + reqUrl := "http://" + server + "/load" + resp, err := http.Get(reqUrl) + if err != nil { + log.Println("Erreur de communication : " + reqUrl) + continue + } + defer resp.Body.Close() + + var avg load.LoadInfo + if err := json.NewDecoder(resp.Body).Decode(&avg); err != nil { + log.Println("Impossible de récupérer : " + reqUrl) + continue + } + + // ---- point ---- + p := influxdb2.NewPoint( + "load_average", + map[string]string{ // tags (ex: host, env…) + "host": server, + }, + map[string]interface{}{ // Données à stocker + "avg1": avg.Avg.Load1, + "avg5": avg.Avg.Load5, + "avg15": avg.Avg.Load15, + }, + time.Now(), + ) + + // Ecriture + if err := writeAPI.WritePoint(context.Background(), p); err != nil { + log.Printf("write failed: %v\n", err) + } + + log.Println("load average written to InfluxDB : ", reqUrl) + } +} diff --git a/buildup/src/goroutines/mem.go b/buildup/src/goroutines/mem.go new file mode 100644 index 0000000..1ef63b6 --- /dev/null +++ b/buildup/src/goroutines/mem.go @@ -0,0 +1,79 @@ +package goroutines + +import ( + "context" + "encoding/json" + "log" + "net/http" + "time" + + "github.com/evoliatis/buildup/memory" + influxdb2 "github.com/influxdata/influxdb-client-go/v2" +) + +func GoMem(server, INFLUXDB_URL, INFLUXDB_ORG, INFLUXDB_BUCKET, INFLUXDB_TOKEN string, tick int) { + + // Connexion + client := influxdb2.NewClient(INFLUXDB_URL, INFLUXDB_TOKEN) + defer client.Close() + + writeAPI := client.WriteAPIBlocking(INFLUXDB_ORG, INFLUXDB_BUCKET) + + ticker := time.NewTicker(time.Duration(tick) * time.Second) + defer ticker.Stop() + + for range ticker.C { + // Récupération des valeurs + reqUrl := "http://" + server + "/mem" + resp, err := http.Get(reqUrl) + if err != nil { + log.Println("Erreur de communication : " + reqUrl) + continue + } + defer resp.Body.Close() + + var mem memory.MemInfo + if err := json.NewDecoder(resp.Body).Decode(&mem); err != nil { + log.Println("Impossible de récupérer : " + reqUrl) + continue + } + + // ---- point ---- + tags := map[string]string{ // tags (ex: host, env…) + "host": server, + } + fields := map[string]interface{}{ + // Tous les champs TimesStat (numériques) + "total": mem.Virtual.Total, + "available": mem.Virtual.Available, + "used": mem.Virtual.Used, + "used_pcent": mem.Virtual.UsedPercent, + "free": mem.Virtual.Free, + "active": mem.Virtual.Active, + "inactive": mem.Virtual.Inactive, + "cached": mem.Virtual.Cached, + "dirty": mem.Virtual.Dirty, + "shared": mem.Virtual.Shared, + "swap_total": mem.Swap.Total, + "swap_used": mem.Swap.Used, + "swap_used_pcent": mem.Swap.UsedPercent, + "swap_free": mem.Swap.Free, + "swap_pgin": mem.Swap.PgIn, + "swap_pgout": mem.Swap.PgOut, + "swap_pgfault": mem.Swap.PgFault, + } + p := influxdb2.NewPoint( + "memory", + tags, + fields, + time.Now(), + ) + + // Ecriture + if err := writeAPI.WritePoint(context.Background(), p); err != nil { + log.Printf("write failed: %v\n", err) + } + + log.Println("memory written to InfluxDB : ", reqUrl) + } +} diff --git a/buildup/src/goroutines/net.go b/buildup/src/goroutines/net.go new file mode 100644 index 0000000..d58e099 --- /dev/null +++ b/buildup/src/goroutines/net.go @@ -0,0 +1,78 @@ +package goroutines + +import ( + "context" + "encoding/json" + "log" + "net/http" + "strconv" + "time" + + "github.com/evoliatis/buildup/netcard" + influxdb2 "github.com/influxdata/influxdb-client-go/v2" +) + +func GoNet(server, INFLUXDB_URL, INFLUXDB_ORG, INFLUXDB_BUCKET, INFLUXDB_TOKEN string, tick int) { + + // Connexion + client := influxdb2.NewClient(INFLUXDB_URL, INFLUXDB_TOKEN) + defer client.Close() + + writeAPI := client.WriteAPIBlocking(INFLUXDB_ORG, INFLUXDB_BUCKET) + + ticker := time.NewTicker(time.Duration(tick) * time.Second) + defer ticker.Stop() + + for range ticker.C { + // Récupération des valeurs + reqUrl := "http://" + server + "/net" + resp, err := http.Get(reqUrl) + if err != nil { + log.Println("Erreur de communication : " + reqUrl) + continue + } + defer resp.Body.Close() + + var nets []netcard.NetCard + if err := json.NewDecoder(resp.Body).Decode(&nets); err != nil { + log.Println("Impossible de récupérer : " + reqUrl) + continue + } + + for _, net := range nets { + + tags := map[string]string{ // tags (ex: host, env…) + "host": "linux", + "name": net.Interface.Name, + "mtu": strconv.FormatInt(int64(net.Interface.MTU), 10), + "hardwareaddr": net.Interface.HardwareAddr, + } + fields := map[string]interface{}{ // Données à stocker + "bytesent": net.IO.BytesSent, + "byterecv": net.IO.BytesRecv, + "packetsent": net.IO.PacketsSent, + "packetrecv": net.IO.PacketsRecv, + "errorin": net.IO.Errin, + "errorout": net.IO.Errout, + "dropin": net.IO.Dropin, + "dropout": net.IO.Dropout, + "fifoin": net.IO.Fifoin, + "fifoout": net.IO.Fifoout, + } + // ---- points ---- + p := influxdb2.NewPoint( + "network", + tags, + fields, + time.Now(), + ) + + // Ecriture + if err := writeAPI.WritePoint(context.Background(), p); err != nil { + log.Printf("write failed: %v \n", err) + } + } + + log.Println("(", len(nets), ") network written to InfluxDB : "+reqUrl) + } +} diff --git a/buildup/src/goroutines/procs.go b/buildup/src/goroutines/procs.go new file mode 100644 index 0000000..5fe27ed --- /dev/null +++ b/buildup/src/goroutines/procs.go @@ -0,0 +1,70 @@ +package goroutines + +import ( + "context" + "encoding/json" + "log" + "net/http" + "strconv" + "time" + + "github.com/evoliatis/buildup/proc" + influxdb2 "github.com/influxdata/influxdb-client-go/v2" +) + +func GoProc(server, INFLUXDB_URL, INFLUXDB_ORG, INFLUXDB_BUCKET, INFLUXDB_TOKEN string, tick int) { + + // Connexion + client := influxdb2.NewClient(INFLUXDB_URL, INFLUXDB_TOKEN) + defer client.Close() + + writeAPI := client.WriteAPIBlocking(INFLUXDB_ORG, INFLUXDB_BUCKET) + + ticker := time.NewTicker(time.Duration(tick) * time.Second) + defer ticker.Stop() + + for range ticker.C { + // Récupération des valeurs + reqUrl := "http://" + server + "/ps" + resp, err := http.Get(reqUrl) + if err != nil { + log.Println("Erreur de communication : " + reqUrl) + continue + } + defer resp.Body.Close() + + var procs []proc.Proc + if err := json.NewDecoder(resp.Body).Decode(&procs); err != nil { + log.Println("Impossible de récupérer : " + reqUrl) + continue + } + + for _, proc := range procs { + + tags := map[string]string{ // tags (ex: host, env…) + "host": "linux", + "name": proc.Name, + "pid": strconv.FormatInt(int64(proc.Pid), 10), + } + fields := map[string]interface{}{ // Données à stocker + "CPU": proc.CPU, + "Memory": proc.Memory, + "Status": proc.Status, + } + // ---- points ---- + p := influxdb2.NewPoint( + "processes", + tags, + fields, + time.Now(), + ) + + // Ecriture + if err := writeAPI.WritePoint(context.Background(), p); err != nil { + log.Printf("write failed: %v \n", err) + } + } + + log.Println("(", len(procs), ") procs written to InfluxDB : "+reqUrl) + } +} diff --git a/buildup/src/handle.go b/buildup/src/handle.go new file mode 100644 index 0000000..2daf3de --- /dev/null +++ b/buildup/src/handle.go @@ -0,0 +1,11 @@ +package main + +import ( + "fmt" + "net/http" +) + +// health URL +func HealthHandle(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "ok") +} diff --git a/buildup/src/load/load.go b/buildup/src/load/load.go new file mode 100644 index 0000000..9b76c10 --- /dev/null +++ b/buildup/src/load/load.go @@ -0,0 +1,27 @@ +package load + +import ( + "github.com/shirou/gopsutil/v4/load" +) + +type LoadInfo struct { + Avg *load.AvgStat `json:"avg"` + Misc *load.MiscStat `json:"misc"` +} + +func ReadLoad() (*LoadInfo, error) { + avg, err := load.Avg() + if err != nil { + return nil, err + } + + misc, err := load.Misc() + if err != nil { + return nil, err + } + + return &LoadInfo{ + Avg: avg, + Misc: misc, + }, nil +} diff --git a/buildup/src/main.go b/buildup/src/main.go new file mode 100644 index 0000000..7c9991f --- /dev/null +++ b/buildup/src/main.go @@ -0,0 +1,40 @@ +package main + +import ( + "log" + "net/http" + "os" + + "github.com/evoliatis/buildup/goroutines" + "github.com/joho/godotenv" +) + +func main() { + // .env 파일을 불러오세요 + if err := godotenv.Load(); err != nil { + log.Fatalln("fichier .env non trouvé") + } + InToken := os.Getenv("INFLUXDB_TOKEN") + InUrl := os.Getenv("INFLUXDB_URL") + InOrg := os.Getenv("INFLUXDB_ORG") + InBucket := os.Getenv("INFLUXDB_BUCKET") + + // 서버 목록을 불러오세요 + cfg, err := LoadConfig("servers.yaml") + if err != nil { + log.Fatalln("fichier servers.yaml non trouvé") + } + + // Go routines + for _, srv := range cfg.Servers { + + go goroutines.GoLoad(srv, InUrl, InOrg, InBucket, InToken, 10) + go goroutines.GoProc(srv, InUrl, InOrg, InBucket, InToken, 10) + go goroutines.GoCPU(srv, InUrl, InOrg, InBucket, InToken, 10) + go goroutines.GoMem(srv, InUrl, InOrg, InBucket, InToken, 10) + go goroutines.GoNet(srv, InUrl, InOrg, InBucket, InToken, 10) + go goroutines.GoDisk(srv, InUrl, InOrg, InBucket, InToken, 10) + } + log.Println("listening on :80") + log.Fatal(http.ListenAndServe(":80", router())) +} diff --git a/buildup/src/memory/memory.go b/buildup/src/memory/memory.go new file mode 100644 index 0000000..e2d3606 --- /dev/null +++ b/buildup/src/memory/memory.go @@ -0,0 +1,27 @@ +package memory + +import ( + "github.com/shirou/gopsutil/v4/mem" +) + +type MemInfo struct { + Virtual *mem.VirtualMemoryStat `json:"virtual"` + Swap *mem.SwapMemoryStat `json:"swap"` +} + +func ReadMemory() (*MemInfo, error) { + vm, err := mem.VirtualMemory() + if err != nil { + return nil, err + } + + sm, err := mem.SwapMemory() + if err != nil { + return nil, err + } + + return &MemInfo{ + Virtual: vm, + Swap: sm, + }, nil +} diff --git a/buildup/src/netcard/netcard.go b/buildup/src/netcard/netcard.go new file mode 100644 index 0000000..d981333 --- /dev/null +++ b/buildup/src/netcard/netcard.go @@ -0,0 +1,42 @@ +package netcard + +import ( + "github.com/shirou/gopsutil/v4/net" +) + +type NetCard struct { + Interface net.InterfaceStat `json:"interface"` + IO *net.IOCountersStat `json:"io,omitempty"` +} + +func ReadNetwork(nom string) (*[]NetCard, error) { + ifaces, err := net.Interfaces() + if err != nil { + return nil, err + } + + counters, err := net.IOCounters(true) // true => stats par interface + if err != nil { + return nil, err + } + + // index des compteurs par nom d'interface + byName := make(map[string]net.IOCountersStat, len(counters)) + for _, c := range counters { + byName[c.Name] = c + } + + // fusion + out := make([]NetCard, 0, len(ifaces)) + for _, itf := range ifaces { + card := NetCard{Interface: itf} + if c, ok := byName[itf.Name]; ok { + tmp := c + card.IO = &tmp + } + if nom == "" || nom == card.Interface.Name { + out = append(out, card) + } + } + return &out, nil +} diff --git a/buildup/src/proc/proc.go b/buildup/src/proc/proc.go new file mode 100644 index 0000000..1d241f1 --- /dev/null +++ b/buildup/src/proc/proc.go @@ -0,0 +1,35 @@ +package proc + +import "github.com/shirou/gopsutil/v4/process" + +type Proc struct { + Pid int32 `json:"pid"` + Name string `json:"name"` + User string `json:"user"` + CPU float64 `json:"cpu"` + Memory float32 `json:"memory"` + Status string `json:"status"` +} + +// Chargement des Processus +func ReadProc(user string) (*[]Proc, error) { + var myProcs []Proc + procs, err := process.Processes() + if err != nil { + return nil, err + } + for _, proc := range procs { + var myProc Proc + myProc.User, _ = proc.Username() + if user == "" || user == myProc.User { + myProc.Pid = proc.Pid + myProc.Name, _ = proc.Name() + myProc.CPU, _ = proc.CPUPercent() + myProc.Memory, _ = proc.MemoryPercent() + status, _ := proc.Status() + myProc.Status = status[0] + myProcs = append(myProcs, myProc) + } + } + return &myProcs, nil +} diff --git a/buildup/src/routes.go b/buildup/src/routes.go new file mode 100644 index 0000000..d02a292 --- /dev/null +++ b/buildup/src/routes.go @@ -0,0 +1,17 @@ +package main + +import "net/http" + +// Routeur pour l'ensemble de nos routes + +func router() http.Handler { + mux := http.NewServeMux() + // health URL + mux.HandleFunc("GET /health", HealthHandle) + + // Autres cas : fichiers statiques + fs := http.FileServer(http.Dir("www")) + mux.Handle("/", fs) + + return mux +} diff --git a/buildup/src/servers.yaml b/buildup/src/servers.yaml new file mode 100644 index 0000000..d58f17b --- /dev/null +++ b/buildup/src/servers.yaml @@ -0,0 +1,2 @@ +servers: + - linux:8080 \ No newline at end of file diff --git a/buildup/src/www/img/logo.png b/buildup/src/www/img/logo.png new file mode 100644 index 0000000..3110f60 Binary files /dev/null and b/buildup/src/www/img/logo.png differ diff --git a/buildup/src/www/index.html b/buildup/src/www/index.html new file mode 100644 index 0000000..a557874 --- /dev/null +++ b/buildup/src/www/index.html @@ -0,0 +1,370 @@ + + + + + + + Reveleant Software Solution — Note interne + + + + + + + +
+
+
+ +
+ Reveleant Software Solution + Note interne • Version corporate, humour contrôlé +
+
+ + +
+ +
+
+
Communication interne
+ +

🎤 Le développeur, la direction… et le vent.

+

+ Ceci est une prise de parole officielle de l'équipe de développement, + mais avec un bug volontaire dans le sarcasme. 🧑‍💻 +

+ +
+ +
+

🧾 Promesses & livrables

+

+ La direction adore dire : “On n’a pas beaucoup de budget, mais c’est un super projet humain.”
+ Traduction : tu seras payé en expérience, en tickets, et en promesses compressées en ZIP. +

+
+ +
+ +
+

🌬️ La direction

+

+ La direction, c’est un peu comme une API REST mal documentée : + elle promet beaucoup, répond rarement, + et quand elle répond, c’est un 200 OK avec un body vide. +

+

+ Les promesses, c’est un peu comme un site rempli de concepts : beaucoup d’air, peu de portance… 💨 +

+

+ On nous vend du disruptif, de l’auto-scalable… et au final on déploie surtout : + une VM trop petite et un développeur trop tard. ⚙️ +

+
+ +
+ +
+

✅ Morale

+

+ Nous aimons les rêves. Nous aimons l’ambition. Mais nous aimons aussi les choses testables : + des specs claires, des dates réelles, et des promesses qui passent les tests unitaires. 🧪 +

+
+ + +
+ +
+ +
+ Logo Reveleant Software Solution +
+
+
+ + +
+ +