Skip to content

Commit

Permalink
Merge pull request #19 from reproio/known_after_apply
Browse files Browse the repository at this point in the history
support to display `known after apply` in-place update
  • Loading branch information
takaishi authored Jul 31, 2023
2 parents b719045 + 6a8665a commit 79d6e35
Show file tree
Hide file tree
Showing 10 changed files with 480 additions and 66 deletions.
19 changes: 1 addition & 18 deletions internal/format/format_json.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,10 @@ package format

import (
"encoding/json"
"errors"
tfjson "github.com/hashicorp/terraform-json"
)

func FormatJsonPlan(plan *tfjson.Plan) (*tfjson.Plan, error) {
var err error
if plan == nil {
return nil, errors.New("nil plan supplied")
}

for i := range plan.ResourceChanges {
plan.ResourceChanges[i].Change, err = formatJsonChange(plan.ResourceChanges[i].Change)
if err != nil {
return nil, err
}
}

return plan, nil
}

func formatJsonChange(change *tfjson.Change) (*tfjson.Change, error) {
func FormatJsonChange(change *tfjson.Change) (*tfjson.Change, error) {
var err error

change.Before, err = formatJsonChangeValue(change.Before)
Expand Down
17 changes: 17 additions & 0 deletions internal/format/format_unknown.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package format

import (
tfjson "github.com/hashicorp/terraform-json"
)

func FormatUnknownChange(change *tfjson.Change) (*tfjson.Change, error) {
if change.Actions.Update() {
for k, v := range change.AfterUnknown.(map[string]interface{}) {
switch v.(type) {
case bool:
change.After.(map[string]interface{})[k] = "(known after apply)"
}
}
}
return change, nil
}
37 changes: 29 additions & 8 deletions internal/terraform/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ func (r ResourceChangeData) GetUnifiedDiffString() (string, error) {
if err != nil {
return "", fmt.Errorf("failed to create diff: %w", err)
}

return diffText, nil
}

Expand Down Expand Up @@ -144,21 +143,43 @@ func (plan *PlanData) Render(w io.Writer, escapeHTML bool) error {
return nil
}

func processPlan(plan *tfjson.Plan) (*tfjson.Plan, error) {
var err error

for i := range plan.ResourceChanges {
plan.ResourceChanges[i].Change, err = sanitize.SanitizeChange(plan.ResourceChanges[i].Change, sanitize.DefaultSensitiveValue)
if err != nil {
return nil, fmt.Errorf("failed to sanitize change: %w", err)
}

plan.ResourceChanges[i].Change, err = format.FormatJsonChange(plan.ResourceChanges[i].Change)
if err != nil {
return nil, fmt.Errorf("failed to format json change: %w", err)
}

plan.ResourceChanges[i].Change, err = format.FormatUnknownChange(plan.ResourceChanges[i].Change)
if err != nil {
return nil, fmt.Errorf("failed to format unknown change: %w", err)
}
}

return plan, nil
}

func NewPlanData(input io.Reader) (*PlanData, error) {
var err error
var plan tfjson.Plan
if err := json.NewDecoder(input).Decode(&plan); err != nil {
return nil, fmt.Errorf("cannot parse input: %w", err)
}
sanitizedPlan, err := sanitize.SanitizePlan(&plan)
if err != nil {
return nil, fmt.Errorf("failed to sanitize plan: %w", err)
}
formattedJsonPlan, err := format.FormatJsonPlan(sanitizedPlan)

processedPlan, err := processPlan(&plan)
if err != nil {
return nil, fmt.Errorf("failed to prettify plan: %w", err)
return nil, err
}

planData := PlanData{}
for _, c := range formattedJsonPlan.ResourceChanges {
for _, c := range processedPlan.ResourceChanges {
if c.Change.Actions.NoOp() || c.Change.Actions.Read() {
continue
}
Expand Down
56 changes: 16 additions & 40 deletions test/format_json_test/format_json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,80 +9,56 @@ import (

func TestFormatJsonPlan(t *testing.T) {
type args struct {
old *tfjson.Plan
old *tfjson.Change
}
tests := []struct {
name string
args args
want *tfjson.Plan
want *tfjson.Change
wantErr bool
}{
{
name: "plain string",
args: args{
old: &tfjson.Plan{
ResourceChanges: []*tfjson.ResourceChange{
{
Change: &tfjson.Change{
Before: "plain string",
After: "plain string",
},
},
},
old: &tfjson.Change{
Before: "plain string",
After: "plain string",
},
},
want: &tfjson.Plan{
ResourceChanges: []*tfjson.ResourceChange{
{
Change: &tfjson.Change{
Before: "plain string",
After: "plain string",
},
},
},
want: &tfjson.Change{
Before: "plain string",
After: "plain string",
},
wantErr: false,
},
{
name: "json string",
args: args{
old: &tfjson.Plan{
ResourceChanges: []*tfjson.ResourceChange{
{
Change: &tfjson.Change{
Before: `{"foo":"bar"}`,
After: `{"foo":"bar"}`,
},
},
},
old: &tfjson.Change{
Before: `{"foo":"bar"}`,
After: `{"foo":"bar"}`,
},
},
want: &tfjson.Plan{
ResourceChanges: []*tfjson.ResourceChange{
{
Change: &tfjson.Change{
Before: `{
want: &tfjson.Change{
Before: `{
"foo": "bar"
}`,
After: `{
After: `{
"foo": "bar"
}`,
},
},
},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := format.FormatJsonPlan(tt.args.old)
got, err := format.FormatJsonChange(tt.args.old)
if (err != nil) != tt.wantErr {
t.Errorf("FormatJsonPlan() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("FormatJsonPlan() got = \n%v\n, want \n%v", got.ResourceChanges[0].Change.After, tt.want.ResourceChanges[0].Change.After)
t.Errorf("FormatJsonPlan() got = \n%v\n, want \n%v", got.After, tt.want.After)
}
})
}
Expand Down
61 changes: 61 additions & 0 deletions test/format_json_test/format_unknown_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package format_json_test

import (
tfjson "github.com/hashicorp/terraform-json"
"github.com/reproio/terraform-j2md/internal/format"
"reflect"
"testing"
)

func TestFormatUnknownPlan(t *testing.T) {
type args struct {
old *tfjson.Change
}
tests := []struct {
name string
args args
want *tfjson.Change
wantErr bool
}{
{
name: "plain string",
args: args{
old: &tfjson.Change{
Actions: tfjson.Actions{tfjson.ActionUpdate},
Before: map[string]interface{}{
"foo": "bar",
},
After: map[string]interface{}{},
AfterUnknown: map[string]interface{}{
"foo": true,
},
},
},
want: &tfjson.Change{
Actions: tfjson.Actions{tfjson.ActionUpdate},
Before: map[string]interface{}{
"foo": "bar",
},
After: map[string]interface{}{
"foo": "(known after apply)",
},
AfterUnknown: map[string]interface{}{
"foo": true,
},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := format.FormatUnknownChange(tt.args.old)
if (err != nil) != tt.wantErr {
t.Errorf("FormatJsonPlan() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("FormatJsonPlan() got = \n%v\n, want \n%v", got.After, tt.want.After)
}
})
}
}
1 change: 1 addition & 0 deletions test/plan_test/plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ func Test_render(t *testing.T) {
{name: "iam_policy", wantErr: false},
{name: "include_code_fence", wantErr: false},
{name: "include_module", wantErr: false},
{name: "known_after_apply", wantErr: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
51 changes: 51 additions & 0 deletions test/testdata/known_after_apply/expected.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
### 1 to add, 1 to change, 1 to destroy, 0 to replace.
- add
- random_id.test2
- change
- env_variable.test
- destroy
- random_id.test
<details><summary>Change details</summary>

````````diff
# env_variable.test will be updated in-place
@@ -1,6 +1,6 @@
{
"id": "07ec30c9d869e0f6392f",
- "name": "07ec30c9d869e0f6392f",
+ "name": "(known after apply)",
"value": "REDACTED_SENSITIVE"
}

````````

````````diff
# random_id.test will be destroyed
@@ -1,11 +1,2 @@
-{
- "b64_std": "B+wwydhp4PY5Lw==",
- "b64_url": "B-wwydhp4PY5Lw",
- "byte_length": 10,
- "dec": "37413512560416367458607",
- "hex": "07ec30c9d869e0f6392f",
- "id": "B-wwydhp4PY5Lw",
- "keepers": null,
- "prefix": null
-}
+null

````````

````````diff
# random_id.test2 will be created
@@ -1,2 +1,6 @@
-null
+{
+ "byte_length": 10,
+ "keepers": null,
+ "prefix": null
+}

````````

</details>
24 changes: 24 additions & 0 deletions test/testdata/known_after_apply/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
terraform {
required_providers {
env = {
source = "tchupp/env"
version = "0.0.2"
}
}
}

provider "env" {
# Configuration options
}

resource "env_variable" "test" {
name = random_id.test2.hex
}

#resource "random_id" "test" {
# byte_length = 10
#}

resource "random_id" "test2" {
byte_length = 10
}
Loading

0 comments on commit 79d6e35

Please sign in to comment.