package containers import ( "bytes" "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 == "" { // In case version couldn't be determined we return "unknown", otherwise returning // error in this case was crashing admin when user fucked up the symlink for the // tech manually. log.Println("failed to extract version from symlink") return &TechInfo{ Tech: "unknown", Version: "", }, nil } return &TechInfo{ Tech: parts[0], Version: version, }, nil }