diffy

package module
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 2, 2025 License: MIT Imports: 15 Imported by: 0

README

diffy Go Reference

Diffy is a terraform schema validation tool that identifies missing required and optional properties in your configurations.

Installation

go get github.com/dkooll/diffy

Usage

as a local test with a relative path:

func TestTerraformSchemaValidation(t *testing.T) {
	findings, err := diffy.ValidateSchema(
		diffy.WithTerraformRoot("../module"),
		func(opts *diffy.SchemaValidatorOptions) {
			opts.Silent = true
		},
	)
	if err != nil {
		t.Fatalf("Validation failed: %v", err)
	}
	for _, finding := range findings {
		t.Logf("%s", diffy.FormatFinding(finding))
	}
	if len(findings) > 0 {
		t.Errorf("Found %d missing properties/blocks in root or submodules. See logs above.", len(findings))
	}
}

within github actions:

func TestTerraformSchemaValidation(t *testing.T) {
	findings, err := diffy.ValidateSchema(
		diffy.WithGitHubIssueCreation(),
		func(opts *diffy.SchemaValidatorOptions) {
			opts.Silent = true
		},
	)
	if err != nil {
		t.Fatalf("Validation failed: %v", err)
	}
	for _, finding := range findings {
		t.Logf("%s", diffy.FormatFinding(finding))
	}
	if len(findings) > 0 {
		t.Errorf("Found %d missing properties/blocks in root or submodules. See logs above.", len(findings))
	}
}
- name: Run schema tests
  working-directory: called/tests
  run: |
    go test -v -run TestTerraformSchemaValidation diffy_test.go
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    TERRAFORM_ROOT: "${{ github.workspace }}/caller"

Features

Automatically validates terraform resources against their provider schemas.

Recursively validates all submodules.

Optionally creates or updates gitHub issues with validation findings.

Designed to work seamlessly in CI/CD workflows with environment variable support.

Respects terraform lifecycle blocks and ignore_changes settings.

Properly handles nested dynamic blocks in your terraform configurations.

Identifies both missing required and optional properties.

Options

Diffy supports a functional options pattern for configuration:

WithTerraformRoot(path): Sets the root directory for Terraform files (defaults to "../../" if not specified)

WithGitHubIssueCreation(): Enables GitHub issue creation based on validation findings

Contributors

We welcome contributions from the community! Whether it's reporting a bug, suggesting a new feature, or submitting a pull request, your input is highly valued.

Notes

The TERRAFORM_ROOT environment variable takes highest priority if set.

A path must be specified either through the TERRAFORM_ROOT environment variable or via WithTerraformRoot() option.

GitHub issue creation requires a GITHUB_TOKEN environment variable.

This approach supports both local testing and CI/CD environments with the same code.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BoolToStr

func BoolToStr(cond bool, yes, no string) string

BoolToStr converts a boolean to a string representation

func FormatFinding

func FormatFinding(f ValidationFinding) string

FormatFinding formats a validation finding as a string

func NormalizeSource

func NormalizeSource(source string) string

NormalizeSource normalizes a provider source

Types

type Block

type Block struct {
	Type   string
	Labels []string
	Body   *Body
}

Block represents a generic HCL block

type BlockData

type BlockData struct {
	Properties    map[string]bool
	StaticBlocks  map[string]*ParsedBlock
	DynamicBlocks map[string]*ParsedBlock
	IgnoreChanges []string
}

BlockData contains the parsed data from a block

func NewBlockData

func NewBlockData() BlockData

NewBlockData creates a new empty BlockData

func (*BlockData) ParseAttributes

func (bd *BlockData) ParseAttributes(body *hclsyntax.Body)

ParseAttributes extracts attributes from a hclsyntax.Body

func (*BlockData) ParseBlocks

func (bd *BlockData) ParseBlocks(body *hclsyntax.Body)

ParseBlocks processes all blocks in a hclsyntax.Body

func (*BlockData) ParseSyntaxAttributes

func (bd *BlockData) ParseSyntaxAttributes(body *hclsyntax.Body)

ParseSyntaxAttributes extracts attributes from a hclsyntax.Body

func (*BlockData) ParseSyntaxBlocks

func (bd *BlockData) ParseSyntaxBlocks(body *hclsyntax.Body)

ParseSyntaxBlocks processes all blocks in a hclsyntax.Body

func (*BlockData) Validate

func (bd *BlockData) Validate(
	resourceType, path string,
	schema *SchemaBlock,
	parentIgnore []string,
	findings *[]ValidationFinding,
)

Validate recursively validates a block against its schema

type BlockProcessor

type BlockProcessor interface {
	ParseAttributes(body *Body)
	ParseBlocks(body *Body)
	Validate(resourceType, path string, schema *SchemaBlock, parentIgnore []string, findings *[]ValidationFinding)
}

BlockProcessor interface defines methods for processing HCL blocks

type Body

type Body struct {
	Attributes map[string]any
	Blocks     []*Block
}

Body represents a generic HCL body interface This is a simplified interface for the example, in real use you'd use the actual HCL types from hashicorp/hcl

type DefaultHCLParser

type DefaultHCLParser struct{}

DefaultHCLParser implements HCLParser

func NewHCLParser

func NewHCLParser() *DefaultHCLParser

NewHCLParser creates a new HCL parser

func (*DefaultHCLParser) ParseMainFile

func (p *DefaultHCLParser) ParseMainFile(ctx context.Context, filename string) ([]ParsedResource, []ParsedDataSource, error)

ParseMainFile parses a main.tf file to extract resources and data sources

func (*DefaultHCLParser) ParseProviderRequirements

func (p *DefaultHCLParser) ParseProviderRequirements(ctx context.Context, filename string) (map[string]ProviderConfig, error)

ParseProviderRequirements parses provider requirements from a terraform.tf file

type DefaultSchemaValidator

type DefaultSchemaValidator struct {
	// contains filtered or unexported fields
}

DefaultSchemaValidator implements SchemaValidator

func NewSchemaValidator

func NewSchemaValidator(logger Logger) *DefaultSchemaValidator

NewSchemaValidator creates a new schema validator

func (*DefaultSchemaValidator) ValidateDataSources

func (v *DefaultSchemaValidator) ValidateDataSources(
	dataSources []ParsedDataSource,
	schema TerraformSchema,
	providers map[string]ProviderConfig,
	dir, submoduleName string,
) []ValidationFinding

ValidateDataSources validates data sources against a schema

func (*DefaultSchemaValidator) ValidateResources

func (v *DefaultSchemaValidator) ValidateResources(
	resources []ParsedResource,
	schema TerraformSchema,
	providers map[string]ProviderConfig,
	dir, submoduleName string,
) []ValidationFinding

ValidateResources validates resources against a schema

type DefaultTerraformRunner

type DefaultTerraformRunner struct{}

DefaultTerraformRunner implements TerraformRunner

func NewTerraformRunner

func NewTerraformRunner() *DefaultTerraformRunner

NewTerraformRunner creates a new Terraform runner

func (*DefaultTerraformRunner) GetSchema

GetSchema gets the provider schema using terraform providers schema

func (*DefaultTerraformRunner) Init

Init runs terraform init in the specified directory

type GitHubIssueManager

type GitHubIssueManager struct {
	RepoOwner string
	RepoName  string
	Token     string
	Client    *http.Client
}

GitHubIssueManager implements IssueManager for GitHub

func NewGitHubIssueManager

func NewGitHubIssueManager(repoOwner, repoName, token string) *GitHubIssueManager

NewGitHubIssueManager creates a new GitHub issue manager

func (*GitHubIssueManager) CreateOrUpdateIssue

func (g *GitHubIssueManager) CreateOrUpdateIssue(ctx context.Context, findings []ValidationFinding) error

CreateOrUpdateIssue creates or updates a GitHub issue with validation findings

type GitRepoInfo

type GitRepoInfo struct {
	TerraformRoot string
}

GitRepoInfo implements RepositoryInfoProvider for Git repositories

func NewGitRepoInfo

func NewGitRepoInfo(terraformRoot string) *GitRepoInfo

NewGitRepoInfo creates a new Git repository info provider

func (*GitRepoInfo) GetRepoInfo

func (g *GitRepoInfo) GetRepoInfo() (owner, repo string)

GetRepoInfo extracts repository information from environment variables

type HCLParser

type HCLParser interface {
	ParseProviderRequirements(ctx context.Context, filename string) (map[string]ProviderConfig, error)
	ParseMainFile(ctx context.Context, filename string) ([]ParsedResource, []ParsedDataSource, error)
}

HCLParser parses Terraform HCL files

type IssueManager

type IssueManager interface {
	CreateOrUpdateIssue(ctx context.Context, findings []ValidationFinding) error
}

IssueManager creates or updates issues based on validation findings

type Logger

type Logger interface {
	Logf(format string, args ...any)
}

Logger provides logging capabilities

type ParsedBlock

type ParsedBlock struct {
	Data BlockData
}

ParsedBlock represents a parsed block

func ParseSyntaxBody

func ParseSyntaxBody(body *hclsyntax.Body) *ParsedBlock

ParseSyntaxBody parses a hclsyntax.Body into a ParsedBlock

func ParseSyntaxBodyFromParser

func ParseSyntaxBodyFromParser(body *hclsyntax.Body) *ParsedBlock

ParseSyntaxBodyFromParser is a wrapper that calls the parser's ParseSyntaxBody

type ParsedDataSource

type ParsedDataSource struct {
	Type string
	Name string
	Data BlockData
}

ParsedDataSource represents a parsed Terraform data source

type ParsedResource

type ParsedResource struct {
	Type string
	Name string
	Data BlockData
}

ParsedResource represents a parsed Terraform resource

type ProviderConfig

type ProviderConfig struct {
	Source  string
	Version string
}

ProviderConfig defines configuration for a provider

type ProviderSchema

type ProviderSchema struct {
	ResourceSchemas   map[string]*ResourceSchema `json:"resource_schemas"`
	DataSourceSchemas map[string]*ResourceSchema `json:"data_source_schemas"`
}

ProviderSchema contains schemas for resources and data sources

type RepositoryInfoProvider

type RepositoryInfoProvider interface {
	GetRepoInfo() (owner, name string)
}

RepositoryInfoProvider provides information about the repository

type ResourceSchema

type ResourceSchema struct {
	Block *SchemaBlock `json:"block"`
}

ResourceSchema defines the schema for a resource or data source

type SchemaAttribute

type SchemaAttribute struct {
	Required bool `json:"required"`
	Optional bool `json:"optional"`
	Computed bool `json:"computed"`
}

SchemaAttribute defines an attribute in a schema

type SchemaBlock

type SchemaBlock struct {
	Attributes map[string]*SchemaAttribute `json:"attributes"`
	BlockTypes map[string]*SchemaBlockType `json:"block_types"`
}

SchemaBlock defines the structure of a block in a schema

type SchemaBlockType

type SchemaBlockType struct {
	Nesting  string       `json:"nesting"`
	MinItems int          `json:"min_items"`
	MaxItems int          `json:"max_items"`
	Block    *SchemaBlock `json:"block"`
}

SchemaBlockType defines a nested block type

type SchemaValidator

type SchemaValidator interface {
	ValidateResources(resources []ParsedResource, schema TerraformSchema, providers map[string]ProviderConfig, dir, submoduleName string) []ValidationFinding
	ValidateDataSources(dataSources []ParsedDataSource, schema TerraformSchema, providers map[string]ProviderConfig, dir, submoduleName string) []ValidationFinding
}

SchemaValidator validates resources against their schema

type SchemaValidatorOption

type SchemaValidatorOption func(*SchemaValidatorOptions)

SchemaValidatorOption is a function that configures SchemaValidatorOptions

func WithGitHubIssueCreation

func WithGitHubIssueCreation() SchemaValidatorOption

WithGitHubIssueCreation enables GitHub issue creation with token

func WithTerraformRoot

func WithTerraformRoot(path string) SchemaValidatorOption

WithTerraformRoot sets the root directory for Terraform files

type SchemaValidatorOptions

type SchemaValidatorOptions struct {
	TerraformRoot     string
	CreateGitHubIssue bool
	Logger            Logger
	GitHubToken       string
	GitHubOwner       string
	GitHubRepo        string
	Silent            bool
}

SchemaValidatorOptions contains options for schema validation

type SimpleLogger

type SimpleLogger struct{}

SimpleLogger is a basic implementation of the Logger interface

func (*SimpleLogger) Logf

func (l *SimpleLogger) Logf(format string, args ...any)

Logf implements the Logger interface

type SubModule

type SubModule struct {
	Name string
	Path string
}

SubModule represents a Terraform submodule

func FindSubmodules

func FindSubmodules(modulesDir string) ([]SubModule, error)

FindSubmodules finds submodules in a directory

type TerraformRunner

type TerraformRunner interface {
	Init(ctx context.Context, dir string) error
	GetSchema(ctx context.Context, dir string) (*TerraformSchema, error)
}

TerraformRunner runs Terraform commands

type TerraformSchema

type TerraformSchema struct {
	ProviderSchemas map[string]*ProviderSchema `json:"provider_schemas"`
}

TerraformSchema represents the schema for Terraform providers

type ValidationFinding

type ValidationFinding struct {
	ResourceType  string
	Path          string // e.g., "root" or "root.some_nested_block"
	Name          string
	Required      bool
	IsBlock       bool
	IsDataSource  bool   // If true, this is a data source, not a resource
	SubmoduleName string // empty => root, else submodule name
}

ValidationFinding represents a finding during validation

func DeduplicateFindings

func DeduplicateFindings(findings []ValidationFinding) []ValidationFinding

DeduplicateFindings removes duplicate findings

func ValidateSchema

func ValidateSchema(options ...SchemaValidatorOption) ([]ValidationFinding, error)

ValidateSchema validates Terraform schema with the specified options

func ValidateTerraformProject

func ValidateTerraformProject(logger Logger, terraformRoot string) ([]ValidationFinding, error)

ValidateTerraformProject validates an entire Terraform project including submodules

func ValidateTerraformSchema

func ValidateTerraformSchema(logger Logger, dir, submoduleName string, parser HCLParser, runner TerraformRunner) ([]ValidationFinding, error)

ValidateTerraformSchema validates a directory against Terraform schema

func ValidateTerraformSchemaInDirectory

func ValidateTerraformSchemaInDirectory(logger Logger, dir, submoduleName string) ([]ValidationFinding, error)

ValidateTerraformSchemaInDirectory validates the Terraform schema in a directory

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL