Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement process entrypoint and run cmd #89

Merged
merged 6 commits into from
Nov 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,19 @@
commit = self.shortRev or "dirty";
};
in
(flake-utils.lib.eachDefaultSystem (system:
{
packages.process-compose = mkPackage nixpkgs.legacyPackages.${system};
(flake-utils.lib.eachDefaultSystem (system: let
pkgs = nixpkgs.legacyPackages.${system};
in {
packages.process-compose = mkPackage pkgs;
defaultPackage = self.packages."${system}".process-compose;
apps.process-compose = flake-utils.lib.mkApp {
drv = self.packages."${system}".process-compose;
};
apps.default = self.apps."${system}".process-compose;
checks.default = self.packages."${system}".process-compose.overrideAttrs (prev: {
doCheck = true;
nativeBuildInputs = prev.nativeBuildInputs ++ (with pkgs; [python3]);
});
})
) // {
overlays.default = final: prev: {
Expand Down
1 change: 1 addition & 0 deletions src/app/commander_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ type Commander interface {
StderrPipe() (io.ReadCloser, error)
Stop(int, bool) error
SetCmdArgs()
AttachIo()
}
51 changes: 37 additions & 14 deletions src/app/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ type Process struct {
liveProber *health.Prober
readyProber *health.Prober
shellConfig command.ShellConfig
printLogs bool
isMain bool
extraArgs []string
}

func NewProcess(
Expand All @@ -62,6 +65,9 @@ func NewProcess(
processState *types.ProcessState,
procLog *pclog.ProcessLogBuffer,
shellConfig command.ShellConfig,
printLogs bool,
isMain bool,
extraArgs []string,
) *Process {
colNumeric := rand.Intn(int(color.FgHiWhite)-int(color.FgHiBlack)) + int(color.FgHiBlack)

Expand All @@ -78,6 +84,9 @@ func NewProcess(
shellConfig: shellConfig,
procStateChan: make(chan string, 1),
procReadyChan: make(chan string, 1),
printLogs: printLogs,
isMain: isMain,
extraArgs: extraArgs,
}

proc.procReadyCtx, proc.readyCancelFn = context.WithCancel(context.Background())
Expand All @@ -92,7 +101,7 @@ func (p *Process) run() int {
}

if err := p.validateProcess(); err != nil {
log.Error().Err(err).Msgf("Failed to run command %s for process %s", p.getCommand(), p.getName())
log.Error().Err(err).Msgf(`Failed to run command ["%v"] for process %s`, strings.Join(p.getCommand(), `" "`), p.getName())
p.onProcessEnd(types.ProcessStateError)
return 1
}
Expand All @@ -101,7 +110,7 @@ func (p *Process) run() int {
for {
err := p.setStateAndRun(p.getStartingStateName(), p.getProcessStarter())
if err != nil {
log.Error().Err(err).Msgf("Failed to run command %s for process %s", p.getCommand(), p.getName())
log.Error().Err(err).Msgf(`Failed to run command ["%v"] for process %s`, strings.Join(p.getCommand(), `" "`), p.getName())
p.logBuffer.Write(err.Error())
p.onProcessEnd(types.ProcessStateError)
return 1
Expand Down Expand Up @@ -151,16 +160,23 @@ func (p *Process) run() int {

func (p *Process) getProcessStarter() func() error {
return func() error {
p.command = command.BuildCommandShellArg(p.shellConfig, p.getCommand())
p.command = command.BuildCommand(
p.procConf.Executable,
append(p.procConf.Args, p.extraArgs...),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If p.procConf.Executable is bash and p.procConf.Args are []string{"-c", "echo Hi"}, p.extraArgs will be ignored.

Instead of that line, I call p.mergeExtraArgs():

func (p *Process) mergeExtraArgs() []string {
	if len(p.extraArgs) == 0 {
		return p.procConf.Args
	}
	tmp := make([]string, len(p.procConf.Args))
	copy(tmp, p.procConf.Args)
	if isStringDefined(p.procConf.Command) {
		lastArg := p.procConf.Args[len(p.procConf.Args)-1]
		lastArg += " " + strings.Join(p.extraArgs, " ")
		return append(tmp[:len(tmp)-1], lastArg)
	} else if len(p.procConf.Entrypoint) > 0 {
		return append(tmp, p.extraArgs...)
	}
	return p.procConf.Args
}

What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you give an example of the final command you’d like executed?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

processes:
  ls:
    command: "ls"
process-compose run ls -- /tmp

run ls will ignore the extraArg /tmp
It worked well with entrypoint instead of command.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This prints: args: 0 1 2

bash -c 'echo args: $0 $1 $2' 0 1 2 

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try:

bash -c "ls" /tmp

vs.

bash -c "ls /tmp"

)
p.command.SetEnv(p.getProcessEnvironment())
p.command.SetDir(p.procConf.WorkingDir)
p.command.SetCmdArgs()
stdout, _ := p.command.StdoutPipe()
stderr, _ := p.command.StderrPipe()
go p.handleOutput(stdout, p.handleInfo)
go p.handleOutput(stderr, p.handleError)
//stdin, _ := p.command.StdinPipe()
//go p.handleInput(stdin)

if p.isMain {
p.command.AttachIo()
} else {
p.command.SetCmdArgs()
stdout, _ := p.command.StdoutPipe()
stderr, _ := p.command.StderrPipe()
go p.handleOutput(stdout, p.handleInfo)
go p.handleOutput(stderr, p.handleError)
}

return p.command.Start()
}
}
Expand Down Expand Up @@ -346,8 +362,11 @@ func (p *Process) getNameWithSmartReplica() string {
return p.procConf.Name
}

func (p *Process) getCommand() string {
return p.procConf.Command
func (p *Process) getCommand() []string {
return append(
[]string{(*p.procConf).Executable},
append(p.procConf.Args, p.extraArgs...)...,
)
}

func (p *Process) updateProcState() {
Expand Down Expand Up @@ -401,13 +420,17 @@ func (p *Process) handleOutput(pipe io.ReadCloser, handler func(message string))

func (p *Process) handleInfo(message string) {
p.logger.Info(message, p.getName(), p.procConf.ReplicaNum)
fmt.Printf("[%s\t] %s\n", p.procColor(p.getName()), message)
if p.printLogs {
fmt.Printf("[%s\t] %s\n", p.procColor(p.getName()), message)
}
p.logBuffer.Write(message)
}

func (p *Process) handleError(message string) {
p.logger.Error(message, p.getName(), p.procConf.ReplicaNum)
fmt.Printf("[%s\t] %s\n", p.procColor(p.getName()), p.redColor(message))
if p.printLogs {
fmt.Printf("[%s\t] %s\n", p.procColor(p.getName()), p.redColor(message))
}
p.logBuffer.Write(message)
}

Expand Down
32 changes: 30 additions & 2 deletions src/app/project_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ type ProjectRunner struct {
logger pclog.PcLogger
waitGroup sync.WaitGroup
exitCode int
mainProcess string
mainProcessArgs []string
}

func (p *ProjectRunner) GetLexicographicProcessNames() ([]string, error) {
Expand Down Expand Up @@ -83,7 +85,25 @@ func (p *ProjectRunner) runProcess(config *types.ProcessConfig) {
procLog = pclog.NewLogBuffer(0)
}
procState, _ := p.GetProcessState(config.ReplicaName)
process := NewProcess(p.project.Environment, procLogger, config, procState, procLog, *p.project.ShellConfig)
isMain := config.Name == p.mainProcess
hasMain := p.mainProcess != ""
printLogs := !hasMain
extraArgs := []string{}
if isMain {
extraArgs = p.mainProcessArgs
config.RestartPolicy.ExitOnEnd = true
F1bonacc1 marked this conversation as resolved.
Show resolved Hide resolved
}
process := NewProcess(
p.project.Environment,
procLogger,
config,
procState,
procLog,
*p.project.ShellConfig,
printLogs,
isMain,
extraArgs,
)
p.addRunningProcess(process)
p.waitGroup.Add(1)
go func() {
Expand Down Expand Up @@ -561,10 +581,18 @@ func (p *ProjectRunner) GetProject() *types.Project {
return p.project
}

func NewProjectRunner(project *types.Project, processesToRun []string, noDeps bool) (*ProjectRunner, error) {
func NewProjectRunner(
project *types.Project,
processesToRun []string,
noDeps bool,
mainProcess string,
mainProcessArgs []string,
) (*ProjectRunner, error) {

runner := &ProjectRunner{
project: project,
mainProcess: mainProcess,
mainProcessArgs: mainProcessArgs,
}

var err error
Expand Down
10 changes: 5 additions & 5 deletions src/app/system_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func TestSystem_TestFixtures(t *testing.T) {
t.Errorf(err.Error())
return
}
runner, err := NewProjectRunner(project, []string{}, false)
runner, err := NewProjectRunner(project, []string{}, false, "", []string{})
if err != nil {
t.Errorf(err.Error())
return
Expand All @@ -48,7 +48,7 @@ func TestSystem_TestComposeWithLog(t *testing.T) {
t.Errorf(err.Error())
return
}
runner, err := NewProjectRunner(project, []string{}, false)
runner, err := NewProjectRunner(project, []string{}, false, "", []string{})
if err != nil {
t.Errorf(err.Error())
return
Expand Down Expand Up @@ -81,7 +81,7 @@ func TestSystem_TestComposeChain(t *testing.T) {
t.Errorf(err.Error())
return
}
runner, err := NewProjectRunner(project, []string{}, false)
runner, err := NewProjectRunner(project, []string{}, false, "", []string{})
if err != nil {
t.Errorf(err.Error())
return
Expand Down Expand Up @@ -117,7 +117,7 @@ func TestSystem_TestComposeChainExit(t *testing.T) {
t.Errorf(err.Error())
return
}
runner, err := NewProjectRunner(project, []string{}, false)
runner, err := NewProjectRunner(project, []string{}, false, "", []string{})
if err != nil {
t.Errorf(err.Error())
return
Expand Down Expand Up @@ -162,7 +162,7 @@ func TestSystem_TestComposeScale(t *testing.T) {
t.Errorf(err.Error())
return
}
runner, err := NewProjectRunner(project, []string{}, false)
runner, err := NewProjectRunner(project, []string{}, false, "", []string{})
if err != nil {
t.Errorf(err.Error())
return
Expand Down
4 changes: 2 additions & 2 deletions src/cmd/project_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"time"
)

func getProjectRunner(process []string, noDeps bool) *app.ProjectRunner {
func getProjectRunner(process []string, noDeps bool, mainProcess string, mainProcessArgs []string) *app.ProjectRunner {
if *pcFlags.HideDisabled {
opts.AddAdmitter(&admitter.DisabledProcAdmitter{})
}
Expand All @@ -23,7 +23,7 @@ func getProjectRunner(process []string, noDeps bool) *app.ProjectRunner {
log.Fatal().Msg(err.Error())
}

runner, err := app.NewProjectRunner(project, process, noDeps)
runner, err := app.NewProjectRunner(project, process, noDeps, mainProcess, mainProcessArgs)
if err != nil {
fmt.Println(err)
log.Fatal().Msg(err.Error())
Expand Down
2 changes: 1 addition & 1 deletion src/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func run(cmd *cobra.Command, args []string) error {
defer func() {
_ = logFile.Close()
}()
runner := getProjectRunner([]string{}, false)
runner := getProjectRunner([]string{}, false, "", []string{})
api.StartHttpServer(!*pcFlags.Headless, *pcFlags.PortNum, runner)
runProject(runner)
return nil
Expand Down
54 changes: 54 additions & 0 deletions src/cmd/run.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package cmd

import (
"fmt"
"os"
"github.com/f1bonacc1/process-compose/src/api"
"github.com/spf13/cobra"
)

// runCmd represents the up command
var runCmd = &cobra.Command{
Use: "run PROCESS [flags] -- [process_args]",
Short: "Run PROCESS in the foreground, and its dependencies in the background",
Long: `Run selected process with std(in|out|err) attached, while other processes run in the background.
Command line arguments, provided after --, are passed to the PROCESS.`,
Args: cobra.MinimumNArgs(1),
// Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
*pcFlags.Headless = false

processName := args[0]

if len(args) > 1 {
argsLenAtDash := cmd.ArgsLenAtDash()
if argsLenAtDash != 1 {
message := "Extra positional arguments provided! To pass args to PROCESS, separate them from process-compose arguments with: --"
fmt.Println(message)
os.Exit(1)
}
args = args[argsLenAtDash:]
} else {
// Clease args as they will contain the processName
args = []string{}
}

runner := getProjectRunner(
[]string{processName},
*pcFlags.NoDependencies,
processName,
args,
)

api.StartHttpServer(false, *pcFlags.PortNum, runner)
runProject(runner)
},
}

func init() {
rootCmd.AddCommand(runCmd)
F1bonacc1 marked this conversation as resolved.
Show resolved Hide resolved

runCmd.Flags().BoolVarP(pcFlags.NoDependencies, "no-deps", "", *pcFlags.NoDependencies, "don't start dependent processes")
runCmd.Flags().AddFlag(rootCmd.Flags().Lookup("config"))

}
2 changes: 1 addition & 1 deletion src/cmd/up.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ var upCmd = &cobra.Command{
If one or more process names are passed as arguments,
will start them and their dependencies only`,
Run: func(cmd *cobra.Command, args []string) {
runner := getProjectRunner(args, *pcFlags.NoDependencies)
runner := getProjectRunner(args, *pcFlags.NoDependencies, "", []string{})
api.StartHttpServer(!*pcFlags.Headless, *pcFlags.PortNum, runner)
runProject(runner)
},
Expand Down
5 changes: 3 additions & 2 deletions src/command/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import (
"runtime"
)

func BuildCommandShellArg(shell ShellConfig, cmd string) *CmdWrapper {

func BuildCommand(cmd string, args []string) *CmdWrapper {
return &CmdWrapper{
cmd: exec.Command(shell.ShellCommand, shell.ShellArgument, cmd),
cmd: exec.Command(cmd, args...),
}
//return NewMockCommand()
}
Expand Down
7 changes: 7 additions & 0 deletions src/command/command_wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package command

import (
"io"
"os"
"os/exec"
)

Expand Down Expand Up @@ -37,6 +38,12 @@ func (c *CmdWrapper) StderrPipe() (io.ReadCloser, error) {
return c.cmd.StderrPipe()
}

func (c *CmdWrapper) AttachIo() () {
c.cmd.Stdin = os.Stdin
c.cmd.Stdout = os.Stdout
c.cmd.Stderr = os.Stderr
}

func (c *CmdWrapper) SetEnv(env []string) {
c.cmd.Env = env
}
Expand Down
2 changes: 1 addition & 1 deletion src/tui/proc-info-form.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ func (pv *pcView) createProcInfoForm(info *types.ProcessConfig, ports *types.Pro
f.SetFieldTextColor(tcell.ColorBlack)
f.SetButtonsAlign(tview.AlignCenter)
f.SetTitle("Process " + info.Name + " Info")
addStringIfNotEmpty("Entrypoint:", strings.Join(info.Entrypoint, " "), f)
addStringIfNotEmpty("Command:", info.Command, f)
addStringIfNotEmpty("Working Directory:", info.WorkingDir, f)
addStringIfNotEmpty("Log Location:", info.LogLocation, f)
f.AddInputField("Replica:", fmt.Sprintf("%d/%d", info.ReplicaNum+1, info.Replicas), 0, nil, nil)
addDropDownIfNotEmpty("Environment:", info.Environment, f)
addCSVIfNotEmpty("Depends On:", mapKeysToSlice(info.DependsOn), f)
if ports != nil {
Expand Down
3 changes: 3 additions & 0 deletions src/types/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type ProcessConfig struct {
Disabled bool `yaml:"disabled,omitempty"`
IsDaemon bool `yaml:"is_daemon,omitempty"`
Command string `yaml:"command"`
F1bonacc1 marked this conversation as resolved.
Show resolved Hide resolved
Entrypoint []string `yaml:"entrypoint"`
LogLocation string `yaml:"log_location,omitempty"`
LoggerConfig *LoggerConfig `yaml:"log_configuration,omitempty"`
Environment Environment `yaml:"environment,omitempty"`
Expand All @@ -31,6 +32,8 @@ type ProcessConfig struct {
Extensions map[string]interface{} `yaml:",inline"`
ReplicaNum int
ReplicaName string
Executable string
Args []string
}

func (p *ProcessConfig) GetDependencies() []string {
Expand Down
Loading