github.com/hs0210/hashicorp-terraform@v0.11.12-beta1/helper/schema/resource_timeout_test.go (about) 1 package schema 2 3 import ( 4 "fmt" 5 "reflect" 6 "testing" 7 "time" 8 9 "github.com/hashicorp/terraform/config" 10 "github.com/hashicorp/terraform/terraform" 11 ) 12 13 func TestResourceTimeout_ConfigDecode_badkey(t *testing.T) { 14 cases := []struct { 15 Name string 16 // what the resource has defined in source 17 ResourceDefaultTimeout *ResourceTimeout 18 // configuration provider by user in tf file 19 Config []map[string]interface{} 20 // what we expect the parsed ResourceTimeout to be 21 Expected *ResourceTimeout 22 // Should we have an error (key not defined in source) 23 ShouldErr bool 24 }{ 25 { 26 Name: "Source does not define 'delete' key", 27 ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 0), 28 Config: expectedConfigForValues(2, 0, 0, 1, 0), 29 Expected: timeoutForValues(10, 0, 5, 0, 0), 30 ShouldErr: true, 31 }, 32 { 33 Name: "Config overrides create", 34 ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 0), 35 Config: expectedConfigForValues(2, 0, 7, 0, 0), 36 Expected: timeoutForValues(2, 0, 7, 0, 0), 37 ShouldErr: false, 38 }, 39 { 40 Name: "Config overrides create, default provided. Should still have zero values", 41 ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 3), 42 Config: expectedConfigForValues(2, 0, 7, 0, 0), 43 Expected: timeoutForValues(2, 0, 7, 0, 3), 44 ShouldErr: false, 45 }, 46 { 47 Name: "Use something besides 'minutes'", 48 ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 3), 49 Config: []map[string]interface{}{ 50 map[string]interface{}{ 51 "create": "2h", 52 }}, 53 Expected: timeoutForValues(120, 0, 5, 0, 3), 54 ShouldErr: false, 55 }, 56 } 57 58 for i, c := range cases { 59 t.Run(fmt.Sprintf("%d-%s", i, c.Name), func(t *testing.T) { 60 r := &Resource{ 61 Timeouts: c.ResourceDefaultTimeout, 62 } 63 64 raw, err := config.NewRawConfig( 65 map[string]interface{}{ 66 "foo": "bar", 67 TimeoutsConfigKey: c.Config, 68 }) 69 if err != nil { 70 t.Fatalf("err: %s", err) 71 } 72 conf := terraform.NewResourceConfig(raw) 73 74 timeout := &ResourceTimeout{} 75 decodeErr := timeout.ConfigDecode(r, conf) 76 if c.ShouldErr { 77 if decodeErr == nil { 78 t.Fatalf("ConfigDecode case (%d): Expected bad timeout key: %s", i, decodeErr) 79 } 80 // should error, err was not nil, continue 81 return 82 } else { 83 if decodeErr != nil { 84 // should not error, error was not nil, fatal 85 t.Fatalf("decodeError was not nil: %s", decodeErr) 86 } 87 } 88 89 if !reflect.DeepEqual(c.Expected, timeout) { 90 t.Fatalf("ConfigDecode match error case (%d), expected:\n%#v\ngot:\n%#v", i, c.Expected, timeout) 91 } 92 }) 93 } 94 } 95 96 func TestResourceTimeout_ConfigDecode(t *testing.T) { 97 r := &Resource{ 98 Timeouts: &ResourceTimeout{ 99 Create: DefaultTimeout(10 * time.Minute), 100 Update: DefaultTimeout(5 * time.Minute), 101 }, 102 } 103 104 raw, err := config.NewRawConfig( 105 map[string]interface{}{ 106 "foo": "bar", 107 TimeoutsConfigKey: []map[string]interface{}{ 108 map[string]interface{}{ 109 "create": "2m", 110 }, 111 map[string]interface{}{ 112 "update": "1m", 113 }, 114 }, 115 }) 116 if err != nil { 117 t.Fatalf("err: %s", err) 118 } 119 c := terraform.NewResourceConfig(raw) 120 121 timeout := &ResourceTimeout{} 122 err = timeout.ConfigDecode(r, c) 123 if err != nil { 124 t.Fatalf("Expected good timeout returned:, %s", err) 125 } 126 127 expected := &ResourceTimeout{ 128 Create: DefaultTimeout(2 * time.Minute), 129 Update: DefaultTimeout(1 * time.Minute), 130 } 131 132 if !reflect.DeepEqual(timeout, expected) { 133 t.Fatalf("bad timeout decode, expected (%#v), got (%#v)", expected, timeout) 134 } 135 } 136 137 func TestResourceTimeout_DiffEncode_basic(t *testing.T) { 138 cases := []struct { 139 Timeout *ResourceTimeout 140 Expected map[string]interface{} 141 // Not immediately clear when an error would hit 142 ShouldErr bool 143 }{ 144 // Two fields 145 { 146 Timeout: timeoutForValues(10, 0, 5, 0, 0), 147 Expected: map[string]interface{}{TimeoutKey: expectedForValues(10, 0, 5, 0, 0)}, 148 ShouldErr: false, 149 }, 150 // Two fields, one is Default 151 { 152 Timeout: timeoutForValues(10, 0, 0, 0, 7), 153 Expected: map[string]interface{}{TimeoutKey: expectedForValues(10, 0, 0, 0, 7)}, 154 ShouldErr: false, 155 }, 156 // All fields 157 { 158 Timeout: timeoutForValues(10, 3, 4, 1, 7), 159 Expected: map[string]interface{}{TimeoutKey: expectedForValues(10, 3, 4, 1, 7)}, 160 ShouldErr: false, 161 }, 162 // No fields 163 { 164 Timeout: &ResourceTimeout{}, 165 Expected: nil, 166 ShouldErr: false, 167 }, 168 } 169 170 for _, c := range cases { 171 state := &terraform.InstanceDiff{} 172 err := c.Timeout.DiffEncode(state) 173 if err != nil && !c.ShouldErr { 174 t.Fatalf("Error, expected:\n%#v\n got:\n%#v\n", c.Expected, state.Meta) 175 } 176 177 // should maybe just compare [TimeoutKey] but for now we're assuming only 178 // that in Meta 179 if !reflect.DeepEqual(state.Meta, c.Expected) { 180 t.Fatalf("Encode not equal, expected:\n%#v\n\ngot:\n%#v\n", c.Expected, state.Meta) 181 } 182 } 183 // same test cases but for InstanceState 184 for _, c := range cases { 185 state := &terraform.InstanceState{} 186 err := c.Timeout.StateEncode(state) 187 if err != nil && !c.ShouldErr { 188 t.Fatalf("Error, expected:\n%#v\n got:\n%#v\n", c.Expected, state.Meta) 189 } 190 191 // should maybe just compare [TimeoutKey] but for now we're assuming only 192 // that in Meta 193 if !reflect.DeepEqual(state.Meta, c.Expected) { 194 t.Fatalf("Encode not equal, expected:\n%#v\n\ngot:\n%#v\n", c.Expected, state.Meta) 195 } 196 } 197 } 198 199 func TestResourceTimeout_MetaDecode_basic(t *testing.T) { 200 cases := []struct { 201 State *terraform.InstanceDiff 202 Expected *ResourceTimeout 203 // Not immediately clear when an error would hit 204 ShouldErr bool 205 }{ 206 // Two fields 207 { 208 State: &terraform.InstanceDiff{Meta: map[string]interface{}{TimeoutKey: expectedForValues(10, 0, 5, 0, 0)}}, 209 Expected: timeoutForValues(10, 0, 5, 0, 0), 210 ShouldErr: false, 211 }, 212 // Two fields, one is Default 213 { 214 State: &terraform.InstanceDiff{Meta: map[string]interface{}{TimeoutKey: expectedForValues(10, 0, 0, 0, 7)}}, 215 Expected: timeoutForValues(10, 7, 7, 7, 7), 216 ShouldErr: false, 217 }, 218 // All fields 219 { 220 State: &terraform.InstanceDiff{Meta: map[string]interface{}{TimeoutKey: expectedForValues(10, 3, 4, 1, 7)}}, 221 Expected: timeoutForValues(10, 3, 4, 1, 7), 222 ShouldErr: false, 223 }, 224 // No fields 225 { 226 State: &terraform.InstanceDiff{}, 227 Expected: &ResourceTimeout{}, 228 ShouldErr: false, 229 }, 230 } 231 232 for _, c := range cases { 233 rt := &ResourceTimeout{} 234 err := rt.DiffDecode(c.State) 235 if err != nil && !c.ShouldErr { 236 t.Fatalf("Error, expected:\n%#v\n got:\n%#v\n", c.Expected, rt) 237 } 238 239 // should maybe just compare [TimeoutKey] but for now we're assuming only 240 // that in Meta 241 if !reflect.DeepEqual(rt, c.Expected) { 242 t.Fatalf("Encode not equal, expected:\n%#v\n\ngot:\n%#v\n", c.Expected, rt) 243 } 244 } 245 } 246 247 func timeoutForValues(create, read, update, del, def int) *ResourceTimeout { 248 rt := ResourceTimeout{} 249 250 if create != 0 { 251 rt.Create = DefaultTimeout(time.Duration(create) * time.Minute) 252 } 253 if read != 0 { 254 rt.Read = DefaultTimeout(time.Duration(read) * time.Minute) 255 } 256 if update != 0 { 257 rt.Update = DefaultTimeout(time.Duration(update) * time.Minute) 258 } 259 if del != 0 { 260 rt.Delete = DefaultTimeout(time.Duration(del) * time.Minute) 261 } 262 263 if def != 0 { 264 rt.Default = DefaultTimeout(time.Duration(def) * time.Minute) 265 } 266 267 return &rt 268 } 269 270 // Generates a ResourceTimeout struct that should reflect the 271 // d.Timeout("key") results 272 func expectedTimeoutForValues(create, read, update, del, def int) *ResourceTimeout { 273 rt := ResourceTimeout{} 274 275 defaultValues := []*int{&create, &read, &update, &del, &def} 276 for _, v := range defaultValues { 277 if *v == 0 { 278 *v = 20 279 } 280 } 281 282 if create != 0 { 283 rt.Create = DefaultTimeout(time.Duration(create) * time.Minute) 284 } 285 if read != 0 { 286 rt.Read = DefaultTimeout(time.Duration(read) * time.Minute) 287 } 288 if update != 0 { 289 rt.Update = DefaultTimeout(time.Duration(update) * time.Minute) 290 } 291 if del != 0 { 292 rt.Delete = DefaultTimeout(time.Duration(del) * time.Minute) 293 } 294 295 if def != 0 { 296 rt.Default = DefaultTimeout(time.Duration(def) * time.Minute) 297 } 298 299 return &rt 300 } 301 302 func expectedForValues(create, read, update, del, def int) map[string]interface{} { 303 ex := make(map[string]interface{}) 304 305 if create != 0 { 306 ex["create"] = DefaultTimeout(time.Duration(create) * time.Minute).Nanoseconds() 307 } 308 if read != 0 { 309 ex["read"] = DefaultTimeout(time.Duration(read) * time.Minute).Nanoseconds() 310 } 311 if update != 0 { 312 ex["update"] = DefaultTimeout(time.Duration(update) * time.Minute).Nanoseconds() 313 } 314 if del != 0 { 315 ex["delete"] = DefaultTimeout(time.Duration(del) * time.Minute).Nanoseconds() 316 } 317 318 if def != 0 { 319 defNano := DefaultTimeout(time.Duration(def) * time.Minute).Nanoseconds() 320 ex["default"] = defNano 321 322 for _, k := range timeoutKeys() { 323 if _, ok := ex[k]; !ok { 324 ex[k] = defNano 325 } 326 } 327 } 328 329 return ex 330 } 331 332 func expectedConfigForValues(create, read, update, delete, def int) []map[string]interface{} { 333 ex := make([]map[string]interface{}, 0) 334 335 if create != 0 { 336 ex = append(ex, map[string]interface{}{"create": fmt.Sprintf("%dm", create)}) 337 } 338 if read != 0 { 339 ex = append(ex, map[string]interface{}{"read": fmt.Sprintf("%dm", read)}) 340 } 341 if update != 0 { 342 ex = append(ex, map[string]interface{}{"update": fmt.Sprintf("%dm", update)}) 343 } 344 if delete != 0 { 345 ex = append(ex, map[string]interface{}{"delete": fmt.Sprintf("%dm", delete)}) 346 } 347 348 if def != 0 { 349 ex = append(ex, map[string]interface{}{"default": fmt.Sprintf("%dm", def)}) 350 } 351 return ex 352 }