-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ci: 🎡 create terraform plan summary and post as comment to pr
- Loading branch information
1 parent
2430aa9
commit 63389d5
Showing
12 changed files
with
14,888 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
package environment | ||
|
||
import ( | ||
"fmt" | ||
|
||
tfjson "github.com/hashicorp/terraform-json" | ||
"github.com/ministryofjustice/cloud-platform-cli/pkg/github" | ||
) | ||
|
||
func CreateCommentBody(tfPlan *tfjson.Plan) string { | ||
resourcesToUpdate := []string{} | ||
resourcesToDelete := []string{} | ||
resourcesToReplace := []string{} | ||
resourcesUnchanged := []string{} | ||
resourcesToCreate := []string{} | ||
body := ` | ||
<h1>Terraform Plan Summary</h1> | ||
` | ||
|
||
for _, resource := range tfPlan.ResourceChanges { | ||
changes := resource.Change | ||
|
||
if len(changes.Actions) > 0 { | ||
address := resource.Address | ||
switch action := changes.Actions[0]; action { | ||
case "no-op": | ||
resourcesUnchanged = append(resourcesUnchanged, address) | ||
break | ||
case "create": | ||
resourcesToCreate = append(resourcesToCreate, address) | ||
break | ||
case "delete": | ||
if len(changes.Actions) > 1 { | ||
resourcesToReplace = append(resourcesToReplace, address) | ||
} else { | ||
resourcesToDelete = append(resourcesToDelete, address) | ||
} | ||
break | ||
case "update": | ||
resourcesToUpdate = append(resourcesToUpdate, address) | ||
break | ||
default: | ||
break | ||
} | ||
|
||
} | ||
} | ||
|
||
body += ` | ||
<details "open"> | ||
<summary> | ||
<b>Terraform Plan: %d to be created, %d to be destroyed, %d to be updated, %d to be replaced and %d unchanged.</b> | ||
</summary> | ||
%s | ||
%s | ||
%s | ||
%s | ||
</details> | ||
` | ||
body = fmt.Sprintf(body, len(resourcesToCreate), len(resourcesToDelete), len(resourcesToUpdate), len(resourcesToReplace), len(resourcesUnchanged), details("create", "+", resourcesToCreate), details("destroy", "-", resourcesToDelete), details("update", "!", resourcesToUpdate), details("replace", "-+", resourcesToReplace)) | ||
|
||
if len(resourcesToCreate) == 0 && len(resourcesToReplace) == 0 && len(resourcesToUpdate) == 0 && len(resourcesToDelete) == 0 { | ||
body = "\n```diff\n+ There are no terraform changes to apply```\n" | ||
} | ||
|
||
return body | ||
} | ||
|
||
func details(action, operator string, resources []string) string { | ||
var str string | ||
|
||
if len(resources) > 0 { | ||
str = ` | ||
#### Resources to %s: | ||
` | ||
str = fmt.Sprintf(str, action) | ||
|
||
str += "```diff\n" | ||
for _, el := range resources { | ||
str += operator + " " + el + "\n" | ||
} | ||
|
||
str += "```\n" | ||
} | ||
|
||
return str | ||
} | ||
|
||
func CreateComment(gh github.GithubIface, tfplan *tfjson.Plan, prNum int) bool { | ||
body := CreateCommentBody(tfplan) | ||
|
||
return gh.CreateComment(prNum, body) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package environment_test | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"os" | ||
"testing" | ||
|
||
tfjson "github.com/hashicorp/terraform-json" | ||
"github.com/ministryofjustice/cloud-platform-cli/pkg/environment" | ||
) | ||
|
||
func readPlanFromJson(path string) *tfjson.Plan { | ||
var initPlan tfjson.Plan | ||
|
||
jsonFile, err := os.Open(path) | ||
if err != nil { | ||
fmt.Println(err) | ||
} | ||
defer jsonFile.Close() | ||
|
||
byteVal, err := io.ReadAll(jsonFile) | ||
|
||
unMarshallErr := initPlan.UnmarshalJSON(byteVal) | ||
|
||
if unMarshallErr != nil { | ||
return nil | ||
} | ||
|
||
return &initPlan | ||
} | ||
|
||
func Test_CreateCommentBody(t *testing.T) { | ||
changesOutput := ` | ||
<h1>Terraform Plan Summary</h1> | ||
<details "open"> | ||
<summary> | ||
<b>Terraform Plan: %d to be created, %d to be destroyed, %d to be updated, %d to be replaced and %d unchanged.</b> | ||
</summary> | ||
%s | ||
</details> | ||
` | ||
|
||
createChangesDiff := "#### Resources to create:\n```" + `diff | ||
+ azurerm_linux_virtual_machine.calvinvm | ||
+ azurerm_network_interface.calvin-nic | ||
+ azurerm_network_interface_security_group_association.calvin-sg-nic | ||
+ azurerm_network_security_group.calvin-security-group | ||
+ azurerm_public_ip.calvin-ip | ||
+ azurerm_resource_group.calvin | ||
+ azurerm_storage_account.calvin-sa | ||
+ azurerm_subnet.calvin-subnet | ||
+ azurerm_virtual_network.calvin-vn | ||
+ random_id.calvin-rid | ||
+ tls_private_key.calvin_ssh | ||
` + "```\n\n\n" | ||
|
||
mixedChangesDiff := "#### Resources to create:\n```" + `diff | ||
+ azurerm_linux_virtual_machine.calvinvm | ||
+ azurerm_network_security_group.calvin-security-group | ||
+ azurerm_public_ip.calvin-ip | ||
+ azurerm_resource_group.calvin | ||
+ azurerm_storage_account.calvin-sa | ||
+ azurerm_subnet.calvin-subnet | ||
+ azurerm_virtual_network.calvin-vn | ||
+ random_id.calvin-rid | ||
+ tls_private_key.calvin_ssh | ||
` + "```\n\n\n#### Resources to destroy:\n```" + `diff | ||
- azurerm_network_interface.calvin-nic | ||
` + "```\n\n\n#### Resources to update:\n```" + `diff | ||
! azurerm_network_interface_security_group_association.calvin-sg-nic | ||
` + "```\n" | ||
|
||
replacedChangesDiff := "#### Resources to create:\n```" + `diff | ||
+ module.aks_lz.module.key_vault.azurerm_private_endpoint.key_vault | ||
` + "```\n\n\n\n#### Resources to update:\n```" + `diff | ||
! module.aks_lz.module.key_vault.azurerm_key_vault.key_vault | ||
` + "```\n\n\n#### Resources to replace:\n```" + `diff | ||
-+ module.aks_lz.azurerm_virtual_hub_connection.aks_vnet_hub_connection[0] | ||
` + "```" | ||
|
||
createChangesExpected := fmt.Sprintf(changesOutput, 11, 0, 0, 0, 0, createChangesDiff) | ||
mixedChangesExpected := fmt.Sprintf(changesOutput, 9, 1, 1, 0, 0, mixedChangesDiff) | ||
replacedChangesExpected := fmt.Sprintf(changesOutput, 1, 0, 1, 1, 35, replacedChangesDiff) | ||
|
||
tfNoChangesPlan := readPlanFromJson("./fixtures/tf_nochanges.json") | ||
tfChangesPlan01 := readPlanFromJson("./fixtures/tf_test01.json") | ||
tfChangesPlan02 := readPlanFromJson("./fixtures/tf_test02.json") | ||
tfChangesPlan03 := readPlanFromJson("./fixtures/tf_test03.json") | ||
|
||
type args struct { | ||
tfPlan *tfjson.Plan | ||
} | ||
tests := []struct { | ||
name string | ||
args args | ||
want string | ||
}{ | ||
{ | ||
"GIVEN a terraform plan with no changes THEN return a comment body stating so", args{tfNoChangesPlan}, "\n```diff\n+ There are no terraform changes to apply```\n", | ||
}, | ||
{ | ||
"GIVEN a terraform plan with CREATE changes THEN return a comment body with correct changes", args{tfChangesPlan01}, createChangesExpected, | ||
}, | ||
{ | ||
"GIVEN a terraform plan with CREATE, UPDATE, DESTROY changes THEN return a comment body with correct changes", args{tfChangesPlan02}, mixedChangesExpected, | ||
}, | ||
{ | ||
"GIVEN a terraform plan with CREATE, UPDATE, DESTROY changes THEN return a comment body with correct changes", args{tfChangesPlan03}, replacedChangesExpected, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
if got := environment.CreateCommentBody(tt.args.tfPlan); got != tt.want { | ||
t.Errorf("createCommentBody() = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.