1
1
package terraform
2
2
3
3
import (
4
+ "bufio"
4
5
"bytes"
5
6
"context"
6
7
"encoding/json"
@@ -17,7 +18,6 @@ import (
17
18
"github.com/hashicorp/go-version"
18
19
tfjson "github.com/hashicorp/terraform-json"
19
20
20
- "github.com/coder/coder/provisionersdk"
21
21
"github.com/coder/coder/provisionersdk/proto"
22
22
)
23
23
@@ -113,14 +113,14 @@ func (e executor) version(ctx context.Context) (*version.Version, error) {
113
113
return version .NewVersion (vj .Version )
114
114
}
115
115
116
- func (e executor ) init (ctx context.Context , logger provisionersdk. Logger ) error {
117
- writer , doneLogging := provisionersdk . LogWriter ( logger , proto .LogLevel_DEBUG )
116
+ func (e executor ) init (ctx context.Context , logr logger ) error {
117
+ writer , doneLogging := logWriter ( logr , proto .LogLevel_DEBUG )
118
118
defer func () { <- doneLogging }()
119
119
return e .execWriteOutput (ctx , []string {"init" }, e .basicEnv (), writer )
120
120
}
121
121
122
122
// revive:disable-next-line:flag-parameter
123
- func (e executor ) plan (ctx context.Context , env , vars []string , logger provisionersdk. Logger , destroy bool ) (* proto.Provision_Response , error ) {
123
+ func (e executor ) plan (ctx context.Context , env , vars []string , logr logger , destroy bool ) (* proto.Provision_Response , error ) {
124
124
planfilePath := filepath .Join (e .workdir , "terraform.tfplan" )
125
125
args := []string {
126
126
"plan" ,
@@ -137,7 +137,7 @@ func (e executor) plan(ctx context.Context, env, vars []string, logger provision
137
137
args = append (args , "-var" , variable )
138
138
}
139
139
140
- writer , doneLogging := provisionLogWriter (logger )
140
+ writer , doneLogging := provisionLogWriter (logr )
141
141
defer func () { <- doneLogging }()
142
142
143
143
err := e .execWriteOutput (ctx , args , env , writer )
@@ -190,7 +190,7 @@ func (e executor) graph(ctx context.Context) (string, error) {
190
190
}
191
191
192
192
// revive:disable-next-line:flag-parameter
193
- func (e executor ) apply (ctx context.Context , env , vars []string , logger provisionersdk. Logger , destroy bool ,
193
+ func (e executor ) apply (ctx context.Context , env , vars []string , logr logger , destroy bool ,
194
194
) (* proto.Provision_Response , error ) {
195
195
args := []string {
196
196
"apply" ,
@@ -207,7 +207,7 @@ func (e executor) apply(ctx context.Context, env, vars []string, logger provisio
207
207
args = append (args , "-var" , variable )
208
208
}
209
209
210
- writer , doneLogging := provisionLogWriter (logger )
210
+ writer , doneLogging := provisionLogWriter (logr )
211
211
defer func () { <- doneLogging }()
212
212
213
213
err := e .execWriteOutput (ctx , args , env , writer )
@@ -262,14 +262,55 @@ func (e executor) state(ctx context.Context) (*tfjson.State, error) {
262
262
return state , nil
263
263
}
264
264
265
- func provisionLogWriter (logger provisionersdk.Logger ) (io.WriteCloser , <- chan any ) {
265
+ type logger interface {
266
+ Log (* proto.Log ) error
267
+ }
268
+
269
+ type streamLogger struct {
270
+ stream proto.DRPCProvisioner_ProvisionStream
271
+ }
272
+
273
+ func (s streamLogger ) Log (l * proto.Log ) error {
274
+ return s .stream .Send (& proto.Provision_Response {
275
+ Type : & proto.Provision_Response_Log {
276
+ Log : l ,
277
+ },
278
+ })
279
+ }
280
+
281
+ // logWriter creates a WriteCloser that will log each line of text at the given level. The WriteCloser must be closed
282
+ // by the caller to end logging, after which the returned channel will be closed to indicate that logging of the written
283
+ // data has finished. Failure to close the WriteCloser will leak a goroutine.
284
+ func logWriter (logr logger , level proto.LogLevel ) (io.WriteCloser , <- chan any ) {
285
+ r , w := io .Pipe ()
286
+ done := make (chan any )
287
+ go readAndLog (logr , r , done , level )
288
+ return w , done
289
+ }
290
+
291
+ func readAndLog (logr logger , r io.Reader , done chan <- any , level proto.LogLevel ) {
292
+ defer close (done )
293
+ scanner := bufio .NewScanner (r )
294
+ for scanner .Scan () {
295
+ err := logr .Log (& proto.Log {Level : level , Output : scanner .Text ()})
296
+ if err != nil {
297
+ // Not much we can do. We can't log because logging is itself breaking!
298
+ return
299
+ }
300
+ }
301
+ }
302
+
303
+ // provisionLogWriter creates a WriteCloser that will log each JSON formatted terraform log. The WriteCloser must be
304
+ // closed by the caller to end logging, after which the returned channel will be closed to indicate that logging of the
305
+ // written data has finished. Failure to close the WriteCloser will leak a goroutine.
306
+ func provisionLogWriter (logr logger ) (io.WriteCloser , <- chan any ) {
266
307
r , w := io .Pipe ()
267
308
done := make (chan any )
268
- go provisionReadAndLog (logger , r , done )
309
+ go provisionReadAndLog (logr , r , done )
269
310
return w , done
270
311
}
271
312
272
- func provisionReadAndLog (logger provisionersdk. Logger , reader io.Reader , done chan <- any ) {
313
+ func provisionReadAndLog (logr logger , reader io.Reader , done chan <- any ) {
273
314
defer close (done )
274
315
decoder := json .NewDecoder (reader )
275
316
for {
@@ -278,9 +319,9 @@ func provisionReadAndLog(logger provisionersdk.Logger, reader io.Reader, done ch
278
319
if err != nil {
279
320
return
280
321
}
281
- logLevel := convertTerraformLogLevel (log .Level , logger )
322
+ logLevel := convertTerraformLogLevel (log .Level , logr )
282
323
283
- err = logger .Log (& proto.Log {Level : logLevel , Output : log .Message })
324
+ err = logr .Log (& proto.Log {Level : logLevel , Output : log .Message })
284
325
if err != nil {
285
326
// Not much we can do. We can't log because logging is itself breaking!
286
327
return
@@ -291,19 +332,19 @@ func provisionReadAndLog(logger provisionersdk.Logger, reader io.Reader, done ch
291
332
}
292
333
293
334
// If the diagnostic is provided, let's provide a bit more info!
294
- logLevel = convertTerraformLogLevel (log .Diagnostic .Severity , logger )
335
+ logLevel = convertTerraformLogLevel (log .Diagnostic .Severity , logr )
295
336
if err != nil {
296
337
continue
297
338
}
298
- err = logger .Log (& proto.Log {Level : logLevel , Output : log .Diagnostic .Detail })
339
+ err = logr .Log (& proto.Log {Level : logLevel , Output : log .Diagnostic .Detail })
299
340
if err != nil {
300
341
// Not much we can do. We can't log because logging is itself breaking!
301
342
return
302
343
}
303
344
}
304
345
}
305
346
306
- func convertTerraformLogLevel (logLevel string , logger provisionersdk. Logger ) proto.LogLevel {
347
+ func convertTerraformLogLevel (logLevel string , logr logger ) proto.LogLevel {
307
348
switch strings .ToLower (logLevel ) {
308
349
case "trace" :
309
350
return proto .LogLevel_TRACE
@@ -316,7 +357,7 @@ func convertTerraformLogLevel(logLevel string, logger provisionersdk.Logger) pro
316
357
case "error" :
317
358
return proto .LogLevel_ERROR
318
359
default :
319
- _ = logger .Log (& proto.Log {
360
+ _ = logr .Log (& proto.Log {
320
361
Level : proto .LogLevel_WARN ,
321
362
Output : fmt .Sprintf ("unable to convert log level %s" , logLevel ),
322
363
})
0 commit comments