Skip to content

Commit

Permalink
Feature/improve parser (#16)
Browse files Browse the repository at this point in the history
* Restructure pkg/ folder in preparation of new parser

* Implement Setters for ParsedLog

* Rework parser to have uniform interface for parse functions

* Remove test that doesn't apply anymore

* Rework Staus to Created/NotCreated/Failed

* Implement correct parsing of Modify/Destroy

* Add tests for new parsers

* Remove legend and make only Failed red

* Fix go vet issues

* Make Created the default for BeforeStatus

* Parse refreshes as well

* Add more State stats to the stats command

* Only export the necessary functions for other packages

* Adapt docs for new parser

* Various small docs changes after review

* Remove redundant command

* Small improvements to README

* Small improvements to docs

* Small improvements to docs

* Rewrite outdated comment

* Improve comments and clean up

* go fmt
  • Loading branch information
QuintenBruynseraede authored May 14, 2023
1 parent 99e29f8 commit 9a65837
Show file tree
Hide file tree
Showing 33 changed files with 1,567 additions and 738 deletions.
Binary file modified .github/stats.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified .github/table.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
67 changes: 37 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
CLI tool to profile Terraform runs, written in Go.

Main features:
- Modern CLI ([cobra](https://github.com/spf13/cobra)-based), including autocomplete
- Modern CLI ([cobra](https://github.com/spf13/cobra)-based) with autocomplete
- Read logs straight from your Terraform process (using pipe) or a log file
- Can generate global stats, resource-level stats or visualizations
- Provides many levels of granularity and aggregation, customizable outputs
- Provides many levels of granularity and aggregation and customizable outputs

## Installation

Expand All @@ -23,7 +23,7 @@ Main features:
If you want to try `tf-profile` without installing anything, you can run it using Docker (or similar).

```bash
❱ cat my_log_file.log | docker run -i qbruynseraede/tf-profile:0.0.1 stats
❱ cat my_log_file.log | docker run -i qbruynseraede/tf-profile:0.2.0 stats

Key Value
Number of resources created 1510
Expand All @@ -37,7 +37,7 @@ Longest apply resource time_sleep.foo[*]
Optionally, define an alias:

```bash
alias tf-profile=docker run -i qbruynseraede/tf-profile:0.0.1
alias tf-profile=docker run -i qbruynseraede/tf-profile:0.2.0
❱ cat my_log_file.log | tf-profile
```

Expand Down Expand Up @@ -68,7 +68,7 @@ Usage:
Three major commands are supported:
- [🔗](#anchor_stats) `tf-profile stats`: provide general statistics about a Terraform run
- [🔗](#anchor_table) `tf-profile table`: provide detailed, resource-level statistics about a Terraform run
- [🔗](#anchor_graph) `tf-profile graph`: generate visual overview of a Terraform run.
- [🔗](#anchor_graph) `tf-profile graph`: generate a visual overview of a Terraform run.


## `tf-profile stats`
Expand All @@ -79,27 +79,33 @@ Three major commands are supported:
```bash
❱ terraform apply -auto-approve > log.txt
❱ tf-profile stats log.txt
❱ tf-profile stats test/many_modules.log

Key Value
-----------------------------------------------------------------
Number of resources created 1510
Number of resources in configuration 1510

Cumulative duration 36m19s
Longest apply time 7m18s
Longest apply resource time_sleep.foo[*]

No. resources in state AllCreated 800
No. resources in state Created 695
No. resources in state Started 15

Number of top-level modules 13
Largest top-level module module.core[2]
Size of largest top-level module 170
Deepest module module.core[2].module.role[47]
Deepest module depth 2
Largest leaf module module.dbt[4]
Size of largest leaf module 40
Cumulative duration 36m19s
Longest apply time 7m18s
Longest apply resource time_sleep.foo[*]

Resources marked for operation Create 892
Resources marked for operation None 18
Resources marked for operation Replace 412

Resources in state AllCreated 800
Resources in state Created 695
Resources in state Started 15

Resources in desired state 1492 out of 1510 (98.8%)
Resources not in desired state 18 out of 1510 (0.01%)

Number of top-level modules 13
Largest top-level module module.core[2]
Size of largest top-level module 170
Deepest module module.core[2].module.role[47]
Deepest module depth 2
Largest leaf module module.dbt[4]
Size of largest leaf module 40
```

For more information, refer to the [reference](./docs/stats.md) for the `stats` command.
Expand All @@ -112,12 +118,13 @@ For more information, refer to the [reference](./docs/stats.md) for the `stats`
❱ terraform apply -auto-approve > log.txt
❱ tf-profile table log.txt

resource n tot_time idx_creation idx_created status
--------------------------------------------------------------------------------------
time_sleep.count[*] 5 11s 0 13 AllCreated
time_sleep.foreach[*] 3 7s 4 11 AllCreated
module.test[1].time_sleep.count[*] 3 5s 3 9 AllCreated
module.test[0].time_sleep.count[*] 3 4s 9 7 AllCreated
resource n tot_time modify_started modify_ended desired_state operation final_state
aws_ssm_parameter.p6 1 0s 6 7 Created Replace Created
aws_ssm_parameter.p1 1 0s 7 5 Created Replace Created
aws_ssm_parameter.p3 1 0s 5 6 Created Replace Created
aws_ssm_parameter.p4 1 0s / 1 NotCreated Destroy NotCreated
aws_ssm_parameter.p5 1 0s 4 4 Created Modify Created
aws_ssm_parameter.p2 1 0s / / Created None Created
```

For a full description of the options, see the [reference](./docs/table.md) page.
Expand Down Expand Up @@ -148,9 +155,9 @@ _Disclaimer:_ Terraform's logs do not contain any absolute timestamps. We can on
## Roadmap

- [x] Release v0.0.1 as binary and as a Docker image
- [ ] Improve parser
- [x] Improve parser
- [x] Detect failed resources (see [#13](https://github.com/QuintenBruynseraede/tf-profile/pull/13))
- [ ] Use plan and refresh phase to discover more resources
- [x] Use plan and refresh phase to discover more resources
- [x] Implement a basic Gantt chart in `tf-profile graph` (see [#14](https://github.com/QuintenBruynseraede/tf-profile/pull/14))
- [ ] Implement a single-resource view in `tf-profile detail <resource>`
- This command should filter logs down to 1 single resource (i.e. refresh, plan, changes, and result)
Expand Down
17 changes: 12 additions & 5 deletions docs/stats.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,29 @@

**Arguments:**

Log_file: Optional. Instruct `tf-profile` to read input from a text file instead of stdin.
- log_file: _Optional_. Instruct `tf-profile` to read input from a text file instead of stdin.

## Description

The following statistics will be printed:

General:
- **Number of resources created**: Number of resources detected in your log.
- **Number of resources created**: Number of resources detected in your log. Depending on which phases (refresh, plan, apply) were present in the log, this number can differ and may not always match what is defined in your code. For example, doing `terraform apply my_plan_file` will not include a resource that is not to be modified in this plan.

Duration:
- **Cumulative duration**: Cumulative duration of modifications. This is the sum of the duration of all modifications in the logs. Because Terraform modifies resources in parallel, this will typically be more than the actual wall time.
- **Longest apply time**: Lngest time it took to modify a single resource. The next metric shows which resource that was.
- **Longest apply resource**: the name of the resource that took the longest to modify.
- **Longest apply time**: Longest time it took to modify a single resource. The next metric shows which resource that was.
- **Longest apply resource**: The name of the resource that took the most time to modify.

Operations:
- **Resources marked for operation \<OPERATION\>**: The amount of resources marked for a certain operation. An Operation can be any of: Create, Destroy, Modify, Replace, None. Resources that are consistent with the state, will be marked for operation None.

Resource status:
- **No. resources in state \<STATE\>**: This statistic shows per state how many resources are in that state after the modifications.
- **Resources in state \<STATE\>**: This statistic shows per state how many resources are in that state after the modifications. In general, resources can be in three states after a Terraform run: Created, NotCreated or Failed.

Desired state:
- **Resources in desired state**: The amount of resources whose `final_state` is equal to their `desired_state`. In a fully applied configuration, this number should be 100%.
- **Resources not in desired state**: Resources whose desired state was not achieved after this run. This can be due to failed creation, failed deletion or because "upstream" resources were not able to get to their desired state.

Modules:
- **Number of top-level modules**: Number of modules called in the root module.
Expand Down
58 changes: 33 additions & 25 deletions docs/table.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,52 +6,60 @@

**Options:**
- -t, --tee: print logs while parsing them. Shorthand for `terraform apply | tee >(tf-profile stats)`
- -d, --max_depth: aggregate resources nested deeper than `-d` levels into a resource that represents the module at depth `-d`.
- -d, --max_depth: aggregate resources nested deeper than `-d` levels into a resource that represents the module at depth `-d`. **Not implented yet**
- -s, --sort: comma-separated key-value pairs that instruct how to sort the output table. Valid values follow the format `column1:(asc|desc),column2:(asc|desc):...`. By default, `tot_time=desc,resource=asc` is used: sort first by descending modification time, second by resource name in alphabetical order.

**Arguments:**

Log_file: Optional. Instruct `tf-profile` to read input from a text file instead of stdin.
- log_file: _Optional_. Instruct `tf-profile` to read input from a text file instead of stdin.

## Description

A table generated based on the log file or input, sorted according to `-s / --sort` and printed to the terminal.
This command prints a table based on the log file or input, sorted according to `-s / --sort` and printed to the terminal. Useful to inspect properties about individual resources.

```
resource n tot_time idx_creation idx_created status
--------------------------------------------------------------------------------------
time_sleep.count[*] 5 15s 0 13 AllCreated
time_sleep.foreach[*] 3 1m30s 4 11 AllCreated
module.test[1].time_sleep.count[*] 3 6 3 9 AllCreated
module.test[0].time_sleep.count[*] 3 3s 9 7 AllCreated
resource n tot_time modify_started modify_ended desired_state operation final_state
---------------------------------------------------------------------------------------------------------
aws_ssm_parameter.p6 1 0s 6 7 Created Replace Created
aws_ssm_parameter.p1 1 0s 7 5 Created Replace Created
aws_ssm_parameter.p3 1 0s 5 6 Created Replace Created
aws_ssm_parameter.p4 1 0s / 1 NotCreated Destroy NotCreated
aws_ssm_parameter.p5 1 0s 4 4 Created Modify Created
aws_ssm_parameter.p2 1 0s / / Created None Created
```

The column names are lowercase and separated by underscores to allow for easy referencing in the `--sort` option. The meaning of each column is:

- **resource**: Name of the resource. In case a resource is created by a `for_each` or `count` statement, resources are aggregated and individual resource identifiers are replaced by an asterisk (*). See
- **resource**: Name of the resource. In case a resource is created by a `for_each` or `count` statement, resources are aggregated and individual resource identifiers are replaced by an asterisk (*).
- **n**: Number of resources represented by this resource name. For regular resources, this will be 1. For resourced created with `for_each` or `count`, this number represents the number of resources created in that loop.
- **tot_time**: Total cumulative time of all resources identified by this resource name. This is typically higher than the actual wall time, as Terraform can modify multiple resources at the same time.
- **idx_creation**: order in which resource creation _started_. This means that Terraform started by creation the resource with `idx_creation = 0`. That does not guarantee the creation of this resource finished first as well (see `idx_created`).
- **idx_created**: order in which resource creation _ended_. this means that the resource with `idx_created = 0` was the first resource to be fully creatd.
- **status**: For single resources, status can be any of: `Started|NotStarted|Created|Failed`. For aggregated resources, status can be any of: `AllCreated|AllFailed|SomeFailed|NoneStarted|AllStarted|SomeStarted`.
With resource aggregation, more informative statuses have precedence over less informative statuses. For example, `AllCreated` will be shown over `AllStarted`.
- **modify_started**: order in which resource modification _started_. This means that Terraform started by modifying the resource with `modify_started = 0`. It does not guarantee the changes to this resource finished first as well (see `modify_ended`). Resources that were already consistent with the desired state do not have this property.
- **modify_ended**: order in which resource modifications _ended_. This means that the resource with `modify_ended = 0` was the first resource to finish its modifications (either a creation, deletion, modification or replacement). Resources that were already consistent with the desired state do not have this property.
- **desired_state**: state (Created, NotCreated) that Terraform will try to achieve with this run. For resources to be modified, created or replaced, Created is the desired state. For resources to be destroyed, NotCreated is the desired state.
- **operation**: the name of the operation the Terraform will use to reconcile the current and desired situation. Operations can be: Create, Destroy, Replace, Modify, None. Resources in the state that are already consistent with the configuration, the operation will be None.
- **final_state**: Final state of the resource after this run. In addition to Created and NotCreated, Failed is used to indicate the operation failed.

## Sorting

Any of the columns above can be used to sort the output table, by means of the `--sort` (shorthand `-s`) option. This option follows the format `column1:(asc|desc),column2:(asc|desc):...`. For example:
- `tot_time=desc,resource=asc`: sort first by total modification time (showing the highest first). For entries with the same modification time, sort alphabetically.
- `idx_creation=asc`: sort in order of creation, showing the resources that Terraform finished modifying first.
- `modify_started=asc`: sort in order modifications, showing the resources that Terraform started modifying first.
- `final_state=asc`: sort by the final state. See below for the sort order.

When sorting on resource status (`status`), statuses are mapped onto integers before sorting.
When sorting on resource status (`desired_state` or `final_state`), statuses are mapped onto integers before sorting.

- NotStarted: 0
- Started: 1
- Unknown: 0
- NotCreated: 1
- Created: 2
- Failed: 3
- SomeStarted: 4
- AllStarted: 5
- NoneStarted: 6
- SomeFailed: 7
- AllFailed: 8
- AllCreated: 9
- Tainted: 4
- Multiple (for aggregated resources): 5

When sorting on resource operations (`operation`), these are mapped onto integers as well:

- None: 0
- Create: 1
- Modify: 2
- Replace: 3
- Destroy: 4
- Multiple (for aggregated resources): 5
Loading

0 comments on commit 9a65837

Please sign in to comment.