implement vpc environment

This commit is contained in:
sangkyu.kim 2021-03-23 16:35:36 +09:00
parent af865b1591
commit cd370aaaad
32 changed files with 1611 additions and 390 deletions

View File

@ -32,7 +32,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
}
func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) (packersdk.Artifact, error) {
ui.Message("Creating Naver Cloud Platform Connection ...")
ui.Message("Creating NAVER CLOUD PLATFORM Connection ...")
config := Config{
AccessKey: b.config.AccessKey,
SecretKey: b.config.SecretKey,
@ -46,60 +46,41 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
b.stateBag.Put("hook", hook)
b.stateBag.Put("ui", ui)
var steps []multistep.Step
steps = []multistep.Step{}
if b.config.Comm.Type == "ssh" {
steps = []multistep.Step{
NewStepValidateTemplate(conn, ui, &b.config),
NewStepCreateLoginKey(conn, ui),
NewStepCreateServerInstance(conn, ui, &b.config),
NewStepCreateBlockStorageInstance(conn, ui, &b.config),
NewStepGetRootPassword(conn, ui, &b.config),
NewStepCreatePublicIPInstance(conn, ui, &b.config),
&communicator.StepConnectSSH{
Config: &b.config.Comm,
Host: func(stateBag multistep.StateBag) (string, error) {
return stateBag.Get("PublicIP").(string), nil
},
SSHConfig: b.config.Comm.SSHConfigFunc(),
steps := []multistep.Step{
NewStepValidateTemplate(conn, ui, &b.config),
NewStepCreateLoginKey(conn, ui, &b.config),
multistep.If(b.config.SupportVPC, NewStepCreateInitScript(conn, ui, &b.config)),
NewStepCreateServerInstance(conn, ui, &b.config),
NewStepCreateBlockStorage(conn, ui, &b.config),
NewStepGetRootPassword(conn, ui, &b.config),
NewStepCreatePublicIP(conn, ui, &b.config),
multistep.If(b.config.Comm.Type == "ssh", &communicator.StepConnectSSH{
Config: &b.config.Comm,
Host: func(stateBag multistep.StateBag) (string, error) {
return stateBag.Get("public_ip").(string), nil
},
&commonsteps.StepProvision{},
&commonsteps.StepCleanupTempKeys{
Comm: &b.config.Comm,
SSHConfig: b.config.Comm.SSHConfigFunc(),
}),
multistep.If(b.config.Comm.Type == "winrm", &communicator.StepConnectWinRM{
Config: &b.config.Comm,
Host: func(stateBag multistep.StateBag) (string, error) {
return stateBag.Get("public_ip").(string), nil
},
NewStepStopServerInstance(conn, ui),
NewStepCreateServerImage(conn, ui, &b.config),
NewStepDeleteBlockStorageInstance(conn, ui, &b.config),
NewStepTerminateServerInstance(conn, ui),
}
} else if b.config.Comm.Type == "winrm" {
steps = []multistep.Step{
NewStepValidateTemplate(conn, ui, &b.config),
NewStepCreateLoginKey(conn, ui),
NewStepCreateServerInstance(conn, ui, &b.config),
NewStepCreateBlockStorageInstance(conn, ui, &b.config),
NewStepGetRootPassword(conn, ui, &b.config),
NewStepCreatePublicIPInstance(conn, ui, &b.config),
&communicator.StepConnectWinRM{
Config: &b.config.Comm,
Host: func(stateBag multistep.StateBag) (string, error) {
return stateBag.Get("PublicIP").(string), nil
},
WinRMConfig: func(state multistep.StateBag) (*communicator.WinRMConfig, error) {
return &communicator.WinRMConfig{
Username: b.config.Comm.WinRMUser,
Password: b.config.Comm.WinRMPassword,
}, nil
},
WinRMConfig: func(state multistep.StateBag) (*communicator.WinRMConfig, error) {
return &communicator.WinRMConfig{
Username: b.config.Comm.WinRMUser,
Password: b.config.Comm.WinRMPassword,
}, nil
},
&commonsteps.StepProvision{},
NewStepStopServerInstance(conn, ui),
NewStepCreateServerImage(conn, ui, &b.config),
NewStepDeleteBlockStorageInstance(conn, ui, &b.config),
NewStepTerminateServerInstance(conn, ui),
}
}),
&commonsteps.StepProvision{},
multistep.If(b.config.Comm.Type == "ssh", &commonsteps.StepCleanupTempKeys{
Comm: &b.config.Comm,
}),
NewStepStopServerInstance(conn, ui, &b.config),
NewStepCreateServerImage(conn, ui, &b.config),
NewStepDeleteBlockStorage(conn, ui, &b.config),
NewStepTerminateServerInstance(conn, ui, &b.config),
}
// Run!
@ -107,7 +88,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
b.runner.Run(ctx, b.stateBag)
// If there was an error, return that
if rawErr, ok := b.stateBag.GetOk("Error"); ok {
if rawErr, ok := b.stateBag.GetOk("error"); ok {
return nil, rawErr.(error)
}
@ -116,7 +97,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
StateData: map[string]interface{}{"generated_data": b.stateBag.Get("generated_data")},
}
if serverImage, ok := b.stateBag.GetOk("memberServerImage"); ok {
if serverImage, ok := b.stateBag.GetOk("member_server_image"); ok {
artifact.MemberServerImage = serverImage.(*server.MemberServerImage)
}

View File

@ -6,6 +6,8 @@ package ncloud
import (
"errors"
"fmt"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vpc"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
"os"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/ncloud"
@ -50,12 +52,19 @@ type Config struct {
BlockStorageSize int `mapstructure:"block_storage_size" required:"false"`
// Name of the region where you want to create an image.
// (default: Korea)
Region string `mapstructure:"region" required:"false"`
Region string `mapstructure:"region" required:"false"`
RegionCode string `mapstructure:"region_code" required:"false"`
// This is used to allow
// winrm access when you create a Windows server. An ACG that specifies an
// access source (0.0.0.0/0) and allowed port (5985) must be created in
// advance.
AccessControlGroupConfigurationNo string `mapstructure:"access_control_group_configuration_no" required:"false"`
// Whether to use VPC. By default, the value is false on "public" site. If you want to use VPC environment. Please set this value true.
SupportVPC bool `mapstructure:"support_vpc" required:"false"`
// The ID of the associated Subnet
SubnetNo string `mapstructure:"subnet_no" required:"false"`
// The ID of the VPC where you want to place the Server Instance
VpcNo string `mapstructure:"vpc_no" required:"false"`
Comm communicator.Config `mapstructure:",squash"`
ctx *interpolate.Context
@ -63,7 +72,7 @@ type Config struct {
// NewConfig checks parameters
func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
warnings := []string{}
var warnings []string
err := config.Decode(c, &config.DecodeOpts{
Interpolate: true,
@ -81,59 +90,63 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
}
if c.AccessKey == "" {
errs = packersdk.MultiErrorAppend(errs, errors.New("access_key is required"))
errs = packersdk.MultiErrorAppend(errs, errors.New("`access_key` is required"))
}
if c.SecretKey == "" {
errs = packersdk.MultiErrorAppend(errs, errors.New("secret_key is required"))
errs = packersdk.MultiErrorAppend(errs, errors.New("`secret_key` is required"))
}
if c.MemberServerImageNo == "" && c.ServerImageProductCode == "" {
errs = packersdk.MultiErrorAppend(errs, errors.New("server_image_product_code or member_server_image_no is required"))
errs = packersdk.MultiErrorAppend(errs, errors.New("`server_image_product_code` or `member_server_image_no` is required"))
}
if c.MemberServerImageNo != "" && c.ServerImageProductCode != "" {
errs = packersdk.MultiErrorAppend(errs, errors.New("Only one of server_image_product_code and member_server_image_no can be set"))
errs = packersdk.MultiErrorAppend(errs, errors.New("only one of `server_image_product_code` and `member_server_image_no` can be set"))
}
if c.ServerImageProductCode != "" && len(c.ServerImageProductCode) > 20 {
errs = packersdk.MultiErrorAppend(errs, errors.New("If server_image_product_code field is set, length of server_image_product_code should be max 20"))
if c.ServerImageProductCode != "" && len(c.ServerImageProductCode) > 50 {
errs = packersdk.MultiErrorAppend(errs, errors.New("if `server_image_product_code` field is set, length of `server_image_product_code` should be max 20"))
}
if c.ServerProductCode != "" && len(c.ServerProductCode) > 20 {
errs = packersdk.MultiErrorAppend(errs, errors.New("If server_product_code field is set, length of server_product_code should be max 20"))
if c.ServerProductCode != "" && len(c.ServerProductCode) > 50 {
errs = packersdk.MultiErrorAppend(errs, errors.New("if `server_product_code` field is set, length of `server_product_code` should be max 20"))
}
if c.ServerImageName != "" && (len(c.ServerImageName) < 3 || len(c.ServerImageName) > 30) {
errs = packersdk.MultiErrorAppend(errs, errors.New("If server_image_name field is set, length of server_image_name should be min 3 and max 20"))
errs = packersdk.MultiErrorAppend(errs, errors.New("if `server_image_name` field is set, length of `server_image_name` should be min 3 and max 20"))
}
if c.ServerImageDescription != "" && len(c.ServerImageDescription) > 1000 {
errs = packersdk.MultiErrorAppend(errs, errors.New("If server_image_description field is set, length of server_image_description should be max 1000"))
errs = packersdk.MultiErrorAppend(errs, errors.New("if `server_image_description` field is set, length of `server_image_description` should be max 1000"))
}
if c.BlockStorageSize != 0 {
if c.BlockStorageSize < 10 || c.BlockStorageSize > 2000 {
errs = packersdk.MultiErrorAppend(errs, errors.New("The size of BlockStorageSize is at least 10 GB and up to 2000GB"))
errs = packersdk.MultiErrorAppend(errs, errors.New("the size of `block_storage_size` is at least 10 GB and up to 2000GB"))
} else if int(c.BlockStorageSize/10)*10 != c.BlockStorageSize {
return nil, errors.New("BlockStorageSize must be a multiple of 10 GB")
}
}
if c.UserData != "" && c.UserDataFile != "" {
errs = packersdk.MultiErrorAppend(errs, errors.New("Only one of user_data or user_data_file can be specified."))
errs = packersdk.MultiErrorAppend(errs, errors.New("only one of user_data or `user_data`_file can be specified."))
} else if c.UserDataFile != "" {
if _, err := os.Stat(c.UserDataFile); err != nil {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("user_data_file not found: %s", c.UserDataFile))
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("`user_data_file` not found: %s", c.UserDataFile))
}
}
if c.UserData != "" && len(c.UserData) > 21847 {
errs = packersdk.MultiErrorAppend(errs, errors.New("If user_data field is set, length of UserData should be max 21847"))
errs = packersdk.MultiErrorAppend(errs, errors.New("if `user_data` field is set, length of UserData should be max 21847"))
}
if c.Comm.Type == "winrm" && c.AccessControlGroupConfigurationNo == "" {
errs = packersdk.MultiErrorAppend(errs, errors.New("If Communicator is winrm, access_control_group_configuration_no is required"))
errs = packersdk.MultiErrorAppend(errs, errors.New("if Communicator is winrm, `access_control_group_configuration_no` (allow 5986 port) is required"))
}
if c.VpcNo != "" || c.SubnetNo != "" {
c.SupportVPC = true
}
if errs != nil && len(errs.Errors) > 0 {
@ -144,7 +157,9 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
}
type NcloudAPIClient struct {
server *server.APIClient
server *server.APIClient
vserver *vserver.APIClient
vpc *vpc.APIClient
}
func (c *Config) Client() (*NcloudAPIClient, error) {
@ -153,6 +168,8 @@ func (c *Config) Client() (*NcloudAPIClient, error) {
SecretKey: c.SecretKey,
}
return &NcloudAPIClient{
server: server.NewAPIClient(server.NewConfiguration(apiKey)),
server: server.NewAPIClient(server.NewConfiguration(apiKey)),
vserver: vserver.NewAPIClient(vserver.NewConfiguration(apiKey)),
vpc: vpc.NewAPIClient(vpc.NewConfiguration(apiKey)),
}, nil
}

View File

@ -29,7 +29,11 @@ type FlatConfig struct {
UserDataFile *string `mapstructure:"user_data_file" required:"false" cty:"user_data_file" hcl:"user_data_file"`
BlockStorageSize *int `mapstructure:"block_storage_size" required:"false" cty:"block_storage_size" hcl:"block_storage_size"`
Region *string `mapstructure:"region" required:"false" cty:"region" hcl:"region"`
RegionCode *string `mapstructure:"region_code" required:"false" cty:"region_code" hcl:"region_code"`
AccessControlGroupConfigurationNo *string `mapstructure:"access_control_group_configuration_no" required:"false" cty:"access_control_group_configuration_no" hcl:"access_control_group_configuration_no"`
SupportVPC *bool `mapstructure:"support_vpc" required:"false" cty:"support_vpc" hcl:"support_vpc"`
SubnetNo *string `mapstructure:"subnet_no" required:"false" cty:"subnet_no" hcl:"subnet_no"`
VpcNo *string `mapstructure:"vpc_no" required:"false" cty:"vpc_no" hcl:"vpc_no"`
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
@ -112,7 +116,11 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"user_data_file": &hcldec.AttrSpec{Name: "user_data_file", Type: cty.String, Required: false},
"block_storage_size": &hcldec.AttrSpec{Name: "block_storage_size", Type: cty.Number, Required: false},
"region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false},
"region_code": &hcldec.AttrSpec{Name: "region_code", Type: cty.String, Required: false},
"access_control_group_configuration_no": &hcldec.AttrSpec{Name: "access_control_group_configuration_no", Type: cty.String, Required: false},
"support_vpc": &hcldec.AttrSpec{Name: "support_vpc", Type: cty.Bool, Required: false},
"subnet_no": &hcldec.AttrSpec{Name: "subnet_no", Type: cty.String, Required: false},
"vpc_no": &hcldec.AttrSpec{Name: "vpc_no", Type: cty.String, Required: false},
"communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false},
"pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false},
"ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false},

View File

@ -6,7 +6,7 @@ import (
func processStepResult(err error, sayError func(error), state multistep.StateBag) multistep.StepAction {
if err != nil {
state.Put("Error", err)
state.Put("error", err)
sayError(err)
return multistep.ActionHalt

View File

@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
"log"
"time"
@ -13,34 +14,49 @@ import (
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
const (
BlockStorageStatusAttached = "ATTAC"
BlockStorageStatusDetached = "DETAC"
)
// StepCreateBlockStorageInstance struct is for making extra block storage
type StepCreateBlockStorageInstance struct {
Conn *NcloudAPIClient
CreateBlockStorageInstance func(serverInstanceNo string) (*string, error)
Say func(message string)
Error func(e error)
Config *Config
type StepCreateBlockStorage struct {
Conn *NcloudAPIClient
CreateBlockStorage func(serverInstanceNo string) (*string, error)
DeleteBlockStorage func(blockStorageInstanceNo string) error
WaiterBlockStorageStatus func(conn *NcloudAPIClient, blockStorageInstanceNo *string, status string, timeout time.Duration) error
Say func(message string)
Error func(e error)
Config *Config
}
// NewStepCreateBlockStorageInstance make StepCreateBlockStorage struct to make extra block storage
func NewStepCreateBlockStorageInstance(conn *NcloudAPIClient, ui packersdk.Ui, config *Config) *StepCreateBlockStorageInstance {
var step = &StepCreateBlockStorageInstance{
func NewStepCreateBlockStorage(conn *NcloudAPIClient, ui packersdk.Ui, config *Config) *StepCreateBlockStorage {
var step = &StepCreateBlockStorage{
Conn: conn,
Say: func(message string) { ui.Say(message) },
Error: func(e error) { ui.Error(e.Error()) },
Config: config,
}
step.CreateBlockStorageInstance = step.createBlockStorageInstance
if config.SupportVPC {
step.CreateBlockStorage = step.createVpcBlockStorage
step.DeleteBlockStorage = step.deleteVpcBlockStorage
step.WaiterBlockStorageStatus = waiterVpcBlockStorageStatus
} else {
step.CreateBlockStorage = step.createClassicBlockStorage
step.DeleteBlockStorage = step.deleteClassicBlockStorage
step.WaiterBlockStorageStatus = waiterClassicBlockStorageStatus
}
return step
}
func (s *StepCreateBlockStorageInstance) createBlockStorageInstance(serverInstanceNo string) (*string, error) {
reqParams := new(server.CreateBlockStorageInstanceRequest)
reqParams.BlockStorageSize = ncloud.Int64(int64(s.Config.BlockStorageSize))
reqParams.ServerInstanceNo = &serverInstanceNo
func (s *StepCreateBlockStorage) createClassicBlockStorage(serverInstanceNo string) (*string, error) {
reqParams := &server.CreateBlockStorageInstanceRequest{
BlockStorageSize: ncloud.Int64(int64(s.Config.BlockStorageSize)),
ServerInstanceNo: &serverInstanceNo,
}
resp, err := s.Conn.server.V2Api.CreateBlockStorageInstance(reqParams)
if err != nil {
@ -50,31 +66,82 @@ func (s *StepCreateBlockStorageInstance) createBlockStorageInstance(serverInstan
blockStorageInstance := resp.BlockStorageInstanceList[0]
log.Println("Block Storage Instance information : ", blockStorageInstance.BlockStorageInstanceNo)
if err := waiterBlockStorageInstanceStatus(s.Conn, blockStorageInstance.BlockStorageInstanceNo, "ATTAC", 10*time.Minute); err != nil {
if err := waiterClassicBlockStorageStatus(s.Conn, blockStorageInstance.BlockStorageInstanceNo, BlockStorageStatusAttached, 10*time.Minute); err != nil {
return nil, errors.New("TIMEOUT : Block Storage instance status is not attached")
}
return blockStorageInstance.BlockStorageInstanceNo, nil
}
func (s *StepCreateBlockStorageInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
func (s *StepCreateBlockStorage) createVpcBlockStorage(serverInstanceNo string) (*string, error) {
reqParams := &vserver.CreateBlockStorageInstanceRequest{
RegionCode: &s.Config.RegionCode,
BlockStorageSize: ncloud.Int32(int32(s.Config.BlockStorageSize)),
BlockStorageDescription: nil,
ServerInstanceNo: &serverInstanceNo,
BlockStorageSnapshotInstanceNo: nil,
ZoneCode: nil,
}
resp, err := s.Conn.vserver.V2Api.CreateBlockStorageInstance(reqParams)
if err != nil {
return nil, err
}
blockStorageInstance := resp.BlockStorageInstanceList[0]
log.Println("Block Storage Instance information : ", blockStorageInstance.BlockStorageInstanceNo)
if err := s.WaiterBlockStorageStatus(s.Conn, blockStorageInstance.BlockStorageInstanceNo, BlockStorageStatusAttached, 10*time.Minute); err != nil {
return nil, errors.New("TIMEOUT : Block Storage instance status is not attached")
}
return blockStorageInstance.BlockStorageInstanceNo, nil
}
func (s *StepCreateBlockStorage) deleteClassicBlockStorage(blockStorageInstanceNo string) error {
reqParams := &server.DeleteBlockStorageInstancesRequest{
BlockStorageInstanceNoList: []*string{&blockStorageInstanceNo},
}
_, err := s.Conn.server.V2Api.DeleteBlockStorageInstances(reqParams)
if err != nil {
return err
}
return nil
}
func (s *StepCreateBlockStorage) deleteVpcBlockStorage(blockStorageInstanceNo string) error {
reqParams := &vserver.DeleteBlockStorageInstancesRequest{
BlockStorageInstanceNoList: []*string{&blockStorageInstanceNo},
}
_, err := s.Conn.vserver.V2Api.DeleteBlockStorageInstances(reqParams)
if err != nil {
return err
}
return nil
}
func (s *StepCreateBlockStorage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
if s.Config.BlockStorageSize == 0 {
return processStepResult(nil, s.Error, state)
}
s.Say("Create extra block storage instance")
serverInstanceNo := state.Get("InstanceNo").(string)
serverInstanceNo := state.Get("instance_no").(string)
blockStorageInstanceNo, err := s.CreateBlockStorageInstance(serverInstanceNo)
blockStorageInstanceNo, err := s.CreateBlockStorage(serverInstanceNo)
if err == nil {
state.Put("BlockStorageInstanceNo", *blockStorageInstanceNo)
state.Put("block_storage_instance_no", *blockStorageInstanceNo)
}
return processStepResult(err, s.Error, state)
}
func (s *StepCreateBlockStorageInstance) Cleanup(state multistep.StateBag) {
func (s *StepCreateBlockStorage) Cleanup(state multistep.StateBag) {
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
@ -86,20 +153,17 @@ func (s *StepCreateBlockStorageInstance) Cleanup(state multistep.StateBag) {
return
}
if blockStorageInstanceNo, ok := state.GetOk("BlockStorageInstanceNo"); ok {
if blockStorageInstanceNo, ok := state.GetOk("block_storage_instance_no"); ok {
s.Say("Clean up Block Storage Instance")
reqParams := server.DeleteBlockStorageInstancesRequest{
BlockStorageInstanceNoList: []*string{blockStorageInstanceNo.(*string)},
}
blockStorageInstanceList, err := s.Conn.server.V2Api.DeleteBlockStorageInstances(&reqParams)
err := s.DeleteBlockStorage(blockStorageInstanceNo.(string))
if err != nil {
s.Error(err)
return
}
s.Say(fmt.Sprintf("Block Storage Instance is deleted. Block Storage InstanceNo is %s", blockStorageInstanceNo.(string)))
log.Println("Block Storage Instance information : ", blockStorageInstanceList.BlockStorageInstanceList[0])
if err := waiterBlockStorageInstanceStatus(s.Conn, blockStorageInstanceNo.(*string), "DETAC", time.Minute); err != nil {
if err := s.WaiterBlockStorageStatus(s.Conn, blockStorageInstanceNo.(*string), BlockStorageStatusDetached, time.Minute); err != nil {
s.Say("TIMEOUT : Block Storage instance status is not deattached")
}
}

View File

@ -10,11 +10,11 @@ import (
func TestStepCreateBlockStorageInstanceShouldFailIfOperationCreateBlockStorageInstanceFails(t *testing.T) {
var testSubject = &StepCreateBlockStorageInstance{
CreateBlockStorageInstance: func(serverInstanceNo string) (*string, error) { return nil, fmt.Errorf("!! Unit Test FAIL !!") },
Say: func(message string) {},
Error: func(e error) {},
Config: new(Config),
var testSubject = &StepCreateBlockStorage{
CreateBlockStorage: func(serverInstanceNo string) (*string, error) { return nil, fmt.Errorf("!! Unit Test FAIL !!") },
Say: func(message string) {},
Error: func(e error) {},
Config: new(Config),
}
testSubject.Config.BlockStorageSize = 10
@ -27,18 +27,18 @@ func TestStepCreateBlockStorageInstanceShouldFailIfOperationCreateBlockStorageIn
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
}
if _, ok := stateBag.GetOk("Error"); ok == false {
if _, ok := stateBag.GetOk("error"); ok == false {
t.Fatal("Expected the step to set stateBag['Error'], but it was not.")
}
}
func TestStepCreateBlockStorageInstanceShouldPassIfOperationCreateBlockStorageInstancePasses(t *testing.T) {
var instanceNo = "a"
var testSubject = &StepCreateBlockStorageInstance{
CreateBlockStorageInstance: func(serverInstanceNo string) (*string, error) { return &instanceNo, nil },
Say: func(message string) {},
Error: func(e error) {},
Config: new(Config),
var testSubject = &StepCreateBlockStorage{
CreateBlockStorage: func(serverInstanceNo string) (*string, error) { return &instanceNo, nil },
Say: func(message string) {},
Error: func(e error) {},
Config: new(Config),
}
testSubject.Config.BlockStorageSize = 10
@ -51,7 +51,7 @@ func TestStepCreateBlockStorageInstanceShouldPassIfOperationCreateBlockStorageIn
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
}
if _, ok := stateBag.GetOk("Error"); ok == true {
if _, ok := stateBag.GetOk("error"); ok == true {
t.Fatalf("Expected the step to not set stateBag['Error'], but it was.")
}
}
@ -59,7 +59,7 @@ func TestStepCreateBlockStorageInstanceShouldPassIfOperationCreateBlockStorageIn
func createTestStateBagStepCreateBlockStorageInstance() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put("InstanceNo", "a")
stateBag.Put("instance_no", "a")
return stateBag
}

View File

@ -0,0 +1,114 @@
package ncloud
import (
"context"
"fmt"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/ncloud"
"io/ioutil"
"time"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
type InitScript struct {
KeyName string
PrivateKey string
}
type StepCreateInitScript struct {
Conn *NcloudAPIClient
CreateInitScript func() (string, error)
DeleteInitScript func(initScriptNo string) error
Say func(message string)
Error func(e error)
Config *Config
}
func NewStepCreateInitScript(conn *NcloudAPIClient, ui packersdk.Ui, config *Config) *StepCreateInitScript {
var step = &StepCreateInitScript{
Conn: conn,
Say: func(message string) { ui.Say(message) },
Error: func(e error) { ui.Error(e.Error()) },
Config: config,
}
if config.SupportVPC {
step.CreateInitScript = step.createVpcInitScript
step.DeleteInitScript = step.deleteVpcInitScript
}
return step
}
func (s *StepCreateInitScript) createVpcInitScript() (string, error) {
name := fmt.Sprintf("packer-%d", time.Now().Unix())
reqParams := &vserver.CreateInitScriptRequest{
RegionCode: &s.Config.RegionCode,
InitScriptName: &name,
}
if s.Config.Comm.Type == "winrm" {
reqParams.OsTypeCode = ncloud.String("WND")
}
if s.Config.UserData != "" {
reqParams.InitScriptContent = &s.Config.UserData
}
if s.Config.UserDataFile != "" {
contents, err := ioutil.ReadFile(s.Config.UserDataFile)
if err != nil {
return "", fmt.Errorf("Problem reading user data file: %s", err)
}
reqParams.InitScriptContent = ncloud.String(string(contents))
}
if reqParams.InitScriptContent == nil {
return "", nil
}
resp, err := s.Conn.vserver.V2Api.CreateInitScript(reqParams)
if err != nil {
return "", err
}
return *resp.InitScriptList[0].InitScriptNo, nil
}
func (s *StepCreateInitScript) deleteVpcInitScript(initScriptNo string) error {
reqParams := &vserver.DeleteInitScriptsRequest{
RegionCode: &s.Config.RegionCode,
InitScriptNoList: []*string{&initScriptNo},
}
_, err := s.Conn.vserver.V2Api.DeleteInitScripts(reqParams)
if err != nil {
return err
}
return nil
}
func (s *StepCreateInitScript) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
s.Say("Create Init script")
initScriptNo, err := s.CreateInitScript()
if err == nil && initScriptNo != "" {
state.Put("init_script_no", initScriptNo)
s.Say(fmt.Sprintf("Init script[%s] is created", initScriptNo))
}
return processStepResult(err, s.Error, state)
}
func (s *StepCreateInitScript) Cleanup(state multistep.StateBag) {
if initScriptNo, ok := state.GetOk("init_script_no"); ok {
s.Say("Cleanup Init script")
if err := s.DeleteInitScript(initScriptNo.(string)); err != nil {
s.Error(err)
return
}
}
}

View File

@ -0,0 +1,63 @@
package ncloud
import (
"context"
"fmt"
"testing"
"github.com/hashicorp/packer-plugin-sdk/multistep"
)
func TestStepCreateInitScriptShouldFailIfOperationCreateInitScriptFails(t *testing.T) {
var testSubject = &StepCreateInitScript{
CreateInitScript: func() (string, error) { return "", fmt.Errorf("!! Unit Test FAIL !!") },
Say: func(message string) {},
Error: func(e error) {},
Config: &Config{
Region: "KR",
SupportVPC: true,
},
}
stateBag := createTestStateBagStepCreateInitScript()
var result = testSubject.Run(context.Background(), stateBag)
if result != multistep.ActionHalt {
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
}
if _, ok := stateBag.GetOk("error"); ok == false {
t.Fatal("Expected the step to set stateBag['Error'], but it was not.")
}
}
func TestStepCreateInitScriptShouldPassIfOperationCreateInitScriptPasses(t *testing.T) {
var testSubject = &StepCreateInitScript{
CreateInitScript: func() (string, error) { return "123", nil },
Say: func(message string) {},
Error: func(e error) {},
Config: &Config{
Region: "KR",
SupportVPC: true,
},
}
stateBag := createTestStateBagStepCreateInitScript()
var result = testSubject.Run(context.Background(), stateBag)
if result != multistep.ActionContinue {
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
}
if _, ok := stateBag.GetOk("error"); ok == true {
t.Fatalf("Expected the step to not set stateBag['Error'], but it was.")
}
}
func createTestStateBagStepCreateInitScript() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
return stateBag
}

View File

@ -6,6 +6,7 @@ import (
"time"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/server"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
@ -18,23 +19,32 @@ type LoginKey struct {
type StepCreateLoginKey struct {
Conn *NcloudAPIClient
CreateLoginKey func() (*LoginKey, error)
DeleteLoginKey func(loginKey string) error
Say func(message string)
Error func(e error)
Config *Config
}
func NewStepCreateLoginKey(conn *NcloudAPIClient, ui packersdk.Ui) *StepCreateLoginKey {
func NewStepCreateLoginKey(conn *NcloudAPIClient, ui packersdk.Ui, config *Config) *StepCreateLoginKey {
var step = &StepCreateLoginKey{
Conn: conn,
Say: func(message string) { ui.Say(message) },
Error: func(e error) { ui.Error(e.Error()) },
Conn: conn,
Say: func(message string) { ui.Say(message) },
Error: func(e error) { ui.Error(e.Error()) },
Config: config,
}
step.CreateLoginKey = step.createLoginKey
if config.SupportVPC {
step.CreateLoginKey = step.createVpcLoginKey
step.DeleteLoginKey = step.deleteVpcLoginKey
} else {
step.CreateLoginKey = step.createClassicLoginKey
step.DeleteLoginKey = step.deleteClassicLoginKey
}
return step
}
func (s *StepCreateLoginKey) createLoginKey() (*LoginKey, error) {
func (s *StepCreateLoginKey) createClassicLoginKey() (*LoginKey, error) {
keyName := fmt.Sprintf("packer-%d", time.Now().Unix())
reqParams := &server.CreateLoginKeyRequest{KeyName: &keyName}
@ -46,12 +56,46 @@ func (s *StepCreateLoginKey) createLoginKey() (*LoginKey, error) {
return &LoginKey{keyName, *privateKey.PrivateKey}, nil
}
func (s *StepCreateLoginKey) createVpcLoginKey() (*LoginKey, error) {
keyName := fmt.Sprintf("packer-%d", time.Now().Unix())
reqParams := &vserver.CreateLoginKeyRequest{KeyName: &keyName}
privateKey, err := s.Conn.vserver.V2Api.CreateLoginKey(reqParams)
if err != nil {
return nil, err
}
return &LoginKey{keyName, *privateKey.PrivateKey}, nil
}
func (s *StepCreateLoginKey) deleteClassicLoginKey(keyName string) error {
reqParams := &server.DeleteLoginKeyRequest{KeyName: &keyName}
_, err := s.Conn.server.V2Api.DeleteLoginKey(reqParams)
if err != nil {
return err
}
return nil
}
func (s *StepCreateLoginKey) deleteVpcLoginKey(keyName string) error {
reqParams := &vserver.DeleteLoginKeysRequest{KeyNameList: []*string{&keyName}}
_, err := s.Conn.vserver.V2Api.DeleteLoginKeys(reqParams)
if err != nil {
return err
}
return nil
}
func (s *StepCreateLoginKey) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
s.Say("Create Login Key")
loginKey, err := s.CreateLoginKey()
if err == nil {
state.Put("LoginKey", loginKey)
state.Put("login_key", loginKey)
s.Say(fmt.Sprintf("Login Key[%s] is created", loginKey.KeyName))
}
@ -59,9 +103,11 @@ func (s *StepCreateLoginKey) Run(ctx context.Context, state multistep.StateBag)
}
func (s *StepCreateLoginKey) Cleanup(state multistep.StateBag) {
if loginKey, ok := state.GetOk("LoginKey"); ok {
if loginKey, ok := state.GetOk("login_key"); ok {
s.Say("Clean up login key")
reqParams := &server.DeleteLoginKeyRequest{KeyName: &loginKey.(*LoginKey).KeyName}
s.Conn.server.V2Api.DeleteLoginKey(reqParams)
if err := s.DeleteLoginKey(loginKey.(*LoginKey).KeyName); err != nil {
s.Error(err)
return
}
}
}

View File

@ -23,7 +23,7 @@ func TestStepCreateLoginKeyShouldFailIfOperationCreateLoginKeyFails(t *testing.T
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
}
if _, ok := stateBag.GetOk("Error"); ok == false {
if _, ok := stateBag.GetOk("error"); ok == false {
t.Fatal("Expected the step to set stateBag['Error'], but it was not.")
}
}
@ -43,7 +43,7 @@ func TestStepCreateLoginKeyShouldPassIfOperationCreateLoginKeyPasses(t *testing.
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
}
if _, ok := stateBag.GetOk("Error"); ok == true {
if _, ok := stateBag.GetOk("error"); ok == true {
t.Fatalf("Expected the step to not set stateBag['Error'], but it was.")
}
}

View File

@ -3,6 +3,7 @@ package ncloud
import (
"context"
"fmt"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
"log"
"time"
@ -12,32 +13,103 @@ import (
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
type StepCreatePublicIPInstance struct {
const (
PublicIpStatusUsed = "USED"
PublicIpStatusCreated = "CREAT"
PublicIpStatusRunning = "RUN"
)
type StepCreatePublicIP struct {
Conn *NcloudAPIClient
CreatePublicIPInstance func(serverInstanceNo string) (*server.PublicIpInstance, error)
GetPublicIP func(publicIPNo string) (*server.PublicIpInstance, error)
CreatePublicIP func(serverInstanceNo string) (*server.PublicIpInstance, error)
DeletePublicIp func(publicIPNo string) error
WaiterAssociatePublicIPToServerInstance func(serverInstanceNo string, publicIP string) error
DisassociatePublicIpFromServerInstance func(publicIPNo string) error
Say func(message string)
Error func(e error)
Config *Config
}
func NewStepCreatePublicIPInstance(conn *NcloudAPIClient, ui packersdk.Ui, config *Config) *StepCreatePublicIPInstance {
var step = &StepCreatePublicIPInstance{
func NewStepCreatePublicIP(conn *NcloudAPIClient, ui packersdk.Ui, config *Config) *StepCreatePublicIP {
var step = &StepCreatePublicIP{
Conn: conn,
Say: func(message string) { ui.Say(message) },
Error: func(e error) { ui.Error(e.Error()) },
Config: config,
}
step.CreatePublicIPInstance = step.createPublicIPInstance
step.WaiterAssociatePublicIPToServerInstance = step.waiterAssociatePublicIPToServerInstance
if config.SupportVPC {
step.GetPublicIP = step.getVpcPublicIP
step.CreatePublicIP = step.createVpcPublicIP
step.WaiterAssociatePublicIPToServerInstance = step.waiterAssociateVpcPublicIPToServerInstance
step.DisassociatePublicIpFromServerInstance = step.disassociateVpcPublicIpFromServerInstance
step.DeletePublicIp = step.deleteVpcPublicIp
} else {
step.GetPublicIP = step.getClassicPublicIP
step.CreatePublicIP = step.createClassicPublicIP
step.WaiterAssociatePublicIPToServerInstance = step.waiterAssociateClassicPublicIPToServerInstance
step.DisassociatePublicIpFromServerInstance = step.disassociateClassicPublicIpFromServerInstance
step.DeletePublicIp = step.deleteClassicPublicIp
}
return step
}
func (s *StepCreatePublicIPInstance) waiterAssociatePublicIPToServerInstance(serverInstanceNo string, publicIP string) error {
reqParams := new(server.GetServerInstanceListRequest)
reqParams.ServerInstanceNoList = []*string{&serverInstanceNo}
func (s *StepCreatePublicIP) getClassicPublicIP(publicIPNo string) (*server.PublicIpInstance, error) {
reqParams := &server.GetPublicIpInstanceListRequest{
PublicIpInstanceNoList: []*string{&publicIPNo},
}
resp, err := s.Conn.server.V2Api.GetPublicIpInstanceList(reqParams)
if err != nil {
return nil, err
}
if resp != nil && *resp.TotalRows > 0 {
return resp.PublicIpInstanceList[0], nil
}
return nil, nil
}
func (s *StepCreatePublicIP) getVpcPublicIP(publicIPNo string) (*server.PublicIpInstance, error) {
reqParams := &vserver.GetPublicIpInstanceDetailRequest{
RegionCode: &s.Config.RegionCode,
PublicIpInstanceNo: &publicIPNo,
}
resp, err := s.Conn.vserver.V2Api.GetPublicIpInstanceDetail(reqParams)
if err != nil {
return nil, err
}
if resp != nil && *resp.TotalRows > 0 {
inst := resp.PublicIpInstanceList[0]
return &server.PublicIpInstance{
PublicIpInstanceNo: inst.PublicIpInstanceNo,
PublicIp: inst.PublicIp,
PublicIpInstanceStatus: &server.CommonCode{
Code: inst.PublicIpInstanceStatus.Code,
CodeName: inst.PublicIpInstanceStatus.CodeName,
},
PublicIpInstanceOperation: &server.CommonCode{
Code: inst.PublicIpInstanceOperation.Code,
CodeName: inst.PublicIpInstanceOperation.CodeName,
},
ServerInstanceAssociatedWithPublicIp: &server.ServerInstance{
ServerInstanceNo: inst.ServerInstanceNo,
},
}, nil
}
return nil, nil
}
func (s *StepCreatePublicIP) waiterAssociateClassicPublicIPToServerInstance(serverInstanceNo string, publicIP string) error {
reqParams := &server.GetServerInstanceListRequest{
ServerInstanceNoList: []*string{&serverInstanceNo},
}
c1 := make(chan error, 1)
@ -68,9 +140,45 @@ func (s *StepCreatePublicIPInstance) waiterAssociatePublicIPToServerInstance(ser
}
}
func (s *StepCreatePublicIPInstance) createPublicIPInstance(serverInstanceNo string) (*server.PublicIpInstance, error) {
reqParams := new(server.CreatePublicIpInstanceRequest)
reqParams.ServerInstanceNo = &serverInstanceNo
func (s *StepCreatePublicIP) waiterAssociateVpcPublicIPToServerInstance(serverInstanceNo string, publicIP string) error {
reqParams := &vserver.GetServerInstanceDetailRequest{
RegionCode: &s.Config.RegionCode,
ServerInstanceNo: &serverInstanceNo,
}
c1 := make(chan error, 1)
go func() {
for {
serverInstanceList, err := s.Conn.vserver.V2Api.GetServerInstanceDetail(reqParams)
if err != nil {
c1 <- err
return
}
if publicIP == *serverInstanceList.ServerInstanceList[0].PublicIp {
c1 <- nil
return
}
s.Say("Wait to associate public ip serverInstance")
time.Sleep(time.Second * 3)
}
}()
select {
case res := <-c1:
return res
case <-time.After(time.Second * 60):
return fmt.Errorf("TIMEOUT : association public ip[%s] to server instance[%s] Failed", publicIP, serverInstanceNo)
}
}
func (s *StepCreatePublicIP) createClassicPublicIP(serverInstanceNo string) (*server.PublicIpInstance, error) {
reqParams := &server.CreatePublicIpInstanceRequest{
ServerInstanceNo: &serverInstanceNo,
}
publicIPInstanceList, err := s.Conn.server.V2Api.CreatePublicIpInstance(reqParams)
if err != nil {
@ -81,7 +189,7 @@ func (s *StepCreatePublicIPInstance) createPublicIPInstance(serverInstanceNo str
publicIP := publicIPInstance.PublicIp
s.Say(fmt.Sprintf("Public IP Instance [%s:%s] is created", *publicIPInstance.PublicIpInstanceNo, *publicIP))
err = s.waiterAssociatePublicIPToServerInstance(serverInstanceNo, *publicIP)
err = s.WaiterAssociatePublicIPToServerInstance(serverInstanceNo, *publicIP)
if err != nil {
return nil, err
}
@ -89,69 +197,49 @@ func (s *StepCreatePublicIPInstance) createPublicIPInstance(serverInstanceNo str
return publicIPInstance, nil
}
func (s *StepCreatePublicIPInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
s.Say("Create Public IP Instance")
serverInstanceNo := state.Get("InstanceNo").(string)
publicIPInstance, err := s.CreatePublicIPInstance(serverInstanceNo)
if err == nil {
state.Put("PublicIP", *publicIPInstance.PublicIp)
state.Put("PublicIPInstance", publicIPInstance)
// instance_id is the generic term used so that users can have access to the
// instance id inside of the provisioners, used in step_provision.
state.Put("instance_id", *publicIPInstance)
func (s *StepCreatePublicIP) createVpcPublicIP(serverInstanceNo string) (*server.PublicIpInstance, error) {
reqParams := &vserver.CreatePublicIpInstanceRequest{
RegionCode: &s.Config.RegionCode,
ServerInstanceNo: &serverInstanceNo,
}
return processStepResult(err, s.Error, state)
publicIPInstanceList, err := s.Conn.vserver.V2Api.CreatePublicIpInstance(reqParams)
if err != nil {
return nil, err
}
publicIPInstance := publicIPInstanceList.PublicIpInstanceList[0]
publicIP := publicIPInstance.PublicIp
s.Say(fmt.Sprintf("Public IP Instance [%s:%s] is created", *publicIPInstance.PublicIpInstanceNo, *publicIP))
err = s.WaiterAssociatePublicIPToServerInstance(serverInstanceNo, *publicIP)
if err != nil {
return nil, err
}
return &server.PublicIpInstance{
PublicIpInstanceNo: publicIPInstance.PublicIpInstanceNo,
PublicIp: publicIP,
}, nil
}
func (s *StepCreatePublicIPInstance) Cleanup(state multistep.StateBag) {
publicIPInstance, ok := state.GetOk("PublicIPInstance")
if !ok {
return
}
s.Say("Clean up Public IP Instance")
publicIPInstanceNo := publicIPInstance.(*server.PublicIpInstance).PublicIpInstanceNo
s.waitPublicIPInstanceStatus(publicIPInstanceNo, "USED")
log.Println("Disassociate Public IP Instance ", publicIPInstanceNo)
reqParams := &server.DisassociatePublicIpFromServerInstanceRequest{PublicIpInstanceNo: publicIPInstanceNo}
s.Conn.server.V2Api.DisassociatePublicIpFromServerInstance(reqParams)
s.waitPublicIPInstanceStatus(publicIPInstanceNo, "CREAT")
reqDeleteParams := &server.DeletePublicIpInstancesRequest{
PublicIpInstanceNoList: ncloud.StringList([]string{*publicIPInstanceNo}),
}
log.Println("Delete Public IP Instance ", publicIPInstanceNo)
s.Conn.server.V2Api.DeletePublicIpInstances(reqDeleteParams)
}
func (s *StepCreatePublicIPInstance) waitPublicIPInstanceStatus(publicIPInstanceNo *string, status string) {
func (s *StepCreatePublicIP) waitPublicIPStatus(publicIPInstanceNo string, status string) error {
c1 := make(chan error, 1)
go func() {
reqParams := new(server.GetPublicIpInstanceListRequest)
reqParams.PublicIpInstanceNoList = []*string{publicIPInstanceNo}
for {
resp, err := s.Conn.server.V2Api.GetPublicIpInstanceList(reqParams)
publicIp, err := s.GetPublicIP(publicIPInstanceNo)
if err != nil {
log.Printf(err.Error())
c1 <- err
return
}
if *resp.TotalRows == 0 {
if publicIp == nil {
c1 <- nil
return
}
instance := resp.PublicIpInstanceList[0]
if *instance.PublicIpInstanceStatus.Code == status && *instance.PublicIpInstanceOperation.Code == "NULL" {
if *publicIp.PublicIpInstanceStatus.Code == status && *publicIp.PublicIpInstanceOperation.Code == "NULL" {
c1 <- nil
return
}
@ -162,8 +250,119 @@ func (s *StepCreatePublicIPInstance) waitPublicIPInstanceStatus(publicIPInstance
select {
case <-c1:
return
return nil
case <-time.After(time.Second * 60):
return fmt.Errorf("TIMEOUT : wait for public ip[%s] to status[%s] Failed", publicIPInstanceNo, status)
}
}
func (s *StepCreatePublicIP) disassociateClassicPublicIpFromServerInstance(publicIPInstanceNo string) error {
reqParams := &server.DisassociatePublicIpFromServerInstanceRequest{
PublicIpInstanceNo: &publicIPInstanceNo,
}
_, err := s.Conn.server.V2Api.DisassociatePublicIpFromServerInstance(reqParams)
return err
}
func (s *StepCreatePublicIP) disassociateVpcPublicIpFromServerInstance(publicIPInstanceNo string) error {
reqParams := &vserver.DisassociatePublicIpFromServerInstanceRequest{
RegionCode: &s.Config.RegionCode,
PublicIpInstanceNo: &publicIPInstanceNo,
}
_, err := s.Conn.vserver.V2Api.DisassociatePublicIpFromServerInstance(reqParams)
return err
}
func (s *StepCreatePublicIP) deleteClassicPublicIp(publicIPInstanceNo string) error {
reqParams := &server.DeletePublicIpInstancesRequest{
PublicIpInstanceNoList: ncloud.StringList([]string{publicIPInstanceNo}),
}
_, err := s.Conn.server.V2Api.DeletePublicIpInstances(reqParams)
return err
}
func (s *StepCreatePublicIP) deleteVpcPublicIp(publicIPInstanceNo string) error {
reqParams := &vserver.DeletePublicIpInstanceRequest{
RegionCode: &s.Config.RegionCode,
PublicIpInstanceNo: &publicIPInstanceNo,
}
_, err := s.Conn.vserver.V2Api.DeletePublicIpInstance(reqParams)
return err
}
func (s *StepCreatePublicIP) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
s.Say("Create Public IP Instance")
serverInstanceNo := state.Get("instance_no").(string)
publicIPInstance, err := s.CreatePublicIP(serverInstanceNo)
if err == nil {
state.Put("public_ip", *publicIPInstance.PublicIp)
state.Put("public_ip_instance", publicIPInstance)
// instance_id is the generic term used so that users can have access to the
// instance id inside of the provisioners, used in step_provision.
state.Put("instance_id", *publicIPInstance)
}
return processStepResult(err, s.Error, state)
}
func (s *StepCreatePublicIP) Cleanup(state multistep.StateBag) {
publicIPInstance, ok := state.GetOk("public_ip_instance")
if !ok {
return
}
s.Say("Clean up Public IP Instance")
publicIPInstanceNo := publicIPInstance.(*server.PublicIpInstance).PublicIpInstanceNo
publicIp, err := s.GetPublicIP(*publicIPInstanceNo)
if err != nil {
s.Error(err)
}
if publicIp == nil {
return
}
publicIpUsedStatus := PublicIpStatusUsed
publicIpCreatedStatus := PublicIpStatusCreated
if s.Config.SupportVPC {
publicIpUsedStatus = PublicIpStatusRunning
publicIpCreatedStatus = PublicIpStatusRunning
}
if publicIp.ServerInstanceAssociatedWithPublicIp != nil &&
publicIp.ServerInstanceAssociatedWithPublicIp.ServerInstanceNo != nil &&
len(*publicIp.ServerInstanceAssociatedWithPublicIp.ServerInstanceNo) > 0 {
if err := s.waitPublicIPStatus(*publicIPInstanceNo, publicIpUsedStatus); err != nil {
s.Error(err)
}
log.Println("Disassociate Public IP Instance ", publicIPInstanceNo)
if err := s.DisassociatePublicIpFromServerInstance(*publicIPInstanceNo); err != nil {
s.Error(err)
return
}
}
if err := s.waitPublicIPStatus(*publicIPInstanceNo, publicIpCreatedStatus); err != nil {
s.Error(err)
return
}
log.Println("Delete Public IP Instance ", publicIPInstanceNo)
if err := s.DeletePublicIp(*publicIPInstanceNo); err != nil {
s.Error(err)
return
}
}

View File

@ -12,8 +12,8 @@ import (
)
func TestStepCreatePublicIPInstanceShouldFailIfOperationCreatePublicIPInstanceFails(t *testing.T) {
var testSubject = &StepCreatePublicIPInstance{
CreatePublicIPInstance: func(serverInstanceNo string) (*server.PublicIpInstance, error) {
var testSubject = &StepCreatePublicIP{
CreatePublicIP: func(serverInstanceNo string) (*server.PublicIpInstance, error) {
return nil, fmt.Errorf("!! Unit Test FAIL !!")
},
Say: func(message string) {},
@ -28,7 +28,7 @@ func TestStepCreatePublicIPInstanceShouldFailIfOperationCreatePublicIPInstanceFa
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
}
if _, ok := stateBag.GetOk("Error"); ok == false {
if _, ok := stateBag.GetOk("error"); ok == false {
t.Fatal("Expected the step to set stateBag['Error'], but it was not.")
}
}
@ -38,8 +38,8 @@ func TestStepCreatePublicIPInstanceShouldPassIfOperationCreatePublicIPInstancePa
c.Comm.Prepare(nil)
c.Comm.Type = "ssh"
var testSubject = &StepCreatePublicIPInstance{
CreatePublicIPInstance: func(serverInstanceNo string) (*server.PublicIpInstance, error) {
var testSubject = &StepCreatePublicIP{
CreatePublicIP: func(serverInstanceNo string) (*server.PublicIpInstance, error) {
return &server.PublicIpInstance{PublicIpInstanceNo: ncloud.String("a"), PublicIp: ncloud.String("b")}, nil
},
Say: func(message string) {},
@ -55,7 +55,7 @@ func TestStepCreatePublicIPInstanceShouldPassIfOperationCreatePublicIPInstancePa
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
}
if _, ok := stateBag.GetOk("Error"); ok == true {
if _, ok := stateBag.GetOk("error"); ok == true {
t.Fatalf("Expected the step to not set stateBag['Error'], but it was.")
}
}
@ -63,7 +63,7 @@ func TestStepCreatePublicIPInstanceShouldPassIfOperationCreatePublicIPInstancePa
func createTestStateBagStepCreatePublicIPInstance() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put("InstanceNo", "a")
stateBag.Put("instance_no", "a")
return stateBag
}

View File

@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
"time"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/server"
@ -11,6 +12,10 @@ import (
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
const (
ServerImageStatusCreated = "CREAT"
)
type StepCreateServerImage struct {
Conn *NcloudAPIClient
CreateServerImage func(serverInstanceNo string) (*server.MemberServerImage, error)
@ -27,21 +32,26 @@ func NewStepCreateServerImage(conn *NcloudAPIClient, ui packersdk.Ui, config *Co
Config: config,
}
step.CreateServerImage = step.createServerImage
if config.SupportVPC {
step.CreateServerImage = step.createVpcServerImage
} else {
step.CreateServerImage = step.createClassicServerImage
}
return step
}
func (s *StepCreateServerImage) createServerImage(serverInstanceNo string) (*server.MemberServerImage, error) {
func (s *StepCreateServerImage) createClassicServerImage(serverInstanceNo string) (*server.MemberServerImage, error) {
// Can't create server image when status of server instance is stopping (not stopped)
if err := waiterServerInstanceStatus(s.Conn, serverInstanceNo, "NSTOP", 1*time.Minute); err != nil {
if err := waiterClassicServerInstanceStatus(s.Conn, serverInstanceNo, ServerInstanceStatusStopped, 1*time.Minute); err != nil {
return nil, err
}
reqParams := new(server.CreateMemberServerImageRequest)
reqParams.MemberServerImageName = &s.Config.ServerImageName
reqParams.MemberServerImageDescription = &s.Config.ServerImageDescription
reqParams.ServerInstanceNo = &serverInstanceNo
reqParams := &server.CreateMemberServerImageRequest{
MemberServerImageName: &s.Config.ServerImageName,
MemberServerImageDescription: &s.Config.ServerImageDescription,
ServerInstanceNo: &serverInstanceNo,
}
memberServerImageList, err := s.Conn.server.V2Api.CreateMemberServerImage(reqParams)
if err != nil {
@ -52,7 +62,7 @@ func (s *StepCreateServerImage) createServerImage(serverInstanceNo string) (*ser
s.Say(fmt.Sprintf("Server Image[%s:%s] is creating...", *serverImage.MemberServerImageName, *serverImage.MemberServerImageNo))
if err := waiterMemberServerImageStatus(s.Conn, *serverImage.MemberServerImageNo, "CREAT", 6*time.Hour); err != nil {
if err := waiterClassicMemberServerImageStatus(s.Conn, *serverImage.MemberServerImageNo, ServerImageStatusCreated, 6*time.Hour); err != nil {
return nil, errors.New("TIMEOUT : Server Image is not created")
}
@ -61,14 +71,56 @@ func (s *StepCreateServerImage) createServerImage(serverInstanceNo string) (*ser
return serverImage, nil
}
func (s *StepCreateServerImage) createVpcServerImage(serverInstanceNo string) (*server.MemberServerImage, error) {
// Can't create server image when status of server instance is stopping (not stopped)
if err := waiterVpcServerInstanceStatus(s.Conn, serverInstanceNo, ServerInstanceStatusStopped, 1*time.Minute); err != nil {
return nil, err
}
reqParams := &vserver.CreateMemberServerImageInstanceRequest{
MemberServerImageName: &s.Config.ServerImageName,
MemberServerImageDescription: &s.Config.ServerImageDescription,
ServerInstanceNo: &serverInstanceNo,
}
memberServerImageList, err := s.Conn.vserver.V2Api.CreateMemberServerImageInstance(reqParams)
if err != nil {
return nil, err
}
serverImage := memberServerImageList.MemberServerImageInstanceList[0]
s.Say(fmt.Sprintf("Server Image[%s:%s] is creating...", *serverImage.MemberServerImageName, *serverImage.MemberServerImageInstanceNo))
if err := waiterVpcMemberServerImageStatus(s.Conn, *serverImage.MemberServerImageInstanceNo, ServerImageStatusCreated, 6*time.Hour); err != nil {
return nil, errors.New("TIMEOUT : Server Image is not created")
}
s.Say(fmt.Sprintf("Server Image[%s:%s] is created", *serverImage.MemberServerImageName, *serverImage.MemberServerImageInstanceNo))
result := &server.MemberServerImage{
MemberServerImageNo: serverImage.MemberServerImageInstanceNo,
MemberServerImageName: serverImage.MemberServerImageName,
}
if serverImage.MemberServerImageInstanceStatus != nil {
result.MemberServerImageStatus = &server.CommonCode{
Code: serverImage.MemberServerImageInstanceStatus.Code,
CodeName: serverImage.MemberServerImageInstanceStatus.CodeName,
}
}
return result, nil
}
func (s *StepCreateServerImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
s.Say("Create Server Image")
serverInstanceNo := state.Get("InstanceNo").(string)
serverInstanceNo := state.Get("instance_no").(string)
serverImage, err := s.CreateServerImage(serverInstanceNo)
if err == nil {
state.Put("memberServerImage", serverImage)
state.Put("member_server_image", serverImage)
}
return processStepResult(err, s.Error, state)

View File

@ -27,7 +27,7 @@ func TestStepCreateServerImageShouldFailIfOperationCreateServerImageFails(t *tes
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
}
if _, ok := stateBag.GetOk("Error"); ok == false {
if _, ok := stateBag.GetOk("error"); ok == false {
t.Fatal("Expected the step to set stateBag['Error'], but it was not.")
}
}
@ -46,7 +46,7 @@ func TestStepCreateServerImageShouldPassIfOperationCreateServerImagePasses(t *te
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
}
if _, ok := stateBag.GetOk("Error"); ok == true {
if _, ok := stateBag.GetOk("error"); ok == true {
t.Fatalf("Expected the step to not set stateBag['Error'], but it was.")
}
}
@ -54,7 +54,7 @@ func TestStepCreateServerImageShouldPassIfOperationCreateServerImagePasses(t *te
func createTestStateBagStepCreateServerImage() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put("InstanceNo", "a")
stateBag.Put("instance_no", "a")
return stateBag
}

View File

@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
"io/ioutil"
"log"
"time"
@ -14,14 +15,21 @@ import (
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
const (
ServerInstanceStatusStopped = "NSTOP"
ServerInstanceStatusTerminated = "TERMT"
ServerInstanceStatusRunning = "RUN"
)
type StepCreateServerInstance struct {
Conn *NcloudAPIClient
CreateServerInstance func(loginKeyName string, zoneNo string, feeSystemTypeCode string) (string, error)
CheckServerInstanceStatusIsRunning func(serverInstanceNo string) error
Say func(message string)
Error func(e error)
Config *Config
serverInstanceNo string
Conn *NcloudAPIClient
CreateServerInstance func(loginKeyName string, feeSystemTypeCode string, state multistep.StateBag) (string, error)
GetServerInstance func() (string, string, error)
WaiterServerInstanceStatus func(conn *NcloudAPIClient, serverInstanceNo string, status string, timeout time.Duration) error
Say func(message string)
Error func(e error)
Config *Config
serverInstanceNo string
}
func NewStepCreateServerInstance(conn *NcloudAPIClient, ui packersdk.Ui, config *Config) *StepCreateServerInstance {
@ -32,21 +40,32 @@ func NewStepCreateServerInstance(conn *NcloudAPIClient, ui packersdk.Ui, config
Config: config,
}
step.CreateServerInstance = step.createServerInstance
if config.SupportVPC {
step.CreateServerInstance = step.createVpcServerInstance
step.WaiterServerInstanceStatus = waiterVpcServerInstanceStatus
step.GetServerInstance = step.getVpcServerInstance
} else {
step.CreateServerInstance = step.createClassicServerInstance
step.WaiterServerInstanceStatus = waiterClassicServerInstanceStatus
step.GetServerInstance = step.getClassicServerInstance
}
return step
}
func (s *StepCreateServerInstance) createServerInstance(loginKeyName string, zoneNo string, feeSystemTypeCode string) (string, error) {
reqParams := new(server.CreateServerInstancesRequest)
reqParams.ServerProductCode = &s.Config.ServerProductCode
reqParams.MemberServerImageNo = &s.Config.MemberServerImageNo
func (s *StepCreateServerInstance) createClassicServerInstance(loginKeyName string, feeSystemTypeCode string, state multistep.StateBag) (string, error) {
var zoneNo = state.Get("zone_no").(string)
reqParams := &server.CreateServerInstancesRequest{
ServerProductCode: &s.Config.ServerProductCode,
MemberServerImageNo: &s.Config.MemberServerImageNo,
LoginKeyName: &loginKeyName,
ZoneNo: &zoneNo,
FeeSystemTypeCode: &feeSystemTypeCode,
}
if s.Config.MemberServerImageNo == "" {
reqParams.ServerImageProductCode = &s.Config.ServerImageProductCode
}
reqParams.LoginKeyName = &loginKeyName
reqParams.ZoneNo = &zoneNo
reqParams.FeeSystemTypeCode = &feeSystemTypeCode
if s.Config.UserData != "" {
reqParams.UserData = &s.Config.UserData
@ -74,7 +93,7 @@ func (s *StepCreateServerInstance) createServerInstance(loginKeyName string, zon
s.Say(fmt.Sprintf("Server Instance is creating. Server InstanceNo is %s", s.serverInstanceNo))
log.Println("Server Instance information : ", serverInstanceList.ServerInstanceList[0])
if err := waiterServerInstanceStatus(s.Conn, s.serverInstanceNo, "RUN", 30*time.Minute); err != nil {
if err := s.WaiterServerInstanceStatus(s.Conn, s.serverInstanceNo, ServerInstanceStatusRunning, 30*time.Minute); err != nil {
return "", errors.New("TIMEOUT : server instance status is not running")
}
@ -83,20 +102,133 @@ func (s *StepCreateServerInstance) createServerInstance(loginKeyName string, zon
return s.serverInstanceNo, nil
}
func (s *StepCreateServerInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
s.Say("Create Server Instance")
func (s *StepCreateServerInstance) createVpcServerInstance(loginKeyName string, feeSystemTypeCode string, state multistep.StateBag) (string, error) {
var initScriptNo string
var acgNo string
var err error
var loginKey = state.Get("LoginKey").(*LoginKey)
var zoneNo = state.Get("ZoneNo").(string)
feeSystemTypeCode := "MTRAT"
if _, ok := state.GetOk("FeeSystemTypeCode"); ok {
feeSystemTypeCode = state.Get("FeeSystemTypeCode").(string)
if v, ok := state.GetOk("init_script_no"); ok {
initScriptNo = v.(string)
}
serverInstanceNo, err := s.CreateServerInstance(loginKey.KeyName, zoneNo, feeSystemTypeCode)
if s.Config.AccessControlGroupConfigurationNo != "" {
acgNo = s.Config.AccessControlGroupConfigurationNo
} else {
acgNo, err = s.getDefaultAccessControlGroup(s.Config.VpcNo)
}
if err != nil {
return "", err
}
reqParams := &vserver.CreateServerInstancesRequest{
RegionCode: &s.Config.RegionCode,
ServerProductCode: &s.Config.ServerProductCode,
MemberServerImageInstanceNo: &s.Config.MemberServerImageNo,
LoginKeyName: &loginKeyName,
FeeSystemTypeCode: &feeSystemTypeCode,
InitScriptNo: &initScriptNo,
VpcNo: &s.Config.VpcNo,
SubnetNo: &s.Config.SubnetNo,
NetworkInterfaceList: []*vserver.NetworkInterfaceParameter{{
NetworkInterfaceOrder: ncloud.Int32(0),
AccessControlGroupNoList: []*string{ncloud.String(acgNo)}},
},
}
if s.Config.MemberServerImageNo == "" {
reqParams.ServerImageProductCode = &s.Config.ServerImageProductCode
}
serverInstanceList, err := s.Conn.vserver.V2Api.CreateServerInstances(reqParams)
if err != nil {
return "", err
}
s.serverInstanceNo = *serverInstanceList.ServerInstanceList[0].ServerInstanceNo
s.Say(fmt.Sprintf("Server Instance is creating. Server InstanceNo is %s", s.serverInstanceNo))
log.Println("Server Instance information : ", serverInstanceList.ServerInstanceList[0])
if err := s.WaiterServerInstanceStatus(s.Conn, s.serverInstanceNo, ServerInstanceStatusRunning, 30*time.Minute); err != nil {
return "", errors.New("TIMEOUT : server instance status is not running")
}
s.Say(fmt.Sprintf("Server Instance is created. Server InstanceNo is %s", s.serverInstanceNo))
return s.serverInstanceNo, nil
}
func (s *StepCreateServerInstance) getDefaultAccessControlGroup(id string) (string, error) {
reqParams := &vserver.GetAccessControlGroupListRequest{
RegionCode: &s.Config.RegionCode,
VpcNo: ncloud.String(id),
}
resp, err := s.Conn.vserver.V2Api.GetAccessControlGroupList(reqParams)
if err != nil {
return "", err
}
if resp == nil || len(resp.AccessControlGroupList) == 0 {
return "", fmt.Errorf("no matching Access Control Group found")
}
for _, i := range resp.AccessControlGroupList {
if *i.IsDefault {
return *i.AccessControlGroupNo, nil
}
}
return "", fmt.Errorf("no matching default Access Control Group found")
}
func (s *StepCreateServerInstance) getClassicServerInstance() (string, string, error) {
reqParams := &server.GetServerInstanceListRequest{
ServerInstanceNoList: []*string{&s.serverInstanceNo},
}
resp, err := s.Conn.server.V2Api.GetServerInstanceList(reqParams)
if err != nil {
return "", "", err
}
if *resp.TotalRows > 0 {
return *resp.ServerInstanceList[0].ServerInstanceNo, *resp.ServerInstanceList[0].ServerInstanceStatus.Code, nil
} else {
return "", "", nil
}
}
func (s *StepCreateServerInstance) getVpcServerInstance() (string, string, error) {
reqParams := &vserver.GetServerInstanceDetailRequest{
ServerInstanceNo: &s.serverInstanceNo,
}
resp, err := s.Conn.vserver.V2Api.GetServerInstanceDetail(reqParams)
if err != nil {
return "", "", err
}
if *resp.TotalRows > 0 {
return *resp.ServerInstanceList[0].ServerInstanceNo, *resp.ServerInstanceList[0].ServerInstanceStatus.Code, nil
} else {
return "", "", nil
}
}
func (s *StepCreateServerInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
s.Say("Create Server Instance")
var loginKey = state.Get("login_key").(*LoginKey)
feeSystemTypeCode := "MTRAT"
if _, ok := state.GetOk("fee_system_type_code"); ok {
feeSystemTypeCode = state.Get("fee_system_type_code").(string)
}
serverInstanceNo, err := s.CreateServerInstance(loginKey.KeyName, feeSystemTypeCode, state)
if err == nil {
state.Put("InstanceNo", serverInstanceNo)
state.Put("instance_no", serverInstanceNo)
// instance_id is the generic term used so that users can have access to the
// instance id inside of the provisioners, used in step_provision.
state.Put("instance_id", serverInstanceNo)
@ -108,6 +240,7 @@ func (s *StepCreateServerInstance) Run(ctx context.Context, state multistep.Stat
func (s *StepCreateServerInstance) Cleanup(state multistep.StateBag) {
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
ui := state.Get("ui").(packersdk.Ui)
if !cancelled && !halted {
return
@ -117,33 +250,31 @@ func (s *StepCreateServerInstance) Cleanup(state multistep.StateBag) {
return
}
reqParams := new(server.GetServerInstanceListRequest)
reqParams.ServerInstanceNoList = []*string{&s.serverInstanceNo}
_, status, err := s.GetServerInstance()
serverInstanceList, err := s.Conn.server.V2Api.GetServerInstanceList(reqParams)
if err != nil || *serverInstanceList.TotalRows == 0 {
if err != nil || len(status) == 0 {
return
}
s.Say("Clean up Server Instance")
serverInstance := serverInstanceList.ServerInstanceList[0]
// stop server instance
if *serverInstance.ServerInstanceStatus.Code != "NSTOP" && *serverInstance.ServerInstanceStatus.Code != "TERMT" {
reqParams := new(server.StopServerInstancesRequest)
reqParams.ServerInstanceNoList = []*string{&s.serverInstanceNo}
log.Println("Stop Server Instance")
s.Conn.server.V2Api.StopServerInstances(reqParams)
waiterServerInstanceStatus(s.Conn, s.serverInstanceNo, "NSTOP", time.Minute)
if status != ServerInstanceStatusStopped && status != ServerInstanceStatusTerminated {
stepStopServerInstance := NewStepStopServerInstance(s.Conn, ui, s.Config)
err := stepStopServerInstance.StopServerInstance(s.serverInstanceNo)
if err != nil {
s.Error(err)
return
}
}
// terminate server instance
if *serverInstance.ServerInstanceStatus.Code != "TERMT" {
reqParams := new(server.TerminateServerInstancesRequest)
reqParams.ServerInstanceNoList = []*string{&s.serverInstanceNo}
log.Println("Terminate Server Instance")
s.Conn.server.V2Api.TerminateServerInstances(reqParams)
if status != ServerInstanceStatusTerminated {
stepStopServerInstance := NewStepTerminateServerInstance(s.Conn, ui, s.Config)
err := stepStopServerInstance.TerminateServerInstance(s.serverInstanceNo)
if err != nil {
s.Error(err)
return
}
}
}

View File

@ -10,7 +10,7 @@ import (
func TestStepCreateServerInstanceShouldFailIfOperationCreateFails(t *testing.T) {
var testSubject = &StepCreateServerInstance{
CreateServerInstance: func(loginKeyName string, zoneNo string, feeSystemTypeCode string) (string, error) {
CreateServerInstance: func(loginKeyName string, feeSystemTypeCode string, state multistep.StateBag) (string, error) {
return "", fmt.Errorf("!! Unit Test FAIL !!")
},
Say: func(message string) {},
@ -25,16 +25,18 @@ func TestStepCreateServerInstanceShouldFailIfOperationCreateFails(t *testing.T)
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
}
if _, ok := stateBag.GetOk("Error"); ok == false {
if _, ok := stateBag.GetOk("error"); ok == false {
t.Fatal("Expected the step to set stateBag['Error'], but it was not.")
}
}
func TestStepCreateServerInstanceShouldPassIfOperationCreatePasses(t *testing.T) {
var testSubject = &StepCreateServerInstance{
CreateServerInstance: func(loginKeyName string, zoneNo string, feeSystemTypeCode string) (string, error) { return "", nil },
Say: func(message string) {},
Error: func(e error) {},
CreateServerInstance: func(loginKeyName string, feeSystemTypeCode string, state multistep.StateBag) (string, error) {
return "", nil
},
Say: func(message string) {},
Error: func(e error) {},
}
stateBag := createTestStateBagStepCreateServerInstance()
@ -45,7 +47,7 @@ func TestStepCreateServerInstanceShouldPassIfOperationCreatePasses(t *testing.T)
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
}
if _, ok := stateBag.GetOk("Error"); ok == true {
if _, ok := stateBag.GetOk("error"); ok == true {
t.Fatalf("Expected the step to not set stateBag['Error'], but it was.")
}
}
@ -53,8 +55,8 @@ func TestStepCreateServerInstanceShouldPassIfOperationCreatePasses(t *testing.T)
func createTestStateBagStepCreateServerInstance() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put("LoginKey", &LoginKey{"a", "b"})
stateBag.Put("ZoneNo", "1")
stateBag.Put("login_key", &LoginKey{"a", "b"})
stateBag.Put("zone_no", "1")
return stateBag
}

View File

@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
"log"
"time"
@ -12,30 +13,35 @@ import (
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
type StepDeleteBlockStorageInstance struct {
Conn *NcloudAPIClient
DeleteBlockStorageInstance func(blockStorageInstanceNo string) error
Say func(message string)
Error func(e error)
Config *Config
type StepDeleteBlockStorage struct {
Conn *NcloudAPIClient
DeleteBlockStorage func(blockStorageNo string) error
Say func(message string)
Error func(e error)
Config *Config
}
func NewStepDeleteBlockStorageInstance(conn *NcloudAPIClient, ui packersdk.Ui, config *Config) *StepDeleteBlockStorageInstance {
var step = &StepDeleteBlockStorageInstance{
func NewStepDeleteBlockStorage(conn *NcloudAPIClient, ui packersdk.Ui, config *Config) *StepDeleteBlockStorage {
var step = &StepDeleteBlockStorage{
Conn: conn,
Say: func(message string) { ui.Say(message) },
Error: func(e error) { ui.Error(e.Error()) },
Config: config,
}
step.DeleteBlockStorageInstance = step.deleteBlockStorageInstance
if config.SupportVPC {
step.DeleteBlockStorage = step.deleteVpcBlockStorage
} else {
step.DeleteBlockStorage = step.deleteClassicBlockStorage
}
return step
}
func (s *StepDeleteBlockStorageInstance) getBlockInstanceList(serverInstanceNo string) []*string {
reqParams := new(server.GetBlockStorageInstanceListRequest)
reqParams.ServerInstanceNo = &serverInstanceNo
func (s *StepDeleteBlockStorage) getClassicBlockList(serverInstanceNo string) []*string {
reqParams := &server.GetBlockStorageInstanceListRequest{
ServerInstanceNo: &serverInstanceNo,
}
blockStorageInstanceList, err := s.Conn.server.V2Api.GetBlockStorageInstanceList(reqParams)
if err != nil {
@ -58,8 +64,34 @@ func (s *StepDeleteBlockStorageInstance) getBlockInstanceList(serverInstanceNo s
return instanceList
}
func (s *StepDeleteBlockStorageInstance) deleteBlockStorageInstance(serverInstanceNo string) error {
blockStorageInstanceList := s.getBlockInstanceList(serverInstanceNo)
func (s *StepDeleteBlockStorage) getVpcBlockList(serverInstanceNo string) []*string {
reqParams := &vserver.GetBlockStorageInstanceListRequest{
ServerInstanceNo: &serverInstanceNo,
}
blockStorageInstanceList, err := s.Conn.vserver.V2Api.GetBlockStorageInstanceList(reqParams)
if err != nil {
return nil
}
if *blockStorageInstanceList.TotalRows == 1 {
return nil
}
var instanceList []*string
for _, blockStorageInstance := range blockStorageInstanceList.BlockStorageInstanceList {
log.Println(blockStorageInstance)
if *blockStorageInstance.BlockStorageType.Code != "BASIC" {
instanceList = append(instanceList, blockStorageInstance.BlockStorageInstanceNo)
}
}
return instanceList
}
func (s *StepDeleteBlockStorage) deleteClassicBlockStorage(serverInstanceNo string) error {
blockStorageInstanceList := s.getClassicBlockList(serverInstanceNo)
if blockStorageInstanceList == nil || len(blockStorageInstanceList) == 0 {
return nil
}
@ -73,26 +105,48 @@ func (s *StepDeleteBlockStorageInstance) deleteBlockStorageInstance(serverInstan
s.Say(fmt.Sprintf("Block Storage Instance is deleted. Block Storage Instance List is %v", blockStorageInstanceList))
if err := waiterDetachedBlockStorageInstance(s.Conn, serverInstanceNo, time.Minute); err != nil {
if err := waiterClassicDetachedBlockStorage(s.Conn, serverInstanceNo, time.Minute); err != nil {
return errors.New("TIMEOUT : Block Storage instance status is not deattached")
}
return nil
}
func (s *StepDeleteBlockStorageInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
func (s *StepDeleteBlockStorage) deleteVpcBlockStorage(serverInstanceNo string) error {
blockStorageInstanceList := s.getVpcBlockList(serverInstanceNo)
if blockStorageInstanceList == nil || len(blockStorageInstanceList) == 0 {
return nil
}
reqParams := vserver.DeleteBlockStorageInstancesRequest{
BlockStorageInstanceNoList: blockStorageInstanceList,
}
_, err := s.Conn.vserver.V2Api.DeleteBlockStorageInstances(&reqParams)
if err != nil {
return err
}
s.Say(fmt.Sprintf("Block Storage Instance is deleted. Block Storage Instance List is %v", blockStorageInstanceList))
if err := waiterVpcDetachedBlockStorage(s.Conn, serverInstanceNo, time.Minute); err != nil {
return errors.New("TIMEOUT : Block Storage instance status is not deattached")
}
return nil
}
func (s *StepDeleteBlockStorage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
if s.Config.BlockStorageSize == 0 {
return processStepResult(nil, s.Error, state)
}
s.Say("Delete Block Storage Instance")
var serverInstanceNo = state.Get("InstanceNo").(string)
var serverInstanceNo = state.Get("instance_no").(string)
err := s.DeleteBlockStorageInstance(serverInstanceNo)
err := s.DeleteBlockStorage(serverInstanceNo)
return processStepResult(err, s.Error, state)
}
func (*StepDeleteBlockStorageInstance) Cleanup(multistep.StateBag) {
func (*StepDeleteBlockStorage) Cleanup(multistep.StateBag) {
}

View File

@ -9,11 +9,11 @@ import (
)
func TestStepDeleteBlockStorageInstanceShouldFailIfOperationDeleteBlockStorageInstanceFails(t *testing.T) {
var testSubject = &StepDeleteBlockStorageInstance{
DeleteBlockStorageInstance: func(blockStorageInstanceNo string) error { return fmt.Errorf("!! Unit Test FAIL !!") },
Say: func(message string) {},
Error: func(e error) {},
Config: &Config{BlockStorageSize: 10},
var testSubject = &StepDeleteBlockStorage{
DeleteBlockStorage: func(blockStorageNo string) error { return fmt.Errorf("!! Unit Test FAIL !!") },
Say: func(message string) {},
Error: func(e error) {},
Config: &Config{BlockStorageSize: 10},
}
stateBag := createTestStateBagStepDeleteBlockStorageInstance()
@ -24,17 +24,17 @@ func TestStepDeleteBlockStorageInstanceShouldFailIfOperationDeleteBlockStorageIn
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
}
if _, ok := stateBag.GetOk("Error"); ok == false {
if _, ok := stateBag.GetOk("error"); ok == false {
t.Fatal("Expected the step to set stateBag['Error'], but it was not.")
}
}
func TestStepDeleteBlockStorageInstanceShouldPassIfOperationDeleteBlockStorageInstancePasses(t *testing.T) {
var testSubject = &StepDeleteBlockStorageInstance{
DeleteBlockStorageInstance: func(blockStorageInstanceNo string) error { return nil },
Say: func(message string) {},
Error: func(e error) {},
Config: &Config{BlockStorageSize: 10},
var testSubject = &StepDeleteBlockStorage{
DeleteBlockStorage: func(blockStorageNo string) error { return nil },
Say: func(message string) {},
Error: func(e error) {},
Config: &Config{BlockStorageSize: 10},
}
stateBag := createTestStateBagStepDeleteBlockStorageInstance()
@ -45,7 +45,7 @@ func TestStepDeleteBlockStorageInstanceShouldPassIfOperationDeleteBlockStorageIn
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
}
if _, ok := stateBag.GetOk("Error"); ok == true {
if _, ok := stateBag.GetOk("error"); ok == true {
t.Fatalf("Expected the step to not set stateBag['Error'], but it was.")
}
}
@ -53,7 +53,7 @@ func TestStepDeleteBlockStorageInstanceShouldPassIfOperationDeleteBlockStorageIn
func createTestStateBagStepDeleteBlockStorageInstance() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put("InstanceNo", "1")
stateBag.Put("instance_no", "1")
return stateBag
}

View File

@ -3,6 +3,7 @@ package ncloud
import (
"context"
"fmt"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/server"
"github.com/hashicorp/packer-plugin-sdk/multistep"
@ -25,15 +26,20 @@ func NewStepGetRootPassword(conn *NcloudAPIClient, ui packersdk.Ui, config *Conf
Config: config,
}
step.GetRootPassword = step.getRootPassword
if config.SupportVPC {
step.GetRootPassword = step.getVpcRootPassword
} else {
step.GetRootPassword = step.getClassicRootPassword
}
return step
}
func (s *StepGetRootPassword) getRootPassword(serverInstanceNo string, privateKey string) (string, error) {
reqParams := new(server.GetRootPasswordRequest)
reqParams.ServerInstanceNo = &serverInstanceNo
reqParams.PrivateKey = &privateKey
func (s *StepGetRootPassword) getClassicRootPassword(serverInstanceNo string, privateKey string) (string, error) {
reqParams := &server.GetRootPasswordRequest{
ServerInstanceNo: &serverInstanceNo,
PrivateKey: &privateKey,
}
rootPassword, err := s.Conn.server.V2Api.GetRootPassword(reqParams)
if err != nil {
@ -45,11 +51,28 @@ func (s *StepGetRootPassword) getRootPassword(serverInstanceNo string, privateKe
return *rootPassword.RootPassword, nil
}
func (s *StepGetRootPassword) getVpcRootPassword(serverInstanceNo string, privateKey string) (string, error) {
reqParams := &vserver.GetRootPasswordRequest{
RegionCode: &s.Config.RegionCode,
ServerInstanceNo: &serverInstanceNo,
PrivateKey: &privateKey,
}
rootPassword, err := s.Conn.vserver.V2Api.GetRootPassword(reqParams)
if err != nil {
return "", err
}
s.Say(fmt.Sprintf("Root password is %s", *rootPassword.RootPassword))
return *rootPassword.RootPassword, nil
}
func (s *StepGetRootPassword) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
s.Say("Get Root Password")
serverInstanceNo := state.Get("InstanceNo").(string)
loginKey := state.Get("LoginKey").(*LoginKey)
serverInstanceNo := state.Get("instance_no").(string)
loginKey := state.Get("login_key").(*LoginKey)
rootPassword, err := s.GetRootPassword(serverInstanceNo, loginKey.PrivateKey)

View File

@ -24,7 +24,7 @@ func TestStepGetRootPasswordShouldFailIfOperationGetRootPasswordFails(t *testing
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
}
if _, ok := stateBag.GetOk("Error"); ok == false {
if _, ok := stateBag.GetOk("error"); ok == false {
t.Fatal("Expected the step to set stateBag['Error'], but it was not.")
}
}
@ -45,7 +45,7 @@ func TestStepGetRootPasswordShouldPassIfOperationGetRootPasswordPasses(t *testin
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
}
if _, ok := stateBag.GetOk("Error"); ok == true {
if _, ok := stateBag.GetOk("error"); ok == true {
t.Fatalf("Expected the step to not set stateBag['Error'], but it was.")
}
}
@ -53,8 +53,8 @@ func TestStepGetRootPasswordShouldPassIfOperationGetRootPasswordPasses(t *testin
func DeleteTestStateBagStepGetRootPassword() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put("LoginKey", &LoginKey{"a", "b"})
stateBag.Put("InstanceNo", "a")
stateBag.Put("login_key", &LoginKey{"a", "b"})
stateBag.Put("instance_no", "a")
return stateBag
}

View File

@ -3,7 +3,7 @@ package ncloud
import (
"context"
"fmt"
"log"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
"time"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/server"
@ -12,27 +12,37 @@ import (
)
type StepStopServerInstance struct {
Conn *NcloudAPIClient
StopServerInstance func(serverInstanceNo string) error
Say func(message string)
Error func(e error)
Conn *NcloudAPIClient
StopServerInstance func(serverInstanceNo string) error
WaiterServerInstanceStatus func(conn *NcloudAPIClient, serverInstanceNo string, status string, timeout time.Duration) error
Say func(message string)
Error func(e error)
Config *Config
}
func NewStepStopServerInstance(conn *NcloudAPIClient, ui packersdk.Ui) *StepStopServerInstance {
func NewStepStopServerInstance(conn *NcloudAPIClient, ui packersdk.Ui, config *Config) *StepStopServerInstance {
var step = &StepStopServerInstance{
Conn: conn,
Say: func(message string) { ui.Say(message) },
Error: func(e error) { ui.Error(e.Error()) },
Conn: conn,
Say: func(message string) { ui.Say(message) },
Error: func(e error) { ui.Error(e.Error()) },
Config: config,
}
step.StopServerInstance = step.stopServerInstance
if config.SupportVPC {
step.StopServerInstance = step.stopVpcServerInstance
step.WaiterServerInstanceStatus = waiterVpcServerInstanceStatus
} else {
step.StopServerInstance = step.stopClassicServerInstance
step.WaiterServerInstanceStatus = waiterClassicServerInstanceStatus
}
return step
}
func (s *StepStopServerInstance) stopServerInstance(serverInstanceNo string) error {
reqParams := new(server.StopServerInstancesRequest)
reqParams.ServerInstanceNoList = []*string{&serverInstanceNo}
func (s *StepStopServerInstance) stopClassicServerInstance(serverInstanceNo string) error {
reqParams := &server.StopServerInstancesRequest{
ServerInstanceNoList: []*string{&serverInstanceNo},
}
serverInstanceList, err := s.Conn.server.V2Api.StopServerInstances(reqParams)
if err != nil {
@ -40,9 +50,30 @@ func (s *StepStopServerInstance) stopServerInstance(serverInstanceNo string) err
}
s.Say(fmt.Sprintf("Server Instance is stopping. Server InstanceNo is %s", *serverInstanceList.ServerInstanceList[0].ServerInstanceNo))
log.Println("Server Instance information : ", serverInstanceList.ServerInstanceList[0])
if err := waiterServerInstanceStatus(s.Conn, serverInstanceNo, "NSTOP", 5*time.Minute); err != nil {
if err := s.WaiterServerInstanceStatus(s.Conn, serverInstanceNo, ServerInstanceStatusStopped, 5*time.Minute); err != nil {
return err
}
s.Say(fmt.Sprintf("Server Instance stopped. Server InstanceNo is %s", *serverInstanceList.ServerInstanceList[0].ServerInstanceNo))
return nil
}
func (s *StepStopServerInstance) stopVpcServerInstance(serverInstanceNo string) error {
reqParams := &vserver.StopServerInstancesRequest{
RegionCode: &s.Config.RegionCode,
ServerInstanceNoList: []*string{&serverInstanceNo},
}
serverInstanceList, err := s.Conn.vserver.V2Api.StopServerInstances(reqParams)
if err != nil {
return err
}
s.Say(fmt.Sprintf("Server Instance is stopping. Server InstanceNo is %s", *serverInstanceList.ServerInstanceList[0].ServerInstanceNo))
if err := s.WaiterServerInstanceStatus(s.Conn, serverInstanceNo, ServerInstanceStatusStopped, 5*time.Minute); err != nil {
return err
}
@ -54,7 +85,7 @@ func (s *StepStopServerInstance) stopServerInstance(serverInstanceNo string) err
func (s *StepStopServerInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
s.Say("Stop Server Instance")
var serverInstanceNo = state.Get("InstanceNo").(string)
var serverInstanceNo = state.Get("instance_no").(string)
err := s.StopServerInstance(serverInstanceNo)

View File

@ -23,7 +23,7 @@ func TestStepStopServerInstanceShouldFailIfOperationStopFails(t *testing.T) {
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
}
if _, ok := stateBag.GetOk("Error"); ok == false {
if _, ok := stateBag.GetOk("error"); ok == false {
t.Fatal("Expected the step to set stateBag['Error'], but it was not.")
}
}
@ -43,7 +43,7 @@ func TestStepStopServerInstanceShouldPassIfOperationStopPasses(t *testing.T) {
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
}
if _, ok := stateBag.GetOk("Error"); ok == true {
if _, ok := stateBag.GetOk("error"); ok == true {
t.Fatalf("Expected the step to not set stateBag['Error'], but it was.")
}
}
@ -51,6 +51,6 @@ func TestStepStopServerInstanceShouldPassIfOperationStopPasses(t *testing.T) {
func createTestStateBagStepStopServerInstance() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put("InstanceNo", "a")
stateBag.Put("instance_no", "a")
return stateBag
}

View File

@ -3,6 +3,9 @@ package ncloud
import (
"context"
"errors"
"fmt"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
"log"
"time"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/server"
@ -15,37 +18,45 @@ type StepTerminateServerInstance struct {
TerminateServerInstance func(serverInstanceNo string) error
Say func(message string)
Error func(e error)
Config *Config
}
func NewStepTerminateServerInstance(conn *NcloudAPIClient, ui packersdk.Ui) *StepTerminateServerInstance {
func NewStepTerminateServerInstance(conn *NcloudAPIClient, ui packersdk.Ui, config *Config) *StepTerminateServerInstance {
var step = &StepTerminateServerInstance{
Conn: conn,
Say: func(message string) { ui.Say(message) },
Error: func(e error) { ui.Error(e.Error()) },
Conn: conn,
Say: func(message string) { ui.Say(message) },
Error: func(e error) { ui.Error(e.Error()) },
Config: config,
}
step.TerminateServerInstance = step.terminateServerInstance
if config.SupportVPC {
step.TerminateServerInstance = step.terminateVpcServerInstance
} else {
step.TerminateServerInstance = step.terminateClassicServerInstance
}
return step
}
func (s *StepTerminateServerInstance) terminateServerInstance(serverInstanceNo string) error {
reqParams := new(server.TerminateServerInstancesRequest)
reqParams.ServerInstanceNoList = []*string{&serverInstanceNo}
func (s *StepTerminateServerInstance) terminateClassicServerInstance(serverInstanceNo string) error {
reqParams := &server.TerminateServerInstancesRequest{
ServerInstanceNoList: []*string{&serverInstanceNo},
}
_, err := s.Conn.server.V2Api.TerminateServerInstances(reqParams)
if err != nil {
return err
}
s.Say(fmt.Sprintf("Server Instance is terminating. Server InstanceNo is %s", serverInstanceNo))
c1 := make(chan error, 1)
go func() {
reqParams := new(server.GetServerInstanceListRequest)
reqParams.ServerInstanceNoList = []*string{&serverInstanceNo}
reqParams := &server.GetServerInstanceListRequest{
ServerInstanceNoList: []*string{&serverInstanceNo},
}
for {
serverInstanceList, err := s.Conn.server.V2Api.GetServerInstanceList(reqParams)
if err != nil {
c1 <- err
@ -55,22 +66,67 @@ func (s *StepTerminateServerInstance) terminateServerInstance(serverInstanceNo s
return
}
log.Printf("Wating for terminating server instance [%s] is %s\n", serverInstanceNo, *serverInstanceList.ServerInstanceList[0].ServerInstanceStatus.Code)
time.Sleep(time.Second * 3)
}
}()
select {
case res := <-c1:
s.Say(fmt.Sprintf("Server Instance terminated. Server InstanceNo is %s", serverInstanceNo))
return res
case <-time.After(time.Second * 60):
return errors.New("TIMEOUT : Can't terminate server instance")
}
}
func (s *StepTerminateServerInstance) terminateVpcServerInstance(serverInstanceNo string) error {
reqParams := &vserver.TerminateServerInstancesRequest{
ServerInstanceNoList: []*string{&serverInstanceNo},
}
_, err := s.Conn.vserver.V2Api.TerminateServerInstances(reqParams)
if err != nil {
return err
}
s.Say(fmt.Sprintf("Server Instance is terminating. Server InstanceNo is %s", serverInstanceNo))
c1 := make(chan error, 1)
go func() {
reqParams := &vserver.GetServerInstanceListRequest{
RegionCode: &s.Config.RegionCode,
ServerInstanceNoList: []*string{&serverInstanceNo},
}
for {
serverInstanceList, err := s.Conn.vserver.V2Api.GetServerInstanceList(reqParams)
if err != nil {
c1 <- err
return
} else if *serverInstanceList.TotalRows == 0 {
c1 <- nil
return
}
log.Printf("Wating for terminating server instance [%s] is %s\n", serverInstanceNo, *serverInstanceList.ServerInstanceList[0].ServerInstanceStatus.Code)
time.Sleep(time.Second * 3)
}
}()
select {
case res := <-c1:
s.Say(fmt.Sprintf("Server Instance terminated. Server InstanceNo is %s", serverInstanceNo))
return res
case <-time.After(time.Second * 120):
return errors.New("TIMEOUT : Can't terminate server instance")
}
}
func (s *StepTerminateServerInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
s.Say("Terminate Server Instance")
var serverInstanceNo = state.Get("InstanceNo").(string)
var serverInstanceNo = state.Get("instance_no").(string)
err := s.TerminateServerInstance(serverInstanceNo)

View File

@ -23,7 +23,7 @@ func TestStepTerminateServerInstanceShouldFailIfOperationTerminationFails(t *tes
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
}
if _, ok := stateBag.GetOk("Error"); ok == false {
if _, ok := stateBag.GetOk("error"); ok == false {
t.Fatal("Expected the step to set stateBag['Error'], but it was not.")
}
}
@ -43,7 +43,7 @@ func TestStepTerminateServerInstanceShouldPassIfOperationTerminationPasses(t *te
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
}
if _, ok := stateBag.GetOk("Error"); ok == true {
if _, ok := stateBag.GetOk("error"); ok == true {
t.Fatalf("Expected the step to not set stateBag['Error'], but it was.")
}
}
@ -51,6 +51,6 @@ func TestStepTerminateServerInstanceShouldPassIfOperationTerminationPasses(t *te
func createTestStateBagStepTerminateServerInstance() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put("InstanceNo", "a")
stateBag.Put("instance_no", "a")
return stateBag
}

View File

@ -5,6 +5,8 @@ import (
"context"
"errors"
"fmt"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vpc"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
"strings"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/ncloud"
@ -16,14 +18,20 @@ import (
//StepValidateTemplate : struct for Validation a template
type StepValidateTemplate struct {
Conn *NcloudAPIClient
Validate func() error
Say func(message string)
Error func(e error)
Config *Config
zoneNo string
regionNo string
FeeSystemTypeCode string
Conn *NcloudAPIClient
Validate func() error
getZone func() error
getMemberServerImageList func() ([]*server.MemberServerImage, error)
getServerImageProductList func(blockStorageSize *int32) ([]*server.Product, error)
getServerProductList func(serverImageProductCode string) ([]*server.Product, error)
Say func(message string)
Error func(e error)
Config *Config
zoneNo string
zoneCode string
regionNo string
regionCode string
FeeSystemTypeCode string
}
// NewStepValidateTemplate : function for Validation a template
@ -35,6 +43,18 @@ func NewStepValidateTemplate(conn *NcloudAPIClient, ui packersdk.Ui, config *Con
Config: config,
}
if config.SupportVPC {
step.getZone = step.getZoneCode
step.getMemberServerImageList = step.getVpcMemberServerImageList
step.getServerImageProductList = step.getVpcServerImageProductList
step.getServerProductList = step.getVpcServerProductList
} else {
step.getZone = step.getZoneNo
step.getMemberServerImageList = step.getClassicMemberServerImageList
step.getServerImageProductList = step.getClassicServerImageProductList
step.getServerProductList = step.getClassicServerProductList
}
step.Validate = step.validateTemplate
return step
@ -51,21 +71,19 @@ func (s *StepValidateTemplate) getZoneNo() error {
return err
}
var regionNo string
for _, region := range regionList.RegionList {
if strings.EqualFold(*region.RegionName, s.Config.Region) {
regionNo = *region.RegionNo
s.regionNo = *region.RegionNo
s.regionCode = *region.RegionCode
}
}
if regionNo == "" {
if s.regionNo == "" {
return fmt.Errorf("region %s is invalid", s.Config.Region)
}
s.regionNo = regionNo
// Get ZoneNo
resp, err := s.Conn.server.V2Api.GetZoneList(&server.GetZoneListRequest{RegionNo: &regionNo})
resp, err := s.Conn.server.V2Api.GetZoneList(&server.GetZoneListRequest{RegionNo: &s.regionNo})
if err != nil {
return err
}
@ -77,19 +95,51 @@ func (s *StepValidateTemplate) getZoneNo() error {
return nil
}
func (s *StepValidateTemplate) validateMemberServerImage() error {
// getZoneCode : get zoneCode
func (s *StepValidateTemplate) getZoneCode() error {
if s.Config.Region == "" {
return nil
}
regionList, err := s.Conn.vserver.V2Api.GetRegionList(&vserver.GetRegionListRequest{})
if err != nil {
return err
}
for _, region := range regionList.RegionList {
if strings.EqualFold(*region.RegionName, s.Config.Region) {
s.regionCode = *region.RegionCode
s.Config.RegionCode = *region.RegionCode
}
}
if s.regionCode == "" {
return fmt.Errorf("region %s is invalid", s.Config.Region)
}
// Get ZoneNo
resp, err := s.Conn.vserver.V2Api.GetZoneList(&vserver.GetZoneListRequest{RegionCode: &s.regionCode})
if err != nil {
return err
}
if len(resp.ZoneList) > 0 {
s.zoneCode = *resp.ZoneList[0].ZoneCode
}
return nil
}
func (s *StepValidateTemplate) validateMemberServerImage(fnGetServerImageList func() ([]*server.MemberServerImage, error)) error {
var serverImageName = s.Config.ServerImageName
reqParams := new(server.GetMemberServerImageListRequest)
reqParams.RegionNo = &s.regionNo
memberServerImageList, err := s.Conn.server.V2Api.GetMemberServerImageList(reqParams)
memberServerImageList, err := fnGetServerImageList()
if err != nil {
return err
}
var isExistMemberServerImageNo = false
for _, image := range memberServerImageList.MemberServerImageList {
for _, image := range memberServerImageList {
// Check duplicate server_image_name
if *image.MemberServerImageName == serverImageName {
return fmt.Errorf("server_image_name %s is exists", serverImageName)
@ -112,16 +162,86 @@ func (s *StepValidateTemplate) validateMemberServerImage() error {
return nil
}
func (s *StepValidateTemplate) getClassicMemberServerImageList() ([]*server.MemberServerImage, error) {
reqParams := &server.GetMemberServerImageListRequest{
RegionNo: &s.regionNo,
}
resp, err := s.Conn.server.V2Api.GetMemberServerImageList(reqParams)
if err != nil {
return nil, err
}
return resp.MemberServerImageList, nil
}
func (s *StepValidateTemplate) getVpcMemberServerImageList() ([]*server.MemberServerImage, error) {
reqParams := &vserver.GetMemberServerImageInstanceListRequest{
RegionCode: &s.regionCode,
}
memberServerImageList, err := s.Conn.vserver.V2Api.GetMemberServerImageInstanceList(reqParams)
if err != nil {
return nil, err
}
var results []*server.MemberServerImage
for _, r := range memberServerImageList.MemberServerImageInstanceList {
results = append(results, &server.MemberServerImage{
MemberServerImageNo: r.MemberServerImageInstanceNo,
MemberServerImageName: r.MemberServerImageName,
MemberServerImageDescription: r.MemberServerImageDescription,
OriginalServerInstanceNo: r.OriginalServerInstanceNo,
OriginalServerProductCode: r.OriginalServerImageProductCode,
})
}
return results, nil
}
func (s *StepValidateTemplate) getClassicServerImageProductList(blockStorageSize *int32) ([]*server.Product, error) {
reqParams := &server.GetServerImageProductListRequest{
RegionNo: &s.regionNo,
BlockStorageSize: blockStorageSize,
}
resp, err := s.Conn.server.V2Api.GetServerImageProductList(reqParams)
if err != nil {
return nil, err
}
return resp.ProductList, nil
}
func (s *StepValidateTemplate) getVpcServerImageProductList(blockStorageSize *int32) ([]*server.Product, error) {
reqParams := &vserver.GetServerImageProductListRequest{
RegionCode: &s.regionCode,
BlockStorageSize: blockStorageSize,
}
resp, err := s.Conn.vserver.V2Api.GetServerImageProductList(reqParams)
if err != nil {
return nil, err
}
var results []*server.Product
for _, r := range resp.ProductList {
results = append(results, &server.Product{
ProductCode: r.ProductCode,
ProductName: r.ProductName,
})
}
return results, nil
}
func (s *StepValidateTemplate) validateServerImageProduct() error {
var serverImageProductCode = s.Config.ServerImageProductCode
if serverImageProductCode == "" {
return nil
}
reqParams := new(server.GetServerImageProductListRequest)
reqParams.RegionNo = &s.regionNo
serverImageProductList, err := s.Conn.server.V2Api.GetServerImageProductList(reqParams)
serverImageProductList, err := s.getServerImageProductList(nil)
if err != nil {
return err
}
@ -132,7 +252,7 @@ func (s *StepValidateTemplate) validateServerImageProduct() error {
table := tablewriter.NewWriter(&buf)
table.SetHeader([]string{"Name", "Code"})
for _, product := range serverImageProductList.ProductList {
for _, product := range serverImageProductList {
// Check exist server image product code
if *product.ProductCode == serverImageProductCode {
isExistServerImage = true
@ -144,14 +264,12 @@ func (s *StepValidateTemplate) validateServerImageProduct() error {
}
if !isExistServerImage {
reqParams.BlockStorageSize = ncloud.Int32(100)
serverImageProductList, err := s.Conn.server.V2Api.GetServerImageProductList(reqParams)
serverImageProductList, err := s.getServerImageProductList(ncloud.Int32(100))
if err != nil {
return err
}
for _, product := range serverImageProductList.ProductList {
for _, product := range serverImageProductList {
// Check exist server image product code
if *product.ProductCode == serverImageProductCode {
isExistServerImage = true
@ -177,21 +295,59 @@ func (s *StepValidateTemplate) validateServerImageProduct() error {
return nil
}
func (s *StepValidateTemplate) getClassicServerProductList(serverImageProductCode string) ([]*server.Product, error) {
reqParams := &server.GetServerProductListRequest{
ServerImageProductCode: &serverImageProductCode,
RegionNo: &s.regionNo,
}
resp, err := s.Conn.server.V2Api.GetServerProductList(reqParams)
if err != nil {
return nil, err
}
return resp.ProductList, nil
}
func (s *StepValidateTemplate) getVpcServerProductList(serverImageProductCode string) ([]*server.Product, error) {
reqParams := &vserver.GetServerProductListRequest{
ServerImageProductCode: &serverImageProductCode,
RegionCode: &s.regionCode,
}
resp, err := s.Conn.vserver.V2Api.GetServerProductList(reqParams)
if err != nil {
return nil, err
}
var results []*server.Product
for _, r := range resp.ProductList {
results = append(results, &server.Product{
ProductCode: r.ProductCode,
ProductName: r.ProductName,
ProductType: &server.CommonCode{
Code: r.ProductType.Code,
CodeName: r.ProductType.CodeName,
},
})
}
return results, nil
}
func (s *StepValidateTemplate) validateServerProductCode() error {
var serverImageProductCode = s.Config.ServerImageProductCode
var productCode = s.Config.ServerProductCode
reqParams := new(server.GetServerProductListRequest)
reqParams.ServerImageProductCode = &serverImageProductCode
reqParams.RegionNo = &s.regionNo
resp, err := s.Conn.server.V2Api.GetServerProductList(reqParams)
productList, err := s.getServerProductList(serverImageProductCode)
if err != nil {
return err
}
var isExistProductCode = false
for _, product := range resp.ProductList {
for _, product := range productList {
// Check exist server image product code
if *product.ProductCode == productCode {
isExistProductCode = true
@ -216,7 +372,7 @@ func (s *StepValidateTemplate) validateServerProductCode() error {
var buf bytes.Buffer
table := tablewriter.NewWriter(&buf)
table.SetHeader([]string{"Name", "Code"})
for _, product := range resp.ProductList {
for _, product := range productList {
table.Append([]string{*product.ProductName, *product.ProductCode})
}
table.Render()
@ -229,15 +385,80 @@ func (s *StepValidateTemplate) validateServerProductCode() error {
return nil
}
func (s *StepValidateTemplate) validateVpc() error {
if !s.Config.SupportVPC {
return nil
}
if s.Config.VpcNo != "" {
reqParam := &vpc.GetVpcDetailRequest{
RegionCode: &s.Config.RegionCode,
VpcNo: &s.Config.VpcNo,
}
resp, err := s.Conn.vpc.V2Api.GetVpcDetail(reqParam)
if err != nil {
return err
}
if resp == nil || *resp.TotalRows == 0 {
return fmt.Errorf("cloud not found VPC `vpc_no` [%s]", s.Config.VpcNo)
}
}
if s.Config.SubnetNo != "" {
reqParam := &vpc.GetSubnetDetailRequest{
RegionCode: &s.Config.RegionCode,
SubnetNo: &s.Config.SubnetNo,
}
resp, err := s.Conn.vpc.V2Api.GetSubnetDetail(reqParam)
if err != nil {
return err
}
if resp != nil && *resp.TotalRows > 0 && *resp.SubnetList[0].SubnetType.Code != "PUBLIC" {
if s.Config.VpcNo == "" {
s.Config.VpcNo = *resp.SubnetList[0].VpcNo
s.Say("Set `vpc_no` is " + s.Config.VpcNo)
}
} else {
return fmt.Errorf("cloud not found public subnet in `vpc_no` [%s]", s.Config.VpcNo)
}
}
if s.Config.VpcNo != "" && s.Config.SubnetNo == "" {
reqParam := &vpc.GetSubnetListRequest{
RegionCode: &s.Config.RegionCode,
VpcNo: &s.Config.VpcNo,
SubnetTypeCode: ncloud.String("PUBLIC"),
}
resp, err := s.Conn.vpc.V2Api.GetSubnetList(reqParam)
if err != nil {
return err
}
if resp != nil && *resp.TotalRows > 0 {
s.Config.SubnetNo = *resp.SubnetList[0].SubnetNo
s.Say("Set `subnet_no` is " + s.Config.SubnetNo)
} else {
return fmt.Errorf("cloud not found public subnet in `vpc_no` [%s]", s.Config.VpcNo)
}
}
return nil
}
// Check ImageName / Product Code / Server Image Product Code / Server Product Code...
func (s *StepValidateTemplate) validateTemplate() error {
// Get RegionNo, ZoneNo
if err := s.getZoneNo(); err != nil {
if err := s.getZone(); err != nil {
return err
}
// Validate member_server_image_no and member_server_image_no
if err := s.validateMemberServerImage(); err != nil {
if err := s.validateMemberServerImage(s.getMemberServerImageList); err != nil {
return err
}
@ -246,6 +467,11 @@ func (s *StepValidateTemplate) validateTemplate() error {
return err
}
// Validate VPC
if err := s.validateVpc(); err != nil {
return err
}
// Validate server_product_code
return s.validateServerProductCode()
}
@ -256,10 +482,10 @@ func (s *StepValidateTemplate) Run(ctx context.Context, state multistep.StateBag
err := s.Validate()
state.Put("ZoneNo", s.zoneNo)
state.Put("zone_no", s.zoneNo)
if s.FeeSystemTypeCode != "" {
state.Put("FeeSystemTypeCode", s.FeeSystemTypeCode)
state.Put("fee_system_type_code", s.FeeSystemTypeCode)
}
return processStepResult(err, s.Error, state)

View File

@ -23,7 +23,7 @@ func TestStepValidateTemplateShouldFailIfValidateFails(t *testing.T) {
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
}
if _, ok := stateBag.GetOk("Error"); ok == false {
if _, ok := stateBag.GetOk("error"); ok == false {
t.Fatal("Expected the step to set stateBag['Error'], but it was not.")
}
}
@ -43,7 +43,7 @@ func TestStepValidateTemplateShouldPassIfValidatePasses(t *testing.T) {
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
}
if _, ok := stateBag.GetOk("Error"); ok == true {
if _, ok := stateBag.GetOk("error"); ok == true {
t.Fatalf("Expected the step to not set stateBag['Error'], but it was.")
}
}

View File

@ -2,15 +2,17 @@ package ncloud
import (
"fmt"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
"log"
"time"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/server"
)
func waiterBlockStorageInstanceStatus(conn *NcloudAPIClient, blockStorageInstanceNo *string, status string, timeout time.Duration) error {
reqParams := new(server.GetBlockStorageInstanceListRequest)
reqParams.BlockStorageInstanceNoList = []*string{blockStorageInstanceNo}
func waiterClassicBlockStorageStatus(conn *NcloudAPIClient, blockStorageInstanceNo *string, status string, timeout time.Duration) error {
reqParams := &server.GetBlockStorageInstanceListRequest{
BlockStorageInstanceNoList: []*string{blockStorageInstanceNo},
}
c1 := make(chan error, 1)
@ -49,12 +51,54 @@ func waiterBlockStorageInstanceStatus(conn *NcloudAPIClient, blockStorageInstanc
}
}
func waiterDetachedBlockStorageInstance(conn *NcloudAPIClient, serverInstanceNo string, timeout time.Duration) error {
reqParams := new(server.GetBlockStorageInstanceListRequest)
reqParams.ServerInstanceNo = &serverInstanceNo
func waiterVpcBlockStorageStatus(conn *NcloudAPIClient, blockStorageInstanceNo *string, status string, timeout time.Duration) error {
reqParams := &vserver.GetBlockStorageInstanceListRequest{
BlockStorageInstanceNoList: []*string{blockStorageInstanceNo},
}
c1 := make(chan error, 1)
go func() {
for {
blockStorageInstanceList, err := conn.vserver.V2Api.GetBlockStorageInstanceList(reqParams)
if err != nil {
c1 <- err
return
}
if status == "DETAC" && len(blockStorageInstanceList.BlockStorageInstanceList) == 0 {
c1 <- nil
return
}
blockStorageInstance := blockStorageInstanceList.BlockStorageInstanceList[0]
code := blockStorageInstance.BlockStorageInstanceStatus.Code
operationCode := blockStorageInstance.BlockStorageInstanceOperation.Code
if *code == status && *operationCode == "NULL" {
c1 <- nil
return
}
log.Println(blockStorageInstance)
time.Sleep(time.Second * 5)
}
}()
select {
case res := <-c1:
return res
case <-time.After(timeout):
return fmt.Errorf("TIMEOUT : block storage instance status is not changed into status %s", status)
}
}
func waiterClassicDetachedBlockStorage(conn *NcloudAPIClient, serverInstanceNo string, timeout time.Duration) error {
reqParams := &server.GetBlockStorageInstanceListRequest{
ServerInstanceNo: &serverInstanceNo,
}
c1 := make(chan error, 1)
go func() {
for {
blockStorageInstanceList, err := conn.server.V2Api.GetBlockStorageInstanceList(reqParams)
@ -79,3 +123,34 @@ func waiterDetachedBlockStorageInstance(conn *NcloudAPIClient, serverInstanceNo
return fmt.Errorf("TIMEOUT : attached block storage instance is not detached")
}
}
func waiterVpcDetachedBlockStorage(conn *NcloudAPIClient, serverInstanceNo string, timeout time.Duration) error {
reqParams := &vserver.GetBlockStorageInstanceListRequest{
ServerInstanceNo: &serverInstanceNo,
}
c1 := make(chan error, 1)
go func() {
for {
blockStorageInstanceList, err := conn.vserver.V2Api.GetBlockStorageInstanceList(reqParams)
if err != nil {
c1 <- err
return
}
if *blockStorageInstanceList.TotalRows == 1 {
c1 <- nil
return
}
time.Sleep(time.Second * 5)
}
}()
select {
case res := <-c1:
return res
case <-time.After(timeout):
return fmt.Errorf("TIMEOUT : attached block storage instance is not detached")
}
}

View File

@ -2,15 +2,17 @@ package ncloud
import (
"fmt"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
"log"
"time"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/server"
)
func waiterMemberServerImageStatus(conn *NcloudAPIClient, memberServerImageNo string, status string, timeout time.Duration) error {
reqParams := new(server.GetMemberServerImageListRequest)
reqParams.MemberServerImageNoList = []*string{&memberServerImageNo}
func waiterClassicMemberServerImageStatus(conn *NcloudAPIClient, memberServerImageNo string, status string, timeout time.Duration) error {
reqParams := &server.GetMemberServerImageListRequest{
MemberServerImageNoList: []*string{&memberServerImageNo},
}
c1 := make(chan error, 1)
@ -29,7 +31,40 @@ func waiterMemberServerImageStatus(conn *NcloudAPIClient, memberServerImageNo st
}
log.Printf("Status of member server image [%s] is %s\n", memberServerImageNo, *code)
log.Println(memberServerImageList.MemberServerImageList[0])
time.Sleep(time.Second * 5)
}
}()
select {
case res := <-c1:
return res
case <-time.After(timeout):
return fmt.Errorf("TIMEOUT : member server image status is not changed into status %s", status)
}
}
func waiterVpcMemberServerImageStatus(conn *NcloudAPIClient, memberServerImageNo string, status string, timeout time.Duration) error {
reqParams := &vserver.GetMemberServerImageInstanceDetailRequest{
MemberServerImageInstanceNo: &memberServerImageNo,
}
c1 := make(chan error, 1)
go func() {
for {
memberServerImageList, err := conn.vserver.V2Api.GetMemberServerImageInstanceDetail(reqParams)
if err != nil {
c1 <- err
return
}
code := memberServerImageList.MemberServerImageInstanceList[0].MemberServerImageInstanceStatus.Code
if *code == status {
c1 <- nil
return
}
log.Printf("Status of member server image [%s] is %s\n", memberServerImageNo, *code)
time.Sleep(time.Second * 5)
}
}()

View File

@ -2,13 +2,14 @@ package ncloud
import (
"fmt"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
"log"
"time"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/server"
)
func waiterServerInstanceStatus(conn *NcloudAPIClient, serverInstanceNo string, status string, timeout time.Duration) error {
func waiterClassicServerInstanceStatus(conn *NcloudAPIClient, serverInstanceNo string, status string, timeout time.Duration) error {
reqParams := new(server.GetServerInstanceListRequest)
reqParams.ServerInstanceNoList = []*string{&serverInstanceNo}
@ -29,7 +30,40 @@ func waiterServerInstanceStatus(conn *NcloudAPIClient, serverInstanceNo string,
}
log.Printf("Status of serverInstanceNo [%s] is %s\n", serverInstanceNo, *code)
log.Println(serverInstanceList.ServerInstanceList[0])
time.Sleep(time.Second * 5)
}
}()
select {
case res := <-c1:
return res
case <-time.After(timeout):
return fmt.Errorf("TIMEOUT : server instance status is not changed into status %s", status)
}
}
func waiterVpcServerInstanceStatus(conn *NcloudAPIClient, serverInstanceNo string, status string, timeout time.Duration) error {
reqParams := &vserver.GetServerInstanceDetailRequest{
ServerInstanceNo: &serverInstanceNo,
}
c1 := make(chan error, 1)
go func() {
for {
serverInstanceList, err := conn.vserver.V2Api.GetServerInstanceDetail(reqParams)
if err != nil {
c1 <- err
return
}
code := serverInstanceList.ServerInstanceList[0].ServerInstanceStatus.Code
if *code == status {
c1 <- nil
return
}
log.Printf("Status of serverInstanceNo [%s] is %s\n", serverInstanceNo, *code)
time.Sleep(time.Second * 5)
}
}()

2
go.mod
View File

@ -11,7 +11,7 @@ require (
github.com/Azure/go-autorest/autorest/date v0.2.0
github.com/Azure/go-autorest/autorest/to v0.3.0
github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022
github.com/NaverCloudPlatform/ncloud-sdk-go-v2 v1.1.0
github.com/NaverCloudPlatform/ncloud-sdk-go-v2 v1.1.7
github.com/Telmate/proxmox-api-go v0.0.0-20200715182505-ec97c70ba887
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190418113227-25233c783f4e
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20170113022742-e6dbea820a9f

2
go.sum
View File

@ -81,6 +81,8 @@ github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022/go.mod h1:nu
github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/NaverCloudPlatform/ncloud-sdk-go-v2 v1.1.0 h1:0nxjOH7NurPGUWNG5BCrASWjB0uuhGbgJAKLqj2ZDTo=
github.com/NaverCloudPlatform/ncloud-sdk-go-v2 v1.1.0/go.mod h1:P+3VS0ETiQPyWOx3vB/oeC8J3qd7jnVZLYAFwWgGRt8=
github.com/NaverCloudPlatform/ncloud-sdk-go-v2 v1.1.7 h1:Kpnbe19WkzVPpLLdAus4LkpNJ+pzLpfAViOUuvPcCqA=
github.com/NaverCloudPlatform/ncloud-sdk-go-v2 v1.1.7/go.mod h1:P+3VS0ETiQPyWOx3vB/oeC8J3qd7jnVZLYAFwWgGRt8=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/Telmate/proxmox-api-go v0.0.0-20200715182505-ec97c70ba887 h1:Q65o4V0g/KR1sSUZIMf4m1rShb7f1tVHuEt30hfnh2A=

View File

@ -27,7 +27,15 @@
- `region` (string) - Name of the region where you want to create an image.
(default: Korea)
- `region_code` (string) - Region Code
- `access_control_group_configuration_no` (string) - This is used to allow
winrm access when you create a Windows server. An ACG that specifies an
access source (0.0.0.0/0) and allowed port (5985) must be created in
advance.
- `support_vpc` (bool) - Whether to use VPC. By default, the value is false on "public" site. If you want to use VPC environment. Please set this value true.
- `subnet_no` (string) - The ID of the associated Subnet
- `vpc_no` (string) - The ID of the VPC where you want to place the Server Instance