github.phpd.cn/hashicorp/packer@v1.3.2/builder/googlecompute/config_test.go (about) 1 package googlecompute 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "strings" 8 "testing" 9 ) 10 11 func TestConfigPrepare(t *testing.T) { 12 cases := []struct { 13 Key string 14 Value interface{} 15 Err bool 16 }{ 17 { 18 "unknown_key", 19 "bad", 20 true, 21 }, 22 23 { 24 "private_key_file", 25 "/tmp/i/should/not/exist", 26 true, 27 }, 28 29 { 30 "project_id", 31 nil, 32 true, 33 }, 34 { 35 "project_id", 36 "foo", 37 false, 38 }, 39 40 { 41 "source_image", 42 nil, 43 true, 44 }, 45 { 46 "source_image", 47 "foo", 48 false, 49 }, 50 51 { 52 "source_image_family", 53 nil, 54 false, 55 }, 56 { 57 "source_image_family", 58 "foo", 59 false, 60 }, 61 62 { 63 "zone", 64 nil, 65 true, 66 }, 67 { 68 "zone", 69 "foo", 70 false, 71 }, 72 73 { 74 "ssh_timeout", 75 "SO BAD", 76 true, 77 }, 78 { 79 "ssh_timeout", 80 "5s", 81 false, 82 }, 83 84 { 85 "state_timeout", 86 "SO BAD", 87 true, 88 }, 89 { 90 "state_timeout", 91 "5s", 92 false, 93 }, 94 { 95 "use_internal_ip", 96 nil, 97 false, 98 }, 99 { 100 "use_internal_ip", 101 false, 102 false, 103 }, 104 { 105 "use_internal_ip", 106 "SO VERY BAD", 107 true, 108 }, 109 { 110 "on_host_maintenance", 111 nil, 112 false, 113 }, 114 { 115 "on_host_maintenance", 116 "TERMINATE", 117 false, 118 }, 119 { 120 "on_host_maintenance", 121 "SO VERY BAD", 122 true, 123 }, 124 { 125 "preemptible", 126 nil, 127 false, 128 }, 129 { 130 "preemptible", 131 false, 132 false, 133 }, 134 { 135 "preemptible", 136 "SO VERY BAD", 137 true, 138 }, 139 { 140 "image_family", 141 nil, 142 false, 143 }, 144 { 145 "image_family", 146 "", 147 false, 148 }, 149 { 150 "image_family", 151 "foo-bar", 152 false, 153 }, 154 { 155 "image_family", 156 "foo bar", 157 true, 158 }, 159 { 160 "scopes", 161 []string{}, 162 false, 163 }, 164 { 165 "scopes", 166 []string{"https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/compute", "https://www.googleapis.com/auth/devstorage.full_control", "https://www.googleapis.com/auth/sqlservice.admin"}, 167 false, 168 }, 169 { 170 "scopes", 171 []string{"https://www.googleapis.com/auth/cloud-platform"}, 172 false, 173 }, 174 175 { 176 "disable_default_service_account", 177 "", 178 false, 179 }, 180 { 181 "disable_default_service_account", 182 nil, 183 false, 184 }, 185 { 186 "disable_default_service_account", 187 false, 188 false, 189 }, 190 { 191 "disable_default_service_account", 192 true, 193 false, 194 }, 195 { 196 "disable_default_service_account", 197 "NOT A BOOL", 198 true, 199 }, 200 } 201 202 for _, tc := range cases { 203 raw, tempfile := testConfig(t) 204 defer os.Remove(tempfile) 205 206 if tc.Value == nil { 207 delete(raw, tc.Key) 208 } else { 209 raw[tc.Key] = tc.Value 210 } 211 212 _, warns, errs := NewConfig(raw) 213 214 if tc.Err { 215 testConfigErr(t, warns, errs, tc.Key) 216 } else { 217 testConfigOk(t, warns, errs) 218 } 219 } 220 } 221 222 func TestConfigPrepareAccelerator(t *testing.T) { 223 cases := []struct { 224 Keys []string 225 Values []interface{} 226 Err bool 227 }{ 228 { 229 []string{"accelerator_count", "on_host_maintenance", "accelerator_type"}, 230 []interface{}{1, "MIGRATE", "something_valid"}, 231 true, 232 }, 233 { 234 []string{"accelerator_count", "on_host_maintenance", "accelerator_type"}, 235 []interface{}{1, "TERMINATE", "something_valid"}, 236 false, 237 }, 238 { 239 []string{"accelerator_count", "on_host_maintenance", "accelerator_type"}, 240 []interface{}{1, "TERMINATE", nil}, 241 true, 242 }, 243 { 244 []string{"accelerator_count", "on_host_maintenance", "accelerator_type"}, 245 []interface{}{1, "TERMINATE", ""}, 246 true, 247 }, 248 { 249 []string{"accelerator_count", "on_host_maintenance", "accelerator_type"}, 250 []interface{}{1, "TERMINATE", "something_valid"}, 251 false, 252 }, 253 } 254 255 for _, tc := range cases { 256 raw, tempfile := testConfig(t) 257 defer os.Remove(tempfile) 258 259 errStr := "" 260 for k := range tc.Keys { 261 262 // Create the string for error reporting 263 // convert value to string if it can be converted 264 errStr += fmt.Sprintf("%s:%v, ", tc.Keys[k], tc.Values[k]) 265 if tc.Values[k] == nil { 266 delete(raw, tc.Keys[k]) 267 } else { 268 raw[tc.Keys[k]] = tc.Values[k] 269 } 270 } 271 272 _, warns, errs := NewConfig(raw) 273 274 if tc.Err { 275 testConfigErr(t, warns, errs, strings.TrimRight(errStr, ", ")) 276 } else { 277 testConfigOk(t, warns, errs) 278 } 279 } 280 } 281 282 func TestConfigPrepareServiceAccount(t *testing.T) { 283 cases := []struct { 284 Keys []string 285 Values []interface{} 286 Err bool 287 }{ 288 { 289 []string{"disable_default_service_account", "service_account_email"}, 290 []interface{}{true, "service@account.email.com"}, 291 true, 292 }, 293 { 294 []string{"disable_default_service_account", "service_account_email"}, 295 []interface{}{false, "service@account.email.com"}, 296 false, 297 }, 298 { 299 []string{"disable_default_service_account", "service_account_email"}, 300 []interface{}{true, ""}, 301 false, 302 }, 303 } 304 305 for _, tc := range cases { 306 raw, tempfile := testConfig(t) 307 defer os.Remove(tempfile) 308 309 errStr := "" 310 for k := range tc.Keys { 311 312 // Create the string for error reporting 313 // convert value to string if it can be converted 314 errStr += fmt.Sprintf("%s:%v, ", tc.Keys[k], tc.Values[k]) 315 if tc.Values[k] == nil { 316 delete(raw, tc.Keys[k]) 317 } else { 318 raw[tc.Keys[k]] = tc.Values[k] 319 } 320 } 321 322 _, warns, errs := NewConfig(raw) 323 324 if tc.Err { 325 testConfigErr(t, warns, errs, strings.TrimRight(errStr, ", ")) 326 } else { 327 testConfigOk(t, warns, errs) 328 } 329 } 330 } 331 332 func TestConfigPrepareStartupScriptFile(t *testing.T) { 333 config := map[string]interface{}{ 334 "project_id": "project", 335 "source_image": "foo", 336 "ssh_username": "packer", 337 "startup_script_file": "no-such-file", 338 "zone": "us-central1-a", 339 } 340 341 _, _, errs := NewConfig(config) 342 343 if errs == nil || !strings.Contains(errs.Error(), "startup_script_file") { 344 t.Fatalf("should error: startup_script_file") 345 } 346 } 347 348 func TestConfigDefaults(t *testing.T) { 349 cases := []struct { 350 Read func(c *Config) interface{} 351 Value interface{} 352 }{ 353 { 354 func(c *Config) interface{} { return c.Comm.Type }, 355 "ssh", 356 }, 357 358 { 359 func(c *Config) interface{} { return c.Comm.SSHPort }, 360 22, 361 }, 362 } 363 364 for _, tc := range cases { 365 raw, tempfile := testConfig(t) 366 defer os.Remove(tempfile) 367 368 c, warns, errs := NewConfig(raw) 369 testConfigOk(t, warns, errs) 370 371 actual := tc.Read(c) 372 if actual != tc.Value { 373 t.Fatalf("bad: %#v", actual) 374 } 375 } 376 } 377 378 func TestImageName(t *testing.T) { 379 raw, tempfile := testConfig(t) 380 defer os.Remove(tempfile) 381 382 c, _, _ := NewConfig(raw) 383 if !strings.HasPrefix(c.ImageName, "packer-") { 384 t.Fatalf("ImageName should have 'packer-' prefix, found %s", c.ImageName) 385 } 386 if strings.Contains(c.ImageName, "{{timestamp}}") { 387 t.Errorf("ImageName should be interpolated; found %s", c.ImageName) 388 } 389 } 390 391 func TestRegion(t *testing.T) { 392 raw, tempfile := testConfig(t) 393 defer os.Remove(tempfile) 394 395 c, _, _ := NewConfig(raw) 396 if c.Region != "us-east1" { 397 t.Fatalf("Region should be 'us-east1' given Zone of 'us-east1-a', but is %s", c.Region) 398 } 399 } 400 401 // Helper stuff below 402 403 func testConfig(t *testing.T) (config map[string]interface{}, tempAccountFile string) { 404 tempAccountFile = testAccountFile(t) 405 406 config = map[string]interface{}{ 407 "account_file": tempAccountFile, 408 "project_id": "hashicorp", 409 "source_image": "foo", 410 "ssh_username": "root", 411 "image_family": "bar", 412 "image_labels": map[string]string{ 413 "label-1": "value-1", 414 "label-2": "value-2", 415 }, 416 "image_licenses": []string{ 417 "test-license", 418 }, 419 "zone": "us-east1-a", 420 } 421 422 return config, tempAccountFile 423 } 424 425 func testConfigStruct(t *testing.T) *Config { 426 raw, tempfile := testConfig(t) 427 defer os.Remove(tempfile) 428 429 c, warns, errs := NewConfig(raw) 430 if len(warns) > 0 { 431 t.Fatalf("bad: %#v", len(warns)) 432 } 433 if errs != nil { 434 t.Fatalf("bad: %#v", errs) 435 } 436 437 return c 438 } 439 440 func testConfigErr(t *testing.T, warns []string, err error, extra string) { 441 if len(warns) > 0 { 442 t.Fatalf("bad: %#v", warns) 443 } 444 if err == nil { 445 t.Fatalf("should error: %s", extra) 446 } 447 } 448 449 func testConfigOk(t *testing.T, warns []string, err error) { 450 if len(warns) > 0 { 451 t.Fatalf("bad: %#v", warns) 452 } 453 if err != nil { 454 t.Fatalf("bad: %s", err) 455 } 456 } 457 458 func testAccountFile(t *testing.T) string { 459 tf, err := ioutil.TempFile("", "packer") 460 if err != nil { 461 t.Fatalf("err: %s", err) 462 } 463 defer tf.Close() 464 465 if _, err := tf.Write([]byte(testAccountContent)); err != nil { 466 t.Fatalf("err: %s", err) 467 } 468 469 return tf.Name() 470 } 471 472 // This is just some dummy data that doesn't actually work (it was revoked 473 // a long time ago). 474 const testAccountContent = `{}`