github.com/richardbowden/terraform@v0.6.12-0.20160901200758-30ea22c25211/terraform/semantics.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hashicorp/go-multierror"
     7  	"github.com/hashicorp/terraform/config"
     8  	"github.com/hashicorp/terraform/dag"
     9  )
    10  
    11  // GraphSemanticChecker is the interface that semantic checks across
    12  // the entire Terraform graph implement.
    13  //
    14  // The graph should NOT be modified by the semantic checker.
    15  type GraphSemanticChecker interface {
    16  	Check(*dag.Graph) error
    17  }
    18  
    19  // UnorderedSemanticCheckRunner is an implementation of GraphSemanticChecker
    20  // that runs a list of SemanticCheckers against the vertices of the graph
    21  // in no specified order.
    22  type UnorderedSemanticCheckRunner struct {
    23  	Checks []SemanticChecker
    24  }
    25  
    26  func (sc *UnorderedSemanticCheckRunner) Check(g *dag.Graph) error {
    27  	var err error
    28  	for _, v := range g.Vertices() {
    29  		for _, check := range sc.Checks {
    30  			if e := check.Check(g, v); e != nil {
    31  				err = multierror.Append(err, e)
    32  			}
    33  		}
    34  	}
    35  
    36  	return err
    37  }
    38  
    39  // SemanticChecker is the interface that semantic checks across the
    40  // Terraform graph implement. Errors are accumulated. Even after an error
    41  // is returned, child vertices in the graph will still be visited.
    42  //
    43  // The graph should NOT be modified by the semantic checker.
    44  //
    45  // The order in which vertices are visited is left unspecified, so the
    46  // semantic checks should not rely on that.
    47  type SemanticChecker interface {
    48  	Check(*dag.Graph, dag.Vertex) error
    49  }
    50  
    51  // SemanticCheckModulesExist is an implementation of SemanticChecker that
    52  // verifies that all the modules that are referenced in the graph exist.
    53  type SemanticCheckModulesExist struct{}
    54  
    55  // TODO: test
    56  func (*SemanticCheckModulesExist) Check(g *dag.Graph, v dag.Vertex) error {
    57  	mn, ok := v.(*GraphNodeConfigModule)
    58  	if !ok {
    59  		return nil
    60  	}
    61  
    62  	if mn.Tree == nil {
    63  		return fmt.Errorf(
    64  			"module '%s' not found", mn.Module.Name)
    65  	}
    66  
    67  	return nil
    68  }
    69  
    70  // smcUserVariables does all the semantic checks to verify that the
    71  // variables given satisfy the configuration itself.
    72  func smcUserVariables(c *config.Config, vs map[string]interface{}) []error {
    73  	var errs []error
    74  
    75  	cvs := make(map[string]*config.Variable)
    76  	for _, v := range c.Variables {
    77  		cvs[v.Name] = v
    78  	}
    79  
    80  	// Check that all required variables are present
    81  	required := make(map[string]struct{})
    82  	for _, v := range c.Variables {
    83  		if v.Required() {
    84  			required[v.Name] = struct{}{}
    85  		}
    86  	}
    87  	for k, _ := range vs {
    88  		delete(required, k)
    89  	}
    90  	if len(required) > 0 {
    91  		for k, _ := range required {
    92  			errs = append(errs, fmt.Errorf(
    93  				"Required variable not set: %s", k))
    94  		}
    95  	}
    96  
    97  	// Check that types match up
    98  	for name, proposedValue := range vs {
    99  		schema, ok := cvs[name]
   100  		if !ok {
   101  			continue
   102  		}
   103  
   104  		declaredType := schema.Type()
   105  
   106  		switch declaredType {
   107  		case config.VariableTypeString:
   108  			switch proposedValue.(type) {
   109  			case string:
   110  				continue
   111  			default:
   112  				errs = append(errs, fmt.Errorf("variable %s should be type %s, got %s",
   113  					name, declaredType.Printable(), hclTypeName(proposedValue)))
   114  			}
   115  		case config.VariableTypeMap:
   116  			switch proposedValue.(type) {
   117  			case map[string]interface{}:
   118  				continue
   119  			default:
   120  				errs = append(errs, fmt.Errorf("variable %s should be type %s, got %s",
   121  					name, declaredType.Printable(), hclTypeName(proposedValue)))
   122  			}
   123  		case config.VariableTypeList:
   124  			switch proposedValue.(type) {
   125  			case []interface{}:
   126  				continue
   127  			default:
   128  				errs = append(errs, fmt.Errorf("variable %s should be type %s, got %s",
   129  					name, declaredType.Printable(), hclTypeName(proposedValue)))
   130  			}
   131  		default:
   132  			errs = append(errs, fmt.Errorf("variable %s should be type %s, got %s",
   133  				name, declaredType.Printable(), hclTypeName(proposedValue)))
   134  		}
   135  	}
   136  
   137  	// TODO(mitchellh): variables that are unknown
   138  
   139  	return errs
   140  }