-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrunner.go
201 lines (176 loc) · 4.68 KB
/
runner.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
package starbox
import (
"context"
"errors"
"fmt"
"strings"
"time"
"github.com/1set/starlet"
)
var (
// ErrNoStarbox is the error for RunnerConfig.Execute() when no Starbox instance is set
ErrNoStarbox = errors.New("no starbox instance")
)
// RunnerConfig defines the execution configuration for a Starbox instance.
type RunnerConfig struct {
_ DoNotCompare
box *Starbox
fileName string
script []byte
ctx context.Context
timeout time.Duration
condREPL InspectCondFunc
extras starlet.StringAnyMap
}
// String returns a string representation of the RunnerConfig.
func (c *RunnerConfig) String() string {
var fields []string
if c.box != nil {
fields = append(fields, c.box.String())
}
if c.fileName != "" {
fields = append(fields, fmt.Sprintf("file:%s", c.fileName))
}
if len(c.script) > 0 {
fields = append(fields, fmt.Sprintf("script:%d", len(c.script)))
}
if c.ctx != nil && c.ctx != context.Background() {
fields = append(fields, fmt.Sprintf("ctx:%v", c.ctx))
}
if c.timeout != 0 {
fields = append(fields, fmt.Sprintf("timeout:%v", c.timeout))
}
if c.condREPL != nil {
fields = append(fields, "inspect:true")
}
if len(c.extras) > 0 {
fields = append(fields, fmt.Sprintf("extras:%v", c.extras))
}
return fmt.Sprintf("🚀Runner{%s}", strings.Join(fields, ","))
}
// NewRunConfig creates a new RunnerConfig instance.
func NewRunConfig() *RunnerConfig {
return &RunnerConfig{}
}
// CreateRunConfig creates a new RunnerConfig instance from a given Starbox instance.
func (s *Starbox) CreateRunConfig() *RunnerConfig {
return &RunnerConfig{box: s}
}
// Clone creates a new RunnerConfig instance from the current one.
func (c *RunnerConfig) Clone() *RunnerConfig {
n := *c
return &n
}
// FileName sets the script file name for the execution.
func (c *RunnerConfig) FileName(name string) *RunnerConfig {
n := *c
n.fileName = name
return &n
}
// Script sets the script content for the execution.
func (c *RunnerConfig) Script(content string) *RunnerConfig {
n := *c
n.script = []byte(content)
return &n
}
// Context sets the context for the execution.
func (c *RunnerConfig) Context(ctx context.Context) *RunnerConfig {
n := *c
n.ctx = ctx
return &n
}
// Timeout sets the timeout for the execution.
func (c *RunnerConfig) Timeout(timeout time.Duration) *RunnerConfig {
n := *c
n.timeout = timeout
return &n
}
// Inspect sets the inspection mode for the execution.
// It works like InspectCond with a condition function that forces the REPL mode, by adding a condition function to force the REPL mode, regardless of the output or error.
// It can be overridden by InspectCond() or Inspect().
func (c *RunnerConfig) Inspect(force bool) *RunnerConfig {
n := *c
n.condREPL = func(starlet.StringAnyMap, error) bool {
return force
}
return &n
}
// InspectCond sets the inspection mode with a condition function for the execution.
// It can be overridden by InspectCond() or Inspect().
func (c *RunnerConfig) InspectCond(cond InspectCondFunc) *RunnerConfig {
n := *c
n.condREPL = cond
return &n
}
// KeyValue sets the key-value pair for the execution.
func (c *RunnerConfig) KeyValue(key string, value interface{}) *RunnerConfig {
n := *c
if n.extras == nil {
n.extras = make(starlet.StringAnyMap)
}
n.extras[key] = value
return &n
}
// KeyValueMap merges the key-value pairs for the execution.
func (c *RunnerConfig) KeyValueMap(extras starlet.StringAnyMap) *RunnerConfig {
n := *c
if n.extras == nil {
n.extras = make(starlet.StringAnyMap)
}
n.extras.Merge(extras)
return &n
}
// Starbox sets the Starbox instance for the execution.
func (c *RunnerConfig) Starbox(b *Starbox) *RunnerConfig {
n := *c
n.box = b
return &n
}
// Execute executes the box with the given configuration.
func (c *RunnerConfig) Execute() (starlet.StringAnyMap, error) {
// config and box
cfg := *c
b := cfg.box
if b == nil {
return nil, ErrNoStarbox
}
// prepare variables
if cfg.fileName == "" {
cfg.fileName = "box.star"
}
if len(cfg.script) == 0 {
cfg.script = nil
}
if cfg.timeout < 0 {
cfg.timeout = 0
}
if cfg.ctx == nil {
cfg.ctx = context.Background()
}
// handle timeout
if cfg.timeout > 0 {
nt, cancel := context.WithTimeout(cfg.ctx, cfg.timeout)
defer cancel()
cfg.ctx = nt
}
// lock the box
b.mu.Lock()
defer b.mu.Unlock()
// if it's the first run, set the environment
if !b.hasExec {
if err := b.prepareEnv(); err != nil {
return nil, err
}
}
// set script things
b.mac.SetScript(cfg.fileName, cfg.script, b.modFS)
// finally, run the script
b.hasExec = true
b.execTimes++
out, err := b.mac.RunWithContext(cfg.ctx, cfg.extras)
// repl
if cfg.condREPL != nil && cfg.condREPL(out, err) {
b.mac.REPL()
}
return out, err
}