Support Naver Cloud Platform

This commit is contained in:
유성덕 2018-01-11 18:57:53 +09:00
parent 60a136b20a
commit 5189d65467
34 changed files with 2555 additions and 0 deletions

View File

@ -0,0 +1,46 @@
package ncloud
import (
"bytes"
"fmt"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
)
const BuilderID = "ncloud.server.image"
type Artifact struct {
ServerImage *ncloud.ServerImage
}
func (*Artifact) BuilderId() string {
return BuilderID
}
func (a *Artifact) Files() []string {
/* no file */
return nil
}
func (a *Artifact) Id() string {
return a.ServerImage.MemberServerImageNo
}
func (a *Artifact) String() string {
var buf bytes.Buffer
// TODO : Logging artifact information
buf.WriteString(fmt.Sprintf("%s:\n\n", a.BuilderId()))
buf.WriteString(fmt.Sprintf("Member Server Image Name: %s\n", a.ServerImage.MemberServerImageName))
buf.WriteString(fmt.Sprintf("Member Server Image No: %s\n", a.ServerImage.MemberServerImageNo))
return buf.String()
}
func (a *Artifact) State(name string) interface{} {
return a.ServerImage.MemberServerImageStatus
}
func (a *Artifact) Destroy() error {
return nil
}

117
builder/ncloud/builder.go Normal file
View File

@ -0,0 +1,117 @@
package ncloud
import (
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
const version = "1.0.0"
// Builder assume this implements packer.Builder
type Builder struct {
config *Config
stateBag multistep.StateBag
runner multistep.Runner
}
func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
c, warnings, errs := NewConfig(raws...)
if errs != nil {
return warnings, errs
}
b.config = c
b.stateBag = new(multistep.BasicStateBag)
return warnings, nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
ui.Say("Running builder for Naver Cloud Platform (version: " + version + ") ...")
ui.Message("Creating Naver Cloud Platform Connection ...")
conn := ncloud.NewConnection(b.config.AccessKey, b.config.SecretKey)
b.stateBag.Put("hook", hook)
b.stateBag.Put("ui", ui)
var steps []multistep.Step
steps = []multistep.Step{}
if b.config.OSType == "Linux" {
steps = []multistep.Step{
NewStepValidateTemplate(conn, ui, b.config),
NewStepCreateLoginKey(conn, ui),
NewStepCreateServerInstance(conn, ui, b.config),
NewStepCreateBlockStorageInstance(conn, ui, b.config),
NewStepGetRootPassword(conn, ui),
NewStepCreatePublicIPInstance(conn, ui, b.config),
&communicator.StepConnectSSH{
Config: &b.config.Comm,
Host: SSHHost,
SSHConfig: SSHConfig(b.config.Comm.SSHUsername),
},
&common.StepProvision{},
NewStepStopServerInstance(conn, ui),
NewStepCreateServerImage(conn, ui, b.config),
NewStepDeleteBlockStorageInstance(conn, ui, b.config),
NewStepTerminateServerInstance(conn, ui),
NewStepDeleteLoginKey(conn, ui),
NewStepDeletePublicIPInstance(conn, ui),
}
} else if b.config.OSType == "Windows" {
steps = []multistep.Step{
NewStepValidateTemplate(conn, ui, b.config),
NewStepCreateLoginKey(conn, ui),
NewStepCreateServerInstance(conn, ui, b.config),
NewStepCreateBlockStorageInstance(conn, ui, b.config),
NewStepGetRootPassword(conn, ui),
NewStepCreatePublicIPInstance(conn, ui, b.config),
&communicator.StepConnectWinRM{
Config: &b.config.Comm,
Host: func(stateBag multistep.StateBag) (string, error) {
return stateBag.Get("WinRMHost").(string), nil
},
WinRMConfig: func(state multistep.StateBag) (*communicator.WinRMConfig, error) {
return &communicator.WinRMConfig{
Username: b.config.Comm.WinRMUser,
Password: state.Get("Password").(string),
}, nil
},
},
&common.StepProvision{},
NewStepStopServerInstance(conn, ui),
NewStepCreateServerImage(conn, ui, b.config),
NewStepDeleteBlockStorageInstance(conn, ui, b.config),
NewStepTerminateServerInstance(conn, ui),
NewStepDeleteLoginKey(conn, ui),
NewStepDeletePublicIPInstance(conn, ui),
}
}
// Run!
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
b.runner.Run(b.stateBag)
// If there was an error, return that
if rawErr, ok := b.stateBag.GetOk("Error"); ok {
return nil, rawErr.(error)
}
// Build the artifact and return it
artifact := &Artifact{}
if serverImage, ok := b.stateBag.GetOk("memberServerImage"); ok {
artifact.ServerImage = serverImage.(*ncloud.ServerImage)
}
return artifact, nil
}
func (b *Builder) Cancel() {
b.runner.Cancel()
}

114
builder/ncloud/config.go Normal file
View File

@ -0,0 +1,114 @@
package ncloud
import (
"errors"
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
)
// Config is structure to use packer builder plugin for Naver Cloud Platform
type Config struct {
common.PackerConfig `mapstructure:",squash"`
AccessKey string `mapstructure:"access_key"`
SecretKey string `mapstructure:"secret_key"`
OSType string `mapstructure:"os_type"`
ServerImageProductCode string `mapstructure:"server_image_product_code"`
ServerProductCode string `mapstructure:"server_product_code"`
MemberServerImageNo string `mapstructure:"member_server_image_no"`
ServerImageName string `mapstructure:"server_image_name"`
ServerImageDescription string `mapstructure:"server_image_description"`
UserData string `mapstructure:"user_data"`
BlockStorageSize int `mapstructure:"block_storage_size"`
Region string `mapstructure:"region"`
AccessControlGroupConfigurationNo string `mapstructure:"access_control_group_configuration_no"`
FeeSystemTypeCode string `mapstructure:"-"`
Comm communicator.Config `mapstructure:",squash"`
ctx *interpolate.Context
}
// NewConfig checks parameters
func NewConfig(raws ...interface{}) (*Config, []string, error) {
c := new(Config)
warnings := []string{}
err := config.Decode(c, &config.DecodeOpts{
Interpolate: true,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{},
},
}, raws...)
if err != nil {
return nil, warnings, err
}
var errs *packer.MultiError
if es := c.Comm.Prepare(nil); len(es) > 0 {
errs = packer.MultiErrorAppend(errs, es...)
}
if c.AccessKey == "" {
errs = packer.MultiErrorAppend(errs, errors.New("access_key is required"))
}
if c.SecretKey == "" {
errs = packer.MultiErrorAppend(errs, errors.New("secret_key is required"))
}
if c.OSType != "Linux" && c.OSType != "Windows" {
errs = packer.MultiErrorAppend(errs, errors.New("os_type is required. ('Linux' or 'Windows')"))
}
if c.MemberServerImageNo == "" && c.ServerImageProductCode == "" {
errs = packer.MultiErrorAppend(errs, errors.New("server_image_product_code or member_server_image_no is required"))
}
if c.MemberServerImageNo != "" && c.ServerImageProductCode != "" {
errs = packer.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 = packer.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 = packer.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 = packer.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 = packer.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 = packer.MultiErrorAppend(errs, errors.New("The size of BlockStorageSize is at least 10 GB and up to 2000GB"))
} else if int(c.BlockStorageSize/10)*10 != c.BlockStorageSize {
return nil, nil, errors.New("BlockStorageSize must be a multiple of 10 GB")
}
}
if c.UserData != "" && len(c.UserData) > 21847 {
errs = packer.MultiErrorAppend(errs, errors.New("If user_data field is set, length of UserData should be max 21847"))
}
if c.OSType == "Windows" && c.AccessControlGroupConfigurationNo == "" {
errs = packer.MultiErrorAppend(errs, errors.New("If os_type is Windows, access_control_group_configuration_no is required"))
}
c.FeeSystemTypeCode = "MTRAT"
if errs != nil && len(errs.Errors) > 0 {
return nil, warnings, errs
}
return c, warnings, nil
}

View File

@ -0,0 +1,149 @@
package ncloud
import (
"strings"
"testing"
)
func testConfig() map[string]interface{} {
return map[string]interface{}{
"access_key": "access_key",
"secret_key": "secret_key",
"os_type": "Windows",
"server_image_product_code": "SPSW0WINNT000016",
"server_product_code": "SPSVRSSD00000011",
"server_image_name": "packer-test {{timestamp}}",
"server_image_description": "server description",
"block_storage_size": 100,
"user_data": "#!/bin/sh\nyum install -y httpd\ntouch /var/www/html/index.html\nchkconfig --level 2345 httpd on",
"region": "Korea",
"access_control_group_configuration_no": "33",
"communicator": "ssh",
"ssh_username": "root",
}
}
func testConfigForMemberServerImage() map[string]interface{} {
return map[string]interface{}{
"access_key": "access_key",
"secret_key": "secret_key",
"os_type": "Windows",
"server_product_code": "SPSVRSSD00000011",
"member_server_image_no": "2440",
"server_image_name": "packer-test {{timestamp}}",
"server_image_description": "server description",
"block_storage_size": 100,
"user_data": "#!/bin/sh\nyum install -y httpd\ntouch /var/www/html/index.html\nchkconfig --level 2345 httpd on",
"region": "Korea",
"access_control_group_configuration_no": "33",
"communicator": "ssh",
"ssh_username": "root",
}
}
func TestConfigWithServerImageProductCode(t *testing.T) {
raw := testConfig()
c, _, _ := NewConfig(raw)
if c.AccessKey != "access_key" {
t.Errorf("Expected 'access_key' to be set to '%s', but got '%s'.", raw["access_key"], c.AccessKey)
}
if c.SecretKey != "secret_key" {
t.Errorf("Expected 'secret_key' to be set to '%s', but got '%s'.", raw["secret_key"], c.SecretKey)
}
if c.ServerImageProductCode != "SPSW0WINNT000016" {
t.Errorf("Expected 'server_image_product_code' to be set to '%s', but got '%s'.", raw["server_image_product_code"], c.ServerImageProductCode)
}
if c.ServerProductCode != "SPSVRSSD00000011" {
t.Errorf("Expected 'server_product_code' to be set to '%s', but got '%s'.", raw["server_product_code"], c.ServerProductCode)
}
if c.BlockStorageSize != 100 {
t.Errorf("Expected 'block_storage_size' to be set to '%d', but got '%d'.", raw["block_storage_size"], c.BlockStorageSize)
}
if c.ServerImageDescription != "server description" {
t.Errorf("Expected 'server_image_description_key' to be set to '%s', but got '%s'.", raw["server_image_description"], c.ServerImageDescription)
}
if c.Region != "Korea" {
t.Errorf("Expected 'region' to be set to '%s', but got '%s'.", raw["server_image_description"], c.Region)
}
}
func TestConfigWithMemberServerImageCode(t *testing.T) {
raw := testConfigForMemberServerImage()
c, _, _ := NewConfig(raw)
if c.AccessKey != "access_key" {
t.Errorf("Expected 'access_key' to be set to '%s', but got '%s'.", raw["access_key"], c.AccessKey)
}
if c.SecretKey != "secret_key" {
t.Errorf("Expected 'secret_key' to be set to '%s', but got '%s'.", raw["secret_key"], c.SecretKey)
}
if c.MemberServerImageNo != "2440" {
t.Errorf("Expected 'member_server_image_no' to be set to '%s', but got '%s'.", raw["member_server_image_no"], c.MemberServerImageNo)
}
if c.ServerProductCode != "SPSVRSSD00000011" {
t.Errorf("Expected 'server_product_code' to be set to '%s', but got '%s'.", raw["server_product_code"], c.ServerProductCode)
}
if c.BlockStorageSize != 100 {
t.Errorf("Expected 'block_storage_size' to be set to '%d', but got '%d'.", raw["block_storage_size"], c.BlockStorageSize)
}
if c.ServerImageDescription != "server description" {
t.Errorf("Expected 'server_image_description_key' to be set to '%s', but got '%s'.", raw["server_image_description"], c.ServerImageDescription)
}
if c.Region != "Korea" {
t.Errorf("Expected 'region' to be set to '%s', but got '%s'.", raw["server_image_description"], c.Region)
}
}
func TestEmptyConfig(t *testing.T) {
raw := new(map[string]interface{})
_, _, err := NewConfig(raw)
if err == nil {
t.Error("Expected Config to require 'access_key', 'secret_key' and some mendatory fields, but it did not")
}
if !strings.Contains(err.Error(), "access_key is required") {
t.Error("Expected Config to require 'access_key', but it did not")
}
if !strings.Contains(err.Error(), "secret_key is required") {
t.Error("Expected Config to require 'secret_key', but it did not")
}
if !strings.Contains(err.Error(), "server_image_product_code or member_server_image_no is required") {
t.Error("Expected Config to require 'server_image_product_code' or 'member_server_image_no', but it did not")
}
}
func TestExistsBothServerImageProductCodeAndMemberServerImageNoConfig(t *testing.T) {
raw := map[string]interface{}{
"access_key": "access_key",
"secret_key": "secret_key",
"os_type": "Windows",
"server_image_product_code": "SPSW0WINNT000016",
"server_product_code": "SPSVRSSD00000011",
"member_server_image_no": "2440",
}
_, _, err := NewConfig(raw)
if !strings.Contains(err.Error(), "Only one of server_image_product_code and member_server_image_no can be set") {
t.Error("Expected Config to require Only one of 'server_image_product_code' and 'member_server_image_no' can be set, but it did not")
}
}

30
builder/ncloud/ssh.go Normal file
View File

@ -0,0 +1,30 @@
package ncloud
import (
packerssh "github.com/hashicorp/packer/communicator/ssh"
"github.com/mitchellh/multistep"
"golang.org/x/crypto/ssh"
)
func SSHHost(state multistep.StateBag) (string, error) {
host := state.Get("SSHHost").(string)
return host, nil
}
// SSHConfig returns a function that can be used for the SSH communicator
// config for connecting to the specified host via SSH
func SSHConfig(username string) func(multistep.StateBag) (*ssh.ClientConfig, error) {
return func(state multistep.StateBag) (*ssh.ClientConfig, error) {
password := state.Get("Password").(string)
return &ssh.ClientConfig{
User: username,
Auth: []ssh.AuthMethod{
ssh.Password(password),
ssh.KeyboardInteractive(
packerssh.PasswordKeyboardInteractive(password)),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}, nil
}
}

16
builder/ncloud/step.go Normal file
View File

@ -0,0 +1,16 @@
package ncloud
import (
"github.com/mitchellh/multistep"
)
func processStepResult(err error, sayError func(error), state multistep.StateBag) multistep.StepAction {
if err != nil {
state.Put("Error", err)
sayError(err)
return multistep.ActionHalt
}
return multistep.ActionContinue
}

View File

@ -0,0 +1,101 @@
package ncloud
import (
"errors"
"fmt"
"log"
"time"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
// StepCreateBlockStorageInstance struct is for making extra block storage
type StepCreateBlockStorageInstance struct {
Conn *ncloud.Conn
CreateBlockStorageInstance func(serverInstanceNo string) (string, error)
Say func(message string)
Error func(e error)
Config *Config
}
// NewStepCreateBlockStorageInstance make StepCreateBlockStorage struct to make extra block storage
func NewStepCreateBlockStorageInstance(conn *ncloud.Conn, ui packer.Ui, config *Config) *StepCreateBlockStorageInstance {
var step = &StepCreateBlockStorageInstance{
Conn: conn,
Say: func(message string) { ui.Say(message) },
Error: func(e error) { ui.Error(e.Error()) },
Config: config,
}
step.CreateBlockStorageInstance = step.createBlockStorageInstance
return step
}
func (s *StepCreateBlockStorageInstance) createBlockStorageInstance(serverInstanceNo string) (string, error) {
reqParams := new(ncloud.RequestBlockStorageInstance)
reqParams.BlockStorageSize = s.Config.BlockStorageSize
reqParams.ServerInstanceNo = serverInstanceNo
blockStorageInstanceList, err := s.Conn.CreateBlockStorageInstance(reqParams)
if err != nil {
return "", fmt.Errorf("error code: %d, error message: %s", blockStorageInstanceList.ReturnCode, blockStorageInstanceList.ReturnMessage)
}
log.Println("Block Storage Instance information : ", blockStorageInstanceList.BlockStorageInstance[0])
if err := waiterBlockStorageInstanceStatus(s.Conn, blockStorageInstanceList.BlockStorageInstance[0].BlockStorageInstanceNo, "ATTAC", 10*time.Minute); err != nil {
return "", errors.New("TIMEOUT : Block Storage instance status is not attached")
}
return blockStorageInstanceList.BlockStorageInstance[0].BlockStorageInstanceNo, nil
}
func (s *StepCreateBlockStorageInstance) Run(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)
blockStorageInstanceNo, err := s.CreateBlockStorageInstance(serverInstanceNo)
if err == nil {
state.Put("BlockStorageInstanceNo", blockStorageInstanceNo)
}
return processStepResult(err, s.Error, state)
}
func (s *StepCreateBlockStorageInstance) Cleanup(state multistep.StateBag) {
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
if !cancelled && !halted {
return
}
if s.Config.BlockStorageSize == 0 {
return
}
if blockStorageInstanceNo, ok := state.GetOk("BlockStorageInstanceNo"); ok {
s.Say("Clean up Block Storage Instance")
no := blockStorageInstanceNo.(string)
blockStorageInstanceList, err := s.Conn.DeleteBlockStorageInstances([]string{no})
if err != nil {
return
}
s.Say(fmt.Sprintf("Block Storage Instance is deleted. Block Storage InstanceNo is %s", no))
log.Println("Block Storage Instance information : ", blockStorageInstanceList.BlockStorageInstance[0])
if err := waiterBlockStorageInstanceStatus(s.Conn, no, "DETAC", time.Minute); err != nil {
s.Say("TIMEOUT : Block Storage instance status is not deattached")
}
}
}

View File

@ -0,0 +1,62 @@
package ncloud
import (
"fmt"
"github.com/mitchellh/multistep"
"testing"
)
func TestStepCreateBlockStorageInstanceShouldFailIfOperationCreateBlockStorageInstanceFails(t *testing.T) {
var testSubject = &StepCreateBlockStorageInstance{
CreateBlockStorageInstance: func(serverInstanceNo string) (string, error) { return "", fmt.Errorf("!! Unit Test FAIL !!") },
Say: func(message string) {},
Error: func(e error) {},
Config: new(Config),
}
testSubject.Config.BlockStorageSize = 10
stateBag := createTestStateBagStepCreateBlockStorageInstance()
var result = testSubject.Run(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 TestStepCreateBlockStorageInstanceShouldPassIfOperationCreateBlockStorageInstancePasses(t *testing.T) {
var testSubject = &StepCreateBlockStorageInstance{
CreateBlockStorageInstance: func(serverInstanceNo string) (string, error) { return "a", nil },
Say: func(message string) {},
Error: func(e error) {},
Config: new(Config),
}
testSubject.Config.BlockStorageSize = 10
stateBag := createTestStateBagStepCreateBlockStorageInstance()
var result = testSubject.Run(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 createTestStateBagStepCreateBlockStorageInstance() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put("InstanceNo", "a")
return stateBag
}

View File

@ -0,0 +1,71 @@
package ncloud
import (
"fmt"
"time"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
type LoginKey struct {
KeyName string
PrivateKey string
}
type StepCreateLoginKey struct {
Conn *ncloud.Conn
CreateLoginKey func() (*LoginKey, error)
Say func(message string)
Error func(e error)
}
func NewStepCreateLoginKey(conn *ncloud.Conn, ui packer.Ui) *StepCreateLoginKey {
var step = &StepCreateLoginKey{
Conn: conn,
Say: func(message string) { ui.Say(message) },
Error: func(e error) { ui.Error(e.Error()) },
}
step.CreateLoginKey = step.createLoginKey
return step
}
func (s *StepCreateLoginKey) createLoginKey() (*LoginKey, error) {
KeyName := fmt.Sprintf("packer-%d", time.Now().Unix())
privateKey, err := s.Conn.CreateLoginKey(KeyName)
if err != nil {
return nil, fmt.Errorf("error code: %d , error message: %s", privateKey.ReturnCode, privateKey.ReturnMessage)
}
return &LoginKey{KeyName, privateKey.PrivateKey}, nil
}
func (s *StepCreateLoginKey) Run(state multistep.StateBag) multistep.StepAction {
s.Say("Create Login Key")
loginKey, err := s.CreateLoginKey()
if err == nil {
state.Put("LoginKey", loginKey)
s.Say(fmt.Sprintf("Login Key[%s] is created", loginKey.KeyName))
}
return processStepResult(err, s.Error, state)
}
func (s *StepCreateLoginKey) Cleanup(state multistep.StateBag) {
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
if !cancelled && !halted {
return
}
if loginKey, ok := state.GetOk("LoginKey"); ok {
s.Say("Clean up login key")
s.Conn.DeleteLoginKey(loginKey.(*LoginKey).KeyName)
}
}

View File

@ -0,0 +1,53 @@
package ncloud
import (
"fmt"
"github.com/mitchellh/multistep"
"testing"
)
func TestStepCreateLoginKeyShouldFailIfOperationCreateLoginKeyFails(t *testing.T) {
var testSubject = &StepCreateLoginKey{
CreateLoginKey: func() (*LoginKey, error) { return nil, fmt.Errorf("!! Unit Test FAIL !!") },
Say: func(message string) {},
Error: func(e error) {},
}
stateBag := createTestStateBagStepCreateLoginKey()
var result = testSubject.Run(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 TestStepCreateLoginKeyShouldPassIfOperationCreateLoginKeyPasses(t *testing.T) {
var testSubject = &StepCreateLoginKey{
CreateLoginKey: func() (*LoginKey, error) { return &LoginKey{"a", "b"}, nil },
Say: func(message string) {},
Error: func(e error) {},
}
stateBag := createTestStateBagStepCreateLoginKey()
var result = testSubject.Run(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 createTestStateBagStepCreateLoginKey() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
return stateBag
}

View File

@ -0,0 +1,167 @@
package ncloud
import (
"fmt"
"log"
"time"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
type StepCreatePublicIPInstance struct {
Conn *ncloud.Conn
CreatePublicIPInstance func(serverInstanceNo string) (*ncloud.PublicIPInstance, error)
WaiterAssociatePublicIPToServerInstance func(serverInstanceNo string, publicIP string) error
Say func(message string)
Error func(e error)
Config *Config
}
func NewStepCreatePublicIPInstance(conn *ncloud.Conn, ui packer.Ui, config *Config) *StepCreatePublicIPInstance {
var step = &StepCreatePublicIPInstance{
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
return step
}
func (s *StepCreatePublicIPInstance) waiterAssociatePublicIPToServerInstance(serverInstanceNo string, publicIP string) error {
reqParams := new(ncloud.RequestGetServerInstanceList)
reqParams.ServerInstanceNoList = []string{serverInstanceNo}
c1 := make(chan error, 1)
go func() {
for {
serverInstanceList, err := s.Conn.GetServerInstanceList(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 *StepCreatePublicIPInstance) createPublicIPInstance(serverInstanceNo string) (*ncloud.PublicIPInstance, error) {
reqParams := new(ncloud.RequestCreatePublicIPInstance)
reqParams.ServerInstanceNo = serverInstanceNo
publicIPInstanceList, err := s.Conn.CreatePublicIPInstance(reqParams)
if err != nil {
return nil, fmt.Errorf("error code: %d, error message: %s", publicIPInstanceList.ReturnCode, publicIPInstanceList.ReturnMessage)
}
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)
return &publicIPInstance, nil
}
func (s *StepCreatePublicIPInstance) Run(state multistep.StateBag) multistep.StepAction {
s.Say("Create Public IP Instance")
serverInstanceNo := state.Get("InstanceNo").(string)
publicIPInstance, err := s.CreatePublicIPInstance(serverInstanceNo)
if err == nil {
switch s.Config.OSType {
case "Linux":
state.Put("SSHHost", publicIPInstance.PublicIP)
case "Windows":
state.Put("WinRMHost", publicIPInstance.PublicIP)
}
state.Put("PublicIPInstance", publicIPInstance)
}
return processStepResult(err, s.Error, state)
}
func (s *StepCreatePublicIPInstance) Cleanup(state multistep.StateBag) {
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
if !cancelled && !halted {
return
}
publicIPInstance, ok := state.GetOk("PublicIPInstance")
if !ok {
return
}
s.Say("Clean up Public IP Instance")
publicIPInstanceNo := publicIPInstance.(*ncloud.PublicIPInstance).PublicIPInstanceNo
s.waitPublicIPInstanceStatus(publicIPInstanceNo, "USED")
log.Println("Disassociate Public IP Instance ", publicIPInstanceNo)
s.Conn.DisassociatePublicIP(publicIPInstanceNo)
s.waitPublicIPInstanceStatus(publicIPInstanceNo, "CREAT")
reqParams := new(ncloud.RequestDeletePublicIPInstances)
reqParams.PublicIPInstanceNoList = []string{publicIPInstanceNo}
log.Println("Delete Public IP Instance ", publicIPInstanceNo)
s.Conn.DeletePublicIPInstances(reqParams)
}
func (s *StepCreatePublicIPInstance) waitPublicIPInstanceStatus(publicIPInstanceNo string, status string) {
c1 := make(chan error, 1)
go func() {
reqParams := new(ncloud.RequestPublicIPInstanceList)
reqParams.PublicIPInstanceNoList = []string{publicIPInstanceNo}
for {
resp, err := s.Conn.GetPublicIPInstanceList(reqParams)
if err != nil {
log.Printf("error code: %d, error message: %s", resp.ReturnCode, resp.ReturnMessage)
c1 <- err
return
}
instance := resp.PublicIPInstanceList[0]
if instance.PublicIPInstanceStatus.Code == status && instance.PublicIPInstanceOperation.Code == "NULL" {
c1 <- nil
return
}
time.Sleep(time.Second * 2)
}
}()
select {
case <-c1:
return
case <-time.After(time.Second * 60):
return
}
}

View File

@ -0,0 +1,62 @@
package ncloud
import (
"fmt"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
"testing"
"github.com/mitchellh/multistep"
)
func TestStepCreatePublicIPInstanceShouldFailIfOperationCreatePublicIPInstanceFails(t *testing.T) {
var testSubject = &StepCreatePublicIPInstance{
CreatePublicIPInstance: func(serverInstanceNo string) (*ncloud.PublicIPInstance, error) {
return nil, fmt.Errorf("!! Unit Test FAIL !!")
},
Say: func(message string) {},
Error: func(e error) {},
}
stateBag := createTestStateBagStepCreateServerImage()
var result = testSubject.Run(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 TestStepCreatePublicIPInstanceShouldPassIfOperationCreatePublicIPInstancePasses(t *testing.T) {
var testSubject = &StepCreatePublicIPInstance{
CreatePublicIPInstance: func(serverInstanceNo string) (*ncloud.PublicIPInstance, error) {
return &ncloud.PublicIPInstance{PublicIPInstanceNo: "a", PublicIP: "b"}, nil
},
Say: func(message string) {},
Error: func(e error) {},
Config: &Config{OSType: "Windows"},
}
stateBag := createTestStateBagStepCreatePublicIPInstance()
var result = testSubject.Run(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 createTestStateBagStepCreatePublicIPInstance() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put("InstanceNo", "a")
return stateBag
}

View File

@ -0,0 +1,77 @@
package ncloud
import (
"errors"
"fmt"
"time"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
type StepCreateServerImage struct {
Conn *ncloud.Conn
CreateServerImage func(serverInstanceNo string) (*ncloud.ServerImage, error)
Say func(message string)
Error func(e error)
Config *Config
}
func NewStepCreateServerImage(conn *ncloud.Conn, ui packer.Ui, config *Config) *StepCreateServerImage {
var step = &StepCreateServerImage{
Conn: conn,
Say: func(message string) { ui.Say(message) },
Error: func(e error) { ui.Error(e.Error()) },
Config: config,
}
step.CreateServerImage = step.createServerImage
return step
}
func (s *StepCreateServerImage) createServerImage(serverInstanceNo string) (*ncloud.ServerImage, error) {
// 서버 인스턴스 상태가 정지 중일 경우에는 서버 이미지 생성할 수 없음.
if err := waiterServerInstanceStatus(s.Conn, serverInstanceNo, "NSTOP", 1*time.Minute); err != nil {
return nil, err
}
reqParams := new(ncloud.RequestCreateServerImage)
reqParams.MemberServerImageName = s.Config.ServerImageName
reqParams.MemberServerImageDescription = s.Config.ServerImageDescription
reqParams.ServerInstanceNo = serverInstanceNo
memberServerImageList, err := s.Conn.CreateMemberServerImage(reqParams)
if err != nil {
return nil, fmt.Errorf("error code: %d , error message: %s", memberServerImageList.ReturnCode, memberServerImageList.ReturnMessage)
}
serverImage := memberServerImageList.MemberServerImageList[0]
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 {
return nil, errors.New("TIMEOUT : Server Image is not created")
}
s.Say(fmt.Sprintf("Server Image[%s:%s] is created", serverImage.MemberServerImageName, serverImage.MemberServerImageNo))
return &serverImage, nil
}
func (s *StepCreateServerImage) Run(state multistep.StateBag) multistep.StepAction {
s.Say("Create Server Image")
serverInstanceNo := state.Get("InstanceNo").(string)
serverImage, err := s.CreateServerImage(serverInstanceNo)
if err == nil {
state.Put("memberServerImage", serverImage)
}
return processStepResult(err, s.Error, state)
}
func (*StepCreateServerImage) Cleanup(multistep.StateBag) {
}

View File

@ -0,0 +1,58 @@
package ncloud
import (
"fmt"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
"testing"
"github.com/mitchellh/multistep"
)
func TestStepCreateServerImageShouldFailIfOperationCreateServerImageFails(t *testing.T) {
var testSubject = &StepCreateServerImage{
CreateServerImage: func(serverInstanceNo string) (*ncloud.ServerImage, error) {
return nil, fmt.Errorf("!! Unit Test FAIL !!")
},
Say: func(message string) {},
Error: func(e error) {},
}
stateBag := createTestStateBagStepCreateServerImage()
var result = testSubject.Run(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 TestStepCreateServerImageShouldPassIfOperationCreateServerImagePasses(t *testing.T) {
var testSubject = &StepCreateServerImage{
CreateServerImage: func(serverInstanceNo string) (*ncloud.ServerImage, error) { return nil, nil },
Say: func(message string) {},
Error: func(e error) {},
}
stateBag := createTestStateBagStepCreateServerImage()
var result = testSubject.Run(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 createTestStateBagStepCreateServerImage() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put("InstanceNo", "a")
return stateBag
}

View File

@ -0,0 +1,129 @@
package ncloud
import (
"errors"
"fmt"
"log"
"time"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
type StepCreateServerInstance struct {
Conn *ncloud.Conn
CreateServerInstance func(loginKeyName string, zoneNo string) (string, error)
CheckServerInstanceStatusIsRunning func(serverInstanceNo string) error
Say func(message string)
Error func(e error)
Config *Config
serverInstanceNo string
}
func NewStepCreateServerInstance(conn *ncloud.Conn, ui packer.Ui, config *Config) *StepCreateServerInstance {
var step = &StepCreateServerInstance{
Conn: conn,
Say: func(message string) { ui.Say(message) },
Error: func(e error) { ui.Error(e.Error()) },
Config: config,
}
step.CreateServerInstance = step.createServerInstance
return step
}
func (s *StepCreateServerInstance) createServerInstance(loginKeyName string, zoneNo string) (string, error) {
reqParams := new(ncloud.RequestCreateServerInstance)
reqParams.ServerProductCode = s.Config.ServerProductCode
reqParams.MemberServerImageNo = s.Config.MemberServerImageNo
if s.Config.MemberServerImageNo == "" {
reqParams.ServerImageProductCode = s.Config.ServerImageProductCode
}
reqParams.LoginKeyName = loginKeyName
reqParams.ZoneNo = zoneNo
reqParams.FeeSystemTypeCode = s.Config.FeeSystemTypeCode
if s.Config.UserData != "" {
reqParams.UserData = s.Config.UserData
}
if s.Config.AccessControlGroupConfigurationNo != "" {
reqParams.AccessControlGroupConfigurationNoList = []string{s.Config.AccessControlGroupConfigurationNo}
}
serverInstanceList, err := s.Conn.CreateServerInstances(reqParams)
if err != nil {
return "", fmt.Errorf("error code: %d, error message: %s", serverInstanceList.ReturnCode, serverInstanceList.ReturnMessage)
}
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 := waiterServerInstanceStatus(s.Conn, s.serverInstanceNo, "RUN", 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) Run(state multistep.StateBag) multistep.StepAction {
s.Say("Create Server Instance")
var loginKey = state.Get("LoginKey").(*LoginKey)
var zoneNo = state.Get("ZoneNo").(string)
serverInstanceNo, err := s.CreateServerInstance(loginKey.KeyName, zoneNo)
if err == nil {
state.Put("InstanceNo", serverInstanceNo)
}
return processStepResult(err, s.Error, state)
}
func (s *StepCreateServerInstance) Cleanup(state multistep.StateBag) {
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
if !cancelled && !halted {
return
}
if s.serverInstanceNo == "" {
return
}
reqParams := new(ncloud.RequestGetServerInstanceList)
reqParams.ServerInstanceNoList = []string{s.serverInstanceNo}
serverInstanceList, err := s.Conn.GetServerInstanceList(reqParams)
if err != nil || serverInstanceList.TotalRows == 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(ncloud.RequestStopServerInstances)
reqParams.ServerInstanceNoList = []string{s.serverInstanceNo}
log.Println("Stop Server Instance")
s.Conn.StopServerInstances(reqParams)
waiterServerInstanceStatus(s.Conn, s.serverInstanceNo, "NSTOP", time.Minute)
}
// terminate server instance
if serverInstance.ServerInstanceStatus.Code != "TERMT" {
reqParams := new(ncloud.RequestTerminateServerInstances)
reqParams.ServerInstanceNoList = []string{s.serverInstanceNo}
log.Println("Terminate Server Instance")
s.Conn.TerminateServerInstances(reqParams)
}
}

View File

@ -0,0 +1,58 @@
package ncloud
import (
"fmt"
"github.com/mitchellh/multistep"
"testing"
)
func TestStepCreateServerInstanceShouldFailIfOperationCreateFails(t *testing.T) {
var testSubject = &StepCreateServerInstance{
CreateServerInstance: func(loginKeyName string, zoneNo string) (string, error) {
return "", fmt.Errorf("!! Unit Test FAIL !!")
},
Say: func(message string) {},
Error: func(e error) {},
}
stateBag := createTestStateBagStepCreateServerInstance()
var result = testSubject.Run(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 TestStepCreateServerInstanceShouldPassIfOperationCreatePasses(t *testing.T) {
var testSubject = &StepCreateServerInstance{
CreateServerInstance: func(loginKeyName string, zoneNo string) (string, error) { return "", nil },
Say: func(message string) {},
Error: func(e error) {},
}
stateBag := createTestStateBagStepCreateServerInstance()
var result = testSubject.Run(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 createTestStateBagStepCreateServerInstance() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put("LoginKey", &LoginKey{"a", "b"})
stateBag.Put("ZoneNo", "1")
return stateBag
}

View File

@ -0,0 +1,95 @@
package ncloud
import (
"errors"
"fmt"
"log"
"time"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
type StepDeleteBlockStorageInstance struct {
Conn *ncloud.Conn
DeleteBlockStorageInstance func(blockStorageInstanceNo string) error
Say func(message string)
Error func(e error)
Config *Config
}
func NewStepDeleteBlockStorageInstance(conn *ncloud.Conn, ui packer.Ui, config *Config) *StepDeleteBlockStorageInstance {
var step = &StepDeleteBlockStorageInstance{
Conn: conn,
Say: func(message string) { ui.Say(message) },
Error: func(e error) { ui.Error(e.Error()) },
Config: config,
}
step.DeleteBlockStorageInstance = step.deleteBlockStorageInstance
return step
}
func (s *StepDeleteBlockStorageInstance) getBlockInstanceList(serverInstanceNo string) []string {
reqParams := new(ncloud.RequestBlockStorageInstanceList)
reqParams.ServerInstanceNo = serverInstanceNo
blockStorageInstanceList, err := s.Conn.GetBlockStorageInstance(reqParams)
if err != nil {
return nil
}
if blockStorageInstanceList.TotalRows == 1 {
return nil
}
var instanceList []string
for _, blockStorageInstance := range blockStorageInstanceList.BlockStorageInstance {
log.Println(blockStorageInstance)
if blockStorageInstance.BlockStorageType.Code != "BASIC" {
instanceList = append(instanceList, blockStorageInstance.BlockStorageInstanceNo)
}
}
return instanceList
}
func (s *StepDeleteBlockStorageInstance) deleteBlockStorageInstance(serverInstanceNo string) error {
blockStorageInstanceList := s.getBlockInstanceList(serverInstanceNo)
if blockStorageInstanceList == nil || len(blockStorageInstanceList) == 0 {
return nil
}
result, err := s.Conn.DeleteBlockStorageInstances(blockStorageInstanceList)
if err != nil {
return fmt.Errorf("error code: %d , error message: %s", result.ReturnCode, result.ReturnMessage)
}
s.Say(fmt.Sprintf("Block Storage Instance is deleted. Block Storage InstanceNo is %s", blockStorageInstanceList))
if err := waiterDetachedBlockStorageInstance(s.Conn, serverInstanceNo, time.Minute); err != nil {
return errors.New("TIMEOUT : Block Storage instance status is not deattached")
}
return nil
}
func (s *StepDeleteBlockStorageInstance) Run(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)
err := s.DeleteBlockStorageInstance(serverInstanceNo)
return processStepResult(err, s.Error, state)
}
func (*StepDeleteBlockStorageInstance) Cleanup(multistep.StateBag) {
}

View File

@ -0,0 +1,57 @@
package ncloud
import (
"fmt"
"github.com/mitchellh/multistep"
"testing"
)
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},
}
stateBag := createTestStateBagStepDeleteBlockStorageInstance()
var result = testSubject.Run(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 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},
}
stateBag := createTestStateBagStepDeleteBlockStorageInstance()
var result = testSubject.Run(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 createTestStateBagStepDeleteBlockStorageInstance() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put("InstanceNo", "1")
return stateBag
}

View File

@ -0,0 +1,51 @@
package ncloud
import (
"fmt"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
type StepDeleteLoginKey struct {
Conn *ncloud.Conn
DeleteLoginKey func(keyName string) error
Say func(message string)
Error func(e error)
}
func NewStepDeleteLoginKey(conn *ncloud.Conn, ui packer.Ui) *StepDeleteLoginKey {
var step = &StepDeleteLoginKey{
Conn: conn,
Say: func(message string) { ui.Say(message) },
Error: func(e error) { ui.Error(e.Error()) },
}
step.DeleteLoginKey = step.deleteLoginKey
return step
}
func (s *StepDeleteLoginKey) deleteLoginKey(keyName string) error {
resp, err := s.Conn.DeleteLoginKey(keyName)
if err != nil {
return fmt.Errorf("error code: %d , error message: %s", resp.ReturnCode, resp.ReturnMessage)
}
return nil
}
func (s *StepDeleteLoginKey) Run(state multistep.StateBag) multistep.StepAction {
var loginKey = state.Get("LoginKey").(*LoginKey)
err := s.DeleteLoginKey(loginKey.KeyName)
if err == nil {
s.Say(fmt.Sprintf("Login Key[%s] is deleted", loginKey.KeyName))
}
return processStepResult(err, s.Error, state)
}
func (*StepDeleteLoginKey) Cleanup(multistep.StateBag) {
}

View File

@ -0,0 +1,55 @@
package ncloud
import (
"fmt"
"github.com/mitchellh/multistep"
"testing"
)
func TestStepDeleteLoginKeyShouldFailIfOperationDeleteLoginKeyFails(t *testing.T) {
var testSubject = &StepDeleteLoginKey{
DeleteLoginKey: func(keyName string) error { return fmt.Errorf("!! Unit Test FAIL !!") },
Say: func(message string) {},
Error: func(e error) {},
}
stateBag := DeleteTestStateBagStepDeleteLoginKey()
var result = testSubject.Run(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 TestStepDeleteLoginKeyShouldPassIfOperationDeleteLoginKeyPasses(t *testing.T) {
var testSubject = &StepDeleteLoginKey{
DeleteLoginKey: func(keyName string) error { return nil },
Say: func(message string) {},
Error: func(e error) {},
}
stateBag := DeleteTestStateBagStepDeleteLoginKey()
var result = testSubject.Run(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 DeleteTestStateBagStepDeleteLoginKey() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put("LoginKey", &LoginKey{"a", "b"})
return stateBag
}

View File

@ -0,0 +1,78 @@
package ncloud
import (
"errors"
"fmt"
"log"
"time"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
type StepDeletePublicIPInstance struct {
Conn *ncloud.Conn
DeletePublicIPInstance func(publicIPInstanceNo string) error
Say func(message string)
Error func(e error)
}
func NewStepDeletePublicIPInstance(conn *ncloud.Conn, ui packer.Ui) *StepDeletePublicIPInstance {
var step = &StepDeletePublicIPInstance{
Conn: conn,
Say: func(message string) { ui.Say(message) },
Error: func(e error) { ui.Error(e.Error()) },
}
step.DeletePublicIPInstance = step.deletePublicIPInstance
return step
}
func (s *StepDeletePublicIPInstance) deletePublicIPInstance(publicIPInstanceNo string) error {
reqParams := new(ncloud.RequestDeletePublicIPInstances)
reqParams.PublicIPInstanceNoList = []string{publicIPInstanceNo}
c1 := make(chan error, 1)
go func() {
for {
resp, err := s.Conn.DeletePublicIPInstances(reqParams)
if err != nil && (resp.ReturnCode == 24073 || resp.ReturnCode == 25032) {
// error code : 24073 : Unable to destroy the server since a public IP is associated with the server. First, please disassociate a public IP from the server.
// error code : 25032 : You may not delete sk since (other) user is changing the target official IP settings.
log.Println(resp.ReturnCode, resp.ReturnMessage)
} else if err != nil {
c1 <- fmt.Errorf("error code: %d, error message: %s", resp.ReturnCode, resp.ReturnMessage)
return
} else if err == nil {
s.Say(fmt.Sprintf("Public IP Instance [%s] is deleted.", publicIPInstanceNo))
c1 <- nil
return
}
time.Sleep(time.Second * 5)
}
}()
select {
case res := <-c1:
return res
case <-time.After(time.Second * 60):
return errors.New("TIMEOUT : Can't delete server instance")
}
}
func (s *StepDeletePublicIPInstance) Run(state multistep.StateBag) multistep.StepAction {
s.Say("Delete Public IP Instance")
publicIPInstance := state.Get("PublicIPInstance").(*ncloud.PublicIPInstance)
err := s.DeletePublicIPInstance(publicIPInstance.PublicIPInstanceNo)
return processStepResult(err, s.Error, state)
}
func (*StepDeletePublicIPInstance) Cleanup(multistep.StateBag) {
}

View File

@ -0,0 +1,57 @@
package ncloud
import (
"fmt"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
"testing"
"github.com/mitchellh/multistep"
)
func TestStepDeletePublicIPInstanceShouldFailIfOperationDeletePublicIPInstanceFails(t *testing.T) {
var testSubject = &StepDeletePublicIPInstance{
DeletePublicIPInstance: func(publicIPInstanceNo string) error { return fmt.Errorf("!! Unit Test FAIL !!") },
Say: func(message string) {},
Error: func(e error) {},
}
stateBag := createTestStateBagStepDeletePublicIPInstance()
var result = testSubject.Run(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 TestStepDeletePublicIPInstanceShouldPassIfOperationDeletePublicIPInstancePasses(t *testing.T) {
var testSubject = &StepDeletePublicIPInstance{
DeletePublicIPInstance: func(publicIPInstanceNo string) error { return nil },
Say: func(message string) {},
Error: func(e error) {},
}
stateBag := createTestStateBagStepDeletePublicIPInstance()
var result = testSubject.Run(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 createTestStateBagStepDeletePublicIPInstance() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put("PublicIPInstance", &ncloud.PublicIPInstance{PublicIPInstanceNo: "22"})
return stateBag
}

View File

@ -0,0 +1,57 @@
package ncloud
import (
"fmt"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
type StepGetRootPassword struct {
Conn *ncloud.Conn
GetRootPassword func(serverInstanceNo string, privateKey string) (string, error)
Say func(message string)
Error func(e error)
}
func NewStepGetRootPassword(conn *ncloud.Conn, ui packer.Ui) *StepGetRootPassword {
var step = &StepGetRootPassword{
Conn: conn,
Say: func(message string) { ui.Say(message) },
Error: func(e error) { ui.Error(e.Error()) },
}
step.GetRootPassword = step.getRootPassword
return step
}
func (s *StepGetRootPassword) getRootPassword(serverInstanceNo string, privateKey string) (string, error) {
reqParams := new(ncloud.RequestGetRootPassword)
reqParams.ServerInstanceNo = serverInstanceNo
reqParams.PrivateKey = privateKey
rootPassword, err := s.Conn.GetRootPassword(reqParams)
if err != nil {
return "", fmt.Errorf("error code: %d, error message: %s", rootPassword.ReturnCode, rootPassword.ReturnMessage)
}
return rootPassword.RootPassword, nil
}
func (s *StepGetRootPassword) Run(state multistep.StateBag) multistep.StepAction {
s.Say("Get Root Password")
serverInstanceNo := state.Get("InstanceNo").(string)
loginKey := state.Get("LoginKey").(*LoginKey)
rootPassword, err := s.GetRootPassword(serverInstanceNo, loginKey.PrivateKey)
state.Put("Password", rootPassword)
return processStepResult(err, s.Error, state)
}
func (*StepGetRootPassword) Cleanup(multistep.StateBag) {
}

View File

@ -0,0 +1,56 @@
package ncloud
import (
"fmt"
"github.com/mitchellh/multistep"
"testing"
)
func TestStepGetRootPasswordShouldFailIfOperationGetRootPasswordFails(t *testing.T) {
var testSubject = &StepGetRootPassword{
GetRootPassword: func(string, string) (string, error) { return "", fmt.Errorf("!! Unit Test FAIL !!") },
Say: func(message string) {},
Error: func(e error) {},
}
stateBag := DeleteTestStateBagStepGetRootPassword()
var result = testSubject.Run(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 TestStepGetRootPasswordShouldPassIfOperationGetRootPasswordPasses(t *testing.T) {
var testSubject = &StepGetRootPassword{
GetRootPassword: func(string, string) (string, error) { return "a", nil },
Say: func(message string) {},
Error: func(e error) {},
}
stateBag := DeleteTestStateBagStepGetRootPassword()
var result = testSubject.Run(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 DeleteTestStateBagStepGetRootPassword() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put("LoginKey", &LoginKey{"a", "b"})
stateBag.Put("InstanceNo", "a")
return stateBag
}

View File

@ -0,0 +1,64 @@
package ncloud
import (
"fmt"
"log"
"time"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
type StepStopServerInstance struct {
Conn *ncloud.Conn
StopServerInstance func(serverInstanceNo string) error
Say func(message string)
Error func(e error)
}
func NewStepStopServerInstance(conn *ncloud.Conn, ui packer.Ui) *StepStopServerInstance {
var step = &StepStopServerInstance{
Conn: conn,
Say: func(message string) { ui.Say(message) },
Error: func(e error) { ui.Error(e.Error()) },
}
step.StopServerInstance = step.stopServerInstance
return step
}
func (s *StepStopServerInstance) stopServerInstance(serverInstanceNo string) error {
reqParams := new(ncloud.RequestStopServerInstances)
reqParams.ServerInstanceNoList = []string{serverInstanceNo}
serverInstanceList, err := s.Conn.StopServerInstances(reqParams)
if err != nil {
return fmt.Errorf("error code: %d , error message: %s", serverInstanceList.ReturnCode, serverInstanceList.ReturnMessage)
}
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 {
return err
}
s.Say(fmt.Sprintf("Server Instance stopped. Server InstanceNo is %s", serverInstanceList.ServerInstanceList[0].ServerInstanceNo))
return nil
}
func (s *StepStopServerInstance) Run(state multistep.StateBag) multistep.StepAction {
s.Say("Stop Server Instance")
var serverInstanceNo = state.Get("InstanceNo").(string)
err := s.StopServerInstance(serverInstanceNo)
return processStepResult(err, s.Error, state)
}
func (*StepStopServerInstance) Cleanup(multistep.StateBag) {
}

View File

@ -0,0 +1,55 @@
package ncloud
import (
"fmt"
"testing"
"github.com/mitchellh/multistep"
)
func TestStepStopServerInstanceShouldFailIfOperationStopFails(t *testing.T) {
var testSubject = &StepStopServerInstance{
StopServerInstance: func(serverInstanceNo string) error { return fmt.Errorf("!! Unit Test FAIL !!") },
Say: func(message string) {},
Error: func(e error) {},
}
stateBag := createTestStateBagStepStopServerInstance()
var result = testSubject.Run(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 TestStepStopServerInstanceShouldPassIfOperationStopPasses(t *testing.T) {
var testSubject = &StepStopServerInstance{
StopServerInstance: func(serverInstanceNo string) error { return nil },
Say: func(message string) {},
Error: func(e error) {},
}
stateBag := createTestStateBagStepStopServerInstance()
var result = testSubject.Run(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 createTestStateBagStepStopServerInstance() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put("InstanceNo", "a")
return stateBag
}

View File

@ -0,0 +1,81 @@
package ncloud
import (
"errors"
"fmt"
"time"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
type StepTerminateServerInstance struct {
Conn *ncloud.Conn
TerminateServerInstance func(serverInstanceNo string) error
Say func(message string)
Error func(e error)
}
func NewStepTerminateServerInstance(conn *ncloud.Conn, ui packer.Ui) *StepTerminateServerInstance {
var step = &StepTerminateServerInstance{
Conn: conn,
Say: func(message string) { ui.Say(message) },
Error: func(e error) { ui.Error(e.Error()) },
}
step.TerminateServerInstance = step.terminateServerInstance
return step
}
func (s *StepTerminateServerInstance) terminateServerInstance(serverInstanceNo string) error {
reqParams := new(ncloud.RequestTerminateServerInstances)
reqParams.ServerInstanceNoList = []string{serverInstanceNo}
serverInstanceList, err := s.Conn.TerminateServerInstances(reqParams)
if err != nil {
return fmt.Errorf("error code: %d , error message: %s", serverInstanceList.ReturnCode, serverInstanceList.ReturnMessage)
}
c1 := make(chan error, 1)
go func() {
reqParams := new(ncloud.RequestGetServerInstanceList)
reqParams.ServerInstanceNoList = []string{serverInstanceNo}
for {
serverInstanceList, err := s.Conn.GetServerInstanceList(reqParams)
if err != nil {
c1 <- fmt.Errorf("error code: %d , error message: %s", serverInstanceList.ReturnCode, serverInstanceList.ReturnMessage)
return
} else if serverInstanceList.TotalRows == 0 {
c1 <- nil
return
}
time.Sleep(time.Second * 3)
}
}()
select {
case res := <-c1:
return res
case <-time.After(time.Second * 60):
return errors.New("TIMEOUT : Can't terminate server instance")
}
}
func (s *StepTerminateServerInstance) Run(state multistep.StateBag) multistep.StepAction {
s.Say("Terminate Server Instance")
var serverInstanceNo = state.Get("InstanceNo").(string)
err := s.TerminateServerInstance(serverInstanceNo)
return processStepResult(err, s.Error, state)
}
func (*StepTerminateServerInstance) Cleanup(multistep.StateBag) {
}

View File

@ -0,0 +1,54 @@
package ncloud
import (
"fmt"
"github.com/mitchellh/multistep"
"testing"
)
func TestStepTerminateServerInstanceShouldFailIfOperationTerminationFails(t *testing.T) {
var testSubject = &StepTerminateServerInstance{
TerminateServerInstance: func(serverInstanceNo string) error { return fmt.Errorf("!! Unit Test FAIL !!") },
Say: func(message string) {},
Error: func(e error) {},
}
stateBag := createTestStateBagStepTerminateServerInstance()
var result = testSubject.Run(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 TestStepTerminateServerInstanceShouldPassIfOperationTerminationPasses(t *testing.T) {
var testSubject = &StepTerminateServerInstance{
TerminateServerInstance: func(serverInstanceNo string) error { return nil },
Say: func(message string) {},
Error: func(e error) {},
}
stateBag := createTestStateBagStepTerminateServerInstance()
var result = testSubject.Run(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 createTestStateBagStepTerminateServerInstance() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put("InstanceNo", "a")
return stateBag
}

View File

@ -0,0 +1,263 @@
package ncloud
import (
"bytes"
"errors"
"fmt"
"strings"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"github.com/olekukonko/tablewriter"
)
//StepValidateTemplate : struct for Validation a tempalte
type StepValidateTemplate struct {
Conn *ncloud.Conn
Validate func() error
Say func(message string)
Error func(e error)
Config *Config
zoneNo string
regionNo string
}
// NewStepValidateTemplate : funciton for Validation a tempalte
func NewStepValidateTemplate(conn *ncloud.Conn, ui packer.Ui, config *Config) *StepValidateTemplate {
var step = &StepValidateTemplate{
Conn: conn,
Say: func(message string) { ui.Say(message) },
Error: func(e error) { ui.Error(e.Error()) },
Config: config,
}
step.Validate = step.validateTemplate
return step
}
// getZoneNo : get zoneNo
func (s *StepValidateTemplate) getZoneNo() error {
if s.Config.Region == "" {
return nil
}
regionList, err := s.Conn.GetRegionList()
if err != nil {
return fmt.Errorf("error code: %d , error message: %s", regionList.ReturnCode, regionList.ReturnMessage)
}
var regionNo string
for _, region := range regionList.RegionList {
if strings.EqualFold(region.RegionName, s.Config.Region) {
regionNo = region.RegionNo
}
}
if regionNo == "" {
return fmt.Errorf("region %s is invalid", s.Config.Region)
}
s.regionNo = regionNo
// Get ZoneNo
ZoneList, err := s.Conn.GetZoneList(regionNo)
if err != nil {
return fmt.Errorf("error code: %d , error message: %s", ZoneList.ReturnCode, ZoneList.ReturnMessage)
}
if len(ZoneList.Zone) > 0 {
s.zoneNo = ZoneList.Zone[0].ZoneNo
}
return nil
}
func (s *StepValidateTemplate) validateMemberServerImage() error {
var serverImageName = s.Config.ServerImageName
reqParams := new(ncloud.RequestServerImageList)
reqParams.RegionNo = s.regionNo
memberServerImageList, err := s.Conn.GetMemberServerImageList(reqParams)
if err != nil {
return err
}
var isExistMemberServerImageNo = false
for _, image := range memberServerImageList.MemberServerImageList {
// Check duplicate server_image_name
if image.MemberServerImageName == serverImageName {
return fmt.Errorf("server_image_name %s is exists", serverImageName)
}
if image.MemberServerImageNo == s.Config.MemberServerImageNo {
isExistMemberServerImageNo = true
if s.Config.ServerProductCode == "" {
s.Config.ServerProductCode = image.OriginalServerProductCode
s.Say("server_product_code for member server image '" + image.OriginalServerProductCode + "' is configured automatically")
}
s.Config.ServerImageProductCode = image.OriginalServerImageProductCode
}
}
if s.Config.MemberServerImageNo != "" && !isExistMemberServerImageNo {
return fmt.Errorf("member_server_image_no %s does not exist", s.Config.MemberServerImageNo)
}
return nil
}
func (s *StepValidateTemplate) validateServerImageProduct() error {
var serverImageProductCode = s.Config.ServerImageProductCode
if serverImageProductCode == "" {
return nil
}
reqParams := new(ncloud.RequestGetServerImageProductList)
reqParams.RegionNo = s.regionNo
serverImageProductList, err := s.Conn.GetServerImageProductList(reqParams)
if err != nil {
return err
}
var isExistServerImage = false
var buf bytes.Buffer
var productName string
table := tablewriter.NewWriter(&buf)
table.SetHeader([]string{"Name", "Code"})
for _, product := range serverImageProductList.Product {
// Check exist server image product code
if product.ProductCode == serverImageProductCode {
isExistServerImage = true
productName = product.ProductName
break
}
table.Append([]string{product.ProductName, product.ProductCode})
}
if !isExistServerImage {
reqParams.BlockStorageSize = 100
serverImageProductList, err := s.Conn.GetServerImageProductList(reqParams)
if err != nil {
return err
}
for _, product := range serverImageProductList.Product {
// Check exist server image product code
if product.ProductCode == serverImageProductCode {
isExistServerImage = true
productName = product.ProductName
break
}
table.Append([]string{product.ProductName, product.ProductCode})
}
}
if !isExistServerImage {
table.Render()
s.Say(buf.String())
return fmt.Errorf("server_image_product_code %s does not exist", serverImageProductCode)
}
if strings.Contains(productName, "mssql") {
s.Config.FeeSystemTypeCode = "FXSUM"
}
return nil
}
func (s *StepValidateTemplate) validateServerProductCode() error {
var serverImageProductCode = s.Config.ServerImageProductCode
var productCode = s.Config.ServerProductCode
reqParams := new(ncloud.RequestGetServerProductList)
reqParams.ServerImageProductCode = serverImageProductCode
reqParams.RegionNo = s.regionNo
productList, err := s.Conn.GetServerProductList(reqParams)
if err != nil {
return err
}
var isExistProductCode = false
for _, product := range productList.Product {
// Check exist server image product code
if product.ProductCode == productCode {
isExistProductCode = true
if strings.Contains(product.ProductName, "mssql") {
s.Config.FeeSystemTypeCode = "FXSUM"
}
if product.ProductType.Code == "VDS" {
return errors.New("You cannot create my server image for VDS servers")
}
break
} else if productCode == "" && product.ProductType.Code == "STAND" {
isExistProductCode = true
s.Config.ServerProductCode = product.ProductCode
s.Say("server_product_code '" + product.ProductCode + "' is configured automatically")
break
}
}
if !isExistProductCode {
var buf bytes.Buffer
table := tablewriter.NewWriter(&buf)
table.SetHeader([]string{"Name", "Code"})
for _, product := range productList.Product {
table.Append([]string{product.ProductName, product.ProductCode})
}
table.Render()
s.Say(buf.String())
return fmt.Errorf("server_product_code %s does not exist", productCode)
}
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 {
return err
}
// Validate member_server_image_no and member_server_image_no
if err := s.validateMemberServerImage(); err != nil {
return err
}
// Validate server_image_product_code
if err := s.validateServerImageProduct(); err != nil {
return err
}
// Validate server_product_code
return s.validateServerProductCode()
}
// Run : main funciton for validation a template
func (s *StepValidateTemplate) Run(state multistep.StateBag) multistep.StepAction {
s.Say("Validating deployment template ...")
err := s.Validate()
state.Put("ZoneNo", s.zoneNo)
return processStepResult(err, s.Error, state)
}
// Cleanup : cleanup on error
func (s *StepValidateTemplate) Cleanup(multistep.StateBag) {
}

View File

@ -0,0 +1,54 @@
package ncloud
import (
"fmt"
"testing"
"github.com/mitchellh/multistep"
)
func TestStepValidateTemplateShouldFailIfValidateFails(t *testing.T) {
var testSubject = &StepValidateTemplate{
Validate: func() error { return fmt.Errorf("!! Unit Test FAIL !!") },
Say: func(message string) {},
Error: func(e error) {},
}
stateBag := createTestStateBagStepValidateTemplate()
var result = testSubject.Run(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 TestStepValidateTemplateShouldPassIfValidatePasses(t *testing.T) {
var testSubject = &StepValidateTemplate{
Validate: func() error { return nil },
Say: func(message string) {},
Error: func(e error) {},
}
stateBag := createTestStateBagStepValidateTemplate()
var result = testSubject.Run(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 createTestStateBagStepValidateTemplate() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
return stateBag
}

View File

@ -0,0 +1,80 @@
package ncloud
import (
"fmt"
"log"
"time"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
)
func waiterBlockStorageInstanceStatus(conn *ncloud.Conn, blockStorageInstanceNo string, status string, timeout time.Duration) error {
reqParams := new(ncloud.RequestBlockStorageInstanceList)
reqParams.BlockStorageInstanceNoList = []string{blockStorageInstanceNo}
c1 := make(chan error, 1)
go func() {
for {
blockStorageInstanceList, err := conn.GetBlockStorageInstance(reqParams)
if err != nil {
c1 <- err
return
}
if status == "DETAC" && len(blockStorageInstanceList.BlockStorageInstance) == 0 {
c1 <- nil
return
}
code := blockStorageInstanceList.BlockStorageInstance[0].BlockStorageInstanceStatus.Code
operationCode := blockStorageInstanceList.BlockStorageInstance[0].BlockStorageInstanceOperation.Code
if code == status && operationCode == "NULL" {
c1 <- nil
return
}
log.Println(blockStorageInstanceList.BlockStorageInstance[0])
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 waiterDetachedBlockStorageInstance(conn *ncloud.Conn, serverInstanceNo string, timeout time.Duration) error {
reqParams := new(ncloud.RequestBlockStorageInstanceList)
reqParams.ServerInstanceNo = serverInstanceNo
c1 := make(chan error, 1)
go func() {
for {
blockStorageInstanceList, err := conn.GetBlockStorageInstance(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

@ -0,0 +1,43 @@
package ncloud
import (
"fmt"
"log"
"time"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
)
func waiterMemberServerImageStatus(conn *ncloud.Conn, memberServerImageNo string, status string, timeout time.Duration) error {
reqParams := new(ncloud.RequestServerImageList)
reqParams.MemberServerImageNoList = []string{memberServerImageNo}
c1 := make(chan error, 1)
go func() {
for {
memberServerImageList, err := conn.GetMemberServerImageList(reqParams)
if err != nil {
c1 <- err
return
}
code := memberServerImageList.MemberServerImageList[0].MemberServerImageStatus.Code
if code == status {
c1 <- nil
return
}
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)
}
}

View File

@ -0,0 +1,43 @@
package ncloud
import (
"fmt"
"log"
"time"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
)
func waiterServerInstanceStatus(conn *ncloud.Conn, serverInstanceNo string, status string, timeout time.Duration) error {
reqParams := new(ncloud.RequestGetServerInstanceList)
reqParams.ServerInstanceNoList = []string{serverInstanceNo}
c1 := make(chan error, 1)
go func() {
for {
serverInstanceList, err := conn.GetServerInstanceList(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)
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)
}
}

View File

@ -29,6 +29,7 @@ import (
hypervvmcxbuilder "github.com/hashicorp/packer/builder/hyperv/vmcx"
lxcbuilder "github.com/hashicorp/packer/builder/lxc"
lxdbuilder "github.com/hashicorp/packer/builder/lxd"
ncloudbuilder "github.com/hashicorp/packer/builder/ncloud"
nullbuilder "github.com/hashicorp/packer/builder/null"
oneandonebuilder "github.com/hashicorp/packer/builder/oneandone"
openstackbuilder "github.com/hashicorp/packer/builder/openstack"
@ -96,6 +97,7 @@ var Builders = map[string]packer.Builder{
"hyperv-vmcx": new(hypervvmcxbuilder.Builder),
"lxc": new(lxcbuilder.Builder),
"lxd": new(lxdbuilder.Builder),
"ncloud": new(ncloudbuilder.Builder),
"null": new(nullbuilder.Builder),
"oneandone": new(oneandonebuilder.Builder),
"openstack": new(openstackbuilder.Builder),