github.com/wikibal01/hashicorp-terraform@v0.11.12-beta1/terraform/eval_output.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "log" 6 7 "github.com/hashicorp/terraform/config" 8 ) 9 10 // EvalDeleteOutput is an EvalNode implementation that deletes an output 11 // from the state. 12 type EvalDeleteOutput struct { 13 Name string 14 } 15 16 // TODO: test 17 func (n *EvalDeleteOutput) Eval(ctx EvalContext) (interface{}, error) { 18 state, lock := ctx.State() 19 if state == nil { 20 return nil, nil 21 } 22 23 // Get a write lock so we can access this instance 24 lock.Lock() 25 defer lock.Unlock() 26 27 // Look for the module state. If we don't have one, create it. 28 mod := state.ModuleByPath(ctx.Path()) 29 if mod == nil { 30 return nil, nil 31 } 32 33 delete(mod.Outputs, n.Name) 34 35 return nil, nil 36 } 37 38 // EvalWriteOutput is an EvalNode implementation that writes the output 39 // for the given name to the current state. 40 type EvalWriteOutput struct { 41 Name string 42 Sensitive bool 43 Value *config.RawConfig 44 // ContinueOnErr allows interpolation to fail during Input 45 ContinueOnErr bool 46 } 47 48 // TODO: test 49 func (n *EvalWriteOutput) Eval(ctx EvalContext) (interface{}, error) { 50 // This has to run before we have a state lock, since interpolation also 51 // reads the state 52 cfg, err := ctx.Interpolate(n.Value, nil) 53 // handle the error after we have the module from the state 54 55 state, lock := ctx.State() 56 if state == nil { 57 return nil, fmt.Errorf("cannot write state to nil state") 58 } 59 60 // Get a write lock so we can access this instance 61 lock.Lock() 62 defer lock.Unlock() 63 // Look for the module state. If we don't have one, create it. 64 mod := state.ModuleByPath(ctx.Path()) 65 if mod == nil { 66 mod = state.AddModule(ctx.Path()) 67 } 68 69 // handling the interpolation error 70 if err != nil { 71 if n.ContinueOnErr || flagWarnOutputErrors { 72 log.Printf("[ERROR] Output interpolation %q failed: %s", n.Name, err) 73 // if we're continuing, make sure the output is included, and 74 // marked as unknown 75 mod.Outputs[n.Name] = &OutputState{ 76 Type: "string", 77 Value: config.UnknownVariableValue, 78 } 79 return nil, EvalEarlyExitError{} 80 } 81 return nil, err 82 } 83 84 // Get the value from the config 85 var valueRaw interface{} = config.UnknownVariableValue 86 if cfg != nil { 87 var ok bool 88 valueRaw, ok = cfg.Get("value") 89 if !ok { 90 valueRaw = "" 91 } 92 if cfg.IsComputed("value") { 93 valueRaw = config.UnknownVariableValue 94 } 95 } 96 97 switch valueTyped := valueRaw.(type) { 98 case string: 99 mod.Outputs[n.Name] = &OutputState{ 100 Type: "string", 101 Sensitive: n.Sensitive, 102 Value: valueTyped, 103 } 104 case []interface{}: 105 mod.Outputs[n.Name] = &OutputState{ 106 Type: "list", 107 Sensitive: n.Sensitive, 108 Value: valueTyped, 109 } 110 case map[string]interface{}: 111 mod.Outputs[n.Name] = &OutputState{ 112 Type: "map", 113 Sensitive: n.Sensitive, 114 Value: valueTyped, 115 } 116 case []map[string]interface{}: 117 // an HCL map is multi-valued, so if this was read out of a config the 118 // map may still be in a slice. 119 if len(valueTyped) == 1 { 120 mod.Outputs[n.Name] = &OutputState{ 121 Type: "map", 122 Sensitive: n.Sensitive, 123 Value: valueTyped[0], 124 } 125 break 126 } 127 return nil, fmt.Errorf("output %s type (%T) with %d values not valid for type map", 128 n.Name, valueTyped, len(valueTyped)) 129 default: 130 return nil, fmt.Errorf("output %s is not a valid type (%T)\n", n.Name, valueTyped) 131 } 132 133 return nil, nil 134 }