github.com/ns1/terraform@v0.7.10-0.20161109153551-8949419bef40/terraform/shadow_context.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 6 "github.com/hashicorp/go-multierror" 7 "github.com/mitchellh/copystructure" 8 ) 9 10 // newShadowContext creates a new context that will shadow the given context 11 // when walking the graph. The resulting context should be used _only once_ 12 // for a graph walk. 13 // 14 // The returned Shadow should be closed after the graph walk with the 15 // real context is complete. Errors from the shadow can be retrieved there. 16 // 17 // Most importantly, any operations done on the shadow context (the returned 18 // context) will NEVER affect the real context. All structures are deep 19 // copied, no real providers or resources are used, etc. 20 func newShadowContext(c *Context) (*Context, *Context, Shadow) { 21 // Copy the targets 22 targetRaw, err := copystructure.Copy(c.targets) 23 if err != nil { 24 panic(err) 25 } 26 27 // Copy the variables 28 varRaw, err := copystructure.Copy(c.variables) 29 if err != nil { 30 panic(err) 31 } 32 33 // Copy the provider inputs 34 providerInputRaw, err := copystructure.Copy(c.providerInputConfig) 35 if err != nil { 36 panic(err) 37 } 38 39 // The factories 40 componentsReal, componentsShadow := newShadowComponentFactory(c.components) 41 42 // Create the shadow 43 shadow := &Context{ 44 components: componentsShadow, 45 destroy: c.destroy, 46 diff: c.diff.DeepCopy(), 47 hooks: nil, 48 module: c.module, 49 state: c.state.DeepCopy(), 50 targets: targetRaw.([]string), 51 variables: varRaw.(map[string]interface{}), 52 53 // NOTE(mitchellh): This is not going to work for shadows that are 54 // testing that input results in the proper end state. At the time 55 // of writing, input is not used in any state-changing graph 56 // walks anyways, so this checks nothing. We set it to this to avoid 57 // any panics but even a "nil" value worked here. 58 uiInput: new(MockUIInput), 59 60 // Hardcoded to 4 since parallelism in the shadow doesn't matter 61 // a ton since we're doing far less compared to the real side 62 // and our operations are MUCH faster. 63 parallelSem: NewSemaphore(4), 64 providerInputConfig: providerInputRaw.(map[string]map[string]interface{}), 65 } 66 67 // Create the real context. This is effectively just a copy of 68 // the context given except we need to modify some of the values 69 // to point to the real side of a shadow so the shadow can compare values. 70 real := &Context{ 71 // The fields below are changed. 72 components: componentsReal, 73 74 // The fields below are direct copies 75 destroy: c.destroy, 76 diff: c.diff, 77 // diffLock - no copy 78 hooks: c.hooks, 79 module: c.module, 80 sh: c.sh, 81 state: c.state, 82 // stateLock - no copy 83 targets: c.targets, 84 uiInput: c.uiInput, 85 variables: c.variables, 86 87 // l - no copy 88 parallelSem: c.parallelSem, 89 providerInputConfig: c.providerInputConfig, 90 runCh: c.runCh, 91 shadowErr: c.shadowErr, 92 } 93 94 return real, shadow, &shadowContextCloser{ 95 Components: componentsShadow, 96 } 97 } 98 99 // shadowContextVerify takes the real and shadow context and verifies they 100 // have equal diffs and states. 101 func shadowContextVerify(real, shadow *Context) error { 102 var result error 103 104 // The states compared must be pruned so they're minimal/clean 105 real.state.prune() 106 shadow.state.prune() 107 108 // Compare the states 109 if !real.state.Equal(shadow.state) { 110 result = multierror.Append(result, fmt.Errorf( 111 "Real and shadow states do not match! "+ 112 "Real state:\n\n%s\n\n"+ 113 "Shadow state:\n\n%s\n\n", 114 real.state, shadow.state)) 115 } 116 117 // Compare the diffs 118 if !real.diff.Equal(shadow.diff) { 119 result = multierror.Append(result, fmt.Errorf( 120 "Real and shadow diffs do not match! "+ 121 "Real diff:\n\n%s\n\n"+ 122 "Shadow diff:\n\n%s\n\n", 123 real.diff, shadow.diff)) 124 } 125 126 return result 127 } 128 129 // shadowContextCloser is the io.Closer returned by newShadowContext that 130 // closes all the shadows and returns the results. 131 type shadowContextCloser struct { 132 Components *shadowComponentFactory 133 } 134 135 // Close closes the shadow context. 136 func (c *shadowContextCloser) CloseShadow() error { 137 return c.Components.CloseShadow() 138 } 139 140 func (c *shadowContextCloser) ShadowError() error { 141 return c.Components.ShadowError() 142 }