mirror of
https://github.com/hamburghammer/gstat.git
synced 2024-05-17 12:44:36 +02:00
Compare commits
33 commits
Author | SHA1 | Date | |
---|---|---|---|
Augusto Dwenger J. | 381dd4819d | ||
1c9b053ec0 | |||
874960b897 | |||
7698718458 | |||
e6ab66ad2e | |||
Augusto Dwenger J. | 676c0ae573 | ||
Augusto Dwenger J. | ecd40f940d | ||
fefa87ad52 | |||
966735a184 | |||
7b1a54d697 | |||
Augusto Dwenger J. | d27183d362 | ||
Augusto Dwenger J. | 8e32c292a5 | ||
66abdebf3b | |||
e62c4853c9 | |||
0b81a55351 | |||
68eaf1d195 | |||
421faacb21 | |||
79db0bb6e5 | |||
fae0bae315 | |||
ceb197d60f | |||
e1a70507ec | |||
8ccd1e13ce | |||
a72b4e2405 | |||
d536dce03c | |||
57c1bca182 | |||
016a109bb0 | |||
2b57fd0b7a | |||
176de4c0d3 | |||
b3cbf3d6f3 | |||
f197208947 | |||
d1e1614737 | |||
d34bb06879 | |||
78a37d9a86 |
32
.drone.yml
Normal file
32
.drone.yml
Normal file
|
@ -0,0 +1,32 @@
|
|||
kind: pipeline
|
||||
name: default
|
||||
|
||||
steps:
|
||||
- name: unit-test
|
||||
image: golang
|
||||
volumes:
|
||||
- name: cache
|
||||
path: /go
|
||||
commands:
|
||||
- go test -coverprofile=coverage.out -covermode=count ./...
|
||||
- go tool cover -func=coverage.out | grep total
|
||||
|
||||
- name: race-test
|
||||
image: golang
|
||||
volumes:
|
||||
- name: cache
|
||||
path: /go
|
||||
commands:
|
||||
- go test -race ./...
|
||||
|
||||
- name: build
|
||||
image: golang
|
||||
volumes:
|
||||
- name: deps
|
||||
path: /go
|
||||
commands:
|
||||
- CGO_ENABLED=0 go build
|
||||
|
||||
volumes:
|
||||
- name: cache
|
||||
temp: {}
|
19
LICENSE
Normal file
19
LICENSE
Normal file
|
@ -0,0 +1,19 @@
|
|||
MIT License Copyright (c) 2020 Augusto Dwenger J.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice (including the next
|
||||
paragraph) shall be included in all copies or substantial portions of the
|
||||
Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
||||
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
||||
OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
83
README.md
83
README.md
|
@ -1,64 +1,51 @@
|
|||
# gstat
|
||||
|
||||
Is a tool to get the system stats in a parsable format.
|
||||
The tool is part of the competition with [Niklas](https://github.com/nhh).
|
||||
[![Build Status](https://cloud.drone.io/api/badges/hamburghammer/gstat/status.svg?ref=refs/heads/master)](https://cloud.drone.io/hamburghammer/gstat)
|
||||
[![Go Report Card](https://goreportcard.com/badge/github.com/hamburghammer/gstat)](https://goreportcard.com/report/github.com/hamburghammer/gstat)
|
||||
|
||||
Checkout his solution: [cstat](https://github.com/nhh/cstat)
|
||||
Is a cli tool to get some system stats in a machine parsable format (JSON). It supports only Linux but might also run on some UNIX based operation systems.
|
||||
|
||||
## Terms of the competition
|
||||
|
||||
### Allgemein:
|
||||
This tool is part of a competition with [nhh](https://github.com/nhh) -> [Details](docs/competition.md)
|
||||
|
||||
Name des Programms:
|
||||
- cstat
|
||||
- gstat
|
||||
**WIP: expect some changes for the stats gathering**
|
||||
|
||||
Das Tool kann mehrere Metriken auf einmal zurückgeben.
|
||||
## Features
|
||||
- Easy to use
|
||||
- Single executable
|
||||
- Runs on Linux
|
||||
|
||||
### Anforderung:
|
||||
## Installation
|
||||
For the time there are no binaries provided this means you need to install it through go.
|
||||
|
||||
- Aktuelle CPU Auslastung
|
||||
- Gesamtverbrauch aller CPU Kerne in %
|
||||
- Die nach cpu sortierten Prozesse als Liste
|
||||
- Aktueller Speicherplatzverbrauch
|
||||
-used / free in megabyte
|
||||
- Aktueller RAM Verbrauch
|
||||
- used / free in megabyte
|
||||
- Healthchecks mit Latency
|
||||
- http / https / *ICMP*
|
||||
- GET /
|
||||
- *Aktueller Network IO (optional)*
|
||||
- *Disk IO (optional)*
|
||||
- JSON Output
|
||||
- Datum und Uhrzeit im ISO Format
|
||||
Requirements:
|
||||
- Go is installed.
|
||||
- You have the `$GOPATH` defined.
|
||||
- The `$GOPATH/bin` directory is in your `$PATH`.
|
||||
|
||||
### Kriterien:
|
||||
Install and update it with `go get -u github.com/hamburghammer/gstat`.
|
||||
|
||||
- Single Executable
|
||||
- Linux
|
||||
## Usage
|
||||
```
|
||||
Usage:
|
||||
gstat [OPTIONS]
|
||||
|
||||
### Bewertungs:
|
||||
Application Options:
|
||||
-c, --cpu Include the total CPU consumption.
|
||||
-m, --mem Include the RAM usage.
|
||||
-d, --disk Include the Disk usage.
|
||||
-p, --proc Include the top 10 running processes with the highest CPU consumption.
|
||||
--health= Make a healthcheck call against the URI.
|
||||
|
||||
1. Wie groß ist das Binary
|
||||
2. Performance von Befehlen
|
||||
3. Cpu Auslastung
|
||||
4. Ram Auslastung
|
||||
Help Options:
|
||||
-h, --help Show this help message
|
||||
```
|
||||
*not all flags a jet supported or fully implemented!
|
||||
|
||||
### Kommandozeilenaufrufe:
|
||||
example output:
|
||||
|
||||
Alphabetische Reihenfolge
|
||||
|
||||
-c -h -d // --healtheck=http://example.com --disk --format=json
|
||||
|
||||
[https://de.wikipedia.org/wiki/Uniform_Resource_Identifier](https://de.wikipedia.org/wiki/Uniform_Resource_Identifier)
|
||||
|
||||
`$ cstat/gstat --cpu --format=json`
|
||||
`$ cstat/gstat --metric=disk`
|
||||
`$ cstat/gstat --check`
|
||||
|
||||
#### Goals:
|
||||
|
||||
cstat/gstat -c -d -i -h https://my-server.com > log.json
|
||||
|
||||
curl -X POST -d $(cstat/gstat -c -d -i -h https://my-server.com) https://my-logging.com/logs
|
||||
|
||||
`gstat -cmd`
|
||||
```json
|
||||
{"Date":"2020-11-21T16:32:18+01:00","CPU":3.49999999997029,"mem":{"used":5777,"total":16022},"disk":{"used":90319,"total":224323}}
|
||||
```
|
||||
|
|
19
args/args.go
19
args/args.go
|
@ -2,6 +2,8 @@ package args
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/jessevdk/go-flags"
|
||||
|
@ -9,11 +11,11 @@ import (
|
|||
|
||||
// Arguments represent the flags given at program start.
|
||||
type Arguments struct {
|
||||
CPU bool `short:"c" long:"cpu" description:"Include the total CPU consumption"`
|
||||
Mem bool `short:"m" long:"mem" description:"Include the total RAM consumption"`
|
||||
Disk bool `short:"d" long:"disk" description:"Include the total CPU consumption"`
|
||||
Processes bool `short:"p" long:"proc" description:"Include the top 10 processes"`
|
||||
Health string `long:"health" description:"Make a healthcheck call against the URI"`
|
||||
CPU bool `short:"c" long:"cpu" description:"Include the total CPU consumption."`
|
||||
Mem bool `short:"m" long:"mem" description:"Include the RAM usage."`
|
||||
Disk bool `short:"d" long:"disk" description:"Include the Disk usage."`
|
||||
Processes bool `short:"p" long:"proc" description:"Include the top 10 running processes with the highest CPU consumption."`
|
||||
Health string `long:"health" description:"Make a healthcheck call against the URI."`
|
||||
rest []string
|
||||
}
|
||||
|
||||
|
@ -61,7 +63,12 @@ func Parse() Arguments {
|
|||
_, err := flags.Parse(&args)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
if _, ok := err.(*flags.Error); ok {
|
||||
os.Exit(1)
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
return args
|
||||
|
|
31
commands/date.go
Normal file
31
commands/date.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/hamburghammer/gstat/args"
|
||||
)
|
||||
|
||||
// Date is a the configuration struct to execute the date command
|
||||
type Date struct {
|
||||
// GetTime is the function to get the actual Time in string format.
|
||||
// It should be use to replace/cusomise the time output.
|
||||
GetTime func() string
|
||||
}
|
||||
|
||||
// NewDate is a convinice constructor for the Date struct.
|
||||
// It sets the GetTime function to standard formatting.
|
||||
func NewDate() Date {
|
||||
return Date{GetTime: getFormattedTime}
|
||||
}
|
||||
|
||||
// Exec is the implementation of the execution interface for the Date struct.
|
||||
func (d Date) Exec(args args.Arguments) ([]byte, error) {
|
||||
data := struct{ Date string }{Date: d.GetTime()}
|
||||
return json.Marshal(data)
|
||||
}
|
||||
|
||||
func getFormattedTime() string {
|
||||
return time.Now().Format(time.RFC3339)
|
||||
}
|
22
commands/date_test.go
Normal file
22
commands/date_test.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
package commands_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hamburghammer/gstat/args"
|
||||
"github.com/hamburghammer/gstat/commands"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDate(t *testing.T) {
|
||||
t.Run("", func(t *testing.T) {
|
||||
customTime := func() string { return "2020-08-09T17:43:31+02:00" }
|
||||
date := commands.Date{GetTime: customTime}
|
||||
|
||||
got, err := date.Exec(args.Arguments{})
|
||||
want := "{\"Date\":\"2020-08-09T17:43:31+02:00\"}"
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, want, string(got))
|
||||
})
|
||||
}
|
|
@ -2,7 +2,6 @@ package commands
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/hamburghammer/gstat/args"
|
||||
goDisk "github.com/shirou/gopsutil/disk"
|
||||
|
@ -29,6 +28,8 @@ func (d Disk) Exec(args args.Arguments) ([]byte, error) {
|
|||
return []byte{}, err
|
||||
}
|
||||
|
||||
data := struct{ Disk string }{Disk: fmt.Sprintf("%d/%d", bytesToMegaByte(usage.Used), bytesToMegaByte(usage.Total))}
|
||||
data := struct {
|
||||
Disk Memory `json:"disk"`
|
||||
}{Disk: Memory{Used: bytesToMegaByte(usage.Used), Total: bytesToMegaByte(usage.Total)}}
|
||||
return json.Marshal(data)
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ func TestDiskExec(t *testing.T) {
|
|||
}}
|
||||
|
||||
got, err := disk.Exec(args.Arguments{Disk: true})
|
||||
want := "{\"Disk\":\"47/95\"}"
|
||||
want := "{\"disk\":{\"used\":47,\"total\":95}}"
|
||||
|
||||
assertNoError(err, t)
|
||||
|
||||
|
|
|
@ -2,12 +2,17 @@ package commands
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/hamburghammer/gstat/args"
|
||||
"github.com/shirou/gopsutil/mem"
|
||||
)
|
||||
|
||||
// Memory usage representation.
|
||||
type Memory struct {
|
||||
Used uint64 `json:"used"`
|
||||
Total uint64 `json:"total"`
|
||||
}
|
||||
|
||||
// Mem holds the memory usage for the json transformation
|
||||
type Mem struct {
|
||||
ReadVirtualMemoryStat func() (*mem.VirtualMemoryStat, error)
|
||||
|
@ -29,8 +34,10 @@ func (m Mem) Exec(args args.Arguments) ([]byte, error) {
|
|||
return []byte{}, err
|
||||
}
|
||||
|
||||
usage := fmt.Sprintf("%d/%d", bytesToMegaByte(mem.Used), bytesToMegaByte(mem.Total))
|
||||
data := struct{ Mem string }{usage}
|
||||
usage := Memory{Used: bytesToMegaByte(mem.Used), Total: bytesToMegaByte(mem.Total)}
|
||||
data := struct {
|
||||
Mem Memory `json:"mem"`
|
||||
}{usage}
|
||||
return json.Marshal(data)
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ func TestMemExec(t *testing.T) {
|
|||
}}
|
||||
|
||||
got, err := disk.Exec(args.Arguments{Mem: true})
|
||||
want := "{\"Mem\":\"47/95\"}"
|
||||
want := "{\"mem\":{\"used\":47,\"total\":95}}"
|
||||
|
||||
assertNoError(err, t)
|
||||
|
||||
|
|
105
commands/processes.go
Normal file
105
commands/processes.go
Normal file
|
@ -0,0 +1,105 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sort"
|
||||
|
||||
"github.com/hamburghammer/gstat/args"
|
||||
"github.com/shirou/gopsutil/process"
|
||||
)
|
||||
|
||||
// Processes holds the function to get the process list
|
||||
type Processes struct {
|
||||
ReadProcesses func() ([]*Process, error)
|
||||
}
|
||||
|
||||
// Exec is the implementation of the execution interface to be able to be used as a command
|
||||
func (p Processes) Exec(args args.Arguments) ([]byte, error) {
|
||||
if !args.Processes {
|
||||
return []byte{}, nil
|
||||
}
|
||||
|
||||
processes, err := p.ReadProcesses()
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
processesWithCPU, err := getProcessesCPUInfos(processes)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
sort.Sort(byCPU(processesWithCPU))
|
||||
|
||||
data := struct{ Processes []cpuProcess }{Processes: getFirstTenOrLess(processesWithCPU)}
|
||||
return json.Marshal(data)
|
||||
|
||||
}
|
||||
|
||||
// NewProcesses is a factory ctor to build a Processes struct
|
||||
func NewProcesses() Processes {
|
||||
return Processes{ReadProcesses: getProcesses}
|
||||
}
|
||||
|
||||
// getProcesses maps the process.Process array to a local Process struct
|
||||
func getProcesses() ([]*Process, error) {
|
||||
processes, err := process.Processes()
|
||||
|
||||
p := make([]*Process, 0, len(processes))
|
||||
|
||||
for _, process := range processes {
|
||||
p = append(p, &Process{Pid: process.Pid, CPUPercent: process.CPUPercent, Name: process.Name})
|
||||
}
|
||||
|
||||
return p, err
|
||||
}
|
||||
|
||||
func getFirstTenOrLess(array []cpuProcess) []cpuProcess {
|
||||
if len(array) >= 9 {
|
||||
return array[0:10]
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
func getProcessesCPUInfos(processes []*Process) ([]cpuProcess, error) {
|
||||
processesWithCPU := make([]cpuProcess, 0, len(processes))
|
||||
for _, process := range processes {
|
||||
processCPUInfo, err := getProcessCPUInfos(process)
|
||||
if err != nil {
|
||||
return processesWithCPU, err
|
||||
}
|
||||
processesWithCPU = append(processesWithCPU, *processCPUInfo)
|
||||
}
|
||||
return processesWithCPU, nil
|
||||
}
|
||||
|
||||
func getProcessCPUInfos(process *Process) (*cpuProcess, error) {
|
||||
cpuPercent, err := process.CPUPercent()
|
||||
if err != nil {
|
||||
return &cpuProcess{}, err
|
||||
}
|
||||
name, err := process.Name()
|
||||
if err != nil {
|
||||
return &cpuProcess{}, err
|
||||
}
|
||||
return &cpuProcess{Pid: process.Pid, CPU: cpuPercent, Name: name}, nil
|
||||
}
|
||||
|
||||
type cpuProcess struct {
|
||||
Name string
|
||||
Pid int32
|
||||
CPU float64
|
||||
}
|
||||
|
||||
type byCPU []cpuProcess
|
||||
|
||||
func (c byCPU) Len() int { return len(c) }
|
||||
func (c byCPU) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
|
||||
func (c byCPU) Less(i, j int) bool { return c[i].CPU > c[j].CPU }
|
||||
|
||||
// Process is an adapter struct for the external process struct from github.com/shirou/gopsutil/process
|
||||
type Process struct {
|
||||
Pid int32
|
||||
Name func() (string, error)
|
||||
CPUPercent func() (float64, error)
|
||||
}
|
137
commands/processes_internals_test.go
Normal file
137
commands/processes_internals_test.go
Normal file
|
@ -0,0 +1,137 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestByCPULen(t *testing.T) {
|
||||
t.Run("three item inside the array", func(t *testing.T) {
|
||||
array := []cpuProcess{{}, {}, {}}
|
||||
got := byCPU(array).Len()
|
||||
want := 3
|
||||
|
||||
assert.Equal(t, want, got, "they should be equal")
|
||||
})
|
||||
|
||||
t.Run("one item inside the array", func(t *testing.T) {
|
||||
array := []cpuProcess{{}}
|
||||
got := byCPU(array).Len()
|
||||
want := 1
|
||||
|
||||
assert.Equal(t, want, got, "they should be equal")
|
||||
})
|
||||
|
||||
t.Run("empty array", func(t *testing.T) {
|
||||
array := []cpuProcess{}
|
||||
got := byCPU(array).Len()
|
||||
want := 0
|
||||
|
||||
assert.Equal(t, want, got, "they should be equal")
|
||||
})
|
||||
}
|
||||
|
||||
func TestByCPUSwap(t *testing.T) {
|
||||
t.Run("swap array items", func(t *testing.T) {
|
||||
unswaped := []cpuProcess{{Name: "foo"}, {Name: "bar"}}
|
||||
swaped := []cpuProcess{{Name: "bar"}, {Name: "foo"}}
|
||||
|
||||
got := byCPU(unswaped)
|
||||
got.Swap(0, 1)
|
||||
want := byCPU(swaped)
|
||||
|
||||
assert.Equal(t, want, got)
|
||||
})
|
||||
}
|
||||
|
||||
func TestByCPULess(t *testing.T) {
|
||||
cpuProcessArray := []cpuProcess{{CPU: 1}, {CPU: 2}}
|
||||
|
||||
t.Run("less on cpu field smaller", func(t *testing.T) {
|
||||
|
||||
got := byCPU(cpuProcessArray).Less(0, 1)
|
||||
want := false
|
||||
|
||||
assert.Equal(t, want, got)
|
||||
})
|
||||
|
||||
t.Run("less on cpu field bigger", func(t *testing.T) {
|
||||
|
||||
got := byCPU(cpuProcessArray).Less(1, 0)
|
||||
want := true
|
||||
|
||||
assert.Equal(t, want, got)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetProcessCPUInfos(t *testing.T) {
|
||||
t.Run("transform Process to cpuProcess", func(t *testing.T) {
|
||||
nameFunc := func() (string, error) { return "foo", nil }
|
||||
cpuProcessFunc := func() (float64, error) { return 0, nil }
|
||||
process := Process{Pid: 1, Name: nameFunc, CPUPercent: cpuProcessFunc}
|
||||
|
||||
got, err := getProcessCPUInfos(&process)
|
||||
want := &cpuProcess{Name: "foo", Pid: 1, CPU: 0}
|
||||
|
||||
assert.Nil(t, err, "No error expected")
|
||||
assert.Equal(t, want, got)
|
||||
})
|
||||
|
||||
t.Run("calling name function returns error", func(t *testing.T) {
|
||||
wantErr := errors.New("error")
|
||||
|
||||
nameFunc := func() (string, error) { return "", wantErr }
|
||||
cpuProcessFunc := func() (float64, error) { return 0, nil }
|
||||
process := Process{Pid: 1, Name: nameFunc, CPUPercent: cpuProcessFunc}
|
||||
|
||||
_, gotErr := getProcessCPUInfos(&process)
|
||||
|
||||
assert.NotNil(t, gotErr, "An error was expected")
|
||||
assert.Equal(t, wantErr, gotErr)
|
||||
})
|
||||
|
||||
t.Run("calling cpuProcess function returns error", func(t *testing.T) {
|
||||
wantErr := errors.New("error")
|
||||
|
||||
nameFunc := func() (string, error) { return "", nil }
|
||||
cpuProcessFunc := func() (float64, error) { return 0, wantErr }
|
||||
process := Process{Pid: 1, Name: nameFunc, CPUPercent: cpuProcessFunc}
|
||||
|
||||
_, gotErr := getProcessCPUInfos(&process)
|
||||
|
||||
assert.NotNil(t, gotErr, "An error was expected")
|
||||
assert.Equal(t, wantErr, gotErr)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetProcessesCPUInfos(t *testing.T) {
|
||||
err := errors.New("error")
|
||||
|
||||
nameFunc := func() (string, error) { return "foo", nil }
|
||||
nameErrFunc := func() (string, error) { return "foo", err }
|
||||
|
||||
cpuProcessFunc := func() (float64, error) { return 0, nil }
|
||||
|
||||
t.Run("transform array of process into an array of cpuProcess", func(t *testing.T) {
|
||||
processes := []*Process{{Pid: 2, Name: nameFunc, CPUPercent: cpuProcessFunc}, {Pid: 1, Name: nameFunc, CPUPercent: cpuProcessFunc}}
|
||||
got, gotErr := getProcessesCPUInfos(processes)
|
||||
want := []cpuProcess{{Name: "foo", CPU: 0, Pid: 2}, {Name: "foo", CPU: 0, Pid: 1}}
|
||||
|
||||
assert.Nil(t, gotErr, "No error expected")
|
||||
assert.Equal(t, want, got)
|
||||
})
|
||||
|
||||
t.Run("return error directly if one happens", func(t *testing.T) {
|
||||
processes := []*Process{{Pid: 2, Name: nameFunc, CPUPercent: cpuProcessFunc}, {Pid: 1, Name: nameErrFunc, CPUPercent: cpuProcessFunc}}
|
||||
got, gotErr := getProcessesCPUInfos(processes)
|
||||
want := []cpuProcess{{Name: "foo", CPU: 0, Pid: 2}}
|
||||
wantErr := err
|
||||
|
||||
assert.NotNil(t, gotErr, "an error was expected")
|
||||
|
||||
assert.Equal(t, wantErr, gotErr)
|
||||
assert.Equal(t, want, got)
|
||||
})
|
||||
}
|
80
commands/processes_test.go
Normal file
80
commands/processes_test.go
Normal file
|
@ -0,0 +1,80 @@
|
|||
package commands_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/hamburghammer/gstat/args"
|
||||
"github.com/hamburghammer/gstat/commands"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestProcessExec(t *testing.T) {
|
||||
t.Run("should test for the Argument for Process", func(t *testing.T) {
|
||||
arguments := args.Arguments{Processes: false}
|
||||
got, err := commands.Processes{}.Exec(arguments)
|
||||
|
||||
assert.Nil(t, err, "no error expected")
|
||||
assert.Equal(t, 0, len(got))
|
||||
})
|
||||
|
||||
t.Run("should exit if the getting the process data returns an error", func(t *testing.T) {
|
||||
wantErr := errors.New("getting data error")
|
||||
arguments := args.Arguments{Processes: true}
|
||||
mockProcessData := func() ([]*commands.Process, error) { return nil, wantErr }
|
||||
_, err := commands.Processes{ReadProcesses: mockProcessData}.Exec(arguments)
|
||||
|
||||
assert.NotNil(t, err, "an error was expected")
|
||||
assert.Equal(t, wantErr, err)
|
||||
})
|
||||
|
||||
t.Run("should pass errors from getting cpu infos to output", func(t *testing.T) {
|
||||
wantErr := errors.New("getting data error")
|
||||
arguments := args.Arguments{Processes: true}
|
||||
nameFunc := func() (string, error) { return "", nil }
|
||||
cpuErrorProcessFunc := func() (float64, error) { return 0, wantErr }
|
||||
process := commands.Process{Pid: 1, Name: nameFunc, CPUPercent: cpuErrorProcessFunc}
|
||||
mockProcessData := func() ([]*commands.Process, error) { return []*commands.Process{&process}, nil }
|
||||
|
||||
_, gotErr := commands.Processes{ReadProcesses: mockProcessData}.Exec(arguments)
|
||||
|
||||
assert.Equal(t, wantErr, gotErr)
|
||||
})
|
||||
|
||||
t.Run("should sort per cpu value", func(t *testing.T) {
|
||||
arguments := args.Arguments{Processes: true}
|
||||
nameFunc := func() (string, error) { return "", nil }
|
||||
cpuProcessFunc1 := func() (float64, error) { return 0, nil }
|
||||
cpuProcessFunc2 := func() (float64, error) { return 1, nil }
|
||||
process1 := commands.Process{Pid: 1, Name: nameFunc, CPUPercent: cpuProcessFunc1}
|
||||
process2 := commands.Process{Pid: 2, Name: nameFunc, CPUPercent: cpuProcessFunc2}
|
||||
mockProcessData := func() ([]*commands.Process, error) { return []*commands.Process{&process1, &process2}, nil }
|
||||
|
||||
got, err := commands.Processes{ReadProcesses: mockProcessData}.Exec(arguments)
|
||||
want := "{\"Processes\":[{\"Name\":\"\",\"Pid\":2,\"CPU\":1},{\"Name\":\"\",\"Pid\":1,\"CPU\":0}]}"
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, want, string(got))
|
||||
})
|
||||
|
||||
t.Run("should return max 10 entries", func(t *testing.T) {
|
||||
arguments := args.Arguments{Processes: true}
|
||||
nameFunc := func() (string, error) { return "", nil }
|
||||
cpuProcessFunc := func() (float64, error) { return 0, nil }
|
||||
|
||||
processes := make([]*commands.Process, 12)
|
||||
for i := 0; i <= 11; i++ {
|
||||
processes[i] = &commands.Process{Pid: int32(i), Name: nameFunc, CPUPercent: cpuProcessFunc}
|
||||
}
|
||||
|
||||
mockProcessData := func() ([]*commands.Process, error) { return processes, nil }
|
||||
|
||||
got, err := commands.Processes{ReadProcesses: mockProcessData}.Exec(arguments)
|
||||
want := "{\"Processes\":[{\"Name\":\"\",\"Pid\":0,\"CPU\":0},{\"Name\":\"\",\"Pid\":1,\"CPU\":0},{\"Name\":\"\",\"Pid\":2,\"CPU\":0},{\"Name\":\"\",\"Pid\":3,\"CPU\":0},{\"Name\":\"\",\"Pid\":4,\"CPU\":0},{\"Name\":\"\",\"Pid\":5,\"CPU\":0},{\"Name\":\"\",\"Pid\":6,\"CPU\":0},{\"Name\":\"\",\"Pid\":7,\"CPU\":0},{\"Name\":\"\",\"Pid\":8,\"CPU\":0},{\"Name\":\"\",\"Pid\":9,\"CPU\":0}]}"
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, want, string(got))
|
||||
})
|
||||
}
|
|
@ -45,7 +45,7 @@ func TestResultEquals(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestExecCommands(t *testing.T) {
|
||||
t.Run("should return string array without opening and closeing brackets", func(t *testing.T) {
|
||||
t.Run("should return string array without opening and closing brackets", func(t *testing.T) {
|
||||
arguments := args.Arguments{}
|
||||
result := commands.NewResult(arguments)
|
||||
|
||||
|
|
56
docs/competition.md
Normal file
56
docs/competition.md
Normal file
|
@ -0,0 +1,56 @@
|
|||
# Competition
|
||||
The tool is part of the competition with [Niklas](https://github.com/nhh).
|
||||
|
||||
Checkout his solution: [cstat](https://github.com/nhh/cstat)
|
||||
|
||||
## Terms of the competition
|
||||
|
||||
### Allgemein:
|
||||
Name des Programms:
|
||||
- cstat
|
||||
- gstat
|
||||
|
||||
Das Tool kann mehrere Metriken auf einmal zurückgeben.
|
||||
|
||||
### Anforderung:
|
||||
- Aktuelle CPU Auslastung
|
||||
- Gesamtverbrauch aller CPU Kerne in %
|
||||
- Die nach cpu sortierten Prozesse als Liste
|
||||
- Aktueller Speicherplatzverbrauch
|
||||
-used / free in megabyte
|
||||
- Aktueller RAM Verbrauch
|
||||
- used / free in megabyte
|
||||
- Healthchecks mit Latency
|
||||
- http / https / *ICMP*
|
||||
- GET /
|
||||
- *Aktueller Network IO (optional)*
|
||||
- *Disk IO (optional)*
|
||||
- JSON Output
|
||||
- Datum und Uhrzeit im ISO Format
|
||||
|
||||
### Kriterien:
|
||||
- Single Executable
|
||||
- Linux
|
||||
|
||||
### Bewertungs:
|
||||
1. Wie groß ist das Binary
|
||||
2. Performance von Befehlen
|
||||
3. Cpu Auslastung
|
||||
4. Ram Auslastung
|
||||
|
||||
### Kommandozeilenaufrufe:
|
||||
Alphabetische Reihenfolge
|
||||
|
||||
-c -h -d // --healtheck=http://example.com --disk --format=json
|
||||
|
||||
[https://de.wikipedia.org/wiki/Uniform_Resource_Identifier](https://de.wikipedia.org/wiki/Uniform_Resource_Identifier)
|
||||
|
||||
`$ cstat/gstat --cpu --format=json`
|
||||
`$ cstat/gstat --metric=disk`
|
||||
`$ cstat/gstat --check`
|
||||
|
||||
#### Goals:
|
||||
cstat/gstat -c -d -i -h https://my-server.com > log.json
|
||||
|
||||
curl -X POST -d $(cstat/gstat -c -d -i -h https://my-server.com) https://my-logging.com/logs
|
||||
|
7
go.mod
7
go.mod
|
@ -3,7 +3,10 @@ module github.com/hamburghammer/gstat
|
|||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
|
||||
github.com/go-ole/go-ole v1.2.4 // indirect
|
||||
github.com/jessevdk/go-flags v1.4.0
|
||||
github.com/shirou/gopsutil v2.20.4+incompatible
|
||||
golang.org/x/sys v0.0.0-20200610111108-226ff32320da // indirect
|
||||
github.com/shirou/gopsutil v2.20.6+incompatible
|
||||
github.com/stretchr/testify v1.6.1
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642 // indirect
|
||||
)
|
||||
|
|
16
go.sum
16
go.sum
|
@ -1,6 +1,22 @@
|
|||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
|
||||
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
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/shirou/gopsutil v2.20.4+incompatible h1:cMT4rxS55zx9NVUnCkrmXCsEB/RNfG9SwHY9evtX8Ng=
|
||||
github.com/shirou/gopsutil v2.20.4+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shirou/gopsutil v2.20.6+incompatible h1:P37G9YH8M4vqkKcwBosp+URN5O8Tay67D2MbR361ioY=
|
||||
github.com/shirou/gopsutil v2.20.6+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
golang.org/x/sys v0.0.0-20200610111108-226ff32320da h1:bGb80FudwxpeucJUjPYJXuJ8Hk91vNtfvrymzwiei38=
|
||||
golang.org/x/sys v0.0.0-20200610111108-226ff32320da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642 h1:B6caxRw+hozq68X2MY7jEpZh/cr4/aHLv9xU8Kkadrw=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
8
main.go
8
main.go
|
@ -11,7 +11,13 @@ func main() {
|
|||
args := args.Parse()
|
||||
|
||||
result := commands.NewResult(args)
|
||||
executs := []commands.Executor{commands.NewCPU(), commands.NewMem(), commands.NewDisk()}
|
||||
executs := []commands.Executor{
|
||||
commands.NewDate(),
|
||||
commands.NewCPU(),
|
||||
commands.NewMem(),
|
||||
commands.NewDisk(),
|
||||
commands.NewProcesses(),
|
||||
}
|
||||
output := result.ExecCommands(executs)
|
||||
|
||||
fmt.Println(formatToJSON(output.Collection.Results))
|
||||
|
|
Loading…
Reference in a new issue