Added verify command
This commit is contained in:
parent
819db3469e
commit
db69a2959b
|
@ -21,3 +21,4 @@ website/node_modules
|
||||||
.*.swp
|
.*.swp
|
||||||
.idea
|
.idea
|
||||||
*.test
|
*.test
|
||||||
|
*.iml
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
module "super#module" {
|
||||||
|
}
|
||||||
|
|
||||||
|
module "super" {
|
||||||
|
source = "${var.modulename}"
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
variable "otherresourcename" {
|
||||||
|
default = "aws_instance.web1"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "vairable_with_interpolation" {
|
||||||
|
default = "${var.otherresourcename}"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_instance" "web" {
|
||||||
|
depends_on = ["${var.otherresourcename}}"]
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
resource "test_instance" "foo" {
|
||||||
|
ami = "bar"
|
||||||
|
|
||||||
|
network_interface {
|
||||||
|
device_index = 0
|
||||||
|
description = "Main network interface ${var.this_is_an_error}"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
resource "test_instance" "foo" {
|
||||||
|
ami = "bar"
|
||||||
|
|
||||||
|
network_interface {
|
||||||
|
device_index = 0
|
||||||
|
name = test
|
||||||
|
description = "Main network interface"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
resource "test_instance" "foo" {
|
||||||
|
ami = "bar"
|
||||||
|
|
||||||
|
network_interface {
|
||||||
|
device_index = 0
|
||||||
|
description = "${var.description}"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
module "multi_module" {
|
||||||
|
}
|
||||||
|
|
||||||
|
module "multi_module" {
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
provider "aws" {
|
||||||
|
access_key = "123"
|
||||||
|
secret_key = "233"
|
||||||
|
region = "us-east-1"
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "aws" {
|
||||||
|
access_key = "123"
|
||||||
|
secret_key = "233"
|
||||||
|
region = "us-east-1"
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
resource "aws_instance" "web" {
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_instance" "web" {
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
output "myvalue" {
|
||||||
|
values = "Some value"
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
resource "test_instance" "foo" {
|
||||||
|
ami = "bar"
|
||||||
|
|
||||||
|
# This is here because at some point it caused a test failure
|
||||||
|
network_interface {
|
||||||
|
device_index = 0
|
||||||
|
description = "Main network interface"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/hashicorp/terraform/config"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ValidateCommand is a Command implementation that validates the terraform files
|
||||||
|
type ValidateCommand struct {
|
||||||
|
Meta
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultPath = "."
|
||||||
|
|
||||||
|
func (c *ValidateCommand) Help() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ValidateCommand) Run(args []string) int {
|
||||||
|
args = c.Meta.process(args, false)
|
||||||
|
var dirPath string
|
||||||
|
|
||||||
|
if len(args) == 1 {
|
||||||
|
dirPath = args[0]
|
||||||
|
} else {
|
||||||
|
dirPath = "."
|
||||||
|
}
|
||||||
|
dir, err := filepath.Abs(dirPath)
|
||||||
|
if err != nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf(
|
||||||
|
"Unable to locate directory %v\n", err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
rtnCode := c.validate(dir)
|
||||||
|
|
||||||
|
return rtnCode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ValidateCommand) Synopsis() string {
|
||||||
|
return "Validates the Terraform files"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ValidateCommand) validate(dir string) int {
|
||||||
|
cfg, err := config.LoadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf(
|
||||||
|
"Error loading files %v\n", err.Error()))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
err = cfg.Validate()
|
||||||
|
if err != nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf(
|
||||||
|
"Error validating: %v\n", err.Error()))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mitchellh/cli"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setupTest(fixturepath string) (*cli.MockUi, int) {
|
||||||
|
ui := new(cli.MockUi)
|
||||||
|
c := &ValidateCommand{
|
||||||
|
Meta: Meta{
|
||||||
|
Ui: ui,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []string{
|
||||||
|
testFixturePath(fixturepath),
|
||||||
|
}
|
||||||
|
|
||||||
|
code := c.Run(args)
|
||||||
|
return ui, code
|
||||||
|
}
|
||||||
|
func TestValidateCommand(t *testing.T) {
|
||||||
|
if ui, code := setupTest("validate-valid"); code != 0 {
|
||||||
|
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateFailingCommand(t *testing.T) {
|
||||||
|
if ui, code := setupTest("validate-invalid"); code != 1 {
|
||||||
|
t.Fatalf("Should have failed: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateFailingCommandMissingQuote(t *testing.T) {
|
||||||
|
ui, code := setupTest("validate-invalid/missing_quote")
|
||||||
|
|
||||||
|
if code != 1 {
|
||||||
|
t.Fatalf("Should have failed: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(strings.TrimSpace(ui.ErrorWriter.String()), "IDENT test") {
|
||||||
|
t.Fatalf("Should have failed: %d\n\n'%s'", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateFailingCommandMissingVariable(t *testing.T) {
|
||||||
|
ui, code := setupTest("validate-invalid/missing_var")
|
||||||
|
if code != 1 {
|
||||||
|
t.Fatalf("Should have failed: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(strings.TrimSpace(ui.ErrorWriter.String()), "config: unknown variable referenced: 'description'. define it with 'variable' blocks") {
|
||||||
|
t.Fatalf("Should have failed: %d\n\n'%s'", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSameProviderMutipleTimesShouldFail(t *testing.T) {
|
||||||
|
ui, code := setupTest("validate-invalid/multiple_providers")
|
||||||
|
if code != 1 {
|
||||||
|
t.Fatalf("Should have failed: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(strings.TrimSpace(ui.ErrorWriter.String()), "provider.aws: declared multiple times, you can only declare a provider once") {
|
||||||
|
t.Fatalf("Should have failed: %d\n\n'%s'", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSameModuleMultipleTimesShouldFail(t *testing.T) {
|
||||||
|
ui, code := setupTest("validate-invalid/multiple_modules")
|
||||||
|
if code != 1 {
|
||||||
|
t.Fatalf("Should have failed: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(strings.TrimSpace(ui.ErrorWriter.String()), "multi_module: module repeated multiple times") {
|
||||||
|
t.Fatalf("Should have failed: %d\n\n'%s'", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSameResourceMultipleTimesShouldFail(t *testing.T) {
|
||||||
|
ui, code := setupTest("validate-invalid/multiple_resources")
|
||||||
|
if code != 1 {
|
||||||
|
t.Fatalf("Should have failed: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(strings.TrimSpace(ui.ErrorWriter.String()), "aws_instance.web: resource repeated multiple times") {
|
||||||
|
t.Fatalf("Should have failed: %d\n\n'%s'", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOutputWithoutValueShouldFail(t *testing.T) {
|
||||||
|
ui, code := setupTest("validate-invalid/outputs")
|
||||||
|
if code != 1 {
|
||||||
|
t.Fatalf("Should have failed: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(strings.TrimSpace(ui.ErrorWriter.String()), "output is missing required 'value' key") {
|
||||||
|
t.Fatalf("Should have failed: %d\n\n'%s'", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestModuleWithIncorrectNameShouldFail(t *testing.T) {
|
||||||
|
ui, code := setupTest("validate-invalid/incorrectmodulename")
|
||||||
|
if code != 1 {
|
||||||
|
t.Fatalf("Should have failed: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(ui.ErrorWriter.String(), "module name can only contain letters, numbers, dashes, and underscores") {
|
||||||
|
t.Fatalf("Should have failed: %d\n\n'%s'", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
if !strings.Contains(ui.ErrorWriter.String(), "module source cannot contain interpolations") {
|
||||||
|
t.Fatalf("Should have failed: %d\n\n'%s'", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWronglyUsedInterpolationShouldFail(t *testing.T) {
|
||||||
|
ui, code := setupTest("validate-invalid/interpolation")
|
||||||
|
if code != 1 {
|
||||||
|
t.Fatalf("Should have failed: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(ui.ErrorWriter.String(), "depends on value cannot contain interpolations") {
|
||||||
|
t.Fatalf("Should have failed: %d\n\n'%s'", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
if !strings.Contains(ui.ErrorWriter.String(), "Variable 'vairable_with_interpolation': cannot contain interpolations") {
|
||||||
|
t.Fatalf("Should have failed: %d\n\n'%s'", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
}
|
|
@ -110,6 +110,12 @@ func init() {
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"validate": func() (cli.Command, error) {
|
||||||
|
return &command.ValidateCommand{
|
||||||
|
Meta: meta,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
|
||||||
"version": func() (cli.Command, error) {
|
"version": func() (cli.Command, error) {
|
||||||
return &command.VersionCommand{
|
return &command.VersionCommand{
|
||||||
Meta: meta,
|
Meta: meta,
|
||||||
|
|
|
@ -35,6 +35,7 @@ Available commands are:
|
||||||
remote Configure remote state storage
|
remote Configure remote state storage
|
||||||
show Inspect Terraform state or plan
|
show Inspect Terraform state or plan
|
||||||
taint Manually mark a resource for recreation
|
taint Manually mark a resource for recreation
|
||||||
|
validate Validates the Terraform files
|
||||||
version Prints the Terraform version
|
version Prints the Terraform version
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
---
|
||||||
|
layout: "docs"
|
||||||
|
page_title: "Command: validate"
|
||||||
|
sidebar_current: "docs-commands-validate"
|
||||||
|
description: |-
|
||||||
|
The `terraform validate` command is used to validate the format and structure of the terraform files.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Command: verify
|
||||||
|
|
||||||
|
The `terraform validate` command is used to validate the syntax of the terraform files.
|
||||||
|
Terraform performs a syntax check on all the terraform files in the directory, and will display an error if the file(s)
|
||||||
|
doesn't validate.
|
||||||
|
|
||||||
|
These errors include:
|
||||||
|
|
||||||
|
* Interpolation in variable values, depends_on, module source etc.
|
||||||
|
|
||||||
|
* Duplicate names in resource, modules and providers.
|
||||||
|
|
||||||
|
* Missing variable values.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Usage: `terraform validate [dir]`
|
||||||
|
|
||||||
|
By default, `validate` requires no flags and looks in the current directory
|
||||||
|
for the configurations.
|
|
@ -106,6 +106,11 @@
|
||||||
<li<%= sidebar_current("docs-commands-taint") %>>
|
<li<%= sidebar_current("docs-commands-taint") %>>
|
||||||
<a href="/docs/commands/taint.html">taint</a>
|
<a href="/docs/commands/taint.html">taint</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-commands-validate") %>>
|
||||||
|
<a href="/docs/commands/validate.html">validate</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue