terraparse

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jul 10, 2023 License: MPL-2.0 Imports: 23 Imported by: 0

README

terraparse

This repository contains a helper library for extracting high-level metadata about Terraform modules from their source code. It processes only a subset of the information Terraform itself would process; in return, it can be broadly compatible with modules written for many different versions of Terraform.

I forked this project from the Hashicorp project terraform-config-inspect.

$ go get github.com/yardbirdsax/terraparse

Usage

import "github.com/yardbirdsax/terraparse/"

// ...

module, diags := terraparse.LoadModule(dir)

// ...

Due to the Terraform v1.0 Compatibility Promises, this library should be able to parse Terraform configurations written in the language defined by Terraform v1.0. However, it may not immediately expose new additions to the language added during the v1.x series.

This library can also interpret valid Terraform configurations targeting Terraform v0.10 through v0.15, although the level of detail returned may be lower in older language versions.

Command Line Tool

The primary way to use this repository is as a Go library. As a convenience, it also contains a CLI tool called terraparse that allows viewing module information in either a Markdown-like format or JSON format. You can install the tool by running go install github.com/yardbirdsax/terraparse/cmd/terraparse.

$ terraparse path/to/module
# Module `path/to/module`

Provider Requirements:
* **null:** (any version)

## Input Variables
* `a` (default `"a default"`)
* `b` (required): The b variable

## Output Values
* `a`
* `b`: I am B

## Managed Resources
* `null_resource.a` from `null`
* `null_resource.b` from `null`
$ terraform-config-inspect --json path/to/module
{
  "path": "path/to/module",
  "variables": {
    "A": {
      "name": "A",
      "default": "A default",
      "pos": {
        "filename": "path/to/module/basics.tf",
        "line": 1
      }
    },
    "B": {
      "name": "B",
      "description": "The B variable",
      "pos": {
        "filename": "path/to/module/basics.tf",
        "line": 5
      }
    }
  },
  "outputs": {
    "A": {
      "name": "A",
      "pos": {
        "filename": "path/to/module/basics.tf",
        "line": 9
      }
    },
    "B": {
      "name": "B",
      "description": "I am B",
      "pos": {
        "filename": "path/to/module/basics.tf",
        "line": 13
      }
    }
  },
  "required_providers": {
    "null": []
  },
  "managed_resources": {
    "null_resource.A": {
      "mode": "managed",
      "type": "null_resource",
      "name": "A",
      "provider": {
        "name": "null"
      },
      "pos": {
        "filename": "path/to/module/basics.tf",
        "line": 18
      }
    },
    "null_resource.B": {
      "mode": "managed",
      "type": "null_resource",
      "name": "B",
      "provider": {
        "name": "null"
      },
      "pos": {
        "filename": "path/to/module/basics.tf",
        "line": 19
      }
    }
  },
  "data_resources": {},
  "module_calls": {}
}

Contributing

As with its upstream inspiration, this project allows parsing a limited set of Terraform dialects. While I will try to keep it in sync with additions or changes to that, there is no expectation that I will do this on any particular schedule.

Bug fixes are welcome so long as they include test coverage proving they work. If you would like to contribute an enhancement or extend the language support of this library, I would suggest opening an issue first for discussion.

Documentation

Overview

Package terraparse is a helper library that does careful, shallow parsing of Terraform modules to provide access to high-level metadata while remaining broadly compatible with configurations targeting various different Terraform versions.

This packge focuses on describing top-level objects only, and in particular does not attempt any sort of processing that would require access to plugins. Currently it allows callers to extract high-level information about variables, outputs, resource blocks, provider dependencies, and Terraform Core dependencies.

This package only works at the level of single modules. A full configuration is a tree of potentially several modules, some of which may be references to remote packages. There are some basic helpers for traversing calls to modules at relative local paths, however.

This package employs a "best effort" parsing strategy, producing as complete a result as possible even though the input may not be entirely valid. The intended use-case is high-level analysis and indexing of externally-facing module characteristics, as opposed to validating or even applying the module.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func IsModuleDir

func IsModuleDir(dir string) bool

IsModuleDir checks if the given path contains terraform configuration files. This allows the caller to decide how to handle directories that do not have tf files.

func IsModuleDirOnFilesystem

func IsModuleDirOnFilesystem(fs FS, dir string) bool

IsModuleDirOnFilesystem checks if the given path in the given FS contains Terraform configuration files. This allows the caller to decide how to handle directories that do not have tf files.

func LoadModule

func LoadModule(dir string) (*Module, Diagnostics)

LoadModule reads the directory at the given path and attempts to interpret it as a Terraform module.

Example (Modulecalls)
got, err := LoadModule("testdata/module-calls")
if err != nil {
	fmt.Printf("error loading file: %v", err)
}
if len(got.ModuleCalls) > 0 {
	fmt.Println("Module calls:\n------")
	sortedKeys := sortModuleMapKeys(got.ModuleCalls)
	for _, k := range sortedKeys {
		if mc, ok := got.ModuleCalls[k]; ok {
			fmt.Printf("%s\n", mc.Name)
			fmt.Printf("\tSource: %s\n", mc.Source)
			fmt.Println("\tAttributes:")
			sortedAttributeKeys := sortModuleMapKeys(mc.Attributes)
			for _, ak := range sortedAttributeKeys {
				if a, ok := mc.Attributes[ak]; ok {
					fmt.Printf("\t\t%s: %s\n", a.Name, a.Value.GoString())
				}
			}
			fmt.Println("---")
		}
	}
}
Output:

Module calls:
------
bar
	Source: ./child
	Attributes:
		unused: cty.NumberIntVal(1)
---
baz
	Source: ../elsewhere
	Attributes:
		unused: cty.NumberIntVal(12)
---
foo
	Source: foo/bar/baz
	Attributes:
		id: cty.StringVal("data.external.something.result.id")
		something: cty.StringVal("var.something")
		something_else: cty.StringVal("${var.something}-2")
		unused: cty.NumberIntVal(2)
---

func LoadModuleFromFile

func LoadModuleFromFile(file *hcl.File, mod *Module) hcl.Diagnostics

LoadModuleFromFile reads given file, interprets it and stores in given Module This is useful for any caller which does tokenization/parsing on its own e.g. because it will reuse these parsed files later for more detailed interpretation.

func LoadModuleFromFilesystem

func LoadModuleFromFilesystem(fs FS, dir string) (*Module, Diagnostics)

LoadModuleFromFilesystem reads the directory at the given path in the given FS and attempts to interpret it as a Terraform module

func RenderMarkdown

func RenderMarkdown(w io.Writer, module *Module) error

Types

type Attribute

type Attribute struct {
	*hcl.Attribute
	// Value is a calculated field whose value depends upon the underlying HCL attribute's
	// definition. If it is a static value (e.g., `foo = "bar"`), then `Value` will be that
	// static value. If it is a calculated value (e.g., `data.something.something.id`,
	// `resource.something.id`, `module.something.id`, or `function(something)`), then the
	// text of that calculated value is the value of `Value`.
	Value cty.Value
}

Attribute represents a single HCL attribute attached to a block. This struct includes the raw attribute as well as a calculated field (Value) whose value depends upon the attribute's definition in the raw HCL. See the field documentation for details.

type Attributes

type Attributes map[string]*Attribute

Attributes is a map of Attribute objects by the name of the attribute.

func NewAttributes

func NewAttributes(attrs hcl.Attributes, file *hcl.File) (mas Attributes, diags hcl.Diagnostics)

NewAttributes contructs as map of Attributes from the raw HCL attributes.

func NewAttributesFromBody

func NewAttributesFromBody(body hcl.Body, file *hcl.File) (mas Attributes, diags hcl.Diagnostics)

NewAttributesFromBody constructs a map of Attributes from an HCL body object.

func (Attributes) MarshalJSON

func (ma Attributes) MarshalJSON() (data []byte, err error)

type DiagSeverity

type DiagSeverity rune

DiagSeverity describes the severity of a Diagnostic.

const DiagError DiagSeverity = 'E'

DiagError indicates a problem that prevented proper processing of the configuration. In the precense of DiagError diagnostics the result is likely to be incomplete.

const DiagWarning DiagSeverity = 'W'

DiagWarning indicates a problem that the user may wish to consider but that did not prevent proper processing of the configuration.

func (DiagSeverity) MarshalJSON

func (s DiagSeverity) MarshalJSON() ([]byte, error)

MarshalJSON is an implementation of encoding/json.Marshaler

type Diagnostic

type Diagnostic struct {
	Severity DiagSeverity `json:"severity"`
	Summary  string       `json:"summary"`
	Detail   string       `json:"detail,omitempty"`

	// Pos is not populated for all diagnostics, but when populated should
	// indicate a particular line that the described problem relates to.
	Pos *SourcePos `json:"pos,omitempty"`
}

Diagnostic describes a problem (error or warning) encountered during configuration loading.

type Diagnostics

type Diagnostics []Diagnostic

Diagnostics represents a sequence of diagnostics. This is the type that should be returned from a function that might generate diagnostics.

func (Diagnostics) Err

func (diags Diagnostics) Err() error

Err returns an error representing the receiver if the receiver HasErrors, or nil otherwise.

The returned error can be type-asserted back to a Diagnostics if needed.

func (Diagnostics) Error

func (diags Diagnostics) Error() string

func (Diagnostics) HasErrors

func (diags Diagnostics) HasErrors() bool

HasErrors returns true if there is at least one Diagnostic of severity DiagError in the receiever.

If a function returns a Diagnostics without errors then the result can be assumed to be complete within the "best effort" constraints of this library. If errors are present then the caller may wish to employ more caution in relying on the result.

type FS

type FS interface {
	Open(name string) (File, error)
	ReadFile(name string) ([]byte, error)
	ReadDir(dirname string) ([]os.FileInfo, error)
}

FS is an interface used by LoadModuleFromFilesystem.

Unfortunately this package implemented a draft version of the io/fs.FS API before it was finalized and so this interface is not compatible with the final design. To use this package with the final filesystem API design, use WrapFS to wrap a standard filesystem implementation so that it implements this interface.

func NewOsFs

func NewOsFs() FS

NewOsFs provides a basic implementation of FS for an OS filesystem

func WrapFS

func WrapFS(wrapped fs.FS) FS

WrapFS wraps a standard library filesystem implementation so that it implements this package's own (slightly-incompatible) interface FS.

type File

type File interface {
	Stat() (os.FileInfo, error)
	Read([]byte) (int, error)
	Close() error
}

File represents an open file in FS.

type Module

type Module struct {
	// Path is the local filesystem directory where the module was loaded from.
	Path string `json:"path"`

	Variables map[string]*Variable `json:"variables"`
	Outputs   map[string]*Output   `json:"outputs"`

	RequiredCore      []string                        `json:"required_core,omitempty"`
	RequiredProviders map[string]*ProviderRequirement `json:"required_providers"`

	ProviderConfigs  map[string]*ProviderConfig `json:"provider_configs,omitempty"`
	ManagedResources map[string]*Resource       `json:"managed_resources"`
	DataResources    map[string]*Resource       `json:"data_resources"`
	ModuleCalls      map[string]*ModuleCall     `json:"module_calls"`

	// Diagnostics records any errors and warnings that were detected during
	// loading, primarily for inclusion in serialized forms of the module
	// since this slice is also returned as a second argument from LoadModule.
	Diagnostics Diagnostics `json:"diagnostics,omitempty"`
}

Module is the top-level type representing a parsed and processed Terraform module.

func NewModule

func NewModule(path string) *Module

NewModule creates new Module representing Terraform module at the given path

type ModuleCall

type ModuleCall struct {
	Name       string     `json:"name"`
	Source     string     `json:"source"`
	Version    string     `json:"version,omitempty"`
	Attributes Attributes `json:"attributes,omitempty"`

	Pos SourcePos `json:"pos"`
}

ModuleCall represents a "module" block within a module. That is, a declaration of a child module from inside its parent.

type Output

type Output struct {
	Name        string    `json:"name"`
	Description string    `json:"description,omitempty"`
	Sensitive   bool      `json:"sensitive,omitempty"`
	Pos         SourcePos `json:"pos"`
}

Output represents a single output from a Terraform module.

type ProviderConfig

type ProviderConfig struct {
	Name  string `json:"name"`
	Alias string `json:"alias,omitempty"`
}

ProviderConfig represents a provider block in the configuration

type ProviderRef

type ProviderRef struct {
	Name  string `json:"name"`
	Alias string `json:"alias,omitempty"` // Empty if the default provider configuration is referenced
}

ProviderRef is a reference to a provider configuration within a module. It represents the contents of a "provider" argument in a resource, or a value in the "providers" map for a module call.

type ProviderRequirement

type ProviderRequirement struct {
	Source               string        `json:"source,omitempty"`
	VersionConstraints   []string      `json:"version_constraints,omitempty"`
	ConfigurationAliases []ProviderRef `json:"aliases,omitempty"`
}

type Resource

type Resource struct {
	Mode       ResourceMode `json:"mode"`
	Type       string       `json:"type"`
	Name       string       `json:"name"`
	Attributes Attributes   `json:"attributes,omitempty"`

	Provider ProviderRef `json:"provider"`

	Pos SourcePos `json:"pos"`
}

Resource represents a single "resource" or "data" block within a module.

func (*Resource) MapKey

func (r *Resource) MapKey() string

MapKey returns a string that can be used to uniquely identify the receiver in a map[string]*Resource.

type ResourceMode

type ResourceMode rune

ResourceMode represents the "mode" of a resource, which is used to distinguish between managed resources ("resource" blocks in config) and data resources ("data" blocks in config).

const DataResourceMode ResourceMode = 'D'
const InvalidResourceMode ResourceMode = 0
const ManagedResourceMode ResourceMode = 'M'

func (ResourceMode) MarshalJSON

func (m ResourceMode) MarshalJSON() ([]byte, error)

MarshalJSON implements encoding/json.Marshaler.

func (ResourceMode) String

func (m ResourceMode) String() string

type SourcePos

type SourcePos struct {
	Filename string `json:"filename"`
	Line     int    `json:"line"`
}

SourcePos is a pointer to a particular location in a source file.

This type is embedded into other structs to allow callers to locate the definition of each described module element. The SourcePos of an element is usually the first line of its definition, although the definition can be a little "fuzzy" with JSON-based config files.

type Variable

type Variable struct {
	Name        string `json:"name"`
	Type        string `json:"type,omitempty"`
	Description string `json:"description,omitempty"`

	// Default is an approximate representation of the default value in
	// the native Go type system. The conversion from the value given in
	// configuration may be slightly lossy. Only values that can be
	// serialized by json.Marshal will be included here.
	Default   interface{} `json:"default"`
	Required  bool        `json:"required"`
	Sensitive bool        `json:"sensitive,omitempty"`

	Pos SourcePos `json:"pos"`
}

Variable represents a single variable from a Terraform module.

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

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