Added verify command

This commit is contained in:
Soren Mathiasen 2015-11-05 15:47:08 +01:00
parent 819db3469e
commit db69a2959b
17 changed files with 297 additions and 0 deletions

1
.gitignore vendored
View File

@ -21,3 +21,4 @@ website/node_modules
.*.swp
.idea
*.test
*.iml

View File

@ -0,0 +1,6 @@
module "super#module" {
}
module "super" {
source = "${var.modulename}"
}

View File

@ -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}}"]
}

View File

@ -0,0 +1,8 @@
resource "test_instance" "foo" {
ami = "bar"
network_interface {
device_index = 0
description = "Main network interface ${var.this_is_an_error}"
}
}

View File

@ -0,0 +1,9 @@
resource "test_instance" "foo" {
ami = "bar"
network_interface {
device_index = 0
name = test
description = "Main network interface"
}
}

View File

@ -0,0 +1,8 @@
resource "test_instance" "foo" {
ami = "bar"
network_interface {
device_index = 0
description = "${var.description}"
}
}

View File

@ -0,0 +1,5 @@
module "multi_module" {
}
module "multi_module" {
}

View File

@ -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"
}

View File

@ -0,0 +1,5 @@
resource "aws_instance" "web" {
}
resource "aws_instance" "web" {
}

View File

@ -0,0 +1,3 @@
output "myvalue" {
values = "Some value"
}

View File

@ -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"
}
}

58
command/validate.go Normal file
View File

@ -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
}

123
command/validate_test.go Normal file
View File

@ -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())
}
}

View File

@ -110,6 +110,12 @@ func init() {
}, nil
},
"validate": func() (cli.Command, error) {
return &command.ValidateCommand{
Meta: meta,
}, nil
},
"version": func() (cli.Command, error) {
return &command.VersionCommand{
Meta: meta,

View File

@ -35,6 +35,7 @@ Available commands are:
remote Configure remote state storage
show Inspect Terraform state or plan
taint Manually mark a resource for recreation
validate Validates the Terraform files
version Prints the Terraform version
```

View File

@ -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.

View File

@ -106,6 +106,11 @@
<li<%= sidebar_current("docs-commands-taint") %>>
<a href="/docs/commands/taint.html">taint</a>
</li>
<li<%= sidebar_current("docs-commands-validate") %>>
<a href="/docs/commands/validate.html">validate</a>
</li>
</ul>
</li>