Skip to content

Commit

Permalink
Feat delete (#34)
Browse files Browse the repository at this point in the history
* test: added aws module to test config
* docs: updated demo gif
* feat: added a delete resource option
  • Loading branch information
idoavrah authored Nov 5, 2023
1 parent 9e4a55d commit 6e373d0
Show file tree
Hide file tree
Showing 24 changed files with 381 additions and 11 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ With its latest version you can easily visualize the complete state tree, gainin

## Key Features

### version 0.8

- [x] Added a delete resource option

### version 0.7

- [x] Added a search option (press `/` to search)
Expand Down Expand Up @@ -45,7 +49,7 @@ With its latest version you can easily visualize the complete state tree, gainin

## Demo

![](https://github.com/idoavrah/terraform-tui/raw/main/demo/demo.gif "demo")
![](demo/demo.gif "demo")

## Installation

Expand Down
Binary file modified demo/demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 20 additions & 9 deletions src/tftui/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ async def refresh_state(self) -> None:
if not global_no_init:
self.app.status.update(f"Executing {global_executable.capitalize()} init")
returncode, stdout = await execute_async(
global_executable, "init", "-no-color"
global_executable, "init -no-color"
)
if returncode != 0:
self.app.exit(message=stdout)
Expand All @@ -112,7 +112,7 @@ async def refresh_state(self) -> None:

self.app.status.update(f"Executing {global_executable.capitalize()} show")

returncode, stdout = await execute_async(global_executable, "show", "-no-color")
returncode, stdout = await execute_async(global_executable, "show -no-color")
if returncode != 0 or not stdout.startswith("#"):
self.app.exit(message=stdout)
global_successful_termination = False
Expand Down Expand Up @@ -202,6 +202,7 @@ def __init__(self, *args, **kwargs):
("Enter", "", "View state"),
Binding("escape", "back", "Back"),
("s", "select", "Select"),
("d", "delete", "Delete"),
("t", "taint", "Taint"),
("u", "untaint", "Untaint"),
("r", "refresh", "Refresh state"),
Expand Down Expand Up @@ -265,25 +266,29 @@ def on_input_changed(self, event: Input.Changed) -> None:
def on_input_submitted(self, event: Input.Submitted) -> None:
self.tree.focus()

async def perform_taint_untaint(self, what_to_do: str) -> None:
async def manipulate_resources(self, what_to_do: str) -> None:
resources = [
f"{node.parent.data}.{node.label.plain}".lstrip(".").replace(
" (tainted)", ""
)
for node in self.tree.selected_nodes
]
for resource in resources:
await execute_async(global_executable, what_to_do, resource)
await execute_async(
global_executable,
(what_to_do if what_to_do != "delete" else "state rm"),
resource,
)

async def perform_action(self) -> None:
if self.switcher.current != "action":
return
if self.selected_action == "taint" or self.selected_action == "untaint":
if self.selected_action in ["taint", "untaint", "delete"]:
self.status.update(
f"Executing {global_executable.capitalize()} {self.selected_action}"
)
self.switcher.current = "loading"
await self.perform_taint_untaint(self.selected_action)
await self.manipulate_resources(self.selected_action)
self.tree.refresh_state()

def perform_search(self, search_string: str) -> None:
Expand Down Expand Up @@ -321,7 +326,7 @@ def action_destroy(self) -> None:
if not self.switcher.current == "tree":
return

def action_taint_untaint(self, what_to_do: str) -> None:
def action_manipulate_resources(self, what_to_do: str) -> None:
if not self.switcher.current == "tree":
return
if not self.tree.selected_nodes:
Expand All @@ -338,12 +343,16 @@ def action_taint_untaint(self, what_to_do: str) -> None:
)
self.switcher.current = "action"

def action_delete(self) -> None:
self.action_manipulate_resources("delete")
OutboundAPIs.post_usage("removed resources from state")

def action_taint(self) -> None:
self.action_taint_untaint("taint")
self.action_manipulate_resources("taint")
OutboundAPIs.post_usage("applied taint")

def action_untaint(self) -> None:
self.action_taint_untaint("untaint")
self.action_manipulate_resources("untaint")
OutboundAPIs.post_usage("applied untaint")

def action_refresh(self) -> None:
Expand Down Expand Up @@ -407,6 +416,8 @@ def find_string_in_node(self, node, search_string: str) -> None:


async def execute_async(*command: str) -> tuple[str, str]:
command = [word for phrase in command for word in phrase.split()]

proc = await asyncio.create_subprocess_exec(
*command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT
)
Expand Down
23 changes: 23 additions & 0 deletions test/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions test/aws/ami.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]

filter {
name = "name"
values = [
"amzn2-ami-hvm-*-x86_64-gp2",
]
}

filter {
name = "owner-alias"
values = [
"amazon",
]
}

filter {
name = "virtualization-type"
values = ["hvm"]
}

}
20 changes: 20 additions & 0 deletions test/aws/aws.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
terraform {
required_version = "> 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
# Fix version version of the AWS provider
version = "= 5.3.0"
}
}
}

provider "aws" {
region = var.region
}

variable "region" {
description = "The name of the AWS Region"
type = string
default = "eu-west-1"
}
3 changes: 3 additions & 0 deletions test/aws/az.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
data "aws_availability_zones" "az" {
state = "available"
}
7 changes: 7 additions & 0 deletions test/aws/igw.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# File generated by aws2tf see https://github.com/aws-samples/aws2tf
# aws_internet_gateway.igw-06fcd611034e99d14:
resource "aws_internet_gateway" "myigw" {
count = var.mycount
tags = {}
vpc_id = aws_vpc.VPC[count.index].id
}
7 changes: 7 additions & 0 deletions test/aws/my-eip.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
resource "aws_eip" "my-eip" {
count = var.mycount
public_ipv4_pool = "amazon"
tags = {}
domain = "vpc"
timeouts {}
}
7 changes: 7 additions & 0 deletions test/aws/my-privrtassociation.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# File generated by aws2tf see https://github.com/aws-samples/aws2tf
# aws_route_table_association.rtbassoc-01b2fe2ddfac5825c:
resource "aws_route_table_association" "myrtbassociation2" {
count = var.mycount
route_table_id = aws_route_table.rtb1-natgw-tgw[count.index].id
subnet_id = aws_subnet.myprivsubnet[count.index].id
}
7 changes: 7 additions & 0 deletions test/aws/my-pubrtassociation.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# File generated by aws2tf see https://github.com/aws-samples/aws2tf
# aws_route_table_association.rtbassoc-01b2fe2ddfac5825c:
resource "aws_route_table_association" "myrtbassociation" {
count = var.mycount
route_table_id = aws_route_table.rtb2-igw[count.index].id
subnet_id = aws_subnet.mypubsubnet[count.index].id
}
81 changes: 81 additions & 0 deletions test/aws/my_instance.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# File generated by aws2tf see https://github.com/aws-samples/aws2tf
# aws_instance.i-07ecaaf125c9a6807:
resource "aws_instance" "myinstance" {
count = var.mycount
ami = data.aws_ami.amazon_linux.id

associate_public_ip_address = false
availability_zone = data.aws_availability_zones.az.names[0]

iam_instance_profile = aws_iam_instance_profile.test_profile.name
instance_type = "t2.micro"
monitoring = false

source_dest_check = true
subnet_id = aws_subnet.myprivsubnet[count.index].id
tags = {
"Name" = format("instance-10-%s-4-first", count.index + 1)
}
tenancy = "default"
lifecycle {
ignore_changes = [user_data, user_data_base64]
}
#user_data_base64 = "IyEvYmluL2Jhc2gKCnl1bSB1cGRhdGUgLXkKCmN1cmwgaHR0cHM6Ly9naXN0LmdpdGh1YnVzZXJjb250ZW50LmNvbS9BbnRvbmlvRmVpamFvVUsvZDg1MzNhNzFlNWVjZmYyOTcxZjY4NTlhN2JlNDI2ZGEvcmF3LzNkMDkzMDAwNGI5MzdmNmRkN2YyNzMwMjEyMTgzMjdiNzEyOWQ2MDkvYXdzLWVjMi11c2VyZGF0YS1sYW5kaW5nLXdlYnBhZ2Uuc2ggfCBiYXNoCgo="
user_data = file("aws/user_data.sh")
volume_tags = {
"Name" = format("instance-10-%s-4-first", count.index + 1)
}
vpc_security_group_ids = [
aws_security_group.mysg[count.index].id,
]

credit_specification {
cpu_credits = "standard"
}

metadata_options {
http_endpoint = "enabled"
http_put_response_hop_limit = 1
http_tokens = "optional"
}

root_block_device {
delete_on_termination = true
encrypted = false
volume_size = 8
volume_type = "gp2"
}

timeouts {}
}

resource "aws_iam_instance_profile" "test_profile" {
name = "test_profile"
role = aws_iam_role.test_role.name
}

resource "aws_iam_role" "test_role" {
name = "test_role"

assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF

}

resource "aws_iam_role_policy_attachment" "test-attach" {
role = aws_iam_role.test_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
5 changes: 5 additions & 0 deletions test/aws/mysg.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
resource "aws_security_group" "mysg" {
count = var.mycount
description = "SG-inbound"
vpc_id = aws_vpc.VPC[count.index].id
}
16 changes: 16 additions & 0 deletions test/aws/mysgingress-80.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# File generated by aws2tf see https://github.com/aws-samples/aws2tf
# aws_security_group_rule.sg-0c991999e3313704a-1:
resource "aws_security_group_rule" "mysgingress-80" {
count = var.mycount
cidr_blocks = [
"10.0.0.0/8"
]

from_port = 80
ipv6_cidr_blocks = []
prefix_list_ids = []
protocol = "tcp"
security_group_id = aws_security_group.mysg[count.index].id
to_port = 80
type = "ingress"
}
17 changes: 17 additions & 0 deletions test/aws/mysgingress-icmp.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# File generated by aws2tf see https://github.com/aws-samples/aws2tf
# aws_security_group_rule.sg-0c991999e3313704a-2:
resource "aws_security_group_rule" "mysgingress-icmp" {
count = var.mycount
cidr_blocks = [
#for avpc in var.aws_vpc :
#lookup(var.aws_cidr, avpc)
"10.0.0.0/8"
]
from_port = -1
ipv6_cidr_blocks = []
prefix_list_ids = []
protocol = "icmp"
security_group_id = aws_security_group.mysg[count.index].id
to_port = -1
type = "ingress"
}
15 changes: 15 additions & 0 deletions test/aws/mysgrule-egress-all.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# File generated by aws2tf see https://github.com/aws-samples/aws2tf
# aws_security_group_rule.sg-0c991999e3313704a:
resource "aws_security_group_rule" "mysgegress" {
count = var.mycount
cidr_blocks = [
"0.0.0.0/0",
]
from_port = 0
ipv6_cidr_blocks = []
prefix_list_ids = []
protocol = "-1"
security_group_id = aws_security_group.mysg[count.index].id
to_port = 0
type = "egress"
}
8 changes: 8 additions & 0 deletions test/aws/nat_gateway.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# File generated by aws2tf see https://github.com/aws-samples/aws2tf
# aws_nat_gateway.nat-070132f292c79b9d6:
resource "aws_nat_gateway" "mynatgw" {
count = var.mycount
allocation_id = aws_eip.my-eip[count.index].id
subnet_id = aws_subnet.mypubsubnet[count.index].id
tags = {}
}
Loading

0 comments on commit 6e373d0

Please sign in to comment.