github.com/muratcelep/terraform@v1.1.0-beta2-not-internal-4/not-internal/plans/planfile/tfplan_test.go (about) 1 package planfile 2 3 import ( 4 "bytes" 5 "testing" 6 7 "github.com/go-test/deep" 8 "github.com/zclconf/go-cty/cty" 9 10 "github.com/muratcelep/terraform/not-internal/addrs" 11 "github.com/muratcelep/terraform/not-internal/lang/marks" 12 "github.com/muratcelep/terraform/not-internal/plans" 13 ) 14 15 func TestTFPlanRoundTrip(t *testing.T) { 16 objTy := cty.Object(map[string]cty.Type{ 17 "id": cty.String, 18 }) 19 20 plan := &plans.Plan{ 21 VariableValues: map[string]plans.DynamicValue{ 22 "foo": mustNewDynamicValueStr("foo value"), 23 }, 24 Changes: &plans.Changes{ 25 Outputs: []*plans.OutputChangeSrc{ 26 { 27 Addr: addrs.OutputValue{Name: "bar"}.Absolute(addrs.RootModuleInstance), 28 ChangeSrc: plans.ChangeSrc{ 29 Action: plans.Create, 30 After: mustDynamicOutputValue("bar value"), 31 }, 32 Sensitive: false, 33 }, 34 { 35 Addr: addrs.OutputValue{Name: "baz"}.Absolute(addrs.RootModuleInstance), 36 ChangeSrc: plans.ChangeSrc{ 37 Action: plans.NoOp, 38 Before: mustDynamicOutputValue("baz value"), 39 After: mustDynamicOutputValue("baz value"), 40 }, 41 Sensitive: false, 42 }, 43 { 44 Addr: addrs.OutputValue{Name: "secret"}.Absolute(addrs.RootModuleInstance), 45 ChangeSrc: plans.ChangeSrc{ 46 Action: plans.Update, 47 Before: mustDynamicOutputValue("old secret value"), 48 After: mustDynamicOutputValue("new secret value"), 49 }, 50 Sensitive: true, 51 }, 52 }, 53 Resources: []*plans.ResourceInstanceChangeSrc{ 54 { 55 Addr: addrs.Resource{ 56 Mode: addrs.ManagedResourceMode, 57 Type: "test_thing", 58 Name: "woot", 59 }.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance), 60 PrevRunAddr: addrs.Resource{ 61 Mode: addrs.ManagedResourceMode, 62 Type: "test_thing", 63 Name: "woot", 64 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 65 ProviderAddr: addrs.AbsProviderConfig{ 66 Provider: addrs.NewDefaultProvider("test"), 67 Module: addrs.RootModule, 68 }, 69 ChangeSrc: plans.ChangeSrc{ 70 Action: plans.DeleteThenCreate, 71 Before: mustNewDynamicValue(cty.ObjectVal(map[string]cty.Value{ 72 "id": cty.StringVal("foo-bar-baz"), 73 "boop": cty.ListVal([]cty.Value{ 74 cty.StringVal("beep"), 75 }), 76 }), objTy), 77 After: mustNewDynamicValue(cty.ObjectVal(map[string]cty.Value{ 78 "id": cty.UnknownVal(cty.String), 79 "boop": cty.ListVal([]cty.Value{ 80 cty.StringVal("beep"), 81 cty.StringVal("honk"), 82 }), 83 }), objTy), 84 AfterValMarks: []cty.PathValueMarks{ 85 { 86 Path: cty.GetAttrPath("boop").IndexInt(1), 87 Marks: cty.NewValueMarks(marks.Sensitive), 88 }, 89 }, 90 }, 91 RequiredReplace: cty.NewPathSet( 92 cty.GetAttrPath("boop"), 93 ), 94 ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate, 95 }, 96 { 97 Addr: addrs.Resource{ 98 Mode: addrs.ManagedResourceMode, 99 Type: "test_thing", 100 Name: "woot", 101 }.Instance(addrs.IntKey(1)).Absolute(addrs.RootModuleInstance), 102 PrevRunAddr: addrs.Resource{ 103 Mode: addrs.ManagedResourceMode, 104 Type: "test_thing", 105 Name: "woot", 106 }.Instance(addrs.IntKey(1)).Absolute(addrs.RootModuleInstance), 107 DeposedKey: "foodface", 108 ProviderAddr: addrs.AbsProviderConfig{ 109 Provider: addrs.NewDefaultProvider("test"), 110 Module: addrs.RootModule, 111 }, 112 ChangeSrc: plans.ChangeSrc{ 113 Action: plans.Delete, 114 Before: mustNewDynamicValue(cty.ObjectVal(map[string]cty.Value{ 115 "id": cty.StringVal("bar-baz-foo"), 116 }), objTy), 117 }, 118 }, 119 }, 120 }, 121 DriftedResources: []*plans.ResourceInstanceChangeSrc{ 122 { 123 Addr: addrs.Resource{ 124 Mode: addrs.ManagedResourceMode, 125 Type: "test_thing", 126 Name: "woot", 127 }.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance), 128 PrevRunAddr: addrs.Resource{ 129 Mode: addrs.ManagedResourceMode, 130 Type: "test_thing", 131 Name: "woot", 132 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 133 ProviderAddr: addrs.AbsProviderConfig{ 134 Provider: addrs.NewDefaultProvider("test"), 135 Module: addrs.RootModule, 136 }, 137 ChangeSrc: plans.ChangeSrc{ 138 Action: plans.DeleteThenCreate, 139 Before: mustNewDynamicValue(cty.ObjectVal(map[string]cty.Value{ 140 "id": cty.StringVal("foo-bar-baz"), 141 "boop": cty.ListVal([]cty.Value{ 142 cty.StringVal("beep"), 143 }), 144 }), objTy), 145 After: mustNewDynamicValue(cty.ObjectVal(map[string]cty.Value{ 146 "id": cty.UnknownVal(cty.String), 147 "boop": cty.ListVal([]cty.Value{ 148 cty.StringVal("beep"), 149 cty.StringVal("bonk"), 150 }), 151 }), objTy), 152 AfterValMarks: []cty.PathValueMarks{ 153 { 154 Path: cty.GetAttrPath("boop").IndexInt(1), 155 Marks: cty.NewValueMarks(marks.Sensitive), 156 }, 157 }, 158 }, 159 }, 160 }, 161 TargetAddrs: []addrs.Targetable{ 162 addrs.Resource{ 163 Mode: addrs.ManagedResourceMode, 164 Type: "test_thing", 165 Name: "woot", 166 }.Absolute(addrs.RootModuleInstance), 167 }, 168 Backend: plans.Backend{ 169 Type: "local", 170 Config: mustNewDynamicValue( 171 cty.ObjectVal(map[string]cty.Value{ 172 "foo": cty.StringVal("bar"), 173 }), 174 cty.Object(map[string]cty.Type{ 175 "foo": cty.String, 176 }), 177 ), 178 Workspace: "default", 179 }, 180 } 181 182 var buf bytes.Buffer 183 err := writeTfplan(plan, &buf) 184 if err != nil { 185 t.Fatal(err) 186 } 187 188 newPlan, err := readTfplan(&buf) 189 if err != nil { 190 t.Fatal(err) 191 } 192 193 { 194 oldDepth := deep.MaxDepth 195 oldCompare := deep.CompareUnexportedFields 196 deep.MaxDepth = 20 197 deep.CompareUnexportedFields = true 198 defer func() { 199 deep.MaxDepth = oldDepth 200 deep.CompareUnexportedFields = oldCompare 201 }() 202 } 203 for _, problem := range deep.Equal(newPlan, plan) { 204 t.Error(problem) 205 } 206 } 207 208 func mustDynamicOutputValue(val string) plans.DynamicValue { 209 ret, err := plans.NewDynamicValue(cty.StringVal(val), cty.DynamicPseudoType) 210 if err != nil { 211 panic(err) 212 } 213 return ret 214 } 215 216 func mustNewDynamicValue(val cty.Value, ty cty.Type) plans.DynamicValue { 217 ret, err := plans.NewDynamicValue(val, ty) 218 if err != nil { 219 panic(err) 220 } 221 return ret 222 } 223 224 func mustNewDynamicValueStr(val string) plans.DynamicValue { 225 realVal := cty.StringVal(val) 226 ret, err := plans.NewDynamicValue(realVal, cty.String) 227 if err != nil { 228 panic(err) 229 } 230 return ret 231 } 232 233 // TestTFPlanRoundTripDestroy ensures that encoding and decoding null values for 234 // destroy doesn't leave us with any nil values. 235 func TestTFPlanRoundTripDestroy(t *testing.T) { 236 objTy := cty.Object(map[string]cty.Type{ 237 "id": cty.String, 238 }) 239 240 plan := &plans.Plan{ 241 Changes: &plans.Changes{ 242 Outputs: []*plans.OutputChangeSrc{ 243 { 244 Addr: addrs.OutputValue{Name: "bar"}.Absolute(addrs.RootModuleInstance), 245 ChangeSrc: plans.ChangeSrc{ 246 Action: plans.Delete, 247 Before: mustDynamicOutputValue("output"), 248 After: mustNewDynamicValue(cty.NullVal(cty.String), cty.String), 249 }, 250 }, 251 }, 252 Resources: []*plans.ResourceInstanceChangeSrc{ 253 { 254 Addr: addrs.Resource{ 255 Mode: addrs.ManagedResourceMode, 256 Type: "test_thing", 257 Name: "woot", 258 }.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance), 259 PrevRunAddr: addrs.Resource{ 260 Mode: addrs.ManagedResourceMode, 261 Type: "test_thing", 262 Name: "woot", 263 }.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance), 264 ProviderAddr: addrs.AbsProviderConfig{ 265 Provider: addrs.NewDefaultProvider("test"), 266 Module: addrs.RootModule, 267 }, 268 ChangeSrc: plans.ChangeSrc{ 269 Action: plans.Delete, 270 Before: mustNewDynamicValue(cty.ObjectVal(map[string]cty.Value{ 271 "id": cty.StringVal("foo-bar-baz"), 272 }), objTy), 273 After: mustNewDynamicValue(cty.NullVal(objTy), objTy), 274 }, 275 }, 276 }, 277 }, 278 DriftedResources: []*plans.ResourceInstanceChangeSrc{}, 279 TargetAddrs: []addrs.Targetable{ 280 addrs.Resource{ 281 Mode: addrs.ManagedResourceMode, 282 Type: "test_thing", 283 Name: "woot", 284 }.Absolute(addrs.RootModuleInstance), 285 }, 286 Backend: plans.Backend{ 287 Type: "local", 288 Config: mustNewDynamicValue( 289 cty.ObjectVal(map[string]cty.Value{ 290 "foo": cty.StringVal("bar"), 291 }), 292 cty.Object(map[string]cty.Type{ 293 "foo": cty.String, 294 }), 295 ), 296 Workspace: "default", 297 }, 298 } 299 300 var buf bytes.Buffer 301 err := writeTfplan(plan, &buf) 302 if err != nil { 303 t.Fatal(err) 304 } 305 306 newPlan, err := readTfplan(&buf) 307 if err != nil { 308 t.Fatal(err) 309 } 310 311 for _, rics := range newPlan.Changes.Resources { 312 ric, err := rics.Decode(objTy) 313 if err != nil { 314 t.Fatal(err) 315 } 316 317 if ric.After == cty.NilVal { 318 t.Fatalf("unexpected nil After value: %#v\n", ric) 319 } 320 } 321 for _, ocs := range newPlan.Changes.Outputs { 322 oc, err := ocs.Decode() 323 if err != nil { 324 t.Fatal(err) 325 } 326 327 if oc.After == cty.NilVal { 328 t.Fatalf("unexpected nil After value: %#v\n", ocs) 329 } 330 } 331 }