github.com/sacloud/libsacloud/v2@v2.32.3/helper/builder/server/builder_test.go (about) 1 // Copyright 2016-2022 The Libsacloud Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package server 16 17 import ( 18 "bytes" 19 "context" 20 "errors" 21 "testing" 22 "time" 23 24 "github.com/sacloud/libsacloud/v2/helper/api" 25 "github.com/sacloud/libsacloud/v2/helper/builder" 26 "github.com/sacloud/libsacloud/v2/helper/builder/disk" 27 "github.com/sacloud/libsacloud/v2/helper/plans" 28 "github.com/sacloud/libsacloud/v2/helper/power" 29 "github.com/sacloud/libsacloud/v2/sacloud" 30 "github.com/sacloud/libsacloud/v2/sacloud/ostype" 31 "github.com/sacloud/libsacloud/v2/sacloud/testutil" 32 "github.com/sacloud/libsacloud/v2/sacloud/types" 33 "github.com/stretchr/testify/require" 34 "golang.org/x/crypto/ssh" 35 ) 36 37 func init() { 38 if !testutil.IsAccTest() { 39 api.SetupFakeDefaults() 40 } 41 } 42 43 func TestBuilder_setDefaults(t *testing.T) { 44 in := &Builder{} 45 in.setDefaults() 46 47 expected := &Builder{ 48 CPU: defaultCPU, 49 MemoryGB: defaultMemoryGB, 50 Commitment: defaultCommitment, 51 Generation: defaultGeneration, 52 InterfaceDriver: defaultInterfaceDriver, 53 } 54 require.Equal(t, expected, in) 55 } 56 57 func TestBuilder_Validate(t *testing.T) { 58 cases := []struct { 59 msg string 60 in *Builder 61 err error 62 }{ 63 { 64 msg: "Client is not set", 65 in: &Builder{}, 66 err: errors.New("client is empty"), 67 }, 68 { 69 msg: "invalid NICs", 70 in: &Builder{ 71 NIC: nil, 72 AdditionalNICs: []AdditionalNICSettingHolder{ 73 &DisconnectedNICSetting{}, 74 }, 75 Client: &APIClient{ 76 ServerPlan: &dummyPlanFinder{}, 77 }, 78 }, 79 err: errors.New("NIC is required when AdditionalNICs is specified"), 80 }, 81 { 82 msg: "Additional NICs over 9", 83 in: &Builder{ 84 NIC: &SharedNICSetting{}, 85 AdditionalNICs: []AdditionalNICSettingHolder{ 86 &DisconnectedNICSetting{}, 87 &DisconnectedNICSetting{}, 88 &DisconnectedNICSetting{}, 89 &DisconnectedNICSetting{}, 90 &DisconnectedNICSetting{}, 91 &DisconnectedNICSetting{}, 92 &DisconnectedNICSetting{}, 93 &DisconnectedNICSetting{}, 94 &DisconnectedNICSetting{}, 95 &DisconnectedNICSetting{}, 96 }, 97 Client: &APIClient{ 98 ServerPlan: &dummyPlanFinder{}, 99 }, 100 }, 101 err: errors.New("AdditionalNICs must be less than 9"), 102 }, 103 { 104 msg: "invalid InterfaceDriver", 105 in: &Builder{ 106 NIC: &SharedNICSetting{}, 107 InterfaceDriver: types.EInterfaceDriver("invalid"), 108 Client: &APIClient{ 109 ServerPlan: &dummyPlanFinder{}, 110 }, 111 }, 112 err: errors.New("invalid InterfaceDriver: invalid"), 113 }, 114 { 115 msg: "finding plan returns unexpected error", 116 in: &Builder{ 117 Client: &APIClient{ 118 ServerPlan: &dummyPlanFinder{ 119 err: errors.New("dummy"), 120 }, 121 }, 122 }, 123 err: errors.New("dummy"), 124 }, 125 { 126 msg: "eth0: switch not found", 127 in: &Builder{ 128 NIC: &ConnectedNICSetting{ 129 SwitchID: 1111111, 130 }, 131 Client: &APIClient{ 132 Switch: &dummySwitchReader{ 133 err: errors.New("not found"), 134 }, 135 }, 136 }, 137 err: errors.New("invalid NIC: reading switch info(id:1111111) is failed: not found"), 138 }, 139 { 140 msg: "eth1: switch not found", 141 in: &Builder{ 142 NIC: &SharedNICSetting{}, 143 AdditionalNICs: []AdditionalNICSettingHolder{ 144 &ConnectedNICSetting{ 145 SwitchID: 1111111, 146 }, 147 }, 148 Client: &APIClient{ 149 Switch: &dummySwitchReader{ 150 err: errors.New("not found"), 151 }, 152 }, 153 }, 154 err: errors.New("invalid AdditionalNICs[0]: reading switch info(id:1111111) is failed: not found"), 155 }, 156 { 157 msg: "plan not found", 158 in: &Builder{ 159 CPU: 1000, 160 MemoryGB: 1024, 161 Client: &APIClient{ 162 ServerPlan: &dummyPlanFinder{}, 163 }, 164 }, 165 err: errors.New("server plan not found"), 166 }, 167 } 168 169 for _, tc := range cases { 170 err := tc.in.Validate(context.Background(), "tk1v") 171 require.Equal(t, tc.err, err, tc.msg) 172 } 173 } 174 175 func TestBuilder_Build(t *testing.T) { 176 cases := []struct { 177 msg string 178 in *Builder 179 out *BuildResult 180 err error 181 }{ 182 { 183 msg: "Validate func is called", 184 in: &Builder{}, 185 out: nil, 186 err: errors.New("client is empty"), 187 }, 188 { 189 msg: "finding server plan API returns error", 190 in: &Builder{ 191 Client: &APIClient{ 192 Switch: &dummySwitchReader{}, 193 PacketFilter: &dummyPackerFilterReader{}, 194 ServerPlan: &dummyPlanFinder{ 195 err: errors.New("dummy"), 196 }, 197 }, 198 }, 199 out: nil, 200 err: errors.New("dummy"), 201 }, 202 { 203 msg: "creating server returns error", 204 in: &Builder{ 205 Client: &APIClient{ 206 Switch: &dummySwitchReader{}, 207 PacketFilter: &dummyPackerFilterReader{}, 208 ServerPlan: &dummyPlanFinder{ 209 plans: []*sacloud.ServerPlan{ 210 { 211 ID: 1, 212 }, 213 }, 214 }, 215 Server: &dummyCreateServerHandler{ 216 err: errors.New("dummy"), 217 }, 218 }, 219 }, 220 out: nil, 221 err: errors.New("dummy"), 222 }, 223 { 224 msg: "validating disk returns error", 225 in: &Builder{ 226 DiskBuilders: []disk.Builder{ 227 &dummyDiskBuilder{ 228 err: errors.New("dummy"), 229 }, 230 }, 231 Client: &APIClient{ 232 Switch: &dummySwitchReader{}, 233 PacketFilter: &dummyPackerFilterReader{}, 234 ServerPlan: &dummyPlanFinder{ 235 plans: []*sacloud.ServerPlan{ 236 { 237 ID: 1, 238 }, 239 }, 240 }, 241 Server: &dummyCreateServerHandler{ 242 server: &sacloud.Server{ID: 1}, 243 }, 244 }, 245 }, 246 out: nil, 247 err: errors.New("dummy"), 248 }, 249 { 250 msg: "updating NIC returns error", 251 in: &Builder{ 252 NIC: &SharedNICSetting{ 253 PacketFilterID: 2, 254 }, 255 Client: &APIClient{ 256 Switch: &dummySwitchReader{}, 257 PacketFilter: &dummyPackerFilterReader{}, 258 ServerPlan: &dummyPlanFinder{ 259 plans: []*sacloud.ServerPlan{ 260 { 261 ID: 1, 262 }, 263 }, 264 }, 265 Server: &dummyCreateServerHandler{ 266 server: &sacloud.Server{ 267 ID: 1, 268 Interfaces: []*sacloud.InterfaceView{ 269 {ID: 1}, 270 }, 271 }, 272 }, 273 Interface: &dummyInterfaceHandler{ 274 err: errors.New("dummy"), 275 }, 276 }, 277 }, 278 out: &BuildResult{ServerID: 1}, 279 err: errors.New("dummy"), 280 }, 281 { 282 msg: "inserting CD-ROM returns error", 283 in: &Builder{ 284 CDROMID: 1, 285 Client: &APIClient{ 286 Switch: &dummySwitchReader{}, 287 PacketFilter: &dummyPackerFilterReader{}, 288 ServerPlan: &dummyPlanFinder{ 289 plans: []*sacloud.ServerPlan{ 290 { 291 ID: 1, 292 }, 293 }, 294 }, 295 Server: &dummyCreateServerHandler{ 296 server: &sacloud.Server{ID: 1}, 297 cdromErr: errors.New("dummy"), 298 }, 299 }, 300 }, 301 out: &BuildResult{ServerID: 1}, 302 err: errors.New("dummy"), 303 }, 304 { 305 msg: "booting server returns error", 306 in: &Builder{ 307 BootAfterCreate: true, 308 Client: &APIClient{ 309 Switch: &dummySwitchReader{}, 310 PacketFilter: &dummyPackerFilterReader{}, 311 ServerPlan: &dummyPlanFinder{ 312 plans: []*sacloud.ServerPlan{ 313 { 314 ID: 1, 315 }, 316 }, 317 }, 318 Server: &dummyCreateServerHandler{ 319 server: &sacloud.Server{ID: 1}, 320 bootErr: errors.New("dummy"), 321 }, 322 }, 323 }, 324 out: &BuildResult{ServerID: 1}, 325 err: errors.New("dummy"), 326 }, 327 } 328 for _, tc := range cases { 329 res, err := tc.in.Build(context.Background(), "tk1v") 330 require.Equal(t, tc.err, err, tc.msg) 331 require.Equal(t, tc.out, res, tc.msg) 332 } 333 } 334 335 type dummyDiskBuilder struct { 336 result *disk.BuildResult 337 updateResult *disk.UpdateResult 338 diskID types.ID 339 updateLevel builder.UpdateLevel 340 noWait bool 341 err error 342 } 343 344 func (d *dummyDiskBuilder) Validate(ctx context.Context, zone string) error { 345 return d.err 346 } 347 348 func (d *dummyDiskBuilder) Build(ctx context.Context, zone string, serverID types.ID) (*disk.BuildResult, error) { 349 if d.err != nil { 350 return nil, d.err 351 } 352 return d.result, nil 353 } 354 355 // Update ディスクの更新 356 func (d *dummyDiskBuilder) Update(ctx context.Context, zone string) (*disk.UpdateResult, error) { 357 if d.err != nil { 358 return nil, d.err 359 } 360 return d.updateResult, nil 361 } 362 363 func (d *dummyDiskBuilder) DiskID() types.ID { 364 return d.diskID 365 } 366 367 func (d *dummyDiskBuilder) UpdateLevel(ctx context.Context, zone string, disk *sacloud.Disk) builder.UpdateLevel { 368 return d.updateLevel 369 } 370 371 func (d *dummyDiskBuilder) NoWaitFlag() bool { 372 return d.noWait 373 } 374 375 func TestBuilder_Build_BlackBox(t *testing.T) { 376 var switchID types.ID 377 var diskIDs []types.ID 378 var blackboxBuilder *Builder 379 var buildResult *BuildResult 380 var testZone = testutil.TestZone() 381 382 testutil.RunCRUD(t, &testutil.CRUDTestCase{ 383 SetupAPICallerFunc: func() sacloud.APICaller { 384 return testutil.SingletonAPICaller() 385 }, 386 Parallel: true, 387 IgnoreStartupWait: true, 388 389 Setup: func(ctx *testutil.CRUDTestContext, caller sacloud.APICaller) error { 390 switchOp := sacloud.NewSwitchOp(caller) 391 sw, err := switchOp.Create(ctx, testZone, 392 &sacloud.SwitchCreateRequest{ 393 Name: "libsacloud-switch-for-builder", 394 }, 395 ) 396 if err != nil { 397 return err 398 } 399 switchID = sw.ID 400 blackboxBuilder = getBlackBoxTestBuilder(switchID) 401 return nil 402 }, 403 404 Create: &testutil.CRUDTestFunc{ 405 Func: func(ctx *testutil.CRUDTestContext, caller sacloud.APICaller) (interface{}, error) { 406 return blackboxBuilder.Build(ctx, testZone) 407 }, 408 SkipExtractID: true, 409 CheckFunc: func(t testutil.TestT, ctx *testutil.CRUDTestContext, v interface{}) error { 410 result := v.(*BuildResult) 411 err := testutil.DoAsserts( 412 testutil.AssertNotEmptyFunc(t, result.ServerID, "BuildResult.ServerID"), 413 testutil.AssertNotEmptyFunc(t, result.GeneratedSSHPrivateKey, "BuildResult.GeneratedSSHPrivateKey"), 414 ) 415 if err != nil { 416 return err 417 } 418 buildResult = result 419 ctx.ID = result.ServerID 420 return nil 421 }, 422 }, 423 424 Read: &testutil.CRUDTestFunc{ 425 Func: func(ctx *testutil.CRUDTestContext, caller sacloud.APICaller) (interface{}, error) { 426 serverOp := sacloud.NewServerOp(caller) 427 server, err := serverOp.Read(ctx, testZone, ctx.ID) 428 if err != nil { 429 return nil, err 430 } 431 diskIDs = []types.ID{} 432 for _, disk := range server.Disks { 433 diskIDs = append(diskIDs, disk.ID) 434 } 435 return server, nil 436 }, 437 CheckFunc: func(t testutil.TestT, ctx *testutil.CRUDTestContext, i interface{}) error { 438 if testutil.IsAccTest() && testZone != "tk1v" { // サンドボックス以外 439 time.Sleep(30 * time.Second) // sshd起動まで少し待つ 440 server := i.(*sacloud.Server) 441 ip := server.Interfaces[0].IPAddress 442 return connectToServerViaSSH(t, "root", ip, []byte(buildResult.GeneratedSSHPrivateKey), []byte("libsacloud-test-passphrase")) 443 } 444 return nil 445 }, 446 SkipExtractID: true, 447 }, 448 Updates: []*testutil.CRUDTestFunc{ 449 { 450 Func: func(ctx *testutil.CRUDTestContext, caller sacloud.APICaller) (interface{}, error) { 451 blackboxBuilder.AdditionalNICs = []AdditionalNICSettingHolder{blackboxBuilder.AdditionalNICs[1]} 452 blackboxBuilder.DiskBuilders = append(blackboxBuilder.DiskBuilders, &disk.BlankBuilder{ 453 Name: "libsacloud-disk-builder", 454 SizeGB: 20, 455 PlanID: types.DiskPlans.SSD, 456 Connection: types.DiskConnections.VirtIO, 457 Description: "libsacloud-disk-builder-description", 458 Tags: types.Tags{"tag1", "tag2"}, 459 Client: disk.NewBuildersAPIClient(testutil.SingletonAPICaller()), 460 }) 461 return blackboxBuilder.Update(ctx, testZone) 462 }, 463 SkipExtractID: true, 464 CheckFunc: func(t testutil.TestT, ctx *testutil.CRUDTestContext, v interface{}) error { 465 result := v.(*BuildResult) 466 buildResult = result 467 ctx.ID = result.ServerID 468 return nil 469 }, 470 }, 471 }, 472 Shutdown: func(ctx *testutil.CRUDTestContext, caller sacloud.APICaller) error { 473 return power.ShutdownServer(ctx, sacloud.NewServerOp(caller), testZone, ctx.ID, true) 474 }, 475 Delete: &testutil.CRUDTestDeleteFunc{ 476 Func: func(ctx *testutil.CRUDTestContext, caller sacloud.APICaller) error { 477 serverOp := sacloud.NewServerOp(caller) 478 if err := serverOp.DeleteWithDisks(ctx, testZone, ctx.ID, &sacloud.ServerDeleteWithDisksRequest{IDs: diskIDs}); err != nil { 479 return err 480 } 481 482 switchOp := sacloud.NewSwitchOp(caller) 483 return switchOp.Delete(ctx, testZone, switchID) 484 }, 485 }, 486 }) 487 } 488 489 func getBlackBoxTestBuilder(switchID types.ID) *Builder { 490 return &Builder{ 491 Name: "libsacloud-server-builder", 492 CPU: 1, 493 MemoryGB: 1, 494 Description: "libsacloud-server-builder-description", 495 Tags: types.Tags{"tag1", "tag2"}, 496 BootAfterCreate: true, 497 NIC: &SharedNICSetting{}, 498 AdditionalNICs: []AdditionalNICSettingHolder{ 499 &DisconnectedNICSetting{}, 500 &ConnectedNICSetting{SwitchID: switchID}, 501 }, 502 DiskBuilders: []disk.Builder{ 503 &disk.FromUnixBuilder{ 504 OSType: ostype.CentOS, 505 Name: "libsacloud-disk-builder", 506 SizeGB: 20, 507 PlanID: types.DiskPlans.SSD, 508 Connection: types.DiskConnections.VirtIO, 509 Description: "libsacloud-disk-builder-description", 510 Tags: types.Tags{"tag1", "tag2"}, 511 EditParameter: &disk.UnixEditRequest{ 512 HostName: "libsacloud-disk-builder", 513 Password: "libsacloud-test-password", 514 DisablePWAuth: true, 515 EnableDHCP: false, 516 ChangePartitionUUID: true, 517 IsSSHKeysEphemeral: true, 518 GenerateSSHKeyName: "libsacloud-sshkey-generated", 519 GenerateSSHKeyDescription: "libsacloud-sshkey-generated-for-builder", 520 GenerateSSHKeyPassPhrase: "libsacloud-test-passphrase", 521 //IPAddress string 522 //NetworkMaskLen int 523 //DefaultRoute string 524 //SSHKeys []string 525 //SSHKeyIDs []types.ID 526 IsNotesEphemeral: true, 527 NoteContents: []string{ 528 `libsacloud-startup-script-for-builder`, 529 }, 530 //Notes []*sacloud.DiskEditNote{}, 531 }, 532 Client: disk.NewBuildersAPIClient(testutil.SingletonAPICaller()), 533 }, 534 }, 535 Client: NewBuildersAPIClient(testutil.SingletonAPICaller()), 536 } 537 } 538 539 func connectToServerViaSSH(t testutil.TestT, user, ip string, privateKey []byte, passPhrase []byte) error { 540 signer, err := ssh.ParsePrivateKeyWithPassphrase(privateKey, passPhrase) 541 if err != nil { 542 return err 543 } 544 545 config := &ssh.ClientConfig{ 546 User: user, 547 Auth: []ssh.AuthMethod{ 548 ssh.PublicKeys(signer), 549 }, 550 HostKeyCallback: ssh.InsecureIgnoreHostKey(), 551 Timeout: 10 * time.Second, 552 } 553 554 client, err := ssh.Dial("tcp", ip+":22", config) 555 if err != nil { 556 return err 557 } 558 defer client.Close() 559 560 session, err := client.NewSession() 561 if err != nil { 562 return err 563 } 564 defer session.Close() 565 566 var b bytes.Buffer 567 session.Stdout = &b 568 if err := session.Run("/usr/bin/whoami"); err != nil { 569 return err 570 } 571 t.Logf("Connect to the Server via SSH: `whoami`: %s\n", b.String()) 572 return nil 573 } 574 575 func TestBuilder_IsNeedShutdown(t *testing.T) { 576 cases := []struct { 577 msg string 578 in *Builder 579 expect bool 580 err error 581 }{ 582 { 583 msg: "server id is empty", 584 expect: false, 585 err: errors.New("server id required"), 586 in: &Builder{}, 587 }, 588 { 589 msg: "in-place update", 590 expect: false, 591 err: nil, 592 in: &Builder{ 593 ServerID: types.ID(1), 594 Name: "update", 595 Description: "update", 596 Tags: types.Tags{"update1", "update2"}, 597 Client: &APIClient{ 598 Server: &dummyCreateServerHandler{ 599 server: &sacloud.Server{ 600 ID: types.ID(1), 601 Name: "update", 602 Description: "update", 603 Tags: types.Tags{"update1", "update2-upd"}, 604 }, 605 }, 606 }, 607 }, 608 }, 609 { 610 msg: "changed: PrivateHostID", 611 expect: true, 612 err: nil, 613 in: &Builder{ 614 ServerID: types.ID(1), 615 PrivateHostID: types.ID(2), 616 Client: &APIClient{ 617 Server: &dummyCreateServerHandler{ 618 server: &sacloud.Server{ 619 ID: types.ID(1), 620 PrivateHostID: types.ID(3), 621 }, 622 }, 623 }, 624 }, 625 }, 626 { 627 msg: "changed: InterfaceDriver", 628 expect: true, 629 err: nil, 630 in: &Builder{ 631 ServerID: types.ID(1), 632 InterfaceDriver: types.InterfaceDrivers.E1000, 633 Client: &APIClient{ 634 Server: &dummyCreateServerHandler{ 635 server: &sacloud.Server{ 636 ID: types.ID(1), 637 InterfaceDriver: types.InterfaceDrivers.VirtIO, 638 }, 639 }, 640 }, 641 }, 642 }, 643 { 644 msg: "changed: Memory Size", 645 expect: true, 646 err: nil, 647 in: &Builder{ 648 ServerID: types.ID(1), 649 MemoryGB: 1, 650 Client: &APIClient{ 651 Server: &dummyCreateServerHandler{ 652 server: &sacloud.Server{ 653 ID: types.ID(1), 654 MemoryMB: 2, 655 }, 656 }, 657 }, 658 }, 659 }, 660 { 661 msg: "changed: CPU", 662 expect: true, 663 err: nil, 664 in: &Builder{ 665 ServerID: types.ID(1), 666 CPU: 1, 667 Client: &APIClient{ 668 Server: &dummyCreateServerHandler{ 669 server: &sacloud.Server{ 670 ID: types.ID(1), 671 CPU: 2, 672 }, 673 }, 674 }, 675 }, 676 }, 677 { 678 msg: "changed: Commitment", 679 expect: true, 680 err: nil, 681 in: &Builder{ 682 ServerID: types.ID(1), 683 CPU: 1, 684 Client: &APIClient{ 685 Server: &dummyCreateServerHandler{ 686 server: &sacloud.Server{ 687 ID: types.ID(1), 688 CPU: 1, 689 ServerPlanCommitment: types.Commitments.DedicatedCPU, 690 }, 691 }, 692 }, 693 }, 694 }, 695 { 696 msg: "changed: add NIC", 697 expect: true, 698 err: nil, 699 in: &Builder{ 700 ServerID: types.ID(1), 701 NIC: &SharedNICSetting{}, 702 Client: &APIClient{ 703 Server: &dummyCreateServerHandler{ 704 server: &sacloud.Server{ 705 ID: types.ID(1), 706 }, 707 }, 708 }, 709 }, 710 }, 711 { 712 msg: "changed: delete NIC", 713 expect: true, 714 err: nil, 715 in: &Builder{ 716 ServerID: types.ID(1), 717 Client: &APIClient{ 718 Server: &dummyCreateServerHandler{ 719 server: &sacloud.Server{ 720 ID: types.ID(1), 721 Interfaces: []*sacloud.InterfaceView{ 722 { 723 ID: types.ID(2), 724 SwitchScope: types.Scopes.Shared, 725 PacketFilterID: 0, 726 UpstreamType: types.UpstreamNetworkTypes.Shared, 727 }, 728 }, 729 }, 730 }, 731 }, 732 }, 733 }, 734 { 735 msg: "changed: Packet Filter ID", 736 expect: false, 737 err: nil, 738 in: &Builder{ 739 ServerID: types.ID(1), 740 NIC: &SharedNICSetting{ 741 PacketFilterID: types.ID(10), 742 }, 743 Client: &APIClient{ 744 Server: &dummyCreateServerHandler{ 745 server: &sacloud.Server{ 746 ID: types.ID(1), 747 Interfaces: []*sacloud.InterfaceView{ 748 { 749 ID: types.ID(2), 750 SwitchScope: types.Scopes.Shared, 751 PacketFilterID: types.ID(11), 752 UpstreamType: types.UpstreamNetworkTypes.Shared, 753 }, 754 }, 755 }, 756 }, 757 }, 758 }, 759 }, 760 } 761 762 for _, tc := range cases { 763 got, err := tc.in.IsNeedShutdown(context.Background(), testutil.TestZone()) 764 require.Equal(t, tc.err, err, tc.msg) 765 require.Equal(t, tc.expect, got, tc.msg) 766 } 767 } 768 769 func TestBuilder_UpdateWithPreviousID(t *testing.T) { 770 ctx := context.Background() 771 builder := &Builder{ 772 Name: testutil.ResourceName("server-builder"), 773 CPU: 1, 774 MemoryGB: 1, 775 Commitment: types.Commitments.Standard, 776 Generation: types.PlanGenerations.Default, 777 Tags: types.Tags{"tag1", "tag2"}, 778 BootAfterCreate: false, 779 Client: NewBuildersAPIClient(testutil.SingletonAPICaller()), 780 ForceShutdown: true, 781 } 782 createResult, err := builder.Build(ctx, testutil.TestZone()) 783 if err != nil { 784 t.Fatal(err) 785 } 786 787 serverOp := sacloud.NewServerOp(testutil.SingletonAPICaller()) 788 server, err := serverOp.Read(ctx, testutil.TestZone(), createResult.ServerID) 789 if err != nil { 790 t.Fatal(err) 791 } 792 793 require.EqualValues(t, builder.Tags, server.Tags) 794 795 // プラン変更 796 builder.ServerID = server.ID 797 builder.CPU = 2 798 builder.MemoryGB = 4 799 800 updateResult, err := builder.Update(ctx, testutil.TestZone()) 801 if err != nil { 802 t.Fatal(err) 803 } 804 805 // IDが変更されているはず 806 require.True(t, createResult.ServerID != updateResult.ServerID) 807 updated, err := serverOp.Read(ctx, testutil.TestZone(), updateResult.ServerID) 808 if err != nil { 809 t.Fatal(err) 810 } 811 812 require.EqualValues(t, plans.AppendPreviousIDTagIfAbsent(server.Tags, server.ID), updated.Tags) 813 814 // cleanup 815 if err := serverOp.Delete(ctx, testutil.TestZone(), updated.ID); err != nil { 816 t.Fatal(err) 817 } 818 } 819 820 func TestBuilder_GPUPlan(t *testing.T) { 821 if !testutil.IsAccTest() { 822 t.Skip("TestBuilder_GPUPlan only exec when running an Acceptance Test") 823 } 824 825 ctx := context.Background() 826 zone := "is1a" 827 builder := &Builder{ 828 Name: testutil.ResourceName("server-builder"), 829 CPU: 4, 830 MemoryGB: 56, 831 GPU: 1, 832 BootAfterCreate: false, 833 Client: NewBuildersAPIClient(testutil.SingletonAPICaller()), 834 ForceShutdown: true, 835 } 836 withGPU, err := builder.Build(ctx, zone) 837 if err != nil { 838 t.Fatal(err) 839 } 840 841 // for update 842 builder.ServerID = withGPU.ServerID 843 builder.GPU = 0 844 _, err = builder.Update(ctx, zone) 845 require.Error(t, err, "server plan not found") 846 847 // cleanup 848 serverOp := sacloud.NewServerOp(testutil.SingletonAPICaller()) 849 if err := serverOp.Delete(ctx, zone, withGPU.ServerID); err != nil { 850 t.Fatal(err) 851 } 852 }