Issue #111 - fix transitive dependency skip on failure

This commit is contained in:
Berger Eugene 2023-12-08 16:25:56 +02:00
parent 50b4a72d14
commit 2b5c7e8233
7 changed files with 150 additions and 14 deletions

View File

@ -0,0 +1,22 @@
version: "0.5"
log_level: debug
log_length: 1000
processes:
procA:
command: "exit 1"
procB:
command: echo "I shouldn't run"
depends_on:
procA:
condition: process_completed_successfully
procC:
command: echo "I shouldn't run"
depends_on:
procB:
condition: process_completed_successfully

View File

@ -0,0 +1,42 @@
version: "0.5"
log_level: debug
log_length: 1000
processes:
procA:
command: "exit 1"
readiness_probe:
exec:
command: "pidof process-compose"
initial_delay_seconds: 1
period_seconds: 1
timeout_seconds: 1
success_threshold: 1
failure_threshold: 20
procB:
command: echo "I shouldn't run"
readiness_probe:
exec:
command: "pidof process-compose"
initial_delay_seconds: 1
period_seconds: 1
timeout_seconds: 1
success_threshold: 1
failure_threshold: 20
depends_on:
procA:
condition: process_healthy
procC:
command: echo "I shouldn't run"
depends_on:
procB:
condition: process_healthy
pc_log:
command: "tail -f -n100 process-compose-${USER}.log"
working_dir: "/tmp"
namespace: debug

View File

@ -0,0 +1,26 @@
version: "0.5"
log_level: debug
log_length: 1000
processes:
procA:
command: "exit 1"
procB:
command: echo "I shouldn't run"
depends_on:
procA:
condition: process_completed_successfully
procC:
command: echo "I shouldn't run"
depends_on:
procB:
condition: process_completed_successfully
pc_log:
command: "tail -f -n100 process-compose-${USER}.log"
working_dir: "/tmp"
namespace: debug

View File

@ -33,6 +33,7 @@ const (
type Process struct {
sync.Mutex
globalEnv []string
confMtx sync.Mutex
procConf *types.ProcessConfig
procState *types.ProcessState
stateMtx sync.Mutex
@ -134,11 +135,11 @@ func (p *Process) run() int {
time.Sleep(50 * time.Millisecond)
_ = p.command.Wait()
p.Lock()
p.procState.ExitCode = p.command.ExitCode()
p.setExitCode(p.command.ExitCode())
p.Unlock()
log.Info().
Str("process", p.getName()).
Int("exit_code", p.procState.ExitCode).
Int("exit_code", p.getExitCode()).
Msg("Exited")
if p.isDaemonLaunched() {
@ -164,7 +165,7 @@ func (p *Process) run() int {
}
}
p.onProcessEnd(types.ProcessStateCompleted)
return p.procState.ExitCode
return p.getExitCode()
}
func (p *Process) getProcessStarter() func() error {
@ -227,7 +228,7 @@ func (p *Process) getProcessEnvironment() []string {
func (p *Process) isRestartable() bool {
p.Lock()
exitCode := p.procState.ExitCode
exitCode := p.getExitCode()
p.Unlock()
if p.procConf.RestartPolicy.Restart == types.RestartPolicyNo ||
p.procConf.RestartPolicy.Restart == "" {
@ -263,7 +264,7 @@ func (p *Process) waitForCompletion() int {
for !p.done {
p.procCond.Wait()
}
return p.procState.ExitCode
return p.getExitCode()
}
func (p *Process) waitUntilReady() bool {
@ -274,13 +275,14 @@ func (p *Process) waitUntilReady() bool {
return true
}
log.Error().Msgf("Process %s was aborted and won't become ready", p.getName())
p.setExitCode(1)
return false
}
}
}
func (p *Process) wontRun() {
p.onProcessEnd(types.ProcessStateCompleted)
p.onProcessEnd(types.ProcessStateSkipped)
}
// perform graceful process shutdown if defined in configuration
@ -506,6 +508,8 @@ func (p *Process) setStateAndRun(state string, runnable func() error) error {
func (p *Process) onStateChange(state string) {
switch state {
case types.ProcessStateSkipped:
p.setExitCode(1)
case types.ProcessStateRestarting:
fallthrough
case types.ProcessStateLaunching:
@ -621,3 +625,15 @@ func (p *Process) getOpenPorts(ports *types.ProcessPorts) error {
}
return nil
}
func (p *Process) getExitCode() int {
defer p.confMtx.Unlock()
p.confMtx.Lock()
return p.procState.ExitCode
}
func (p *Process) setExitCode(code int) {
defer p.confMtx.Unlock()
p.confMtx.Lock()
p.procState.ExitCode = code
}

View File

@ -109,18 +109,18 @@ func (p *ProjectRunner) runProcess(config *types.ProcessConfig) {
)
p.addRunningProcess(process)
p.waitGroup.Add(1)
go func() {
defer p.removeRunningProcess(process)
go func(proc *Process) {
defer p.removeRunningProcess(proc)
defer p.waitGroup.Done()
if err = p.waitIfNeeded(process.procConf); err != nil {
if err = p.waitIfNeeded(proc.procConf); err != nil {
log.Error().Msgf("Error: %s", err.Error())
log.Error().Msgf("Error: process %s won't run", process.getName())
process.wontRun()
log.Error().Msgf("Error: process %s won't run", proc.getName())
proc.wontRun()
} else {
exitCode := process.run()
p.onProcessEnd(exitCode, process.procConf)
exitCode := proc.run()
p.onProcessEnd(exitCode, proc.procConf)
}
}()
}(process)
}
func (p *ProjectRunner) waitIfNeeded(process *types.ProcessConfig) error {
@ -145,7 +145,10 @@ func (p *ProjectRunner) waitIfNeeded(process *types.ProcessConfig) error {
}
}
} else {
log.Error().Msgf("Error: process %s depends on %s, but it isn't running", process.ReplicaName, k)
}
}
return nil
}

View File

@ -281,3 +281,29 @@ func TestSystem_TestComposeScale(t *testing.T) {
}
})
}
func TestSystem_TestTransitiveDependency(t *testing.T) {
fixture1 := filepath.Join("..", "..", "fixtures-code", "process-compose-transitive-dep.yaml")
t.Run(fixture1, func(t *testing.T) {
project, err := loader.Load(&loader.LoaderOptions{
FileNames: []string{fixture1},
})
if err != nil {
t.Errorf(err.Error())
return
}
runner, err := NewProjectRunner(&ProjectOpts{
project: project,
processesToRun: []string{},
mainProcessArgs: []string{},
})
runner.Run()
states, err := runner.GetProcessesState()
for _, state := range states.States {
if state.ExitCode != 1 {
t.Errorf("process %s exit code is not 1", state.Name)
}
}
})
}

View File

@ -123,6 +123,7 @@ const (
ProcessStateRestarting = "Restarting"
ProcessStateTerminating = "Terminating"
ProcessStateCompleted = "Completed"
ProcessStateSkipped = "Skipped"
ProcessStateError = "Error"
)