Skip to content
This repository has been archived by the owner on Mar 28, 2020. It is now read-only.

Commit

Permalink
Support multiple puma state files within one app 🚀
Browse files Browse the repository at this point in the history
Signed-off-by: Valentin Pichard <valentin.pichard@dimelo.com>
  • Loading branch information
Valentin Pichard committed May 2, 2019
1 parent 2ddf1c4 commit 5f3d943
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 119 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ applications:
# Path to your application
path : "/home/path/to/your/app"

# Mandtory - array of string
# Path(s) to puma state file(s)
pumastatepaths:
- /home/your_app/current/tmp/pids/puma.state
- /home/your_app/current/tmp/pids/puma_api.state

# Optional - string
# Description or informations related to the application
# Default description: ""
Expand Down
15 changes: 10 additions & 5 deletions cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,14 @@ func buildStructGlobing(files []string) error {
}

cutpath := strings.Split(files[fid], "/")
cfgdata[cutpath[2]] = helper.PumaHelperCfgData{
Path: "/home/" + cutpath[2],
PumastatePath: files[fid],
if key, ok := cfgdata[cutpath[2]]; ok {
key.PumastatePaths = append(key.PumastatePaths, files[fid])
cfgdata[cutpath[2]] = key
} else {
cfgdata[cutpath[2]] = helper.PumaHelperCfgData{
Path: "/home/" + cutpath[2],
PumastatePaths: []string{files[fid]},
}
}
}

Expand All @@ -150,8 +155,8 @@ func buildAndWriteConfigFile(appname, apppath, pumastatepath string) error {
cfgdata := make(map[string]helper.PumaHelperCfgData)

cfgdata[appname] = helper.PumaHelperCfgData{
Path: apppath,
PumastatePath: pumastatepath,
Path: apppath,
PumastatePaths: []string{pumastatepath},
}

return marshalAndWriteConfigFile(
Expand Down
18 changes: 9 additions & 9 deletions helper/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ type PumaHelperCfg struct {

// PumaHelperCfgData contain all options per app from puma-helper.yml file
type PumaHelperCfgData struct {
Path string `mapstructure:"path"`
Description string `mapstructure:"description"`
PumastatePath string `mapstructure:"pumastatepath"`
ThreadWarn int `mapstructure:"thread_warn"`
ThreadCritical int `mapstructure:"thread_critical"`
CPUWarn int `mapstructure:"cpu_warn"`
CPUCritical int `mapstructure:"cpu_critical"`
MemoryWarn int `mapstructure:"memory_warn"`
MemoryCritical int `mapstructure:"memory_critical"`
Path string `mapstructure:"path"`
Description string `mapstructure:"description"`
PumastatePaths []string `mapstructure:"pumastatepaths"`
ThreadWarn int `mapstructure:"thread_warn"`
ThreadCritical int `mapstructure:"thread_critical"`
CPUWarn int `mapstructure:"cpu_warn"`
CPUCritical int `mapstructure:"cpu_critical"`
MemoryWarn int `mapstructure:"memory_warn"`
MemoryCritical int `mapstructure:"memory_critical"`
}

const (
Expand Down
12 changes: 8 additions & 4 deletions helper/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,18 @@ type pumaStatusFinalOutput struct {
}

type pumaStatusApplication struct {
Name string `json:"name"`
Description string `json:"description"`
RootPath string `json:"root_path"`
Name string `json:"name"`
Description string `json:"description"`
RootPath string `json:"root_path"`
PumaStatePaths []pumaStatusStatePaths `json:"puma_state_paths"`
}

type pumaStatusStatePaths struct {
PumaStatePath string `json:"puma_state_path"`
BootedWorkers int `json:"booted_workers"`
AppCurrentPhase int `json:"app_current_phase"`
OldWorkers int `json:"old_workers"`
Worker []pumaStatusWorker `json:"worker"`
Workers []pumaStatusWorker `json:"workers"`
TotalMaxThreads int `json:"total_max_threads"`
TotalCurrentThreads int `json:"total_current_threads"`
MainPid int `json:"main_pid"`
Expand Down
35 changes: 20 additions & 15 deletions helper/print.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,29 +21,34 @@ func printStatusGlobalInformations() {
// printStatusApps print apps context one by one
func (ps pumaStatusFinalOutput) printStatusApps() error {
fmt.Println("----------- Application groups -----------")

line := 0

for _, key := range ps.Application {
currentApp = key.Name

if ExpandDetails {
fmt.Printf("-> %s application\n", key.Name)
if key.Description != "" {
fmt.Printf(" About: %s\n", key.Description)
}
fmt.Printf(" App root: %s\n", key.RootPath)
fmt.Printf(" Booted workers: %d | PID: %d\n", key.BootedWorkers, key.MainPid)
fmt.Printf(" Current phase: %d | Old workers: %d | Active threads: %s\n\n", key.AppCurrentPhase, key.OldWorkers, asciiThreadLoad(key.TotalCurrentThreads, key.TotalMaxThreads))
basetitle := fmt.Sprintf("-> %s application | App root: %s", currentApp, key.RootPath)
if len(key.Description) == 0 {
fmt.Printf(basetitle)
} else {
if key.OldWorkers > 0 {
fmt.Printf("-> %s %d (%s) Phase: %d | Workers: %d (Old: %d) | Active threads: %s\n\n", key.Name, key.MainPid, key.RootPath, key.AppCurrentPhase, key.BootedWorkers, key.OldWorkers, asciiThreadLoad(key.TotalCurrentThreads, key.TotalMaxThreads))
fmt.Printf(basetitle + " | About: " + key.Description)
}

for _, keypath := range key.PumaStatePaths {
if ExpandDetails {
fmt.Printf("\n -> File: %s\n", keypath.PumaStatePath)
fmt.Printf(" Booted workers: %d | PID: %d\n", keypath.BootedWorkers, keypath.MainPid)
fmt.Printf(" Current phase: %d | Old workers: %d | Active threads: %s\n\n", keypath.AppCurrentPhase, keypath.OldWorkers, asciiThreadLoad(keypath.TotalCurrentThreads, keypath.TotalMaxThreads))
} else {
fmt.Printf("-> %s %d (%s) Phase: %d | Workers: %d | Active threads: %s\n\n", key.Name, key.MainPid, key.RootPath, key.AppCurrentPhase, key.BootedWorkers, asciiThreadLoad(key.TotalCurrentThreads, key.TotalMaxThreads))
if keypath.OldWorkers > 0 {
fmt.Printf("\n-> %d (%s) Phase: %d | Workers: %d (Old: %d) | Active threads: %s\n\n", keypath.MainPid, keypath.PumaStatePath, keypath.AppCurrentPhase, keypath.BootedWorkers, keypath.OldWorkers, asciiThreadLoad(keypath.TotalCurrentThreads, keypath.TotalMaxThreads))
} else {
fmt.Printf("\n-> %d (%s) Phase: %d | Workers: %d | Active threads: %s\n\n", keypath.MainPid, keypath.PumaStatePath, keypath.AppCurrentPhase, keypath.BootedWorkers, asciiThreadLoad(keypath.TotalCurrentThreads, keypath.TotalMaxThreads))
}
}
}

if err := printStatusWorkers(key.Worker, key.AppCurrentPhase); err != nil {
return err
if err := printStatusWorkers(keypath.Workers, keypath.AppCurrentPhase); err != nil {
return err
}
}

if line < len(ps.Application)-1 {
Expand Down
123 changes: 68 additions & 55 deletions helper/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,75 +43,88 @@ func retrieveStatusData() (*pumaStatusFinalOutput, error) {
continue
}

pspath := key.Path + pumastatePath
if key.PumastatePath != "" {
pspath = key.PumastatePath
var pspath []string
pspath = append(pspath, key.Path+pumastatePath)
if len(key.PumastatePaths) > 0 {
pspath = key.PumastatePaths
}

ps, err := readPumaStats(pspath)
pss, err := readPumaStats(pspath)
if err != nil {
log.Warn(fmt.Sprintf("[%s] configuration is invalid. Error: %v\n\n", appName, err))
continue
}

tmthreads := 0
tcthreads := 0
workers := []pumaStatusWorker{}
for _, v := range ps.WorkerStatus {
pid := int32(v.Pid)

cpu, err := getCPUPercentFromPID(pid)
if err != nil {
return nil, err
}

cput, err := getCPUTimesFromPID(pid)
if err != nil {
return nil, err
}

mem, err := getMemoryFromPID(pid)
if err != nil {
return nil, err
}

// Assuming this timestamp is in milliseconds
utime, err := getTotalUptimeFromPID(pid)
if err != nil {
return nil, err
pssps := []pumaStatusStatePaths{}

for fid, ps := range pss {

workers := []pumaStatusWorker{}
tmthreads := 0
tcthreads := 0

for _, v := range ps.WorkerStatus {
pid := int32(v.Pid)

cpu, err := getCPUPercentFromPID(pid)
if err != nil {
return nil, err
}

cput, err := getCPUTimesFromPID(pid)
if err != nil {
return nil, err
}

mem, err := getMemoryFromPID(pid)
if err != nil {
return nil, err
}

// Assuming this timestamp is in milliseconds
utime, err := getTotalUptimeFromPID(pid)
if err != nil {
return nil, err
}

worker := pumaStatusWorker{
ID: v.Index,
IsBooted: v.Booted,
Pid: v.Pid,
LastCheckin: v.LastCheckin,
CurrentThreads: v.LastStatus.MaxThreads - v.LastStatus.PoolCapacity,
MaxThreads: v.LastStatus.MaxThreads,
CPUPercent: cpu,
Memory: mem,
CurrentPhase: v.Phase,
Uptime: utime / 1000,
CPUTimes: cput,
}

tcthreads += (v.LastStatus.MaxThreads - v.LastStatus.PoolCapacity)
tmthreads += v.LastStatus.MaxThreads

workers = append(workers, worker)
}

worker := pumaStatusWorker{
ID: v.Index,
IsBooted: v.Booted,
Pid: v.Pid,
LastCheckin: v.LastCheckin,
CurrentThreads: v.LastStatus.MaxThreads - v.LastStatus.PoolCapacity,
MaxThreads: v.LastStatus.MaxThreads,
CPUPercent: cpu,
Memory: mem,
CurrentPhase: v.Phase,
Uptime: utime / 1000,
CPUTimes: cput,
pssp := pumaStatusStatePaths{
PumaStatePath: pspath[fid],
BootedWorkers: ps.BootedWorkers,
Workers: workers,
TotalCurrentThreads: tcthreads,
TotalMaxThreads: tmthreads,
MainPid: ps.MainPid,
AppCurrentPhase: ps.Phase,
}

tcthreads += (v.LastStatus.MaxThreads - v.LastStatus.PoolCapacity)
tmthreads += v.LastStatus.MaxThreads

workers = append(workers, worker)
pssps = append(pssps, pssp)
}

app := pumaStatusApplication{
Name: appName,
Description: key.Description,
RootPath: key.Path,
PumaStatePath: pspath,
BootedWorkers: ps.BootedWorkers,
Worker: workers,
AppCurrentPhase: ps.Phase,
TotalCurrentThreads: tcthreads,
TotalMaxThreads: tmthreads,
MainPid: ps.MainPid,
Name: appName,
Description: key.Description,
RootPath: key.Path,
PumaStatePaths: pssps,
}

apps = append(apps, app)
Expand Down
69 changes: 38 additions & 31 deletions helper/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,48 +27,55 @@ type pumaStateFile struct {
}

// readPumaStats get and unmarshal JSON using puma unix socket
func readPumaStats(pspath string) (*pumaStatus, error) {
data, err := ioutil.ReadFile(pspath)
if err != nil {
return nil, err
}
func readPumaStats(paths []string) ([]pumaStatus, error) {

var psf pumaStateFile
var pspaths []pumaStatus

if err := yaml.Unmarshal(data, &psf); err != nil {
return nil, err
}
for _, path := range paths {
data, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}

httpc := http.Client{
Transport: &http.Transport{
Dial: func(_, _ string) (net.Conn, error) {
return net.Dial("unix", strings.TrimPrefix(psf.ControlURL, "unix://"))
var psf pumaStateFile

if err := yaml.Unmarshal(data, &psf); err != nil {
return nil, err
}

httpc := http.Client{
Transport: &http.Transport{
Dial: func(_, _ string) (net.Conn, error) {
return net.Dial("unix", strings.TrimPrefix(psf.ControlURL, "unix://"))
},
},
},
}
}

r, err := httpc.Get(fmt.Sprintf("http://unix/stats?token=%s", psf.ControlAuthToken))
if err != nil {
return nil, err
}
r, err := httpc.Get(fmt.Sprintf("http://unix/stats?token=%s", psf.ControlAuthToken))
if err != nil {
return nil, err
}

body, err := ioutil.ReadAll(r.Body)
if err != nil {
return nil, err
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
return nil, err
}

//fmt.Println(pcpath, pspath)
//output, err := exec.Command("cat", "/go/src/github.com/dimelo/puma-helper/output.txt").Output()
ps := pumaStatus{
MainPid: psf.Pid,
}

ps := pumaStatus{
MainPid: psf.Pid,
}
if err := json.Unmarshal(body, &ps); err != nil {
return nil, err
}

if err := json.Unmarshal(body, &ps); err != nil {
return nil, err
pspaths = append(pspaths, ps)
}

return &ps, nil
//body, _ := exec.Command("cat", "/go/src/github.com/dimelo/puma-helper/output.txt").Output()
//fmt.Println(pspath)

return pspaths, nil
}

func getTotalUptimeFromPID(pid int32) (int64, error) {
Expand Down

0 comments on commit 5f3d943

Please sign in to comment.