Skip to content

Commit

Permalink
feat(rename): whitelist to ignore
Browse files Browse the repository at this point in the history
  • Loading branch information
Laudenlaruto committed Jan 7, 2025
1 parent 8ec8754 commit 6e3c815
Show file tree
Hide file tree
Showing 8 changed files with 176 additions and 181 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ resource "azurerm_resource_group" "network" {
name...
```
You can also whitelist entire checks in **module** by adding them to a `.guacamoleignore` file at the root of your codebase.
You can also whitelist entire checks in **modules** by adding them to a `.guacamoleignore` file at the root of your codebase.
The format of the file should be: path of the `module - check ID` to ignore.
```bash
Expand Down
8 changes: 4 additions & 4 deletions checks/module_static_checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@ func ModuleStaticChecks() []data.Check {
defer wg.Done()

check, err := checkFunction(modules)
// Apply whitelist on Terraform code blocks checks errors
// Apply ignore on Terraform code blocks checks errors
// This only cover Terraform resources that have a POS attribute
for i := len(check.Errors) - 1; i >= 0; i-- {
check, _ = helpers.ApplyWhitelistOnCodeBlock(check, i, modules)
check, _ = helpers.ApplyWhitelistOnModule(check, i, modules)
check, _ = helpers.ApplyIgnoreOnCodeBlock(check, i, modules)
check, _ = helpers.ApplyIgnoreOnModule(check, i, modules)
}
// Replace the check error status with the array after whitelisting
// Replace the check error status with the array after ignoreing
if len(check.Errors) == 0 {
check.Status = "✅"
}
Expand Down
6 changes: 3 additions & 3 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (

var cfgFile string
var codebasePath string
var whitelistPath string
var ignorePath string
var terraformBin string

// rootCmd represents the base command when called without any subcommands
Expand Down Expand Up @@ -48,10 +48,10 @@ func init() {
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
rootCmd.PersistentFlags().StringVarP(&codebasePath, "codebase-path", "p", ".", "path to your IaC codebase")
rootCmd.PersistentFlags().StringVarP(&whitelistPath, "whitelistfile-path", "w", ".guacamoleignore", "path to your whitelist file")
rootCmd.PersistentFlags().StringVarP(&ignorePath, "guacamoleignore-path", "w", ".guacamoleignore", "path to your ignore file")

viper.BindPFlag("codebase-path", rootCmd.PersistentFlags().Lookup("codebase-path"))
viper.BindPFlag("whitelistfile-path", rootCmd.PersistentFlags().Lookup("whitelistfile-path"))
viper.BindPFlag("guacamoleignore-path", rootCmd.PersistentFlags().Lookup("guacamoleignore-path"))

rootCmd.PersistentFlags().BoolP("verbose", "v", false, "Display verbose output")
viper.BindPFlag("verbose", rootCmd.PersistentFlags().Lookup("verbose"))
Expand Down
4 changes: 2 additions & 2 deletions data/whitelist.go → data/ignore.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package data

type WhitelistComment struct {
type IgnoreComment struct {
CheckID string
LineNumber int
Path string
}

type WhitelistModule struct {
type IgnoreModule struct {
CheckID string
ModulePath string
}
12 changes: 6 additions & 6 deletions data/terraform_module.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ type TerraformModule struct {
FullPath string
ModuleConfig tfconfig.Module
Resources map[string]TerraformCodeBlock
Whitelist []WhitelistModule
Ignore []IgnoreModule
}

type TerraformCodeBlock struct {
Name string
ModulePath string
Pos int
FilePath string
WhitelistComments []WhitelistComment
Name string
ModulePath string
Pos int
FilePath string
IgnoreComments []IgnoreComment
}
54 changes: 27 additions & 27 deletions helpers/getters.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
func GetModules() (map[string]data.TerraformModule, error) {
codebasePath := viper.GetString("codebase-path")
modules := make(map[string]data.TerraformModule)
whitelistOnModule, _ := GetWhitelistingInFile()
ignoreOnModule, _ := GetIgnoreingInFile()
//Get all subdirectories in root path
err := filepath.Walk(codebasePath, func(path string, info os.FileInfo, err error) error {
if err != nil {
Expand All @@ -27,8 +27,8 @@ func GetModules() (map[string]data.TerraformModule, error) {
if !info.IsDir() && strings.HasSuffix(info.Name(), ".tf") {
// exclude the files which are in the .terragrunt-cache or .terraform directory
if !regexp.MustCompile(`\.terragrunt-cache|\.terraform`).MatchString(path) {
// Get all whitelisting comments
whitelistCommentOfFile, _ := GetWhitelistingComments(path)
// Get all ignoreing comments
ignoreCommentOfFile, _ := GetIgnoreingComments(path)
// Check if the module is already in the list
alreadyInList := false
for _, m := range modules {
Expand All @@ -39,8 +39,8 @@ func GetModules() (map[string]data.TerraformModule, error) {
// If not in list create the module
if !alreadyInList {
modules[filepath.Dir(path)], _ = LoadModule(filepath.Dir(path))
// Associate whitelisting comments on module from the .guacamoleignore file
AssociateWhitelistingCommentsOnModule(whitelistOnModule, filepath.Dir(path), modules)
// Associate ignoreing comments on module from the .guacamoleignore file
AssociateIgnoreingCommentsOnModule(ignoreOnModule, filepath.Dir(path), modules)
}
// Create a temporary object with all resources in order
resourcesInFile := make(map[string]data.TerraformCodeBlock)
Expand All @@ -59,8 +59,8 @@ func GetModules() (map[string]data.TerraformModule, error) {
return resourcesInFile[keys[i]].Pos < resourcesInFile[keys[j]].Pos
})

// Associate the whitelisting comments to a resource (Resource, Data, Variable or Output)
AssociateWhitelistingComments(whitelistCommentOfFile, keys, resourcesInFile, modules, path)
// Associate the ignoreing comments to a resource (Resource, Data, Variable or Output)
AssociateIgnoreingComments(ignoreCommentOfFile, keys, resourcesInFile, modules, path)
}
}
return nil
Expand Down Expand Up @@ -119,41 +119,41 @@ func LoadModule(path string) (data.TerraformModule, error) {
// Create map of code blocks (resources, data, variables and outputs) of module
for _, resource := range moduleConfig.ManagedResources {
resources[resource.Type+resource.Name] = data.TerraformCodeBlock{
Name: resource.Type + " " + resource.Name,
ModulePath: path,
Pos: resource.Pos.Line,
FilePath: resource.Pos.Filename,
WhitelistComments: []data.WhitelistComment{},
Name: resource.Type + " " + resource.Name,
ModulePath: path,
Pos: resource.Pos.Line,
FilePath: resource.Pos.Filename,
IgnoreComments: []data.IgnoreComment{},
}
}
// Load data
for _, resource := range moduleConfig.DataResources {
resources[resource.Type+resource.Name] = data.TerraformCodeBlock{
Name: resource.Type + " " + resource.Name,
ModulePath: path,
Pos: resource.Pos.Line,
WhitelistComments: []data.WhitelistComment{},
FilePath: resource.Pos.Filename,
Name: resource.Type + " " + resource.Name,
ModulePath: path,
Pos: resource.Pos.Line,
IgnoreComments: []data.IgnoreComment{},
FilePath: resource.Pos.Filename,
}
}
// Load variables
for _, variable := range moduleConfig.Variables {
resources["variable "+variable.Type+variable.Name] = data.TerraformCodeBlock{
Name: "variable " + variable.Type + " " + variable.Name,
ModulePath: path,
Pos: variable.Pos.Line,
WhitelistComments: []data.WhitelistComment{},
FilePath: variable.Pos.Filename,
Name: "variable " + variable.Type + " " + variable.Name,
ModulePath: path,
Pos: variable.Pos.Line,
IgnoreComments: []data.IgnoreComment{},
FilePath: variable.Pos.Filename,
}
}
// Load outputs
for _, output := range moduleConfig.Outputs {
resources["output"+output.Name] = data.TerraformCodeBlock{
Name: "output " + output.Name,
ModulePath: path,
Pos: output.Pos.Line,
WhitelistComments: []data.WhitelistComment{},
FilePath: output.Pos.Filename,
Name: "output " + output.Name,
ModulePath: path,
Pos: output.Pos.Line,
IgnoreComments: []data.IgnoreComment{},
FilePath: output.Pos.Filename,
}
}
// Assemble the module object
Expand Down
133 changes: 133 additions & 0 deletions helpers/ignore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package helpers

import (
"bufio"
"os"
"path/filepath"
"regexp"
"strings"

"github.com/padok-team/guacamole/data"
"github.com/spf13/viper"
)

func GetIgnoreingComments(path string) ([]data.IgnoreComment, error) {
ignoreComments := []data.IgnoreComment{}
// Parse the file to get ignore comments
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
// Read the file and find comments containing guacamole-ignore
scanner := bufio.NewScanner(file)
i := 1 //Set cursor to the start of the file
for scanner.Scan() {
line := scanner.Text()
if strings.Contains(line, "guacamole-ignore") {
ignoreComment := data.IgnoreComment{}
// Regex to match the check ID in the form of TF/TG_XXX_0XX
regexp := regexp.MustCompile(`(T[F|G]_(\w+)_\d+)`)
match := regexp.FindStringSubmatch(line)
if len(match) > 0 {
ignoreComment.CheckID = match[0]
ignoreComment.LineNumber = i
ignoreComment.Path = path

// Attach comment to an object
}
ignoreComments = append(ignoreComments, ignoreComment)
}
i++
}
return ignoreComments, nil
}

// Get a list of ignoreing in the .guacamoleignore file
// To enable ignoreing of providers
// The format of the file should be: path of the module - check ID

func GetIgnoreingInFile() ([]data.IgnoreModule, error) {
ignorefile := viper.GetString("guacamoleignore-path")
ignore := []data.IgnoreModule{}
// Parse the file to get ignore comments
file, err := os.Open(ignorefile)
if err != nil {
return nil, err
}
defer file.Close()
// Read the file and find comments lines module path and check ID
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
parts := strings.Fields(line)
path := parts[0]
id := parts[1]
// Split by commers to get multiple checks
for _, id := range strings.Split(id, ",") {
// Add check to validate the format of the check ID and the path
ignoreModule := data.IgnoreModule{}
ignoreModule.CheckID = id
ignoreModule.ModulePath = path
ignore = append(ignore, ignoreModule)
}
}
return ignore, nil
}

// AssociateIgnoreingComments associates the ignoreing comments to a resource (Resource, Data, Variable or Output)
// Not provider config
func AssociateIgnoreingComments(ignoreCommentOfFile []data.IgnoreComment, keys []string, resourcesInFile map[string]data.TerraformCodeBlock, modules map[string]data.TerraformModule, path string) {
for _, ignoreingComment := range ignoreCommentOfFile {
previousPos := 0
for _, i := range keys {
// Find closest code block within the file
if previousPos < ignoreingComment.LineNumber && ignoreingComment.LineNumber < resourcesInFile[i].Pos {
r := modules[filepath.Dir(path)].Resources[i]
r.IgnoreComments = append(r.IgnoreComments, ignoreingComment)
modules[filepath.Dir(path)].Resources[i] = r
break
}
previousPos = resourcesInFile[i].Pos
}
}
}

func AssociateIgnoreingCommentsOnModule(ignoreOnModule []data.IgnoreModule, path string, modules map[string]data.TerraformModule) {
for _, ignore := range ignoreOnModule {
if ignore.ModulePath == path {
module := modules[path]
module.Ignore = append(module.Ignore, ignore)
modules[path] = module
}
}
}

// Remove the check from the list if it is ignoreed in the code
func ApplyIgnoreOnCodeBlock(checks data.Check, indexOfCheckedcheck int, modules map[string]data.TerraformModule) (data.Check, error) {
for _, module := range modules {
for _, resource := range module.Resources {
for _, ignore := range resource.IgnoreComments {
if strings.Contains(checks.Errors[indexOfCheckedcheck].Path, resource.FilePath) && checks.Errors[indexOfCheckedcheck].LineNumber == resource.Pos && checks.ID == ignore.CheckID {
checks.Errors = append(checks.Errors[:indexOfCheckedcheck], checks.Errors[indexOfCheckedcheck+1:]...)
return checks, nil
}
}
}
}
return checks, nil
}

// Remove the check from the list if it is ignoreed at the module level
func ApplyIgnoreOnModule(checks data.Check, indexOfCheckedcheck int, modules map[string]data.TerraformModule) (data.Check, error) {
for _, module := range modules {
for _, ignore := range module.Ignore {
// If check ID and path match, remove the check from the list
if checks.ID == ignore.CheckID && strings.Contains(checks.Errors[indexOfCheckedcheck].Path, ignore.ModulePath) {
checks.Errors = append(checks.Errors[:indexOfCheckedcheck], checks.Errors[indexOfCheckedcheck+1:]...)
return checks, nil
}
}
}
return checks, nil
}
Loading

0 comments on commit 6e3c815

Please sign in to comment.