@@ -13,6 +13,9 @@ import (
13
13
"io"
14
14
"io/ioutil"
15
15
"os"
16
+ "time"
17
+
18
+ "github.com/MattWindsor91/c4t/internal/act"
16
19
17
20
"github.com/MattWindsor91/c4t/internal/helper/errhelp"
18
21
@@ -60,6 +63,12 @@ const (
60
63
flagDryRun = "dry-run"
61
64
flagDryRunShort = "d"
62
65
usageDryRun = "if true, print any external commands run instead of running them"
66
+ flagTimeout = "timeout"
67
+ flagTimeoutShort = "t"
68
+ usageTimeout = "`DURATION` to wait before trying to stop the backend"
69
+ flagGrace = "grace"
70
+ flagGraceShort = "g"
71
+ usageGrace = "`DURATION` to wait between sigterm and sigkill when timing out"
63
72
)
64
73
65
74
// App is the c4-backend app.
@@ -83,6 +92,8 @@ func flags() []c.Flag {
83
92
& c.GenericFlag {Name : flagBackendIDGlob , Aliases : []string {flagBackendIDGlobShort }, Usage : usageBackendIDGlob , Value : & id.ID {}},
84
93
& c.GenericFlag {Name : flagBackendStyleGlob , Aliases : []string {flagBackendStyleGlobShort }, Usage : usageBackendStyleGlob , Value : & id.ID {}},
85
94
& c.BoolFlag {Name : flagDryRun , Aliases : []string {flagDryRunShort }, Usage : usageDryRun },
95
+ & c.DurationFlag {Name : flagTimeout , Aliases : []string {flagTimeoutShort }, Usage : usageTimeout },
96
+ & c.DurationFlag {Name : flagGrace , Aliases : []string {flagGraceShort }, Usage : usageGrace },
86
97
}
87
98
return append (ownFlags , stdflag .ActRunnerCliFlags ()... )
88
99
}
@@ -93,50 +104,60 @@ func run(ctx *c.Context, outw io.Writer, errw io.Writer) error {
93
104
return fmt .Errorf ("while getting config: %w" , err )
94
105
}
95
106
c4f := stdflag .ActRunnerFromCli (ctx , errw )
96
- cri := criteriaFromCli ( ctx )
97
- fn , err := inputNameFromCli ( ctx )
107
+
108
+ td , err := ioutil . TempDir ( "" , "c4t-backend" )
98
109
if err != nil {
99
110
return err
100
111
}
101
- arch := idFromCli (ctx , flagArchID )
102
112
103
- bspec , b , err := getBackend ( cfg , cri )
113
+ fn , err := inputNameFromCli ( ctx )
104
114
if err != nil {
105
115
return err
106
116
}
107
117
108
- in , err := backend . InputFromFile ( ctx . Context , fn , c4f )
118
+ bspec , b , err := getBackend ( cfg , criteriaFromCli ( ctx ) )
109
119
if err != nil {
110
120
return err
111
121
}
112
122
113
- td , err := ioutil . TempDir ( "" , "c4t-backend" )
123
+ j , err := jobFromCli ( ctx , fn , c4f , bspec , td )
114
124
if err != nil {
115
125
return err
116
126
}
127
+ perr := runParseAndDump (ctx , outw , b , j , makeRunner (ctx , errw ))
128
+ derr := os .RemoveAll (td )
129
+ return errhelp .FirstError (perr , derr )
130
+ }
131
+
132
+ func jobFromCli (ctx * c.Context , fn string , c4f * act.Runner , bspec * backend.Spec , td string ) (backend.LiftJob , error ) {
133
+ in , err := backend .InputFromFile (ctx .Context , fn , c4f )
134
+ if err != nil {
135
+ return backend.LiftJob {}, err
136
+ }
137
+
117
138
j := backend.LiftJob {
118
139
Backend : bspec ,
119
- Arch : arch ,
140
+ Arch : idFromCli ( ctx , flagArchID ) ,
120
141
In : in ,
121
142
Out : backend.LiftOutput {Dir : td , Target : backend .ToStandalone },
122
143
}
123
- xr := makeRunner (ctx , errw )
124
- perr := runParseAndDump (ctx , outw , b , j , xr )
125
- derr := os .RemoveAll (td )
126
- return errhelp .FirstError (perr , derr )
144
+ return j , nil
127
145
}
128
146
129
147
func makeRunner (ctx * c.Context , errw io.Writer ) service.Runner {
130
148
// TODO(@MattWindsor91): the backend logic isn't very resilient against having external commands not run.
131
149
if ctx .Bool (flagDryRun ) {
132
150
return srvrun.DryRunner {Writer : errw }
133
151
}
134
- return srvrun .NewExecRunner (srvrun .StderrTo (errw ))
152
+ // TODO(@MattWindsor91): use grace in the rest of c4t
153
+ return srvrun .NewExecRunner (srvrun .StderrTo (errw ), srvrun .WithGrace (ctx .Duration (flagGrace )))
135
154
}
136
155
137
156
func runParseAndDump (ctx * c.Context , outw io.Writer , b backend2.Backend , j backend.LiftJob , xr service.Runner ) error {
138
157
var o obs.Obs
139
- if err := runAndParse (ctx .Context , b , j , & o , xr ); err != nil {
158
+
159
+ to := ctx .Duration (flagTimeout )
160
+ if err := runAndParse (ctx .Context , to , b , j , & o , xr ); err != nil {
140
161
return err
141
162
}
142
163
@@ -145,9 +166,10 @@ func runParseAndDump(ctx *c.Context, outw io.Writer, b backend2.Backend, j backe
145
166
return e .Encode (o )
146
167
}
147
168
148
- func runAndParse (ctx context.Context , b backend2.Backend , j backend.LiftJob , o * obs.Obs , xr service.Runner ) error {
149
- // TODO(@MattWindsor91): deduplicate with runAndParseBin?.
150
- r , err := b .Lift (ctx , j , xr )
169
+ func runAndParse (ctx context.Context , to time.Duration , b backend2.Backend , j backend.LiftJob , o * obs.Obs , xr service.Runner ) error {
170
+ // TODO(@MattWindsor91): clean this function up, eg making a separate struct...
171
+
172
+ r , err := liftWithTimeout (ctx , to , b , j , xr )
151
173
if err != nil {
152
174
return err
153
175
}
@@ -164,6 +186,17 @@ func runAndParse(ctx context.Context, b backend2.Backend, j backend.LiftJob, o *
164
186
return nil
165
187
}
166
188
189
+ func liftWithTimeout (ctx context.Context , to time.Duration , b backend2.Backend , j backend.LiftJob , xr service.Runner ) (recipe.Recipe , error ) {
190
+ cf := func () {}
191
+ if to != 0 {
192
+ ctx , cf = context .WithTimeout (ctx , to )
193
+ }
194
+ defer cf ()
195
+
196
+ // TODO(@MattWindsor91): deduplicate with runAndParseBin?.
197
+ return b .Lift (ctx , j , xr )
198
+ }
199
+
167
200
func parseFile (ctx context.Context , b backend2.Backend , j backend.LiftJob , o * obs.Obs , fname string ) error {
168
201
f , err := os .Open (fname )
169
202
if err != nil {
0 commit comments