This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / spindle / engines / dummy / engine.go
3.1 kB 106 lines
1package dummy 2 3import ( 4 "context" 5 "fmt" 6 "log/slog" 7 "time" 8 9 "gopkg.in/yaml.v3" 10 "tangled.org/core/api/tangled" 11 "tangled.org/core/spindle/engine" 12 "tangled.org/core/spindle/models" 13 "tangled.org/core/spindle/secrets" 14) 15 16// DummyEngine is a no-op engine that logs all lifecycle events via slog and writes 17// step output to the workflow logger. Useful for testing pipeline plumbing 18// without a real execution backend. 19type DummyEngine struct { 20 l *slog.Logger 21 StepDelay time.Duration 22} 23 24func New(l *slog.Logger) *DummyEngine { 25 return &DummyEngine{l: l.With("engine", "dummy")} 26} 27 28type Step struct { 29 name string 30 kind models.StepKind 31 command string 32} 33 34func (s Step) Name() string { return s.name } 35func (s Step) Command() string { return s.command } 36func (s Step) Kind() models.StepKind { return s.kind } 37 38func (e *DummyEngine) InitWorkflow(twf tangled.Pipeline_Workflow, _ tangled.Pipeline) (*models.Workflow, error) { 39 dwf := &struct { 40 Steps []struct { 41 Name string `yaml:"name"` 42 Command string `yaml:"command"` 43 } `yaml:"steps"` 44 Environment map[string]string `yaml:"environment"` 45 }{} 46 47 if err := yaml.Unmarshal([]byte(twf.Raw), dwf); err != nil { 48 return nil, err 49 } 50 51 wf := &models.Workflow{ 52 Name: twf.Name, 53 Environment: dwf.Environment, 54 } 55 for _, ds := range dwf.Steps { 56 wf.Steps = append(wf.Steps, Step{ 57 name: ds.Name, 58 kind: models.StepKindUser, 59 command: ds.Command, 60 }) 61 } 62 63 e.l.Info("workflow initialised", "name", twf.Name, "steps", len(wf.Steps)) 64 return wf, nil 65} 66 67func (e *DummyEngine) SetupWorkflow(_ context.Context, wid models.WorkflowId, wf *models.Workflow, wfLogger models.WorkflowLogger) error { 68 e.l.Info("setting up workflow", "wid", wid) 69 70 setupStep := Step{name: "dummy setup", kind: models.StepKindSystem} 71 const setupIdx = -1 72 73 wfLogger.ControlWriter(setupIdx, setupStep, models.StepStatusStart).Write([]byte{0}) 74 defer wfLogger.ControlWriter(setupIdx, setupStep, models.StepStatusEnd).Write([]byte{0}) 75 76 fmt.Fprintf(wfLogger.DataWriter(setupIdx, "stdout"), "dummy engine: workflow %q ready", wf.Name) 77 return nil 78} 79 80func (e *DummyEngine) WorkflowTimeout() time.Duration { 81 return 5 * time.Minute 82} 83 84// no capacity limit, so always a no-op slot regardless of mode 85func (e *DummyEngine) AcquireWorkflowSlot(_ context.Context, _ models.WorkflowId, _ *models.Workflow, _ engine.AcquireMode) (engine.WorkflowSlot, error) { 86 return engine.NoopSlot{}, nil 87} 88 89func (e *DummyEngine) DestroyWorkflow(_ context.Context, wid models.WorkflowId) error { 90 e.l.Info("destroying workflow", "wid", wid) 91 return nil 92} 93 94func (e *DummyEngine) RunStep(ctx context.Context, wid models.WorkflowId, w *models.Workflow, idx int, _ []secrets.UnlockedSecret, wfLogger models.WorkflowLogger) error { 95 step := w.Steps[idx] 96 e.l.Info("running step", "wid", wid, "step", step.Name(), "command", step.Command()) 97 fmt.Fprintf(wfLogger.DataWriter(idx, "stdout"), "$ %s", step.Command()) 98 if e.StepDelay > 0 { 99 select { 100 case <-ctx.Done(): 101 return ctx.Err() 102 case <-time.After(e.StepDelay): 103 } 104 } 105 return nil 106}