From 6d0a191ec4762852e878a7470d5d31f35a3875a3 Mon Sep 17 00:00:00 2001 From: Sebastian Rivera Date: Thu, 16 Feb 2023 10:54:58 -0500 Subject: [PATCH] Handle provisioner log types in renderer --- internal/cloud/backend_apply_test.go | 97 +++++++++++++++ .../apply.log | 9 ++ .../apply-json-with-provisioner-error/main.tf | 5 + .../plan-redacted.json | 116 ++++++++++++++++++ .../plan.log | 3 + .../apply-json-with-provisioner/apply.log | 10 ++ .../apply-json-with-provisioner/main.tf | 5 + .../plan-redacted.json | 116 ++++++++++++++++++ .../apply-json-with-provisioner/plan.log | 3 + internal/command/jsonformat/renderer.go | 53 +++++--- 10 files changed, 401 insertions(+), 16 deletions(-) create mode 100644 internal/cloud/testdata/apply-json-with-provisioner-error/apply.log create mode 100644 internal/cloud/testdata/apply-json-with-provisioner-error/main.tf create mode 100644 internal/cloud/testdata/apply-json-with-provisioner-error/plan-redacted.json create mode 100644 internal/cloud/testdata/apply-json-with-provisioner-error/plan.log create mode 100644 internal/cloud/testdata/apply-json-with-provisioner/apply.log create mode 100644 internal/cloud/testdata/apply-json-with-provisioner/main.tf create mode 100644 internal/cloud/testdata/apply-json-with-provisioner/plan-redacted.json create mode 100644 internal/cloud/testdata/apply-json-with-provisioner/plan.log diff --git a/internal/cloud/backend_apply_test.go b/internal/cloud/backend_apply_test.go index e03d3b4b0d..c10d9ed071 100644 --- a/internal/cloud/backend_apply_test.go +++ b/internal/cloud/backend_apply_test.go @@ -1262,6 +1262,103 @@ func TestCloud_applyDestroyNoConfig(t *testing.T) { } } +func TestCloud_applyJSONWithProvisioner(t *testing.T) { + b, bCleanup := testBackendWithName(t) + defer bCleanup() + + stream, close := terminal.StreamsForTesting(t) + + b.renderer = &jsonformat.Renderer{ + Streams: stream, + Colorize: mockColorize(), + } + input := testInput(t, map[string]string{ + "approve": "yes", + }) + + op, configCleanup, done := testOperationApply(t, "./testdata/apply-json-with-provisioner") + defer configCleanup() + defer done(t) + + op.UIIn = input + op.UIOut = b.CLI + op.Workspace = testBackendSingleWorkspaceName + + mockSROWorkspace(t, b, op.Workspace) + + run, err := b.Operation(context.Background(), op) + if err != nil { + t.Fatalf("error starting operation: %v", err) + } + + <-run.Done() + if run.Result != backend.OperationSuccess { + t.Fatalf("operation failed: %s", b.CLI.(*cli.MockUi).ErrorWriter.String()) + } + + if run.PlanEmpty { + t.Fatalf("expected a non-empty plan") + } + + if len(input.answers) > 0 { + t.Fatalf("expected no unused answers, got: %v", input.answers) + } + + outp := close(t) + gotOut := outp.Stdout() + if !strings.Contains(gotOut, "null_resource.foo: Provisioning with 'local-exec'") { + t.Fatalf("expected provisioner local-exec start in logs: %s", gotOut) + } + + if !strings.Contains(gotOut, "null_resource.foo: (local-exec):") { + t.Fatalf("expected provisioner local-exec progress in logs: %s", gotOut) + } + + if !strings.Contains(gotOut, "Hello World!") { + t.Fatalf("expected provisioner local-exec output in logs: %s", gotOut) + } + + stateMgr, _ := b.StateMgr(testBackendSingleWorkspaceName) + // An error suggests that the state was not unlocked after apply + if _, err := stateMgr.Lock(statemgr.NewLockInfo()); err != nil { + t.Fatalf("unexpected error locking state after apply: %s", err.Error()) + } +} + +func TestCloud_applyJSONWithProvisionerError(t *testing.T) { + b, bCleanup := testBackendWithName(t) + defer bCleanup() + + stream, close := terminal.StreamsForTesting(t) + + b.renderer = &jsonformat.Renderer{ + Streams: stream, + Colorize: mockColorize(), + } + + op, configCleanup, done := testOperationApply(t, "./testdata/apply-json-with-provisioner-error") + defer configCleanup() + defer done(t) + + op.Workspace = testBackendSingleWorkspaceName + + mockSROWorkspace(t, b, op.Workspace) + + run, err := b.Operation(context.Background(), op) + if err != nil { + t.Fatalf("error starting operation: %v", err) + } + + <-run.Done() + + outp := close(t) + gotOut := outp.Stdout() + + if !strings.Contains(gotOut, "local-exec provisioner error") { + t.Fatalf("unexpected error in apply logs: %s", gotOut) + } +} + func TestCloud_applyPolicyPass(t *testing.T) { b, bCleanup := testBackendWithName(t) defer bCleanup() diff --git a/internal/cloud/testdata/apply-json-with-provisioner-error/apply.log b/internal/cloud/testdata/apply-json-with-provisioner-error/apply.log new file mode 100644 index 0000000000..64f949fb8e --- /dev/null +++ b/internal/cloud/testdata/apply-json-with-provisioner-error/apply.log @@ -0,0 +1,9 @@ +{"@level":"info","@message":"Terraform 1.3.7","@module":"terraform.ui","@timestamp":"2023-01-20T15:50:04.623068-05:00","terraform":"1.3.7","type":"version","ui":"1.0"} +{"@level":"info","@message":"null_resource.foo: Destroying... [id=5383176453498935794]","@module":"terraform.ui","@timestamp":"2023-02-16T10:13:14.725584-05:00","hook":{"resource":{"addr":"null_resource.foo","module":"","resource":"null_resource.foo","implied_provider":"null","resource_type":"null_resource","resource_name":"foo","resource_key":null},"action":"delete","id_key":"id","id_value":"5383176453498935794"},"type":"apply_start"} +{"@level":"info","@message":"null_resource.foo: Destruction complete after 0s","@module":"terraform.ui","@timestamp":"2023-02-16T10:13:14.728526-05:00","hook":{"resource":{"addr":"null_resource.foo","module":"","resource":"null_resource.foo","implied_provider":"null","resource_type":"null_resource","resource_name":"foo","resource_key":null},"action":"delete","elapsed_seconds":0},"type":"apply_complete"} +{"@level":"info","@message":"null_resource.foo: Creating...","@module":"terraform.ui","@timestamp":"2023-02-16T10:13:14.745016-05:00","hook":{"resource":{"addr":"null_resource.foo","module":"","resource":"null_resource.foo","implied_provider":"null","resource_type":"null_resource","resource_name":"foo","resource_key":null},"action":"create"},"type":"apply_start"} +{"@level":"info","@message":"null_resource.foo: Provisioning with 'local-exec'...","@module":"terraform.ui","@timestamp":"2023-02-16T10:13:14.748796-05:00","hook":{"resource":{"addr":"null_resource.foo","module":"","resource":"null_resource.foo","implied_provider":"null","resource_type":"null_resource","resource_name":"foo","resource_key":null},"provisioner":"local-exec"},"type":"provision_start"} +{"@level":"info","@message":"null_resource.foo: (local-exec): Executing: [\"/bin/sh\" \"-c\" \"exit 125\"]","@module":"terraform.ui","@timestamp":"2023-02-16T10:13:14.749082-05:00","hook":{"resource":{"addr":"null_resource.foo","module":"","resource":"null_resource.foo","implied_provider":"null","resource_type":"null_resource","resource_name":"foo","resource_key":null},"provisioner":"local-exec","output":"Executing: [\"/bin/sh\" \"-c\" \"exit 125\"]"},"type":"provision_progress"} +{"@level":"info","@message":"null_resource.foo: (local-exec) Provisioning errored","@module":"terraform.ui","@timestamp":"2023-02-16T10:13:14.751770-05:00","hook":{"resource":{"addr":"null_resource.foo","module":"","resource":"null_resource.foo","implied_provider":"null","resource_type":"null_resource","resource_name":"foo","resource_key":null},"provisioner":"local-exec"},"type":"provision_errored"} +{"@level":"info","@message":"null_resource.foo: Creation errored after 0s","@module":"terraform.ui","@timestamp":"2023-02-16T10:13:14.752082-05:00","hook":{"resource":{"addr":"null_resource.foo","module":"","resource":"null_resource.foo","implied_provider":"null","resource_type":"null_resource","resource_name":"foo","resource_key":null},"action":"create","elapsed_seconds":0},"type":"apply_errored"} +{"@level":"error","@message":"Error: local-exec provisioner error","@module":"terraform.ui","@timestamp":"2023-02-16T10:13:14.761681-05:00","diagnostic":{"severity":"error","summary":"local-exec provisioner error","detail":"Error running command 'exit 125': exit status 125. Output: ","address":"null_resource.foo","range":{"filename":"main.tf","start":{"line":2,"column":28,"byte":60},"end":{"line":2,"column":29,"byte":61}},"snippet":{"context":"resource \"null_resource\" \"foo\"","code":" provisioner \"local-exec\" {","start_line":2,"highlight_start_offset":27,"highlight_end_offset":28,"values":[]}},"type":"diagnostic"} diff --git a/internal/cloud/testdata/apply-json-with-provisioner-error/main.tf b/internal/cloud/testdata/apply-json-with-provisioner-error/main.tf new file mode 100644 index 0000000000..fb1ce036c0 --- /dev/null +++ b/internal/cloud/testdata/apply-json-with-provisioner-error/main.tf @@ -0,0 +1,5 @@ +resource "null_resource" "foo" { + provisioner "local-exec" { + command = "exit 125" + } +} diff --git a/internal/cloud/testdata/apply-json-with-provisioner-error/plan-redacted.json b/internal/cloud/testdata/apply-json-with-provisioner-error/plan-redacted.json new file mode 100644 index 0000000000..9becf1bcd0 --- /dev/null +++ b/internal/cloud/testdata/apply-json-with-provisioner-error/plan-redacted.json @@ -0,0 +1,116 @@ +{ + "plan_format_version": "1.1", + "resource_drift": [], + "resource_changes": [ + { + "address": "null_resource.foo", + "mode": "managed", + "type": "null_resource", + "name": "foo", + "provider_name": "registry.terraform.io/hashicorp/null", + "change": { + "actions": [ + "create" + ], + "before": null, + "after": { + "triggers": null + }, + "after_unknown": { + "id": true + }, + "before_sensitive": false, + "after_sensitive": {} + } + } + ], + "relevant_attributes": [], + "output_changes": {}, + "provider_schemas": { + "registry.terraform.io/hashicorp/null": { + "provider": { + "version": 0, + "block": { + "description_kind": "plain" + } + }, + "resource_schemas": { + "null_resource": { + "version": 0, + "block": { + "attributes": { + "id": { + "type": "string", + "description": "This is set to a random value at create time.", + "description_kind": "plain", + "computed": true + }, + "triggers": { + "type": [ + "map", + "string" + ], + "description": "A map of arbitrary strings that, when changed, will force the null resource to be replaced, re-running any associated provisioners.", + "description_kind": "plain", + "optional": true + } + }, + "description": "The `null_resource` resource implements the standard resource lifecycle but takes no further action.\n\nThe `triggers` argument allows specifying an arbitrary set of values that, when changed, will cause the resource to be replaced.", + "description_kind": "plain" + } + } + }, + "data_source_schemas": { + "null_data_source": { + "version": 0, + "block": { + "attributes": { + "has_computed_default": { + "type": "string", + "description": "If set, its literal value will be stored and returned. If not, its value defaults to `\"default\"`. This argument exists primarily for testing and has little practical use.", + "description_kind": "plain", + "optional": true, + "computed": true + }, + "id": { + "type": "string", + "description": "This attribute is only present for some legacy compatibility issues and should not be used. It will be removed in a future version.", + "description_kind": "plain", + "deprecated": true, + "computed": true + }, + "inputs": { + "type": [ + "map", + "string" + ], + "description": "A map of arbitrary strings that is copied into the `outputs` attribute, and accessible directly for interpolation.", + "description_kind": "plain", + "optional": true + }, + "outputs": { + "type": [ + "map", + "string" + ], + "description": "After the data source is \"read\", a copy of the `inputs` map.", + "description_kind": "plain", + "computed": true + }, + "random": { + "type": "string", + "description": "A random value. This is primarily for testing and has little practical use; prefer the [hashicorp/random provider](https://registry.terraform.io/providers/hashicorp/random) for more practical random number use-cases.", + "description_kind": "plain", + "computed": true + } + }, + "description": "The `null_data_source` data source implements the standard data source lifecycle but does not\ninteract with any external APIs.\n\nHistorically, the `null_data_source` was typically used to construct intermediate values to re-use elsewhere in configuration. The\nsame can now be achieved using [locals](https://www.terraform.io/docs/language/values/locals.html).\n", + "description_kind": "plain", + "deprecated": true + } + } + } + } + }, + "provider_format_version": "1.0" +} diff --git a/internal/cloud/testdata/apply-json-with-provisioner-error/plan.log b/internal/cloud/testdata/apply-json-with-provisioner-error/plan.log new file mode 100644 index 0000000000..26d39210b7 --- /dev/null +++ b/internal/cloud/testdata/apply-json-with-provisioner-error/plan.log @@ -0,0 +1,3 @@ +{"@level":"info","@message":"Terraform 1.3.7","@module":"terraform.ui","@timestamp":"2023-01-20T15:50:04.623068-05:00","terraform":"1.3.7","type":"version","ui":"1.0"} +{"@level":"info","@message":"null_resource.foo: Plan to create","@module":"terraform.ui","@timestamp":"2023-01-20T15:50:04.822722-05:00","change":{"resource":{"addr":"null_resource.foo","module":"","resource":"null_resource.foo","implied_provider":"null","resource_type":"null_resource","resource_name":"foo","resource_key":null},"action":"create"},"type":"planned_change"} +{"@level":"info","@message":"Plan: 1 to add, 0 to change, 0 to destroy.","@module":"terraform.ui","@timestamp":"2023-01-20T15:50:04.822787-05:00","changes":{"add":1,"change":0,"remove":0,"operation":"plan"},"type":"change_summary"} diff --git a/internal/cloud/testdata/apply-json-with-provisioner/apply.log b/internal/cloud/testdata/apply-json-with-provisioner/apply.log new file mode 100644 index 0000000000..78acd7862e --- /dev/null +++ b/internal/cloud/testdata/apply-json-with-provisioner/apply.log @@ -0,0 +1,10 @@ +{"@level":"info","@message":"null_resource.foo: Destroying... [id=102500065134967380]","@module":"terraform.ui","@timestamp":"2023-02-16T10:15:39.614616-05:00","hook":{"resource":{"addr":"null_resource.foo","module":"","resource":"null_resource.foo","implied_provider":"null","resource_type":"null_resource","resource_name":"foo","resource_key":null},"action":"delete","id_key":"id","id_value":"102500065134967380"},"type":"apply_start"} +{"@level":"info","@message":"null_resource.foo: Destruction complete after 0s","@module":"terraform.ui","@timestamp":"2023-02-16T10:15:39.615777-05:00","hook":{"resource":{"addr":"null_resource.foo","module":"","resource":"null_resource.foo","implied_provider":"null","resource_type":"null_resource","resource_name":"foo","resource_key":null},"action":"delete","elapsed_seconds":0},"type":"apply_complete"} +{"@level":"info","@message":"null_resource.foo: Creating...","@module":"terraform.ui","@timestamp":"2023-02-16T10:15:39.621975-05:00","hook":{"resource":{"addr":"null_resource.foo","module":"","resource":"null_resource.foo","implied_provider":"null","resource_type":"null_resource","resource_name":"foo","resource_key":null},"action":"create"},"type":"apply_start"} +{"@level":"info","@message":"null_resource.foo: Provisioning with 'local-exec'...","@module":"terraform.ui","@timestamp":"2023-02-16T10:15:39.622630-05:00","hook":{"resource":{"addr":"null_resource.foo","module":"","resource":"null_resource.foo","implied_provider":"null","resource_type":"null_resource","resource_name":"foo","resource_key":null},"provisioner":"local-exec"},"type":"provision_start"} +{"@level":"info","@message":"null_resource.foo: (local-exec): Executing: [\"/bin/sh\" \"-c\" \"echo Hello World!\"]","@module":"terraform.ui","@timestamp":"2023-02-16T10:15:39.622702-05:00","hook":{"resource":{"addr":"null_resource.foo","module":"","resource":"null_resource.foo","implied_provider":"null","resource_type":"null_resource","resource_name":"foo","resource_key":null},"provisioner":"local-exec","output":"Executing: [\"/bin/sh\" \"-c\" \"echo Hello World!\"]"},"type":"provision_progress"} +{"@level":"info","@message":"null_resource.foo: (local-exec): Hello World!","@module":"terraform.ui","@timestamp":"2023-02-16T10:15:39.623236-05:00","hook":{"resource":{"addr":"null_resource.foo","module":"","resource":"null_resource.foo","implied_provider":"null","resource_type":"null_resource","resource_name":"foo","resource_key":null},"provisioner":"local-exec","output":"Hello World!"},"type":"provision_progress"} +{"@level":"info","@message":"null_resource.foo: (local-exec) Provisioning complete","@module":"terraform.ui","@timestamp":"2023-02-16T10:15:39.623275-05:00","hook":{"resource":{"addr":"null_resource.foo","module":"","resource":"null_resource.foo","implied_provider":"null","resource_type":"null_resource","resource_name":"foo","resource_key":null},"provisioner":"local-exec"},"type":"provision_complete"} +{"@level":"info","@message":"null_resource.foo: Creation complete after 0s [id=7836952171100801169]","@module":"terraform.ui","@timestamp":"2023-02-16T10:15:39.623320-05:00","hook":{"resource":{"addr":"null_resource.foo","module":"","resource":"null_resource.foo","implied_provider":"null","resource_type":"null_resource","resource_name":"foo","resource_key":null},"action":"create","id_key":"id","id_value":"7836952171100801169","elapsed_seconds":0},"type":"apply_complete"} +{"@level":"info","@message":"Apply complete! Resources: 1 added, 0 changed, 1 destroyed.","@module":"terraform.ui","@timestamp":"2023-02-16T10:15:39.631098-05:00","changes":{"add":1,"change":0,"remove":1,"operation":"apply"},"type":"change_summary"} +{"@level":"info","@message":"Outputs: 0","@module":"terraform.ui","@timestamp":"2023-02-16T10:15:39.631112-05:00","outputs":{},"type":"outputs"} diff --git a/internal/cloud/testdata/apply-json-with-provisioner/main.tf b/internal/cloud/testdata/apply-json-with-provisioner/main.tf new file mode 100644 index 0000000000..20bf745ad4 --- /dev/null +++ b/internal/cloud/testdata/apply-json-with-provisioner/main.tf @@ -0,0 +1,5 @@ +resource "null_resource" "foo" { + provisioner "local-exec" { + command = "echo Hello World!" + } +} diff --git a/internal/cloud/testdata/apply-json-with-provisioner/plan-redacted.json b/internal/cloud/testdata/apply-json-with-provisioner/plan-redacted.json new file mode 100644 index 0000000000..9becf1bcd0 --- /dev/null +++ b/internal/cloud/testdata/apply-json-with-provisioner/plan-redacted.json @@ -0,0 +1,116 @@ +{ + "plan_format_version": "1.1", + "resource_drift": [], + "resource_changes": [ + { + "address": "null_resource.foo", + "mode": "managed", + "type": "null_resource", + "name": "foo", + "provider_name": "registry.terraform.io/hashicorp/null", + "change": { + "actions": [ + "create" + ], + "before": null, + "after": { + "triggers": null + }, + "after_unknown": { + "id": true + }, + "before_sensitive": false, + "after_sensitive": {} + } + } + ], + "relevant_attributes": [], + "output_changes": {}, + "provider_schemas": { + "registry.terraform.io/hashicorp/null": { + "provider": { + "version": 0, + "block": { + "description_kind": "plain" + } + }, + "resource_schemas": { + "null_resource": { + "version": 0, + "block": { + "attributes": { + "id": { + "type": "string", + "description": "This is set to a random value at create time.", + "description_kind": "plain", + "computed": true + }, + "triggers": { + "type": [ + "map", + "string" + ], + "description": "A map of arbitrary strings that, when changed, will force the null resource to be replaced, re-running any associated provisioners.", + "description_kind": "plain", + "optional": true + } + }, + "description": "The `null_resource` resource implements the standard resource lifecycle but takes no further action.\n\nThe `triggers` argument allows specifying an arbitrary set of values that, when changed, will cause the resource to be replaced.", + "description_kind": "plain" + } + } + }, + "data_source_schemas": { + "null_data_source": { + "version": 0, + "block": { + "attributes": { + "has_computed_default": { + "type": "string", + "description": "If set, its literal value will be stored and returned. If not, its value defaults to `\"default\"`. This argument exists primarily for testing and has little practical use.", + "description_kind": "plain", + "optional": true, + "computed": true + }, + "id": { + "type": "string", + "description": "This attribute is only present for some legacy compatibility issues and should not be used. It will be removed in a future version.", + "description_kind": "plain", + "deprecated": true, + "computed": true + }, + "inputs": { + "type": [ + "map", + "string" + ], + "description": "A map of arbitrary strings that is copied into the `outputs` attribute, and accessible directly for interpolation.", + "description_kind": "plain", + "optional": true + }, + "outputs": { + "type": [ + "map", + "string" + ], + "description": "After the data source is \"read\", a copy of the `inputs` map.", + "description_kind": "plain", + "computed": true + }, + "random": { + "type": "string", + "description": "A random value. This is primarily for testing and has little practical use; prefer the [hashicorp/random provider](https://registry.terraform.io/providers/hashicorp/random) for more practical random number use-cases.", + "description_kind": "plain", + "computed": true + } + }, + "description": "The `null_data_source` data source implements the standard data source lifecycle but does not\ninteract with any external APIs.\n\nHistorically, the `null_data_source` was typically used to construct intermediate values to re-use elsewhere in configuration. The\nsame can now be achieved using [locals](https://www.terraform.io/docs/language/values/locals.html).\n", + "description_kind": "plain", + "deprecated": true + } + } + } + } + }, + "provider_format_version": "1.0" +} diff --git a/internal/cloud/testdata/apply-json-with-provisioner/plan.log b/internal/cloud/testdata/apply-json-with-provisioner/plan.log new file mode 100644 index 0000000000..26d39210b7 --- /dev/null +++ b/internal/cloud/testdata/apply-json-with-provisioner/plan.log @@ -0,0 +1,3 @@ +{"@level":"info","@message":"Terraform 1.3.7","@module":"terraform.ui","@timestamp":"2023-01-20T15:50:04.623068-05:00","terraform":"1.3.7","type":"version","ui":"1.0"} +{"@level":"info","@message":"null_resource.foo: Plan to create","@module":"terraform.ui","@timestamp":"2023-01-20T15:50:04.822722-05:00","change":{"resource":{"addr":"null_resource.foo","module":"","resource":"null_resource.foo","implied_provider":"null","resource_type":"null_resource","resource_name":"foo","resource_key":null},"action":"create"},"type":"planned_change"} +{"@level":"info","@message":"Plan: 1 to add, 0 to change, 0 to destroy.","@module":"terraform.ui","@timestamp":"2023-01-20T15:50:04.822787-05:00","changes":{"add":1,"change":0,"remove":0,"operation":"plan"},"type":"change_summary"} diff --git a/internal/command/jsonformat/renderer.go b/internal/command/jsonformat/renderer.go index 0f3c0eb63a..6dd0d13e9c 100644 --- a/internal/command/jsonformat/renderer.go +++ b/internal/command/jsonformat/renderer.go @@ -20,22 +20,28 @@ import ( type JSONLogType string type JSONLog struct { - Message string `json:"@message"` - Type JSONLogType `json:"type"` - Diagnostic *viewsjson.Diagnostic `json:"diagnostic"` - Outputs viewsjson.Outputs `json:"outputs"` + Message string `json:"@message"` + Type JSONLogType `json:"type"` + Diagnostic *viewsjson.Diagnostic `json:"diagnostic"` + Outputs viewsjson.Outputs `json:"outputs"` + Hook map[string]interface{} `json:"hook"` } const ( - LogVersion JSONLogType = "version" - LogDiagnostic JSONLogType = "diagnostic" - LogPlannedChange JSONLogType = "planned_change" - LogRefreshStart JSONLogType = "refresh_start" - LogRefreshComplete JSONLogType = "refresh_complete" - LogApplyStart JSONLogType = "apply_start" - LogApplyComplete JSONLogType = "apply_complete" - LogChangeSummary JSONLogType = "change_summary" - LogOutputs JSONLogType = "outputs" + LogVersion JSONLogType = "version" + LogDiagnostic JSONLogType = "diagnostic" + LogPlannedChange JSONLogType = "planned_change" + LogRefreshStart JSONLogType = "refresh_start" + LogRefreshComplete JSONLogType = "refresh_complete" + LogApplyStart JSONLogType = "apply_start" + LogApplyErrored JSONLogType = "apply_errored" + LogApplyComplete JSONLogType = "apply_complete" + LogChangeSummary JSONLogType = "change_summary" + LogProvisionStart JSONLogType = "provision_start" + LogProvisionProgress JSONLogType = "provision_progress" + LogProvisionComplete JSONLogType = "provision_complete" + LogProvisionErrored JSONLogType = "provision_errored" + LogOutputs JSONLogType = "outputs" ) type Renderer struct { @@ -86,11 +92,16 @@ func (renderer Renderer) RenderHumanState(state State) { func (r Renderer) RenderLog(log *JSONLog) error { switch log.Type { - case LogRefreshComplete, LogVersion, LogPlannedChange: + case LogRefreshComplete, + LogVersion, + LogPlannedChange, + LogProvisionComplete, + LogProvisionErrored, + LogApplyErrored: // We won't display these types of logs return nil - case LogApplyStart, LogApplyComplete, LogRefreshStart: + case LogApplyStart, LogApplyComplete, LogRefreshStart, LogProvisionStart: msg := fmt.Sprintf(r.Colorize.Color("[bold]%s[reset]"), log.Message) r.Streams.Println(msg) @@ -119,8 +130,18 @@ func (r Renderer) RenderLog(log *JSONLog) error { } } + case LogProvisionProgress: + provisioner := log.Hook["provisioner"].(string) + output := log.Hook["output"].(string) + resource := log.Hook["resource"].(map[string]interface{}) + resourceAddr := resource["addr"].(string) + + msg := fmt.Sprintf(r.Colorize.Color("[bold]%s: (%s):[reset] %s"), + resourceAddr, provisioner, output) + r.Streams.Println(msg) + case LogChangeSummary: - // We will only render the apply change summary since the renderer + // Normally, we will only render the apply change summary since the renderer // generates a plan change summary for us msg := fmt.Sprintf(r.Colorize.Color("[bold][green]%s[reset]"), log.Message) r.Streams.Println("\n" + msg + "\n")