mirror of https://github.com/databricks/cli.git
Add library for spawning a daemon
This commit is contained in:
parent
2d09636611
commit
d86ccf6b49
|
@ -75,5 +75,9 @@ func New(ctx context.Context) *cobra.Command {
|
||||||
cli.AddCommand(sync.New())
|
cli.AddCommand(sync.New())
|
||||||
cli.AddCommand(version.New())
|
cli.AddCommand(version.New())
|
||||||
|
|
||||||
|
cli.AddCommand(&cobra.Command{
|
||||||
|
Use: "stream-",
|
||||||
|
})
|
||||||
|
|
||||||
return cli
|
return cli
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
package selftest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PrintStdinParentPid = "DATABRICKS_CLI_PRINT_STDIN_PARENT_PID"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO CONTINUE: Write command that wait for each other via the PID.
|
||||||
|
// Ensure to check the process name as the PID otherwise can be reused pretty
|
||||||
|
// quick.
|
||||||
|
|
||||||
|
func newPrintStdin() *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "print-stdin",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if os.Getenv(PrintStdinParentPid) != "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package selftest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func New() *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "selftest",
|
||||||
|
Short: "Non functional CLI commands that are useful for testing",
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.AddCommand(newPrintStdin())
|
||||||
|
return cmd
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Daemon struct {
|
||||||
|
// If provided, the child process will create a pid file at this path.
|
||||||
|
PidFilePath string
|
||||||
|
|
||||||
|
// Environment variables to set in the child process.
|
||||||
|
Env []string
|
||||||
|
|
||||||
|
// Arguments to pass to the child process.
|
||||||
|
Args []string
|
||||||
|
|
||||||
|
cmd *exec.Cmd
|
||||||
|
stdin io.WriteCloser
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Daemon) Start() error {
|
||||||
|
cli, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
d.cmd = exec.Command(cli, d.Args...)
|
||||||
|
d.cmd.Env = d.Env
|
||||||
|
d.cmd.SysProcAttr = sysProcAttr()
|
||||||
|
|
||||||
|
d.stdin, err = d.cmd.StdinPipe()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get stdin pipe: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = d.cmd.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.PidFilePath != "" {
|
||||||
|
err = os.WriteFile(d.PidFilePath, []byte(strconv.Itoa(d.cmd.Process.Pid)), 0o644)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to write pid file: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Daemon) Release() error {
|
||||||
|
if d.PidFilePath != "" {
|
||||||
|
err := os.Remove(d.PidFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to remove pid file: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.stdin != nil {
|
||||||
|
err := d.stdin.Close()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to close stdin: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.cmd == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.cmd.Process.Release()
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
//go:build linux || darwin
|
||||||
|
|
||||||
|
package daemon
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
// References:
|
||||||
|
// 1. linux: https://go.dev/src/syscall/exec_linux.go
|
||||||
|
// 2. macos (arm): https://go.dev/src/syscall/exec_libc2.go
|
||||||
|
func sysProcAttr() *syscall.SysProcAttr {
|
||||||
|
return &syscall.SysProcAttr{
|
||||||
|
// Create a new session for the child process. This ensures that the daemon
|
||||||
|
// is not terminated when the parent session is closed. This can happen
|
||||||
|
// for example when a ssh session is terminated.
|
||||||
|
// TODO: Test this.
|
||||||
|
Setsid: true,
|
||||||
|
Noctty: true,
|
||||||
|
|
||||||
|
// Start a new process group for the child process. This ensures that
|
||||||
|
// termination signals to the parent's process group are not propagated to
|
||||||
|
// the child process.
|
||||||
|
Setpgid: true,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
func sysProcAttr() *syscall.SysProcAttr {
|
||||||
|
return &syscall.SysProcAttr{
|
||||||
|
CreationFlags: windows.CREATE_NEW_PROCESS_GROUP | windows.DETACHED_PROCESS | windows.CREATE_NO_WINDOW,
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue