184 lines
3.9 KiB
Go
184 lines
3.9 KiB
Go
package containers
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/rosti-cz/node-api/apps"
|
|
)
|
|
|
|
// Return bytes, inodes occupied by a directory and/or error if there is any
|
|
func du(path string) (int, int, error) {
|
|
space, inodes := 0, 0
|
|
|
|
// Occupied space
|
|
var out bytes.Buffer
|
|
var errOut bytes.Buffer
|
|
command := exec.Command("/usr/bin/du", "-b", "-s", path)
|
|
command.Stdout = &out
|
|
command.Stderr = &errOut
|
|
err := command.Run()
|
|
if err != nil {
|
|
log.Println(errOut.String())
|
|
return space, inodes, err
|
|
}
|
|
fields := strings.Fields(strings.TrimSpace(out.String()))
|
|
out.Reset()
|
|
errOut.Reset()
|
|
|
|
if len(fields) == 2 {
|
|
space, err = strconv.Atoi(fields[0])
|
|
if err != nil {
|
|
return space, inodes, err
|
|
}
|
|
}
|
|
|
|
// Occupied inodes
|
|
command = exec.Command("/usr/bin/du", "--inodes", "-s", path)
|
|
command.Stdout = &out
|
|
command.Stderr = &errOut
|
|
err = command.Run()
|
|
if err != nil {
|
|
log.Println(errOut.String())
|
|
return space, inodes, err
|
|
}
|
|
fields = strings.Fields(strings.TrimSpace(out.String()))
|
|
out.Reset()
|
|
errOut.Reset()
|
|
|
|
if len(fields) == 2 {
|
|
inodes, err = strconv.Atoi(fields[0])
|
|
if err != nil {
|
|
return inodes, inodes, err
|
|
}
|
|
}
|
|
|
|
return space, inodes, nil
|
|
}
|
|
|
|
// Removes content of given directory and then the directory itself
|
|
func removeDirectory(dir string) error {
|
|
d, err := os.Open(dir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer d.Close()
|
|
names, err := d.Readdirnames(-1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, name := range names {
|
|
err = os.RemoveAll(filepath.Join(dir, name))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
err = os.Remove(filepath.Join(dir))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// CPUMemoryStats returns list of applications with updated CPU and memory usage effectively.
|
|
// Sample is number of seconds between two measurements of the CPU usage. More means more precise.
|
|
func CPUMemoryStats(applist *[]apps.App, sample int) (*[]apps.App, error) {
|
|
if sample <= 0 {
|
|
sample = 1
|
|
}
|
|
|
|
containers := []Container{}
|
|
for _, app := range *applist {
|
|
containers = append(containers, Container{App: &app})
|
|
}
|
|
|
|
startMap := make(map[string]int64)
|
|
endMap := make(map[string]int64)
|
|
|
|
start := time.Now().UnixNano()
|
|
for _, container := range containers {
|
|
cpu, _, err := container.GetRawResourceStats()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
startMap[container.App.Name] = cpu
|
|
}
|
|
|
|
time.Sleep(time.Duration(sample) * time.Second)
|
|
|
|
for idx, container := range containers {
|
|
cpu, memory, err := container.GetRawResourceStats()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
endMap[container.App.Name] = cpu
|
|
containers[idx].App.MemoryUsage = memory
|
|
}
|
|
|
|
end := time.Now().UnixNano()
|
|
difference := (float64(end) - float64(start)) / float64(1000000000)
|
|
|
|
updatedApps := []apps.App{}
|
|
for _, container := range containers {
|
|
app := *container.App
|
|
app.CPUUsage = (float64(endMap[app.Name]) - float64(startMap[app.Name])) / difference / 10000000.0
|
|
updatedApps = append(updatedApps, app)
|
|
}
|
|
|
|
return &updatedApps, nil
|
|
}
|
|
|
|
func getTechAndVersion(symlink string) (*TechInfo, error) {
|
|
link, err := os.Readlink(symlink)
|
|
if os.IsNotExist(err) {
|
|
return &TechInfo{
|
|
Tech: "default",
|
|
Version: "",
|
|
}, nil
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error reading symlink: %w", err)
|
|
}
|
|
|
|
absLink, err := filepath.Abs(link)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error getting absolute path: %w", err)
|
|
}
|
|
absLink = strings.TrimSuffix(absLink, "/bin")
|
|
|
|
dirName := filepath.Base(absLink)
|
|
parts := strings.Split(dirName, "-")
|
|
fmt.Println("DEBUG", symlink)
|
|
fmt.Println("DEBUG", absLink)
|
|
fmt.Println("DEBUG", dirName)
|
|
fmt.Println("DEBUG", parts)
|
|
if len(parts) < 2 {
|
|
return &TechInfo{
|
|
Tech: "default",
|
|
Version: "",
|
|
}, nil
|
|
}
|
|
|
|
re := regexp.MustCompile(`\d+\.\d+\.\d+`)
|
|
version := re.FindString(parts[1])
|
|
if version == "" {
|
|
return nil, errors.New("failed to extract version from symlink")
|
|
}
|
|
|
|
return &TechInfo{
|
|
Tech: parts[0],
|
|
Version: version,
|
|
}, nil
|
|
}
|