Skip to content

Commit 7143587

Browse files
tcm: add the tt tcm status command
@TarantoolBot document Title: add the tt tcm status command. Add `tt tcm status` command that displays whether TCM is running in: - watchdog mode (with auto-recovery) - interactive mode Closes #TNTP-1992
1 parent 4206e3b commit 7143587

File tree

7 files changed

+702
-122
lines changed

7 files changed

+702
-122
lines changed

CHANGELOG.md

Lines changed: 104 additions & 96 deletions
Large diffs are not rendered by default.

cli/cmd/tcm.go

Lines changed: 105 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,31 @@ package cmd
22

33
import (
44
"errors"
5+
"fmt"
6+
"log"
57
"os"
68
"os/exec"
9+
"path/filepath"
710
"time"
811

12+
"github.com/jedib0t/go-pretty/v6/table"
13+
"github.com/jedib0t/go-pretty/v6/text"
914
"github.com/spf13/cobra"
1015
"github.com/tarantool/tt/cli/cmdcontext"
1116
"github.com/tarantool/tt/cli/modules"
17+
"github.com/tarantool/tt/cli/process_utils"
1218
tcmCmd "github.com/tarantool/tt/cli/tcm"
1319
"github.com/tarantool/tt/cli/util"
20+
libwatchdog "github.com/tarantool/tt/lib/watchdog"
1421
)
1522

1623
var tcmCtx = tcmCmd.TcmCtx{}
1724

25+
const (
26+
tcmPidFile = "tcmPidFile.pid"
27+
watchdogPidFile = "watchdogPidFile.pid"
28+
)
29+
1830
func newTcmStartCmd() *cobra.Command {
1931
var tcmCmd = &cobra.Command{
2032
Use: "start",
@@ -26,7 +38,6 @@ func newTcmStartCmd() *cobra.Command {
2638
cmdCtx.CommandName = cmd.Name()
2739
err := modules.RunCmd(&cmdCtx, cmd.CommandPath(), &modulesInfo, internalStartTcm, args)
2840
util.HandleCmdErr(cmd, err)
29-
3041
},
3142
}
3243
tcmCmd.Flags().StringVar(&tcmCtx.Executable, "path", "", "the path to the tcm binary file")
@@ -35,40 +46,73 @@ func newTcmStartCmd() *cobra.Command {
3546
return tcmCmd
3647
}
3748

49+
func newTcmStatusCmd() *cobra.Command {
50+
var tcmCmd = &cobra.Command{
51+
Use: "status",
52+
Short: "Status tcm application",
53+
Long: `Status to the tcm.
54+
tt tcm status`,
55+
Run: func(cmd *cobra.Command, args []string) {
56+
cmdCtx.CommandName = cmd.Name()
57+
err := modules.RunCmd(&cmdCtx, cmd.CommandPath(), &modulesInfo, internalTcmStatus, args)
58+
util.HandleCmdErr(cmd, err)
59+
},
60+
}
61+
return tcmCmd
62+
}
63+
64+
func newTcmStopCmd() *cobra.Command {
65+
var tcmCmd = &cobra.Command{
66+
Use: "stop",
67+
Short: "Stop tcm application",
68+
Long: `Stop to the tcm. tt tcm stop`,
69+
Run: func(cmd *cobra.Command, args []string) {
70+
cmdCtx.CommandName = cmd.Name()
71+
err := modules.RunCmd(&cmdCtx, cmd.CommandPath(), &modulesInfo, internalTcmStop, args)
72+
util.HandleCmdErr(cmd, err)
73+
},
74+
}
75+
return tcmCmd
76+
}
77+
3878
func NewTcmCmd() *cobra.Command {
3979
var tcmCmd = &cobra.Command{
4080
Use: "tcm",
4181
Short: "Manage tcm application",
4282
}
4383
tcmCmd.AddCommand(
4484
newTcmStartCmd(),
85+
newTcmStatusCmd(),
86+
newTcmStopCmd(),
4587
)
4688
return tcmCmd
4789
}
4890

4991
func startTcmInteractive() error {
5092
tcmApp := exec.Command(tcmCtx.Executable)
5193

52-
tcmApp.Stdout = os.Stdout
53-
tcmApp.Stderr = os.Stderr
54-
55-
if err := tcmApp.Run(); err != nil {
94+
if err := tcmApp.Start(); err != nil {
5695
return err
5796
}
5897

59-
return nil
60-
}
98+
if tcmApp == nil || tcmApp.Process == nil {
99+
return errors.New("process is not running")
100+
}
61101

62-
func startTcmUnderWatchDog() error {
63-
wd, err := tcmCmd.NewWatchdog(5 * time.Second)
102+
err := process_utils.CreatePIDFile(tcmPidFile, tcmApp.Process.Pid)
64103
if err != nil {
65104
return err
66105
}
67106

107+
log.Printf("(INFO): Interactive process PID %d written to %s\n", tcmApp.Process.Pid, tcmPidFile)
108+
return nil
109+
}
110+
111+
func startTcmUnderWatchDog() error {
112+
wd := libwatchdog.NewWatchdog(tcmPidFile, watchdogPidFile, 5*time.Second)
68113
if err := wd.Start(tcmCtx.Executable); err != nil {
69114
return err
70115
}
71-
72116
return nil
73117
}
74118

@@ -87,11 +131,61 @@ func internalStartTcm(cmdCtx *cmdcontext.CmdCtx, args []string) error {
87131
if err := startTcmInteractive(); err != nil {
88132
return err
89133
}
134+
} else {
135+
if err := startTcmUnderWatchDog(); err != nil {
136+
return err
137+
}
90138
}
91139

92-
if err := startTcmUnderWatchDog(); err != nil {
140+
return nil
141+
}
142+
143+
func internalTcmStatus(cmdCtx *cmdcontext.CmdCtx, args []string) error {
144+
pidAbsPath, err := filepath.Abs(tcmPidFile)
145+
if err != nil {
93146
return err
94147
}
95148

149+
if _, err := os.Stat(pidAbsPath); err != nil {
150+
return fmt.Errorf("path does not exist: %v", err)
151+
}
152+
153+
ts := table.NewWriter()
154+
ts.SetOutputMirror(os.Stdout)
155+
156+
ts.AppendHeader(
157+
table.Row{"APPLICATION", "STATUS", "PID"})
158+
159+
ts.SetColumnConfigs([]table.ColumnConfig{
160+
{Number: 1, Align: text.AlignLeft, AlignHeader: text.AlignLeft},
161+
{Number: 2, Align: text.AlignLeft, AlignHeader: text.AlignLeft},
162+
{Number: 3, Align: text.AlignLeft, AlignHeader: text.AlignLeft},
163+
{Number: 4, Align: text.AlignLeft, AlignHeader: text.AlignLeft},
164+
})
165+
166+
status := process_utils.ProcessStatus(pidAbsPath)
167+
168+
ts.AppendRows([]table.Row{
169+
{"TCM", status.Status, status.PID},
170+
})
171+
ts.Render()
172+
return nil
173+
}
174+
175+
func internalTcmStop(cmdCtx *cmdcontext.CmdCtx, args []string) error {
176+
if isExists, _ := process_utils.ExistsAndRecord(watchdogPidFile); isExists {
177+
_, err := process_utils.StopProcess(watchdogPidFile)
178+
if err != nil {
179+
return err
180+
}
181+
log.Println("Watchdog and TCM stoped")
182+
} else {
183+
_, err := process_utils.StopProcess(tcmPidFile)
184+
if err != nil {
185+
return err
186+
}
187+
log.Println("TCM stoped")
188+
}
189+
96190
return nil
97191
}

cli/process_utils/process_utils.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,27 @@ func CheckPIDFile(pidFileName string) error {
104104
return nil
105105
}
106106

107+
// ExistsAndRecord checks if the process with the given pidFileName exists and is alive.
108+
// If it does, returns true, otherwise returns false.
109+
// If something went wrong while trying to read the PID file, returns an error.
110+
func ExistsAndRecord(pidFileName string) (bool, error) {
111+
if _, err := os.Stat(pidFileName); err == nil {
112+
// The PID file already exists. We have to check if the process is alive.
113+
pid, err := GetPIDFromFile(pidFileName)
114+
if err != nil {
115+
return false, fmt.Errorf(`pID file exists, but PID can't be read. Error: "%v"`, err)
116+
}
117+
if res, _ := IsProcessAlive(pid); res {
118+
return true, nil
119+
}
120+
} else if !os.IsNotExist(err) {
121+
return false, fmt.Errorf(`something went wrong while trying to read the PID file. Error: "%v"`,
122+
err)
123+
}
124+
125+
return false, nil
126+
}
127+
107128
// CreatePIDFile checks that the instance PID file is absent or
108129
// deprecated and creates a new one. Returns an error on failure.
109130
func CreatePIDFile(pidFileName string, pid int) error {

cli/tcm/tcm.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,8 @@ package tcm
22

33
type TcmCtx struct {
44
Executable string
5-
Watchdog bool
5+
TcmPidFile string
6+
7+
Watchdog bool
8+
WathdogPidFile string
69
}

0 commit comments

Comments
 (0)