Merge pull request #5791 from YuSungDuk/master

Naver Cloud Platform builder
This commit is contained in:
Matthew Hooker 2018-02-08 11:10:18 -08:00 committed by GitHub
commit 6205c71f98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
81 changed files with 8034 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
}

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

@ -0,0 +1,111 @@
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/helper/multistep"
"github.com/hashicorp/packer/packer"
)
// 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.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.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),
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: SSHConfig(b.config.Comm.SSHUsername),
},
&common.StepProvision{},
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),
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: state.Get("Password").(string),
}, nil
},
},
&common.StepProvision{},
NewStepStopServerInstance(conn, ui),
NewStepCreateServerImage(conn, ui, b.config),
NewStepDeleteBlockStorageInstance(conn, ui, b.config),
NewStepTerminateServerInstance(conn, ui),
}
}
// Run!
b.runner = common.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, b.stateBag)
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()
}

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

@ -0,0 +1,117 @@
package ncloud
import (
"errors"
"fmt"
"os"
"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"`
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"`
UserDataFile string `mapstructure:"user_data_file"`
BlockStorageSize int `mapstructure:"block_storage_size"`
Region string `mapstructure:"region"`
AccessControlGroupConfigurationNo string `mapstructure:"access_control_group_configuration_no"`
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.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 != "" && c.UserDataFile != "" {
errs = packer.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 = packer.MultiErrorAppend(errs, fmt.Errorf("user_data_file not found: %s", c.UserDataFile))
}
}
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.Comm.Type == "wrinrm" && c.AccessControlGroupConfigurationNo == "" {
errs = packer.MultiErrorAppend(errs, errors.New("If Communicator is winrm, access_control_group_configuration_no is required"))
}
if errs != nil && len(errs.Errors) > 0 {
return nil, warnings, errs
}
return c, warnings, nil
}

View File

@ -0,0 +1,146 @@
package ncloud
import (
"strings"
"testing"
)
func testConfig() map[string]interface{} {
return map[string]interface{}{
"access_key": "access_key",
"secret_key": "secret_key",
"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",
"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",
"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")
}
}

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

@ -0,0 +1,25 @@
package ncloud
import (
packerssh "github.com/hashicorp/packer/communicator/ssh"
"github.com/hashicorp/packer/helper/multistep"
"golang.org/x/crypto/ssh"
)
// 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/hashicorp/packer/helper/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,102 @@
package ncloud
import (
"context"
"errors"
"fmt"
"log"
"time"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
// 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 "", err
}
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(_ 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)
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,64 @@
package ncloud
import (
"context"
"fmt"
"testing"
"github.com/hashicorp/packer/helper/multistep"
)
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(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 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(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 createTestStateBagStepCreateBlockStorageInstance() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put("InstanceNo", "a")
return stateBag
}

View File

@ -0,0 +1,65 @@
package ncloud
import (
"context"
"fmt"
"time"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
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, err
}
return &LoginKey{KeyName, privateKey.PrivateKey}, nil
}
func (s *StepCreateLoginKey) Run(_ context.Context, 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) {
if loginKey, ok := state.GetOk("LoginKey"); ok {
s.Say("Clean up login key")
s.Conn.DeleteLoginKey(loginKey.(*LoginKey).KeyName)
}
}

View File

@ -0,0 +1,55 @@
package ncloud
import (
"context"
"fmt"
"testing"
"github.com/hashicorp/packer/helper/multistep"
)
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(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 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(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 createTestStateBagStepCreateLoginKey() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
return stateBag
}

View File

@ -0,0 +1,160 @@
package ncloud
import (
"context"
"fmt"
"log"
"time"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
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, 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)
return &publicIPInstance, nil
}
func (s *StepCreatePublicIPInstance) Run(_ 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)
}
return processStepResult(err, s.Error, state)
}
func (s *StepCreatePublicIPInstance) Cleanup(state multistep.StateBag) {
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(err.Error())
c1 <- err
return
}
if resp.TotalRows == 0 {
c1 <- nil
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,68 @@
package ncloud
import (
"context"
"fmt"
"testing"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
"github.com/hashicorp/packer/helper/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(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 TestStepCreatePublicIPInstanceShouldPassIfOperationCreatePublicIPInstancePasses(t *testing.T) {
c := new(Config)
c.Comm.Prepare(nil)
c.Comm.Type = "ssh"
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: c,
}
stateBag := createTestStateBagStepCreatePublicIPInstance()
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 createTestStateBagStepCreatePublicIPInstance() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put("InstanceNo", "a")
return stateBag
}

View File

@ -0,0 +1,78 @@
package ncloud
import (
"context"
"errors"
"fmt"
"time"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
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) {
// 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 {
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, err
}
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(_ context.Context, 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,60 @@
package ncloud
import (
"context"
"fmt"
"testing"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
"github.com/hashicorp/packer/helper/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(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 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(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 createTestStateBagStepCreateServerImage() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put("InstanceNo", "a")
return stateBag
}

View File

@ -0,0 +1,145 @@
package ncloud
import (
"context"
"errors"
"fmt"
"io/ioutil"
"log"
"time"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
type StepCreateServerInstance struct {
Conn *ncloud.Conn
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
}
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, feeSystemTypeCode 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 = feeSystemTypeCode
if s.Config.UserData != "" {
reqParams.UserData = 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.UserData = string(contents)
}
if s.Config.AccessControlGroupConfigurationNo != "" {
reqParams.AccessControlGroupConfigurationNoList = []string{s.Config.AccessControlGroupConfigurationNo}
}
serverInstanceList, err := s.Conn.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 := 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(_ context.Context, state multistep.StateBag) multistep.StepAction {
s.Say("Create Server Instance")
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)
}
serverInstanceNo, err := s.CreateServerInstance(loginKey.KeyName, zoneNo, feeSystemTypeCode)
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,60 @@
package ncloud
import (
"context"
"fmt"
"testing"
"github.com/hashicorp/packer/helper/multistep"
)
func TestStepCreateServerInstanceShouldFailIfOperationCreateFails(t *testing.T) {
var testSubject = &StepCreateServerInstance{
CreateServerInstance: func(loginKeyName string, zoneNo string, feeSystemTypeCode string) (string, error) {
return "", fmt.Errorf("!! Unit Test FAIL !!")
},
Say: func(message string) {},
Error: func(e error) {},
}
stateBag := createTestStateBagStepCreateServerInstance()
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 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) {},
}
stateBag := createTestStateBagStepCreateServerInstance()
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 createTestStateBagStepCreateServerInstance() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put("LoginKey", &LoginKey{"a", "b"})
stateBag.Put("ZoneNo", "1")
return stateBag
}

View File

@ -0,0 +1,96 @@
package ncloud
import (
"context"
"errors"
"fmt"
"log"
"time"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
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
}
_, err := s.Conn.DeleteBlockStorageInstances(blockStorageInstanceList)
if err != nil {
return err
}
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(_ 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)
err := s.DeleteBlockStorageInstance(serverInstanceNo)
return processStepResult(err, s.Error, state)
}
func (*StepDeleteBlockStorageInstance) Cleanup(multistep.StateBag) {
}

View File

@ -0,0 +1,59 @@
package ncloud
import (
"context"
"fmt"
"testing"
"github.com/hashicorp/packer/helper/multistep"
)
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(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 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(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 createTestStateBagStepDeleteBlockStorageInstance() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put("InstanceNo", "1")
return stateBag
}

View File

@ -0,0 +1,60 @@
package ncloud
import (
"context"
"fmt"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
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 "", err
}
s.Say(fmt.Sprintf("Root password is %s", rootPassword.RootPassword))
return rootPassword.RootPassword, nil
}
func (s *StepGetRootPassword) Run(_ context.Context, 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,58 @@
package ncloud
import (
"context"
"fmt"
"testing"
"github.com/hashicorp/packer/helper/multistep"
)
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(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 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(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 DeleteTestStateBagStepGetRootPassword() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put("LoginKey", &LoginKey{"a", "b"})
stateBag.Put("InstanceNo", "a")
return stateBag
}

View File

@ -0,0 +1,65 @@
package ncloud
import (
"context"
"fmt"
"log"
"time"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
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 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 {
return err
}
s.Say(fmt.Sprintf("Server Instance stopped. Server InstanceNo is %s", serverInstanceList.ServerInstanceList[0].ServerInstanceNo))
return nil
}
func (s *StepStopServerInstance) Run(_ context.Context, 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,56 @@
package ncloud
import (
"context"
"fmt"
"testing"
"github.com/hashicorp/packer/helper/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(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 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(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 createTestStateBagStepStopServerInstance() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put("InstanceNo", "a")
return stateBag
}

View File

@ -0,0 +1,81 @@
package ncloud
import (
"context"
"errors"
"time"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
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}
_, err := s.Conn.TerminateServerInstances(reqParams)
if err != nil {
return err
}
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 <- err
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(_ context.Context, 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,56 @@
package ncloud
import (
"context"
"fmt"
"testing"
"github.com/hashicorp/packer/helper/multistep"
)
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(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 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(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 createTestStateBagStepTerminateServerInstance() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put("InstanceNo", "a")
return stateBag
}

View File

@ -0,0 +1,269 @@
package ncloud
import (
"bytes"
"context"
"errors"
"fmt"
"strings"
ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"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
FeeSystemTypeCode 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 err
}
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 err
}
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.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.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(_ context.Context, state multistep.StateBag) multistep.StepAction {
s.Say("Validating deployment template ...")
err := s.Validate()
state.Put("ZoneNo", s.zoneNo)
if s.FeeSystemTypeCode != "" {
state.Put("FeeSystemTypeCode", s.FeeSystemTypeCode)
}
return processStepResult(err, s.Error, state)
}
// Cleanup : cleanup on error
func (s *StepValidateTemplate) Cleanup(multistep.StateBag) {
}

View File

@ -0,0 +1,55 @@
package ncloud
import (
"context"
"fmt"
"testing"
"github.com/hashicorp/packer/helper/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(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 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(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 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"
@ -97,6 +98,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),

View File

@ -0,0 +1,7 @@
Copyright 2017 NAVER BUSINESS PLATFORM Corp.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,26 @@
ncp-sdk-go
This project contains subcomponents with separate copyright notices and license terms.
Your use of the source code for these subcomponents is subject to the terms and conditions of the following licenses.
=======================================================================
OAuth 1.0 Library for Go (https://github.com/mrjones/oauth/)
=======================================================================
Copyright (C) 2013 Matthew R. Jones
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,14 @@
package common
import "encoding/xml"
type CommonResponse struct {
RequestID string `xml:"requestId"`
ReturnCode int `xml:"returnCode"`
ReturnMessage string `xml:"returnMessage"`
}
type ResponseError struct {
ResponseError xml.Name `xml:"responseError"`
CommonResponse
}

View File

@ -0,0 +1,10 @@
package common
type CommonCode struct {
CodeKind string `xml:"codeKind"`
DetailCategorizeCode string `xml:"detailCategorizeCode"`
Code string `xml:"code"`
CodeName string `xml:"codeName"`
CodeOrder int `xml:"codeOrder"`
JavaConstantCode string `xml:"javaConstantCode"`
}

View File

@ -0,0 +1,20 @@
package common
import (
"encoding/xml"
"strings"
)
func ParseErrorResponse(bytes []byte) (*ResponseError, error) {
responseError := ResponseError{}
if err := xml.Unmarshal([]byte(bytes), &responseError); err != nil {
return nil, err
}
if responseError.ReturnMessage != "" {
responseError.ReturnMessage = strings.TrimSpace(responseError.ReturnMessage)
}
return &responseError, nil
}

View File

@ -0,0 +1,7 @@
package common
type Region struct {
RegionNo string `xml:"regionNo"`
RegionCode string `xml:"regionCode"`
RegionName string `xml:"regionName"`
}

View File

@ -0,0 +1,7 @@
package common
type Zone struct {
ZoneNo string `xml:"zoneNo"`
ZoneName string `xml:"zoneName"`
ZoneDescription string `xml:"zoneDescription"`
}

View File

@ -0,0 +1,370 @@
package oauth
import (
"crypto"
"crypto/hmac"
_ "crypto/sha1"
"encoding/base64"
"fmt"
"math/rand"
"net/url"
"os"
"sort"
"strconv"
"sync"
"time"
)
const (
OAUTH_VERSION = "1.0"
SIGNATURE_METHOD_HMAC = "HMAC-"
HTTP_AUTH_HEADER = "Authorization"
OAUTH_HEADER = "OAuth "
CONSUMER_KEY_PARAM = "oauth_consumer_key"
NONCE_PARAM = "oauth_nonce"
SIGNATURE_METHOD_PARAM = "oauth_signature_method"
SIGNATURE_PARAM = "oauth_signature"
TIMESTAMP_PARAM = "oauth_timestamp"
TOKEN_PARAM = "oauth_token"
TOKEN_SECRET_PARAM = "oauth_token_secret"
VERSION_PARAM = "oauth_version"
)
var HASH_METHOD_MAP = map[crypto.Hash]string{
crypto.SHA1: "SHA1",
crypto.SHA256: "SHA256",
}
// Creates a new Consumer instance, with a HMAC-SHA1 signer
func NewConsumer(consumerKey string, consumerSecret string, requestMethod string, requestURL string) *Consumer {
clock := &defaultClock{}
consumer := &Consumer{
consumerKey: consumerKey,
consumerSecret: consumerSecret,
requestMethod: requestMethod,
requestURL: requestURL,
clock: clock,
nonceGenerator: newLockedNonceGenerator(clock),
AdditionalParams: make(map[string]string),
}
consumer.signer = &HMACSigner{
consumerSecret: consumerSecret,
hashFunc: crypto.SHA1,
}
return consumer
}
// lockedNonceGenerator wraps a non-reentrant random number generator with alock
type lockedNonceGenerator struct {
nonceGenerator nonceGenerator
lock sync.Mutex
}
func newLockedNonceGenerator(c clock) *lockedNonceGenerator {
return &lockedNonceGenerator{
nonceGenerator: rand.New(rand.NewSource(c.Nanos())),
}
}
func (n *lockedNonceGenerator) Int63() int64 {
n.lock.Lock()
r := n.nonceGenerator.Int63()
n.lock.Unlock()
return r
}
type clock interface {
Seconds() int64
Nanos() int64
}
type nonceGenerator interface {
Int63() int64
}
type signer interface {
Sign(message string) (string, error)
Verify(message string, signature string) error
SignatureMethod() string
HashFunc() crypto.Hash
Debug(enabled bool)
}
type defaultClock struct{}
func (*defaultClock) Seconds() int64 {
return time.Now().Unix()
}
func (*defaultClock) Nanos() int64 {
return time.Now().UnixNano()
}
type Consumer struct {
AdditionalParams map[string]string
// The rest of this class is configured via the NewConsumer function.
consumerKey string
consumerSecret string
requestMethod string
requestURL string
debug bool
// Private seams for mocking dependencies when testing
clock clock
// Seeded generators are not reentrant
nonceGenerator nonceGenerator
signer signer
}
type HMACSigner struct {
consumerSecret string
hashFunc crypto.Hash
debug bool
}
func (s *HMACSigner) Debug(enabled bool) {
s.debug = enabled
}
func (s *HMACSigner) Sign(message string) (string, error) {
key := escape(s.consumerSecret)
if s.debug {
fmt.Println("Signing:", message)
fmt.Println("Key:", key)
}
h := hmac.New(s.HashFunc().New, []byte(key+"&"))
h.Write([]byte(message))
rawSignature := h.Sum(nil)
base64signature := base64.StdEncoding.EncodeToString(rawSignature)
if s.debug {
fmt.Println("Base64 signature:", base64signature)
}
return base64signature, nil
}
func (s *HMACSigner) Verify(message string, signature string) error {
if s.debug {
fmt.Println("Verifying Base64 signature:", signature)
}
validSignature, err := s.Sign(message)
if err != nil {
return err
}
if validSignature != signature {
decodedSigniture, _ := url.QueryUnescape(signature)
if validSignature != decodedSigniture {
return fmt.Errorf("signature did not match")
}
}
return nil
}
func (s *HMACSigner) SignatureMethod() string {
return SIGNATURE_METHOD_HMAC + HASH_METHOD_MAP[s.HashFunc()]
}
func (s *HMACSigner) HashFunc() crypto.Hash {
return s.hashFunc
}
func escape(s string) string {
t := make([]byte, 0, 3*len(s))
for i := 0; i < len(s); i++ {
c := s[i]
if isEscapable(c) {
t = append(t, '%')
t = append(t, "0123456789ABCDEF"[c>>4])
t = append(t, "0123456789ABCDEF"[c&15])
} else {
t = append(t, s[i])
}
}
return string(t)
}
func isEscapable(b byte) bool {
return !('A' <= b && b <= 'Z' || 'a' <= b && b <= 'z' || '0' <= b && b <= '9' || b == '-' || b == '.' || b == '_' || b == '~')
}
func (c *Consumer) Debug(enabled bool) {
c.debug = enabled
c.signer.Debug(enabled)
}
func (c *Consumer) GetRequestUrl() (loginUrl string, err error) {
if os.Getenv("GO_ENV") == "development" {
c.AdditionalParams["approachKey"] = c.consumerKey
c.AdditionalParams["secretKey"] = c.consumerSecret
}
c.AdditionalParams["responseFormatType"] = "xml"
params := c.baseParams(c.consumerKey, c.AdditionalParams)
if c.debug {
fmt.Println("params:", params)
}
req := &request{
method: c.requestMethod,
url: c.requestURL,
oauthParams: params,
}
signature, err := c.signRequest(req)
if err != nil {
return "", err
}
result := req.url + "?"
for pos, key := range req.oauthParams.Keys() {
for innerPos, value := range req.oauthParams.Get(key) {
if pos+innerPos != 0 {
result += "&"
}
result += fmt.Sprintf("%s=%s", key, value)
}
}
result += fmt.Sprintf("&%s=%s", SIGNATURE_PARAM, escape(signature))
if c.debug {
fmt.Println("req: ", result)
}
return result, nil
}
func (c *Consumer) baseParams(consumerKey string, additionalParams map[string]string) *OrderedParams {
params := NewOrderedParams()
params.Add(VERSION_PARAM, OAUTH_VERSION)
params.Add(SIGNATURE_METHOD_PARAM, c.signer.SignatureMethod())
params.Add(TIMESTAMP_PARAM, strconv.FormatInt(c.clock.Seconds(), 10))
params.Add(NONCE_PARAM, strconv.FormatInt(c.nonceGenerator.Int63(), 10))
params.Add(CONSUMER_KEY_PARAM, consumerKey)
for key, value := range additionalParams {
params.Add(key, value)
}
return params
}
func (c *Consumer) signRequest(req *request) (string, error) {
baseString := c.requestString(req.method, req.url, req.oauthParams)
if c.debug {
fmt.Println("baseString: ", baseString)
}
signature, err := c.signer.Sign(baseString)
if err != nil {
return "", err
}
return signature, nil
}
func (c *Consumer) requestString(method string, url string, params *OrderedParams) string {
result := method + "&" + escape(url)
for pos, key := range params.Keys() {
for innerPos, value := range params.Get(key) {
if pos+innerPos == 0 {
result += "&"
} else {
result += escape("&")
}
result += escape(fmt.Sprintf("%s=%s", key, value))
}
}
return result
}
type request struct {
method string
url string
oauthParams *OrderedParams
userParams map[string]string
}
//
// String Sorting helpers
//
type ByValue []string
func (a ByValue) Len() int {
return len(a)
}
func (a ByValue) Swap(i, j int) {
a[i], a[j] = a[j], a[i]
}
func (a ByValue) Less(i, j int) bool {
return a[i] < a[j]
}
//
// ORDERED PARAMS
//
type OrderedParams struct {
allParams map[string][]string
keyOrdering []string
}
func NewOrderedParams() *OrderedParams {
return &OrderedParams{
allParams: make(map[string][]string),
keyOrdering: make([]string, 0),
}
}
func (o *OrderedParams) Get(key string) []string {
sort.Sort(ByValue(o.allParams[key]))
return o.allParams[key]
}
func (o *OrderedParams) Keys() []string {
sort.Sort(o)
return o.keyOrdering
}
func (o *OrderedParams) Add(key, value string) {
o.AddUnescaped(key, escape(value))
}
func (o *OrderedParams) AddUnescaped(key, value string) {
if _, exists := o.allParams[key]; !exists {
o.keyOrdering = append(o.keyOrdering, key)
o.allParams[key] = make([]string, 1)
o.allParams[key][0] = value
} else {
o.allParams[key] = append(o.allParams[key], value)
}
}
func (o *OrderedParams) Len() int {
return len(o.keyOrdering)
}
func (o *OrderedParams) Less(i int, j int) bool {
return o.keyOrdering[i] < o.keyOrdering[j]
}
func (o *OrderedParams) Swap(i int, j int) {
o.keyOrdering[i], o.keyOrdering[j] = o.keyOrdering[j], o.keyOrdering[i]
}

View File

@ -0,0 +1,38 @@
package request
import (
"io/ioutil"
"net/http"
"github.com/NaverCloudPlatform/ncloud-sdk-go/oauth"
)
// NewRequest is http request with oauth
func NewRequest(accessKey string, secretKey string, method string, url string, params map[string]string) ([]byte, *http.Response, error) {
c := oauth.NewConsumer(accessKey, secretKey, method, url)
for k, v := range params {
c.AdditionalParams[k] = v
}
reqURL, err := c.GetRequestUrl()
if err != nil {
return nil, nil, err
}
req, err := http.NewRequest(method, reqURL, nil)
if err != nil {
return nil, nil, err
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, nil, err
}
defer resp.Body.Close()
bytes, _ := ioutil.ReadAll(resp.Body)
return bytes, resp, nil
}

View File

@ -0,0 +1,21 @@
package sdk
import (
"os"
)
// NewConnection create connection for server api
func NewConnection(accessKey string, secretKey string) *Conn {
conn := &Conn{
accessKey: accessKey,
secretKey: secretKey,
apiURL: "https://api.ncloud.com/",
}
// for other phase(dev, test, beta ...) test
if os.Getenv("NCLOUD_API_GW") != "" {
conn.apiURL = os.Getenv("NCLOUD_API_GW")
}
return conn
}

View File

@ -0,0 +1,87 @@
package sdk
import (
"encoding/xml"
"errors"
"fmt"
"strconv"
common "github.com/NaverCloudPlatform/ncloud-sdk-go/common"
request "github.com/NaverCloudPlatform/ncloud-sdk-go/request"
)
func processCreateBlockStorageInstanceParams(reqParams *RequestBlockStorageInstance) (map[string]string, error) {
params := make(map[string]string)
if reqParams.BlockStorageName != "" {
if len := len(reqParams.BlockStorageName); len < 3 || len > 30 {
return nil, errors.New("Length of BlockStorageName should be min 3 or max 30")
}
params["blockStorageName"] = reqParams.BlockStorageName
}
if reqParams.BlockStorageSize < 10 || reqParams.BlockStorageSize > 2000 {
return nil, errors.New("BlockStorageSize should be min 10 or max 2000")
}
if reqParams.BlockStorageDescription != "" {
if len := len(reqParams.BlockStorageDescription); len > 1000 {
return nil, errors.New("Length of BlockStorageDescription should be max 1000")
}
params["blockStorageDescription"] = reqParams.BlockStorageDescription
}
if int(reqParams.BlockStorageSize/10)*10 != reqParams.BlockStorageSize {
return nil, errors.New("BlockStorageSize must be a multiple of 10 GB")
}
if reqParams.BlockStorageSize == 0 {
return nil, errors.New("BlockStorageSize field is required")
}
params["blockStorageSize"] = strconv.Itoa(reqParams.BlockStorageSize)
if reqParams.ServerInstanceNo == "" {
return nil, errors.New("ServerInstanceNo field is required")
}
params["serverInstanceNo"] = reqParams.ServerInstanceNo
return params, nil
}
// CreateBlockStorageInstance create block storage instance
func (s *Conn) CreateBlockStorageInstance(reqParams *RequestBlockStorageInstance) (*BlockStorageInstanceList, error) {
params, err := processCreateBlockStorageInstanceParams(reqParams)
if err != nil {
return nil, err
}
params["action"] = "createBlockStorageInstance"
bytes, resp, err := request.NewRequest(s.accessKey, s.secretKey, "GET", s.apiURL+"server/", params)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
responseError, err := common.ParseErrorResponse(bytes)
if err != nil {
return nil, err
}
respError := BlockStorageInstanceList{}
respError.ReturnCode = responseError.ReturnCode
respError.ReturnMessage = responseError.ReturnMessage
return &respError, fmt.Errorf("%s %s - error code: %d , error message: %s", resp.Status, string(bytes), responseError.ReturnCode, responseError.ReturnMessage)
}
var blockStorageInstanceList = BlockStorageInstanceList{}
if err := xml.Unmarshal([]byte(bytes), &blockStorageInstanceList); err != nil {
return nil, err
}
return &blockStorageInstanceList, nil
}

View File

@ -0,0 +1,64 @@
package sdk
import (
"encoding/xml"
"errors"
"fmt"
"strings"
common "github.com/NaverCloudPlatform/ncloud-sdk-go/common"
request "github.com/NaverCloudPlatform/ncloud-sdk-go/request"
)
func processCreateLoginKeyParams(keyName string) error {
if keyName == "" {
return errors.New("KeyName is required field")
}
if len := len(keyName); len < 3 || len > 30 {
return errors.New("Length of KeyName should be min 3 or max 30")
}
return nil
}
// CreateLoginKey create loginkey with keyName
func (s *Conn) CreateLoginKey(keyName string) (*PrivateKey, error) {
if err := processCreateLoginKeyParams(keyName); err != nil {
return nil, err
}
params := make(map[string]string)
params["keyName"] = keyName
params["action"] = "createLoginKey"
bytes, resp, err := request.NewRequest(s.accessKey, s.secretKey, "GET", s.apiURL+"server/", params)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
responseError, err := common.ParseErrorResponse(bytes)
if err != nil {
return nil, err
}
respError := PrivateKey{}
respError.ReturnCode = responseError.ReturnCode
respError.ReturnMessage = responseError.ReturnMessage
return &respError, fmt.Errorf("%s %s - error code: %d , error message: %s", resp.Status, string(bytes), responseError.ReturnCode, responseError.ReturnMessage)
}
privateKey := PrivateKey{}
if err := xml.Unmarshal([]byte(bytes), &privateKey); err != nil {
return nil, err
}
if privateKey.PrivateKey != "" {
privateKey.PrivateKey = strings.TrimSpace(privateKey.PrivateKey)
}
return &privateKey, nil
}

View File

@ -0,0 +1,74 @@
package sdk
import (
"encoding/xml"
"errors"
"fmt"
common "github.com/NaverCloudPlatform/ncloud-sdk-go/common"
request "github.com/NaverCloudPlatform/ncloud-sdk-go/request"
)
func processCreatePublicIPInstanceParams(reqParams *RequestCreatePublicIPInstance) (map[string]string, error) {
params := make(map[string]string)
if reqParams.ServerInstanceNo != "" {
params["serverInstanceNo"] = reqParams.ServerInstanceNo
}
if reqParams.PublicIPDescription != "" {
if len := len(reqParams.PublicIPDescription); len > 1000 {
return params, errors.New("Length of publicIpDescription should be max 1000")
}
params["publicIpDescription"] = reqParams.PublicIPDescription
}
if reqParams.InternetLineTypeCode != "" {
if reqParams.InternetLineTypeCode != "PUBLC" && reqParams.InternetLineTypeCode != "GLBL" {
return params, errors.New("InternetLineTypeCode should be PUBLC or GLBL")
}
params["internetLineTypeCode"] = reqParams.InternetLineTypeCode
}
if reqParams.RegionNo != "" {
params["regionNo"] = reqParams.RegionNo
}
return params, nil
}
// CreatePublicIPInstance create public ip instance and allocate it to server instance
func (s *Conn) CreatePublicIPInstance(reqParams *RequestCreatePublicIPInstance) (*PublicIPInstanceList, error) {
params, err := processCreatePublicIPInstanceParams(reqParams)
if err != nil {
return nil, err
}
params["action"] = "createPublicIpInstance"
bytes, resp, err := request.NewRequest(s.accessKey, s.secretKey, "GET", s.apiURL+"server/", params)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
responseError, err := common.ParseErrorResponse(bytes)
if err != nil {
return nil, err
}
respError := PublicIPInstanceList{}
respError.ReturnCode = responseError.ReturnCode
respError.ReturnMessage = responseError.ReturnMessage
return &respError, fmt.Errorf("%s %s - error code: %d , error message: %s", resp.Status, string(bytes), responseError.ReturnCode, responseError.ReturnMessage)
}
var responseCreatePublicIPInstances = PublicIPInstanceList{}
if err := xml.Unmarshal([]byte(bytes), &responseCreatePublicIPInstances); err != nil {
fmt.Println(err)
return nil, err
}
return &responseCreatePublicIPInstances, nil
}

View File

@ -0,0 +1,71 @@
package sdk
import (
"encoding/xml"
"errors"
"fmt"
common "github.com/NaverCloudPlatform/ncloud-sdk-go/common"
request "github.com/NaverCloudPlatform/ncloud-sdk-go/request"
)
func processCreateMemberServerImageParams(reqParams *RequestCreateServerImage) (map[string]string, error) {
params := make(map[string]string)
if reqParams == nil || reqParams.ServerInstanceNo == "" {
return params, errors.New("ServerInstanceNo is required field")
}
if reqParams.MemberServerImageName != "" {
if len := len(reqParams.MemberServerImageName); len < 3 || len > 30 {
return nil, errors.New("Length of MemberServerImageName should be min 3 or max 30")
}
params["memberServerImageName"] = reqParams.MemberServerImageName
}
if reqParams.MemberServerImageDescription != "" {
if len := len(reqParams.MemberServerImageDescription); len > 1000 {
return nil, errors.New("Length of MemberServerImageDescription should be smaller than 1000")
}
params["memberServerImageDescription"] = reqParams.MemberServerImageDescription
}
params["serverInstanceNo"] = reqParams.ServerInstanceNo
return params, nil
}
// CreateMemberServerImage create member server image and retrun member server image list
func (s *Conn) CreateMemberServerImage(reqParams *RequestCreateServerImage) (*MemberServerImageList, error) {
params, err := processCreateMemberServerImageParams(reqParams)
if err != nil {
return nil, err
}
params["action"] = "createMemberServerImage"
bytes, resp, err := request.NewRequest(s.accessKey, s.secretKey, "GET", s.apiURL+"server/", params)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
responseError, err := common.ParseErrorResponse(bytes)
if err != nil {
return nil, err
}
respError := MemberServerImageList{}
respError.ReturnCode = responseError.ReturnCode
respError.ReturnMessage = responseError.ReturnMessage
return &respError, fmt.Errorf("%s %s - error code: %d , error message: %s", resp.Status, string(bytes), responseError.ReturnCode, responseError.ReturnMessage)
}
var serverImageListsResp = MemberServerImageList{}
if err := xml.Unmarshal([]byte(bytes), &serverImageListsResp); err != nil {
return nil, err
}
return &serverImageListsResp, nil
}

View File

@ -0,0 +1,148 @@
package sdk
import (
"encoding/base64"
"encoding/xml"
"errors"
"fmt"
"strconv"
common "github.com/NaverCloudPlatform/ncloud-sdk-go/common"
request "github.com/NaverCloudPlatform/ncloud-sdk-go/request"
)
func processCreateServerInstancesParams(reqParams *RequestCreateServerInstance) (map[string]string, error) {
params := make(map[string]string)
if reqParams == nil {
return params, nil
}
if reqParams.ServerImageProductCode != "" {
if len := len(reqParams.ServerImageProductCode); len > 20 {
return nil, errors.New("Length of ServerImageProductCode should be min 1 or max 20")
}
params["serverImageProductCode"] = reqParams.ServerImageProductCode
}
if reqParams.ServerProductCode != "" {
if len := len(reqParams.ServerProductCode); len > 20 {
return nil, errors.New("Length of ServerProductCode should be min 1 or max 20")
}
params["serverProductCode"] = reqParams.ServerProductCode
}
if reqParams.MemberServerImageNo != "" {
params["memberServerImageNo"] = reqParams.MemberServerImageNo
}
if reqParams.ServerName != "" {
if len := len(reqParams.ServerName); len < 3 || len > 30 {
return nil, errors.New("Length of ServerName should be min 3 or max 30")
}
params["serverName"] = reqParams.ServerName
}
if reqParams.ServerDescription != "" {
if len := len(reqParams.ServerDescription); len > 1000 {
return nil, errors.New("Length of ServerDescription should be min 1 or max 1000")
}
params["serverDescription"] = reqParams.ServerDescription
}
if reqParams.LoginKeyName != "" {
if len := len(reqParams.LoginKeyName); len < 3 || len > 30 {
return nil, errors.New("Length of LoginKeyName should be min 3 or max 30")
}
params["loginKeyName"] = reqParams.LoginKeyName
}
if reqParams.IsProtectServerTermination == true {
params["isProtectServerTermination"] = "true"
}
if reqParams.ServerCreateCount > 0 {
if reqParams.ServerCreateCount > 20 {
return nil, errors.New("ServerCreateCount should be min 1 or max 20")
}
params["serverCreateCount"] = strconv.Itoa(reqParams.ServerCreateCount)
}
if reqParams.ServerCreateStartNo > 0 {
if reqParams.ServerCreateCount+reqParams.ServerCreateStartNo > 1000 {
return nil, errors.New("Sum of ServerCreateCount and ServerCreateStartNo should be less than 1000")
}
params["serverCreateStartNo"] = strconv.Itoa(reqParams.ServerCreateStartNo)
}
if reqParams.InternetLineTypeCode != "" {
if reqParams.InternetLineTypeCode != "PUBLC" && reqParams.InternetLineTypeCode != "GLBL" {
return nil, errors.New("InternetLineTypeCode should be PUBLC or GLBL")
}
params["internetLineTypeCode"] = reqParams.InternetLineTypeCode
}
if reqParams.FeeSystemTypeCode != "" {
if reqParams.FeeSystemTypeCode != "FXSUM" && reqParams.FeeSystemTypeCode != "MTRAT" {
return nil, errors.New("FeeSystemTypeCode should be FXSUM or MTRAT")
}
params["feeSystemTypeCode"] = reqParams.FeeSystemTypeCode
}
if reqParams.UserData != "" {
if len := len(reqParams.UserData); len > 21847 {
return nil, errors.New("Length of UserData should be min 1 or max 21847")
}
params["userData"] = base64.StdEncoding.EncodeToString([]byte(reqParams.UserData))
}
if reqParams.ZoneNo != "" {
params["zoneNo"] = reqParams.ZoneNo
}
if len(reqParams.AccessControlGroupConfigurationNoList) > 0 {
for k, v := range reqParams.AccessControlGroupConfigurationNoList {
params[fmt.Sprintf("accessControlGroupConfigurationNoList.%d", k+1)] = v
}
}
return params, nil
}
// CreateServerInstances create server instances
func (s *Conn) CreateServerInstances(reqParams *RequestCreateServerInstance) (*ServerInstanceList, error) {
params, err := processCreateServerInstancesParams(reqParams)
if err != nil {
return nil, err
}
params["action"] = "createServerInstances"
bytes, resp, err := request.NewRequest(s.accessKey, s.secretKey, "GET", s.apiURL+"server/", params)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
responseError, err := common.ParseErrorResponse(bytes)
if err != nil {
return nil, err
}
respError := ServerInstanceList{}
respError.ReturnCode = responseError.ReturnCode
respError.ReturnMessage = responseError.ReturnMessage
return &respError, fmt.Errorf("%s %s - error code: %d , error message: %s", resp.Status, string(bytes), responseError.ReturnCode, responseError.ReturnMessage)
}
var responseCreateServerInstances = ServerInstanceList{}
if err := xml.Unmarshal([]byte(bytes), &responseCreateServerInstances); err != nil {
fmt.Println(err)
return nil, err
}
return &responseCreateServerInstances, nil
}

View File

@ -0,0 +1,60 @@
package sdk
import (
"encoding/xml"
"errors"
"fmt"
common "github.com/NaverCloudPlatform/ncloud-sdk-go/common"
request "github.com/NaverCloudPlatform/ncloud-sdk-go/request"
)
func processDeleteBlockStorageInstancesParams(blockStorageInstanceNoList []string) (map[string]string, error) {
params := make(map[string]string)
if len(blockStorageInstanceNoList) == 0 {
return params, errors.New("BlockStorageInstanceNoList field is Required")
}
for k, v := range blockStorageInstanceNoList {
params[fmt.Sprintf("blockStorageInstanceNoList.%d", k+1)] = v
}
return params, nil
}
// DeleteBlockStorageInstances delete block storage instances
func (s *Conn) DeleteBlockStorageInstances(blockStorageInstanceNoList []string) (*BlockStorageInstanceList, error) {
params, err := processDeleteBlockStorageInstancesParams(blockStorageInstanceNoList)
if err != nil {
return nil, err
}
params["action"] = "deleteBlockStorageInstances"
bytes, resp, err := request.NewRequest(s.accessKey, s.secretKey, "GET", s.apiURL+"server/", params)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
responseError, err := common.ParseErrorResponse(bytes)
if err != nil {
return nil, err
}
respError := BlockStorageInstanceList{}
respError.ReturnCode = responseError.ReturnCode
respError.ReturnMessage = responseError.ReturnMessage
return &respError, fmt.Errorf("%s %s - error code: %d , error message: %s", resp.Status, string(bytes), responseError.ReturnCode, responseError.ReturnMessage)
}
var blockStorageInstanceList = BlockStorageInstanceList{}
if err := xml.Unmarshal([]byte(bytes), &blockStorageInstanceList); err != nil {
fmt.Println(err)
return nil, err
}
return &blockStorageInstanceList, nil
}

View File

@ -0,0 +1,59 @@
package sdk
import (
"encoding/xml"
"errors"
"fmt"
common "github.com/NaverCloudPlatform/ncloud-sdk-go/common"
request "github.com/NaverCloudPlatform/ncloud-sdk-go/request"
)
func processDeleteLoginKeyParams(keyName string) error {
if keyName == "" {
return errors.New("KeyName is required field")
}
if len := len(keyName); len < 3 || len > 30 {
return errors.New("Length of KeyName should be min 3 or max 30")
}
return nil
}
// DeleteLoginKey delete login key with keyName
func (s *Conn) DeleteLoginKey(keyName string) (*common.CommonResponse, error) {
if err := processDeleteLoginKeyParams(keyName); err != nil {
return nil, err
}
params := make(map[string]string)
params["keyName"] = keyName
params["action"] = "deleteLoginKey"
bytes, resp, err := request.NewRequest(s.accessKey, s.secretKey, "GET", s.apiURL+"server/", params)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
responseError, err := common.ParseErrorResponse(bytes)
if err != nil {
return nil, err
}
respError := common.CommonResponse{}
respError.ReturnCode = responseError.ReturnCode
respError.ReturnMessage = responseError.ReturnMessage
return &respError, fmt.Errorf("%s %s - error code: %d , error message: %s", resp.Status, string(bytes), responseError.ReturnCode, responseError.ReturnMessage)
}
var responseDeleteLoginKey = common.CommonResponse{}
if err := xml.Unmarshal([]byte(bytes), &responseDeleteLoginKey); err != nil {
return nil, err
}
return &responseDeleteLoginKey, nil
}

View File

@ -0,0 +1,62 @@
package sdk
import (
"encoding/xml"
"errors"
"fmt"
common "github.com/NaverCloudPlatform/ncloud-sdk-go/common"
request "github.com/NaverCloudPlatform/ncloud-sdk-go/request"
)
func processDeletePublicIPInstancesParams(reqParams *RequestDeletePublicIPInstances) (map[string]string, error) {
params := make(map[string]string)
if reqParams == nil || len(reqParams.PublicIPInstanceNoList) == 0 {
return params, errors.New("Required field is not specified. location : publicIpInstanceNoList.N")
}
if len(reqParams.PublicIPInstanceNoList) > 0 {
for k, v := range reqParams.PublicIPInstanceNoList {
params[fmt.Sprintf("publicIpInstanceNoList.%d", k+1)] = v
}
}
return params, nil
}
// DeletePublicIPInstances delete public ip instances
func (s *Conn) DeletePublicIPInstances(reqParams *RequestDeletePublicIPInstances) (*PublicIPInstanceList, error) {
params, err := processDeletePublicIPInstancesParams(reqParams)
if err != nil {
return nil, err
}
params["action"] = "deletePublicIpInstances"
bytes, resp, err := request.NewRequest(s.accessKey, s.secretKey, "GET", s.apiURL+"server/", params)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
responseError, err := common.ParseErrorResponse(bytes)
if err != nil {
return nil, err
}
respError := PublicIPInstanceList{}
respError.ReturnCode = responseError.ReturnCode
respError.ReturnMessage = responseError.ReturnMessage
return &respError, fmt.Errorf("%s %s - error code: %d , error message: %s", resp.Status, string(bytes), responseError.ReturnCode, responseError.ReturnMessage)
}
var publicIPInstanceList = PublicIPInstanceList{}
if err := xml.Unmarshal([]byte(bytes), &publicIPInstanceList); err != nil {
fmt.Println(err)
return nil, err
}
return &publicIPInstanceList, nil
}

View File

@ -0,0 +1,54 @@
package sdk
import (
"encoding/xml"
"errors"
"fmt"
common "github.com/NaverCloudPlatform/ncloud-sdk-go/common"
request "github.com/NaverCloudPlatform/ncloud-sdk-go/request"
)
func processDisassociatePublicIPParams(PublicIPInstanceNo string) error {
if PublicIPInstanceNo == "" {
return errors.New("Required field is not specified. location : publicIpInstanceNo")
}
return nil
}
// DisassociatePublicIP diassociate public ip from server instance
func (s *Conn) DisassociatePublicIP(PublicIPInstanceNo string) (*PublicIPInstanceList, error) {
if err := processDisassociatePublicIPParams(PublicIPInstanceNo); err != nil {
return nil, err
}
params := make(map[string]string)
params["publicIpInstanceNo"] = PublicIPInstanceNo
params["action"] = "disassociatePublicIpFromServerInstance"
bytes, resp, err := request.NewRequest(s.accessKey, s.secretKey, "GET", s.apiURL+"server/", params)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
responseError, err := common.ParseErrorResponse(bytes)
if err != nil {
return nil, err
}
respError := PublicIPInstanceList{}
respError.ReturnCode = responseError.ReturnCode
respError.ReturnMessage = responseError.ReturnMessage
return &respError, fmt.Errorf("%s %s - error code: %d , error message: %s", resp.Status, string(bytes), responseError.ReturnCode, responseError.ReturnMessage)
}
var responseDisassociatePublicIPInstances = PublicIPInstanceList{}
if err := xml.Unmarshal([]byte(bytes), &responseDisassociatePublicIPInstances); err != nil {
return nil, err
}
return &responseDisassociatePublicIPInstances, nil
}

View File

@ -0,0 +1,89 @@
package sdk
import (
"encoding/xml"
"errors"
"fmt"
"strconv"
common "github.com/NaverCloudPlatform/ncloud-sdk-go/common"
request "github.com/NaverCloudPlatform/ncloud-sdk-go/request"
)
func processGetAccessControlGroupListParams(reqParams *RequestAccessControlGroupList) (map[string]string, error) {
params := make(map[string]string)
if reqParams == nil {
return params, nil
}
if len(reqParams.AccessControlGroupConfigurationNoList) > 0 {
for k, v := range reqParams.AccessControlGroupConfigurationNoList {
params[fmt.Sprintf("accessControlGroupConfigurationNoList.%d", k+1)] = v
}
}
if reqParams.IsDefault {
params["isDefault"] = "true"
}
if reqParams.AccessControlGroupName != "" {
if len(reqParams.AccessControlGroupName) < 3 || len(reqParams.AccessControlGroupName) > 30 {
return nil, errors.New("AccessControlGroupName must be between 3 and 30 characters in length")
}
params["accessControlGroupName"] = reqParams.AccessControlGroupName
}
if reqParams.PageNo != 0 {
if reqParams.PageNo > 2147483647 {
return nil, errors.New("PageNo should be up to 2147483647")
}
params["pageNo"] = strconv.Itoa(reqParams.PageNo)
}
if reqParams.PageSize != 0 {
if reqParams.PageSize > 2147483647 {
return nil, errors.New("PageSize should be up to 2147483647")
}
params["pageSize"] = strconv.Itoa(reqParams.PageSize)
}
return params, nil
}
// GetAccessControlGroupList get access control group list
func (s *Conn) GetAccessControlGroupList(reqParams *RequestAccessControlGroupList) (*AccessControlGroupList, error) {
params, err := processGetAccessControlGroupListParams(reqParams)
if err != nil {
return nil, err
}
params["action"] = "getAccessControlGroupList"
bytes, resp, err := request.NewRequest(s.accessKey, s.secretKey, "GET", s.apiURL+"server/", params)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
responseError, err := common.ParseErrorResponse(bytes)
if err != nil {
return nil, err
}
respError := AccessControlGroupList{}
respError.ReturnCode = responseError.ReturnCode
respError.ReturnMessage = responseError.ReturnMessage
return &respError, fmt.Errorf("%s %s - error code: %d , error message: %s", resp.Status, string(bytes), responseError.ReturnCode, responseError.ReturnMessage)
}
var AccessControlGroupList = AccessControlGroupList{}
if err := xml.Unmarshal([]byte(bytes), &AccessControlGroupList); err != nil {
return nil, err
}
return &AccessControlGroupList, nil
}

View File

@ -0,0 +1,61 @@
package sdk
import (
"encoding/xml"
"errors"
"fmt"
"strconv"
common "github.com/NaverCloudPlatform/ncloud-sdk-go/common"
request "github.com/NaverCloudPlatform/ncloud-sdk-go/request"
)
func checkGetAccessControlRuleListParams(accessControlGroupConfigurationNo string) error {
if accessControlGroupConfigurationNo == "" {
return errors.New("accessControlGroupConfigurationNo is required")
}
if no, err := strconv.Atoi(accessControlGroupConfigurationNo); err != nil {
return err
} else if no < 0 || no > 2147483647 {
return errors.New("accessControlGroupConfigurationNoeNo must be up to 2147483647")
}
return nil
}
// GetAccessControlRuleList get access control group list
func (s *Conn) GetAccessControlRuleList(accessControlGroupConfigurationNo string) (*AccessControlRuleList, error) {
if err := checkGetAccessControlRuleListParams(accessControlGroupConfigurationNo); err != nil {
return nil, err
}
params := make(map[string]string)
params["accessControlGroupConfigurationNo"] = accessControlGroupConfigurationNo
params["action"] = "getAccessControlRuleList"
bytes, resp, err := request.NewRequest(s.accessKey, s.secretKey, "GET", s.apiURL+"server/", params)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
responseError, err := common.ParseErrorResponse(bytes)
if err != nil {
return nil, err
}
respError := AccessControlRuleList{}
respError.ReturnCode = responseError.ReturnCode
respError.ReturnMessage = responseError.ReturnMessage
return &respError, fmt.Errorf("%s %s - error code: %d , error message: %s", resp.Status, string(bytes), responseError.ReturnCode, responseError.ReturnMessage)
}
var AccessControlRuleList = AccessControlRuleList{}
if err := xml.Unmarshal([]byte(bytes), &AccessControlRuleList); err != nil {
return nil, err
}
return &AccessControlRuleList, nil
}

View File

@ -0,0 +1,144 @@
package sdk
import (
"encoding/xml"
"errors"
"fmt"
"strconv"
"strings"
common "github.com/NaverCloudPlatform/ncloud-sdk-go/common"
request "github.com/NaverCloudPlatform/ncloud-sdk-go/request"
)
func processGetBlockStorageInstanceListParams(reqParams *RequestBlockStorageInstanceList) (map[string]string, error) {
params := make(map[string]string)
if reqParams == nil {
return params, nil
}
if reqParams.ServerInstanceNo != "" {
params["serverInstanceNo"] = reqParams.ServerInstanceNo
}
if len(reqParams.BlockStorageInstanceNoList) > 0 {
for k, v := range reqParams.BlockStorageInstanceNoList {
params[fmt.Sprintf("blockStorageInstanceNoList.%d", k+1)] = v
}
}
if reqParams.SearchFilterName != "" {
if reqParams.SearchFilterName != "blockStorageName" && reqParams.SearchFilterName != "attachmentInformation" {
return nil, errors.New("SearchFilterName should be blockStorageName or attachmentInformation")
}
params["searchFilterName"] = reqParams.SearchFilterName
}
if reqParams.SearchFilterValue == "" {
params["searchFilterValue"] = reqParams.SearchFilterValue
}
if len(reqParams.BlockStorageTypeCodeList) > 0 {
for k, v := range reqParams.BlockStorageTypeCodeList {
if v != "BASIC" && v != "SVRBS" {
return nil, errors.New("BlockStorageTypeCodeList value should be BASIC or SVRBS")
}
params[fmt.Sprintf("blockStorageTypeCodeList.%d", k+1)] = v
}
}
if reqParams.PageNo != 0 {
if reqParams.PageNo > 2147483647 {
return nil, errors.New("PageNo should be up to 2147483647")
}
params["pageNo"] = strconv.Itoa(reqParams.PageNo)
}
if reqParams.PageSize != 0 {
if reqParams.PageSize > 2147483647 {
return nil, errors.New("PageSize should be up to 2147483647")
}
params["pageSize"] = strconv.Itoa(reqParams.PageSize)
}
if reqParams.BlockStorageInstanceStatusCode != "" {
if reqParams.BlockStorageInstanceStatusCode != "ATTAC" && reqParams.BlockStorageInstanceStatusCode != "CRAET" {
return nil, errors.New("BlockStorageInstanceStatusCode should be ATTAC or CRAET")
}
params["blockStorageInstanceStatusCode"] = reqParams.BlockStorageInstanceStatusCode
}
if reqParams.DiskTypeCode != "" {
if reqParams.DiskTypeCode != "NET" && reqParams.DiskTypeCode != "LOCAL" {
return nil, errors.New("DiskTypeCode should be NET or LOCAL")
}
params["diskTypeCode"] = reqParams.DiskTypeCode
}
if reqParams.DiskDetailTypeCode != "" {
if reqParams.DiskDetailTypeCode != "HDD" && reqParams.DiskDetailTypeCode != "SSD" {
return nil, errors.New("DiskDetailTypeCode should be HDD or SSD")
}
params["diskDetailTypeCode"] = reqParams.DiskDetailTypeCode
}
if reqParams.RegionNo != "" {
params["regionNo"] = reqParams.RegionNo
}
if reqParams.SortedBy != "" {
if strings.EqualFold(reqParams.SortedBy, "blockStorageName") || strings.EqualFold(reqParams.SortedBy, "blockStorageInstanceNo") {
params["sortedBy"] = reqParams.SortedBy
} else {
return nil, errors.New("SortedBy should be blockStorageName or blockStorageInstanceNo")
}
}
if reqParams.SortingOrder != "" {
if strings.EqualFold(reqParams.SortingOrder, "ascending") || strings.EqualFold(reqParams.SortingOrder, "descending") {
params["sortingOrder"] = reqParams.SortingOrder
} else {
return nil, errors.New("SortingOrder should be ascending or descending")
}
}
return params, nil
}
// GetBlockStorageInstance Get block storage instance list
func (s *Conn) GetBlockStorageInstance(reqParams *RequestBlockStorageInstanceList) (*BlockStorageInstanceList, error) {
params, err := processGetBlockStorageInstanceListParams(reqParams)
if err != nil {
return nil, err
}
params["action"] = "getBlockStorageInstanceList"
bytes, resp, err := request.NewRequest(s.accessKey, s.secretKey, "GET", s.apiURL+"server/", params)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
responseError, err := common.ParseErrorResponse(bytes)
if err != nil {
return nil, err
}
respError := BlockStorageInstanceList{}
respError.ReturnCode = responseError.ReturnCode
respError.ReturnMessage = responseError.ReturnMessage
return &respError, fmt.Errorf("%s %s - error code: %d , error message: %s", resp.Status, string(bytes), responseError.ReturnCode, responseError.ReturnMessage)
}
var blockStorageInstanceList = BlockStorageInstanceList{}
if err := xml.Unmarshal([]byte(bytes), &blockStorageInstanceList); err != nil {
return nil, err
}
return &blockStorageInstanceList, nil
}

View File

@ -0,0 +1,77 @@
package sdk
import (
"encoding/xml"
"errors"
"fmt"
"strconv"
common "github.com/NaverCloudPlatform/ncloud-sdk-go/common"
request "github.com/NaverCloudPlatform/ncloud-sdk-go/request"
)
func processGetLoginKeyListParams(reqParams *RequestGetLoginKeyList) (map[string]string, error) {
params := make(map[string]string)
if reqParams == nil {
return params, nil
}
if reqParams.KeyName != "" {
if len := len(reqParams.KeyName); len < 3 || len > 20 {
return nil, errors.New("Length of KeyName should be min 3 or max 30")
}
params["keyName"] = reqParams.KeyName
}
if reqParams.PageNo > 0 {
if reqParams.PageNo > 2147483647 {
return nil, errors.New("PageNo should be min 0 or max 2147483647")
}
params["pageNo"] = strconv.Itoa(reqParams.PageNo)
}
if reqParams.PageSize > 0 {
if reqParams.PageSize > 2147483647 {
return nil, errors.New("PageSize should be min 0 or max 2147483647")
}
params["pageSize"] = strconv.Itoa(reqParams.PageSize)
}
return params, nil
}
// GetLoginKeyList get login key list
func (s *Conn) GetLoginKeyList(reqParams *RequestGetLoginKeyList) (*LoginKeyList, error) {
params, err := processGetLoginKeyListParams(reqParams)
if err != nil {
return nil, err
}
params["action"] = "getLoginKeyList"
bytes, resp, err := request.NewRequest(s.accessKey, s.secretKey, "GET", s.apiURL+"server/", params)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
responseError, err := common.ParseErrorResponse(bytes)
if err != nil {
return nil, err
}
respError := LoginKeyList{}
respError.ReturnCode = responseError.ReturnCode
respError.ReturnMessage = responseError.ReturnMessage
return &respError, fmt.Errorf("%s %s - error code: %d , error message: %s", resp.Status, string(bytes), responseError.ReturnCode, responseError.ReturnMessage)
}
loginKeyList := LoginKeyList{}
if err := xml.Unmarshal([]byte(bytes), &loginKeyList); err != nil {
return nil, err
}
return &loginKeyList, nil
}

View File

@ -0,0 +1,87 @@
package sdk
import (
"encoding/xml"
"fmt"
"strconv"
common "github.com/NaverCloudPlatform/ncloud-sdk-go/common"
request "github.com/NaverCloudPlatform/ncloud-sdk-go/request"
)
func processGetMemberServerImageListParams(reqParams *RequestServerImageList) (map[string]string, error) {
params := make(map[string]string)
if reqParams == nil {
return params, nil
}
if len(reqParams.MemberServerImageNoList) > 0 {
for k, v := range reqParams.MemberServerImageNoList {
params[fmt.Sprintf("memberServerImageNoList.%d", k+1)] = v
}
}
if len(reqParams.PlatformTypeCodeList) > 0 {
for k, v := range reqParams.PlatformTypeCodeList {
params[fmt.Sprintf("platformTypeCodeList.%d", k+1)] = v
}
}
if reqParams.PageNo != 0 {
params["pageNo"] = strconv.Itoa(reqParams.PageNo)
}
if reqParams.PageSize != 0 {
params["pageSize"] = strconv.Itoa(reqParams.PageSize)
}
if reqParams.RegionNo != "" {
params["regionNo"] = reqParams.RegionNo
}
if reqParams.SortedBy != "" {
params["sortedBy"] = reqParams.SortedBy
}
if reqParams.SortingOrder != "" {
params["sortingOrder"] = reqParams.SortingOrder
}
return params, nil
}
// GetMemberServerImageList get member server image list
func (s *Conn) GetMemberServerImageList(reqParams *RequestServerImageList) (*MemberServerImageList, error) {
params, err := processGetMemberServerImageListParams(reqParams)
if err != nil {
return nil, err
}
params["action"] = "getMemberServerImageList"
bytes, resp, err := request.NewRequest(s.accessKey, s.secretKey, "GET", s.apiURL+"server/", params)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
responseError, err := common.ParseErrorResponse(bytes)
if err != nil {
return nil, err
}
respError := MemberServerImageList{}
respError.ReturnCode = responseError.ReturnCode
respError.ReturnMessage = responseError.ReturnMessage
return &respError, fmt.Errorf("%s %s - error code: %d , error message: %s", resp.Status, string(bytes), responseError.ReturnCode, responseError.ReturnMessage)
}
var serverImageListsResp = MemberServerImageList{}
if err := xml.Unmarshal([]byte(bytes), &serverImageListsResp); err != nil {
return nil, err
}
return &serverImageListsResp, nil
}

View File

@ -0,0 +1,130 @@
package sdk
import (
"encoding/xml"
"errors"
"fmt"
"strconv"
"strings"
common "github.com/NaverCloudPlatform/ncloud-sdk-go/common"
request "github.com/NaverCloudPlatform/ncloud-sdk-go/request"
)
func processGetPublicIPInstanceListParams(reqParams *RequestPublicIPInstanceList) (map[string]string, error) {
params := make(map[string]string)
if reqParams == nil {
return params, nil
}
if reqParams.IsAssociated {
params["isAssociated"] = "true"
}
if len(reqParams.PublicIPInstanceNoList) > 0 {
for k, v := range reqParams.PublicIPInstanceNoList {
params[fmt.Sprintf("publicIpInstanceNoList.%d", k+1)] = v
}
}
if len(reqParams.PublicIPList) > 0 {
for k, v := range reqParams.PublicIPList {
if len(reqParams.PublicIPList) < 5 || len(reqParams.PublicIPList) > 15 {
return nil, errors.New("PublicIPList must be between 5 and 15 characters in length")
}
params[fmt.Sprintf("publicIpList.%d", k+1)] = v
}
}
if reqParams.SearchFilterName != "" {
if reqParams.SearchFilterName != "publicIp" && reqParams.SearchFilterName != "associatedServerName" {
return nil, errors.New("SearchFilterName must be publicIp or associatedServerName")
}
params["searchFilterName"] = reqParams.SearchFilterName
}
if reqParams.SearchFilterValue != "" {
params["searchFilterValue"] = reqParams.SearchFilterValue
}
if reqParams.InternetLineTypeCode != "" {
if reqParams.InternetLineTypeCode != "PUBLC" && reqParams.InternetLineTypeCode != "GLBL" {
return params, errors.New("InternetLineTypeCode must be PUBLC or GLBL")
}
params["internetLineTypeCode"] = reqParams.InternetLineTypeCode
}
if reqParams.RegionNo != "" {
params["regionNo"] = reqParams.RegionNo
}
if reqParams.PageNo != 0 {
if reqParams.PageNo > 2147483647 {
return nil, errors.New("PageNo must be up to 2147483647")
}
params["pageNo"] = strconv.Itoa(reqParams.PageNo)
}
if reqParams.PageSize != 0 {
if reqParams.PageSize > 2147483647 {
return nil, errors.New("PageSize must be up to 2147483647")
}
params["pageSize"] = strconv.Itoa(reqParams.PageSize)
}
if reqParams.SortedBy != "" {
if strings.EqualFold(reqParams.SortedBy, "publicIp") || strings.EqualFold(reqParams.SortedBy, "publicIpInstanceNo") {
params["sortedBy"] = reqParams.SortedBy
} else {
return nil, errors.New("SortedBy must be publicIp or publicIpInstanceNo")
}
}
if reqParams.SortingOrder != "" {
if strings.EqualFold(reqParams.SortingOrder, "ascending") || strings.EqualFold(reqParams.SortingOrder, "descending") {
params["sortingOrder"] = reqParams.SortingOrder
} else {
return nil, errors.New("SortingOrder must be ascending or descending")
}
}
return params, nil
}
// GetPublicIPInstanceList get public ip instance list
func (s *Conn) GetPublicIPInstanceList(reqParams *RequestPublicIPInstanceList) (*PublicIPInstanceList, error) {
params, err := processGetPublicIPInstanceListParams(reqParams)
if err != nil {
return nil, err
}
params["action"] = "getPublicIpInstanceList"
bytes, resp, err := request.NewRequest(s.accessKey, s.secretKey, "GET", s.apiURL+"server/", params)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
responseError, err := common.ParseErrorResponse(bytes)
if err != nil {
return nil, err
}
respError := PublicIPInstanceList{}
respError.ReturnCode = responseError.ReturnCode
respError.ReturnMessage = responseError.ReturnMessage
return &respError, fmt.Errorf("%s %s - error code: %d , error message: %s", resp.Status, string(bytes), responseError.ReturnCode, responseError.ReturnMessage)
}
var publicIPInstanceList = PublicIPInstanceList{}
if err := xml.Unmarshal([]byte(bytes), &publicIPInstanceList); err != nil {
return nil, err
}
return &publicIPInstanceList, nil
}

View File

@ -0,0 +1,40 @@
package sdk
import (
"encoding/xml"
"fmt"
common "github.com/NaverCloudPlatform/ncloud-sdk-go/common"
request "github.com/NaverCloudPlatform/ncloud-sdk-go/request"
)
// GetRegionList gets region list
func (s *Conn) GetRegionList() (*RegionList, error) {
params := make(map[string]string)
params["action"] = "getRegionList"
bytes, resp, err := request.NewRequest(s.accessKey, s.secretKey, "GET", s.apiURL+"server/", params)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
responseError, err := common.ParseErrorResponse(bytes)
if err != nil {
return nil, err
}
respError := RegionList{}
respError.ReturnCode = responseError.ReturnCode
respError.ReturnMessage = responseError.ReturnMessage
return &respError, fmt.Errorf("%s %s - error code: %d , error message: %s", resp.Status, string(bytes), responseError.ReturnCode, responseError.ReturnMessage)
}
regionList := RegionList{}
if err := xml.Unmarshal([]byte(bytes), &regionList); err != nil {
return nil, err
}
return &regionList, nil
}

View File

@ -0,0 +1,67 @@
package sdk
import (
"encoding/xml"
"errors"
"fmt"
"strings"
common "github.com/NaverCloudPlatform/ncloud-sdk-go/common"
request "github.com/NaverCloudPlatform/ncloud-sdk-go/request"
)
func processGetRootPasswordParams(reqParams *RequestGetRootPassword) (map[string]string, error) {
params := make(map[string]string)
if reqParams.ServerInstanceNo == "" {
return params, errors.New("Required field is not specified. location : serverInstanceNo")
}
if reqParams.PrivateKey == "" {
return params, errors.New("Required field is not specified. location : privateKey")
}
params["serverInstanceNo"] = reqParams.ServerInstanceNo
params["privateKey"] = reqParams.PrivateKey
return params, nil
}
// GetRootPassword get root password from server instance
func (s *Conn) GetRootPassword(reqParams *RequestGetRootPassword) (*RootPassword, error) {
params, err := processGetRootPasswordParams(reqParams)
if err != nil {
return nil, err
}
params["action"] = "getRootPassword"
bytes, resp, err := request.NewRequest(s.accessKey, s.secretKey, "GET", s.apiURL+"server/", params)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
responseError, err := common.ParseErrorResponse(bytes)
if err != nil {
return nil, err
}
respError := RootPassword{}
respError.ReturnCode = responseError.ReturnCode
respError.ReturnMessage = responseError.ReturnMessage
return &respError, fmt.Errorf("%s %s - error code: %d , error message: %s", resp.Status, string(bytes), responseError.ReturnCode, responseError.ReturnMessage)
}
responseGetRootPassword := RootPassword{}
if err := xml.Unmarshal([]byte(bytes), &responseGetRootPassword); err != nil {
return nil, err
}
if responseGetRootPassword.RootPassword != "" {
responseGetRootPassword.RootPassword = strings.TrimSpace(responseGetRootPassword.RootPassword)
}
return &responseGetRootPassword, nil
}

View File

@ -0,0 +1,88 @@
package sdk
import (
"encoding/xml"
"errors"
"fmt"
"strconv"
common "github.com/NaverCloudPlatform/ncloud-sdk-go/common"
request "github.com/NaverCloudPlatform/ncloud-sdk-go/request"
)
func processGetServerImageProductListParams(reqParams *RequestGetServerImageProductList) (map[string]string, error) {
params := make(map[string]string)
if reqParams == nil {
return params, nil
}
if reqParams.ExclusionProductCode != "" {
if len(reqParams.ExclusionProductCode) > 20 {
return params, errors.New("Length of exclusionProductCode should be max 20")
}
params["exclusionProductCode"] = reqParams.ExclusionProductCode
}
if reqParams.ProductCode != "" {
if len(reqParams.ProductCode) > 20 {
return params, errors.New("Length of productCode should be max 20")
}
params["productCode"] = reqParams.ProductCode
}
if len(reqParams.PlatformTypeCodeList) > 0 {
for k, v := range reqParams.PlatformTypeCodeList {
params[fmt.Sprintf("platformTypeCodeList.%d", k+1)] = v
}
}
if reqParams.BlockStorageSize > 0 {
if reqParams.BlockStorageSize != 50 && reqParams.BlockStorageSize != 100 {
return nil, errors.New("blockStorageSize should be null, 50 or 100")
}
params["blockStorageSize"] = strconv.Itoa(reqParams.BlockStorageSize)
}
if reqParams.RegionNo != "" {
params["regionNo"] = reqParams.RegionNo
}
return params, nil
}
// GetServerImageProductList gets server image product list
func (s *Conn) GetServerImageProductList(reqParams *RequestGetServerImageProductList) (*ProductList, error) {
params, err := processGetServerImageProductListParams(reqParams)
if err != nil {
return nil, err
}
params["action"] = "getServerImageProductList"
bytes, resp, err := request.NewRequest(s.accessKey, s.secretKey, "GET", s.apiURL+"server/", params)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
responseError, err := common.ParseErrorResponse(bytes)
if err != nil {
return nil, err
}
respError := ProductList{}
respError.ReturnCode = responseError.ReturnCode
respError.ReturnMessage = responseError.ReturnMessage
return &respError, fmt.Errorf("%s %s - error code: %d , error message: %s", resp.Status, string(bytes), responseError.ReturnCode, responseError.ReturnMessage)
}
var productList = ProductList{}
if err := xml.Unmarshal([]byte(bytes), &productList); err != nil {
fmt.Println(err)
return nil, err
}
return &productList, nil
}

View File

@ -0,0 +1,133 @@
package sdk
import (
"encoding/xml"
"errors"
"fmt"
"strconv"
common "github.com/NaverCloudPlatform/ncloud-sdk-go/common"
request "github.com/NaverCloudPlatform/ncloud-sdk-go/request"
)
func processGetServerInstanceListParams(reqParams *RequestGetServerInstanceList) (map[string]string, error) {
params := make(map[string]string)
if reqParams == nil {
return params, nil
}
if len(reqParams.ServerInstanceNoList) > 0 {
for k, v := range reqParams.ServerInstanceNoList {
params[fmt.Sprintf("serverInstanceNoList.%d", k+1)] = v
}
}
if reqParams.SearchFilterName != "" {
params["searchFilterName"] = reqParams.SearchFilterName
}
if reqParams.SearchFilterValue != "" {
params["searchFilterValue"] = reqParams.SearchFilterValue
}
if reqParams.PageNo > 0 {
if reqParams.PageNo > 2147483647 {
return nil, errors.New("PageNo should be less than 2147483647")
}
params["pageNo"] = strconv.Itoa(reqParams.PageNo)
}
if reqParams.PageSize > 0 {
if reqParams.PageSize > 2147483647 {
return nil, errors.New("PageSize should be less than 2147483647")
}
params["pageSize"] = strconv.Itoa(reqParams.PageSize)
}
if reqParams.ServerInstanceStatusCode != "" {
if reqParams.ServerInstanceStatusCode != "RUN" && reqParams.ServerInstanceStatusCode != "NSTOP" && reqParams.ServerInstanceStatusCode != "ING" {
return nil, errors.New("ServerInstanceStatusCode should be RUN, NSTOP or ING")
}
params["serverInstanceStatusCode"] = reqParams.ServerInstanceStatusCode
}
if reqParams.InternetLineTypeCode != "" {
if reqParams.InternetLineTypeCode != "PUBLC" && reqParams.InternetLineTypeCode != "GLBL" {
return nil, errors.New("InternetLineTypeCode should be PUBLC or GLBL")
}
params["internetLineTypeCode"] = reqParams.InternetLineTypeCode
}
if reqParams.RegionNo != "" {
params["regionNo"] = reqParams.RegionNo
}
if reqParams.BaseBlockStorageDiskTypeCode != "" {
if reqParams.BaseBlockStorageDiskTypeCode != "NET" && reqParams.BaseBlockStorageDiskTypeCode != "LOCAL" {
return nil, errors.New("BaseBlockStorageDiskTypeCode should be NET or LOCAL")
}
params["baseBlockStorageDiskTypeCode"] = reqParams.BaseBlockStorageDiskTypeCode
}
if reqParams.BaseBlockStorageDiskDetailTypeCode != "" {
if reqParams.BaseBlockStorageDiskDetailTypeCode != "HDD" && reqParams.BaseBlockStorageDiskDetailTypeCode != "SSD" {
return nil, errors.New("BaseBlockStorageDiskDetailTypeCode should be HDD or SSD")
}
params["baseBlockStorageDiskDetailTypeCode"] = reqParams.BaseBlockStorageDiskDetailTypeCode
}
if reqParams.SortedBy != "" {
if reqParams.SortedBy != "serverName" && reqParams.SortedBy != "serverInstanceNo" {
return nil, errors.New("SortedBy should be serverName or serverInstanceNo")
}
params["sortedBy"] = reqParams.SortedBy
}
if reqParams.SortingOrder != "" {
if reqParams.SortingOrder != "ascending" && reqParams.SortingOrder != "descending" {
return nil, errors.New("SortingOrder should be ascending or descending")
}
params["sortingOrder"] = reqParams.SortingOrder
}
return params, nil
}
// GetServerInstanceList get server instance list
func (s *Conn) GetServerInstanceList(reqParams *RequestGetServerInstanceList) (*ServerInstanceList, error) {
params, err := processGetServerInstanceListParams(reqParams)
if err != nil {
return nil, err
}
params["action"] = "getServerInstanceList"
bytes, resp, err := request.NewRequest(s.accessKey, s.secretKey, "GET", s.apiURL+"server/", params)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
responseError, err := common.ParseErrorResponse(bytes)
if err != nil {
return nil, err
}
respError := ServerInstanceList{}
respError.ReturnCode = responseError.ReturnCode
respError.ReturnMessage = responseError.ReturnMessage
return &respError, fmt.Errorf("%s %s - error code: %d , error message: %s", resp.Status, string(bytes), responseError.ReturnCode, responseError.ReturnMessage)
}
var serverInstanceList = ServerInstanceList{}
if err := xml.Unmarshal([]byte(bytes), &serverInstanceList); err != nil {
fmt.Println(err)
return nil, err
}
return &serverInstanceList, nil
}

View File

@ -0,0 +1,79 @@
package sdk
import (
"encoding/xml"
"errors"
"fmt"
common "github.com/NaverCloudPlatform/ncloud-sdk-go/common"
request "github.com/NaverCloudPlatform/ncloud-sdk-go/request"
)
func processGetServerProductListParams(reqParams *RequestGetServerProductList) (map[string]string, error) {
params := make(map[string]string)
if reqParams == nil || reqParams.ServerImageProductCode == "" {
return params, errors.New("ServerImageProductCode field is required")
}
if len(reqParams.ServerImageProductCode) > 20 {
return params, errors.New("Length of serverImageProductCode should be max 20")
}
params["serverImageProductCode"] = reqParams.ServerImageProductCode
if reqParams.ExclusionProductCode != "" {
if len(reqParams.ExclusionProductCode) > 20 {
return params, errors.New("Length of exclusionProductCode should be max 20")
}
params["exclusionProductCode"] = reqParams.ExclusionProductCode
}
if reqParams.ProductCode != "" {
if len(reqParams.ProductCode) > 20 {
return params, errors.New("Length of productCode should be max 20")
}
params["productCode"] = reqParams.ProductCode
}
if reqParams.RegionNo != "" {
params["regionNo"] = reqParams.RegionNo
}
return params, nil
}
// GetServerProductList : Get Server product list with server image product code by default.
func (s *Conn) GetServerProductList(reqParams *RequestGetServerProductList) (*ProductList, error) {
params, err := processGetServerProductListParams(reqParams)
if err != nil {
return nil, err
}
params["action"] = "getServerProductList"
bytes, resp, err := request.NewRequest(s.accessKey, s.secretKey, "GET", s.apiURL+"server/", params)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
responseError, err := common.ParseErrorResponse(bytes)
if err != nil {
return nil, err
}
respError := ProductList{}
respError.ReturnCode = responseError.ReturnCode
respError.ReturnMessage = responseError.ReturnMessage
return &respError, fmt.Errorf("%s %s - error code: %d , error message: %s", resp.Status, string(bytes), responseError.ReturnCode, responseError.ReturnMessage)
}
var productListResp = ProductList{}
if err := xml.Unmarshal([]byte(bytes), &productListResp); err != nil {
return nil, err
}
return &productListResp, nil
}

View File

@ -0,0 +1,44 @@
package sdk
import (
"encoding/xml"
"fmt"
common "github.com/NaverCloudPlatform/ncloud-sdk-go/common"
request "github.com/NaverCloudPlatform/ncloud-sdk-go/request"
)
// GetZoneList get zone list
func (s *Conn) GetZoneList(regionNo string) (*ZoneList, error) {
params := make(map[string]string)
params["action"] = "getZoneList"
if regionNo != "" {
params["regionNo"] = regionNo
}
bytes, resp, err := request.NewRequest(s.accessKey, s.secretKey, "GET", s.apiURL+"server/", params)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
responseError, err := common.ParseErrorResponse(bytes)
if err != nil {
return nil, err
}
respError := ZoneList{}
respError.ReturnCode = responseError.ReturnCode
respError.ReturnMessage = responseError.ReturnMessage
return &respError, fmt.Errorf("%s %s - error code: %d , error message: %s", resp.Status, string(bytes), responseError.ReturnCode, responseError.ReturnMessage)
}
ZoneList := ZoneList{}
if err := xml.Unmarshal([]byte(bytes), &ZoneList); err != nil {
return nil, err
}
return &ZoneList, nil
}

View File

@ -0,0 +1,359 @@
package sdk
import (
common "github.com/NaverCloudPlatform/ncloud-sdk-go/common"
)
type Conn struct {
accessKey string
secretKey string
apiURL string
}
// ServerImage structures
type ServerImage struct {
MemberServerImageNo string `xml:"memberServerImageNo"`
MemberServerImageName string `xml:"memberServerImageName"`
MemberServerImageDescription string `xml:"memberServerImageDescription"`
OriginalServerInstanceNo string `xml:"originalServerInstanceNo"`
OriginalServerProductCode string `xml:"originalServerProductCode"`
OriginalServerName string `xml:"originalServerName"`
OriginalBaseBlockStorageDiskType common.CommonCode `xml:"originalBaseBlockStorageDiskType"`
OriginalServerImageProductCode string `xml:"originalServerImageProductCode"`
OriginalOsInformation string `xml:"originalOsInformation"`
OriginalServerImageName string `xml:"originalServerImageName"`
MemberServerImageStatusName string `xml:"memberServerImageStatusName"`
MemberServerImageStatus common.CommonCode `xml:"memberServerImageStatus"`
MemberServerImageOperation common.CommonCode `xml:"memberServerImageOperation"`
MemberServerImagePlatformType common.CommonCode `xml:"memberServerImagePlatformType"`
CreateDate string `xml:"createDate"`
Zone common.Zone `xml:"zone"`
Region common.Region `xml:"region"`
MemberServerImageBlockStorageTotalRows int `xml:"memberServerImageBlockStorageTotalRows"`
MemberServerImageBlockStorageTotalSize int `xml:"memberServerImageBlockStorageTotalSize"`
}
type MemberServerImageList struct {
common.CommonResponse
TotalRows int `xml:"totalRows"`
MemberServerImageList []ServerImage `xml:"memberServerImageList>memberServerImage,omitempty"`
}
type RequestServerImageList struct {
MemberServerImageNoList []string
PlatformTypeCodeList []string
PageNo int
PageSize int
RegionNo string
SortedBy string
SortingOrder string
}
type RequestCreateServerImage struct {
MemberServerImageName string
MemberServerImageDescription string
ServerInstanceNo string
}
type RequestGetServerImageProductList struct {
ExclusionProductCode string
ProductCode string
PlatformTypeCodeList []string
BlockStorageSize int
RegionNo string
}
// ProductList : Response of server product list
type ProductList struct {
common.CommonResponse
TotalRows int `xml:"totalRows"`
Product []Product `xml:"productList>product,omitempty"`
}
// Product : Product information of Server
type Product struct {
ProductCode string `xml:"productCode"`
ProductName string `xml:"productName"`
ProductType common.CommonCode `xml:"productType"`
ProductDescription string `xml:"productDescription"`
InfraResourceType common.CommonCode `xml:"infraResourceType"`
CPUCount int `xml:"cpuCount"`
MemorySize int `xml:"memorySize"`
BaseBlockStorageSize int `xml:"baseBlockStorageSize"`
PlatformType common.CommonCode `xml:"platformType"`
OsInformation string `xml:"osInformation"`
AddBlockStroageSize int `xml:"addBlockStroageSize"`
}
// RequestCreateServerInstance is Server Instances structures
type RequestCreateServerInstance struct {
ServerImageProductCode string
ServerProductCode string
MemberServerImageNo string
ServerName string
ServerDescription string
LoginKeyName string
IsProtectServerTermination bool
ServerCreateCount int
ServerCreateStartNo int
InternetLineTypeCode string
FeeSystemTypeCode string
UserData string
ZoneNo string
AccessControlGroupConfigurationNoList []string
}
type ServerInstanceList struct {
common.CommonResponse
TotalRows int `xml:"totalRows"`
ServerInstanceList []ServerInstance `xml:"serverInstanceList>serverInstance,omitempty"`
}
type ServerInstance struct {
ServerInstanceNo string `xml:"serverInstanceNo"`
ServerName string `xml:"serverName"`
ServerDescription string `xml:"serverDescription"`
CPUCount int `xml:"cpuCount"`
MemorySize int `xml:"memorySize"`
BaseBlockStorageSize int `xml:"baseBlockStorageSize"`
PlatformType common.CommonCode `xml:"platformType"`
LoginKeyName string `xml:"loginKeyName"`
IsFeeChargingMonitoring bool `xml:"isFeeChargingMonitoring"`
PublicIP string `xml:"publicIp"`
PrivateIP string `xml:"privateIp"`
ServerImageName string `xml:"serverImageName"`
ServerInstanceStatus common.CommonCode `xml:"serverInstanceStatus"`
ServerInstanceOperation common.CommonCode `xml:"serverInstanceOperation"`
ServerInstanceStatusName string `xml:"serverInstanceStatusName"`
CreateDate string `xml:"createDate"`
Uptime string `xml:"uptime"`
ServerImageProductCode string `xml:"serverImageProductCode"`
ServerProductCode string `xml:"serverProductCode"`
IsProtectServerTermination bool `xml:"isProtectServerTermination"`
PortForwardingPublicIP string `xml:"portForwardingPublicIp"`
PortForwardingExternalPort int `xml:"portForwardingExternalPort"`
PortForwardingInternalPort int `xml:"portForwardingInternalPort"`
Zone common.Zone `xml:"zone"`
Region common.Region `xml:"region"`
BaseBlockStorageDiskType common.CommonCode `xml:"baseBlockStorageDiskType"`
BaseBlockStroageDiskDetailType common.CommonCode `xml:"baseBlockStroageDiskDetailType"`
InternetLineType common.CommonCode `xml:"internetLineType"`
UserData string `xml:"userData"`
AccessControlGroupList []AccessControlGroup `xml:"accessControlGroupList>accessControlGroup"`
}
type AccessControlGroup struct {
AccessControlGroupConfigurationNo string `xml:"accessControlGroupConfigurationNo"`
AccessControlGroupName string `xml:"accessControlGroupName"`
AccessControlGroupDescription string `xml:"accessControlGroupDescription"`
IsDefault bool `xml:"isDefault"`
CreateDate string `xml:"createDate"`
}
// RequestGetLoginKeyList is Login Key structures
type RequestGetLoginKeyList struct {
KeyName string
PageNo int
PageSize int
}
type LoginKeyList struct {
common.CommonResponse
TotalRows int `xml:"totalRows"`
LoginKeyList []LoginKey `xml:"loginKeyList>loginKey,omitempty"`
}
type LoginKey struct {
Fingerprint string `xml:"fingerprint"`
KeyName string `xml:"keyName"`
CreateDate string `xml:"createDate"`
}
type PrivateKey struct {
common.CommonResponse
PrivateKey string `xml:"privateKey"`
}
type RequestCreatePublicIPInstance struct {
ServerInstanceNo string
PublicIPDescription string
InternetLineTypeCode string
RegionNo string
}
type RequestPublicIPInstanceList struct {
IsAssociated bool
PublicIPInstanceNoList []string
PublicIPList []string
SearchFilterName string
SearchFilterValue string
InternetLineTypeCode string
RegionNo string
PageNo int
PageSize int
SortedBy string
SortingOrder string
}
type PublicIPInstanceList struct {
common.CommonResponse
TotalRows int `xml:"totalRows"`
PublicIPInstanceList []PublicIPInstance `xml:"publicIpInstanceList>publicIpInstance,omitempty"`
}
type PublicIPInstance struct {
PublicIPInstanceNo string `xml:"publicIpInstanceNo"`
PublicIP string `xml:"publicIp"`
PublicIPDescription string `xml:"publicIpDescription"`
CreateDate string `xml:"createDate"`
InternetLineType common.CommonCode `xml:"internetLineType"`
PublicIPInstanceStatusName string `xml:"publicIpInstanceStatusName"`
PublicIPInstanceStatus common.CommonCode `xml:"publicIpInstanceStatus"`
PublicIPInstanceOperation common.CommonCode `xml:"publicIpInstanceOperation"`
PublicIPKindType common.CommonCode `xml:"publicIpKindType"`
ServerInstance ServerInstance `xml:"serverInstanceAssociatedWithPublicIp"`
}
type RequestDeletePublicIPInstances struct {
PublicIPInstanceNoList []string
}
// RequestGetServerInstanceList : Get Server Instance List
type RequestGetServerInstanceList struct {
ServerInstanceNoList []string
SearchFilterName string
SearchFilterValue string
PageNo int
PageSize int
ServerInstanceStatusCode string
InternetLineTypeCode string
RegionNo string
BaseBlockStorageDiskTypeCode string
BaseBlockStorageDiskDetailTypeCode string
SortedBy string
SortingOrder string
}
type RequestStopServerInstances struct {
ServerInstanceNoList []string
}
type RequestTerminateServerInstances struct {
ServerInstanceNoList []string
}
// RequestGetRootPassword : Request to get root password of the server
type RequestGetRootPassword struct {
ServerInstanceNo string
PrivateKey string
}
// RootPassword : Response of getting root password of the server
type RootPassword struct {
common.CommonResponse
TotalRows int `xml:"totalRows"`
RootPassword string `xml:"rootPassword"`
}
// RequestGetZoneList : Request to get zone list
type RequestGetZoneList struct {
regionNo string
}
// ZoneList : Response of getting zone list
type ZoneList struct {
common.CommonResponse
TotalRows int `xml:"totalRows"`
Zone []common.Zone `xml:"zoneList>zone"`
}
// RegionList : Response of getting region list
type RegionList struct {
common.CommonResponse
TotalRows int `xml:"totalRows"`
RegionList []common.Region `xml:"regionList>region,omitempty"`
}
type RequestBlockStorageInstance struct {
BlockStorageName string
BlockStorageSize int
BlockStorageDescription string
ServerInstanceNo string
}
type RequestBlockStorageInstanceList struct {
ServerInstanceNo string
BlockStorageInstanceNoList []string
SearchFilterName string
SearchFilterValue string
BlockStorageTypeCodeList []string
PageNo int
PageSize int
BlockStorageInstanceStatusCode string
DiskTypeCode string
DiskDetailTypeCode string
RegionNo string
SortedBy string
SortingOrder string
}
type BlockStorageInstanceList struct {
common.CommonResponse
TotalRows int `xml:"totalRows"`
BlockStorageInstance []BlockStorageInstance `xml:"blockStorageInstanceList>blockStorageInstance,omitempty"`
}
type BlockStorageInstance struct {
BlockStorageInstanceNo string `xml:"blockStorageInstanceNo"`
ServerInstanceNo string `xml:"serverInstanceNo"`
ServerName string `xml:"serverName"`
BlockStorageType common.CommonCode `xml:"blockStorageType"`
BlockStorageName string `xml:"blockStorageName"`
BlockStorageSize int `xml:"blockStorageSize"`
DeviceName string `xml:"deviceName"`
BlockStorageProductCode string `xml:"blockStorageProductCode"`
BlockStorageInstanceStatus common.CommonCode `xml:"blockStorageInstanceStatus"`
BlockStorageInstanceOperation common.CommonCode `xml:"blockStorageInstanceOperation"`
BlockStorageInstanceStatusName string `xml:"blockStorageInstanceStatusName"`
CreateDate string `xml:"createDate"`
BlockStorageInstanceDescription string `xml:"blockStorageInstanceDescription"`
DiskType common.CommonCode `xml:"diskType"`
DiskDetailType common.CommonCode `xml:"diskDetailType"`
}
// RequestGetServerProductList : Request to get server product list
type RequestGetServerProductList struct {
ExclusionProductCode string
ProductCode string
ServerImageProductCode string
RegionNo string
}
type RequestAccessControlGroupList struct {
AccessControlGroupConfigurationNoList []string
IsDefault bool
AccessControlGroupName string
PageNo int
PageSize int
}
type AccessControlGroupList struct {
common.CommonResponse
TotalRows int `xml:"totalRows"`
AccessControlGroup []AccessControlGroup `xml:"accessControlGroupList>accessControlGroup,omitempty"`
}
type AccessControlRuleList struct {
common.CommonResponse
TotalRows int `xml:"totalRows"`
AccessControlRuleList []AccessControlRule `xml:"accessControlRuleList>accessControlRule,omitempty"`
}
type AccessControlRule struct {
AccessControlRuleConfigurationNo string `xml:"accessControlRuleConfigurationNo"`
AccessControlRuleDescription string `xml:"accessControlRuleDescription"`
SourceAccessControlRuleConfigurationNo string `xml:"sourceAccessControlRuleConfigurationNo"`
SourceAccessControlRuleName string `xml:"sourceAccessControlRuleName"`
ProtocolType common.CommonCode `xml:"protocolType"`
SourceIP string `xml:"sourceIp"`
DestinationPort string `xml:"destinationPort"`
}

View File

@ -0,0 +1,62 @@
package sdk
import (
"encoding/xml"
"errors"
"fmt"
common "github.com/NaverCloudPlatform/ncloud-sdk-go/common"
request "github.com/NaverCloudPlatform/ncloud-sdk-go/request"
)
func processStopServerInstancesParams(reqParams *RequestStopServerInstances) (map[string]string, error) {
params := make(map[string]string)
if reqParams == nil || len(reqParams.ServerInstanceNoList) == 0 {
return params, errors.New("serverInstanceNoList is required")
}
if len(reqParams.ServerInstanceNoList) > 0 {
for k, v := range reqParams.ServerInstanceNoList {
params[fmt.Sprintf("serverInstanceNoList.%d", k+1)] = v
}
}
return params, nil
}
// StopServerInstances stop server instances
func (s *Conn) StopServerInstances(reqParams *RequestStopServerInstances) (*ServerInstanceList, error) {
params, err := processStopServerInstancesParams(reqParams)
if err != nil {
return nil, err
}
params["action"] = "stopServerInstances"
bytes, resp, err := request.NewRequest(s.accessKey, s.secretKey, "GET", s.apiURL+"server/", params)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
responseError, err := common.ParseErrorResponse(bytes)
if err != nil {
return nil, err
}
respError := ServerInstanceList{}
respError.ReturnCode = responseError.ReturnCode
respError.ReturnMessage = responseError.ReturnMessage
return &respError, fmt.Errorf("%s %s - error code: %d , error message: %s", resp.Status, string(bytes), responseError.ReturnCode, responseError.ReturnMessage)
}
var serverInstanceList = ServerInstanceList{}
if err := xml.Unmarshal([]byte(bytes), &serverInstanceList); err != nil {
fmt.Println(err)
return nil, err
}
return &serverInstanceList, nil
}

View File

@ -0,0 +1,62 @@
package sdk
import (
"encoding/xml"
"errors"
"fmt"
common "github.com/NaverCloudPlatform/ncloud-sdk-go/common"
request "github.com/NaverCloudPlatform/ncloud-sdk-go/request"
)
func processTerminateServerInstancesParams(reqParams *RequestTerminateServerInstances) (map[string]string, error) {
params := make(map[string]string)
if reqParams == nil || len(reqParams.ServerInstanceNoList) == 0 {
return params, errors.New("serverInstanceNoList is required")
}
if len(reqParams.ServerInstanceNoList) > 0 {
for k, v := range reqParams.ServerInstanceNoList {
params[fmt.Sprintf("serverInstanceNoList.%d", k+1)] = v
}
}
return params, nil
}
// TerminateServerInstances terminate server instances
func (s *Conn) TerminateServerInstances(reqParams *RequestTerminateServerInstances) (*ServerInstanceList, error) {
params, err := processTerminateServerInstancesParams(reqParams)
if err != nil {
return nil, err
}
params["action"] = "terminateServerInstances"
bytes, resp, err := request.NewRequest(s.accessKey, s.secretKey, "GET", s.apiURL+"server/", params)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
responseError, err := common.ParseErrorResponse(bytes)
if err != nil {
return nil, err
}
respError := ServerInstanceList{}
respError.ReturnCode = responseError.ReturnCode
respError.ReturnMessage = responseError.ReturnMessage
return &respError, fmt.Errorf("%s %s - error code: %d , error message: %s", resp.Status, string(bytes), responseError.ReturnCode, responseError.ReturnMessage)
}
var serverInstanceList = ServerInstanceList{}
if err := xml.Unmarshal([]byte(bytes), &serverInstanceList); err != nil {
fmt.Println(err)
return nil, err
}
return &serverInstanceList, nil
}

21
vendor/github.com/mattn/go-runewidth/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Yasuhiro Matsumoto
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

27
vendor/github.com/mattn/go-runewidth/README.mkd generated vendored Normal file
View File

@ -0,0 +1,27 @@
go-runewidth
============
[![Build Status](https://travis-ci.org/mattn/go-runewidth.png?branch=master)](https://travis-ci.org/mattn/go-runewidth)
[![Coverage Status](https://coveralls.io/repos/mattn/go-runewidth/badge.png?branch=HEAD)](https://coveralls.io/r/mattn/go-runewidth?branch=HEAD)
[![GoDoc](https://godoc.org/github.com/mattn/go-runewidth?status.svg)](http://godoc.org/github.com/mattn/go-runewidth)
[![Go Report Card](https://goreportcard.com/badge/github.com/mattn/go-runewidth)](https://goreportcard.com/report/github.com/mattn/go-runewidth)
Provides functions to get fixed width of the character or string.
Usage
-----
```go
runewidth.StringWidth("つのだ☆HIRO") == 12
```
Author
------
Yasuhiro Matsumoto
License
-------
under the MIT License: http://mattn.mit-license.org/2013

1224
vendor/github.com/mattn/go-runewidth/runewidth.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

8
vendor/github.com/mattn/go-runewidth/runewidth_js.go generated vendored Normal file
View File

@ -0,0 +1,8 @@
// +build js
package runewidth
func IsEastAsian() bool {
// TODO: Implement this for the web. Detect east asian in a compatible way, and return true.
return false
}

View File

@ -0,0 +1,77 @@
// +build !windows,!js
package runewidth
import (
"os"
"regexp"
"strings"
)
var reLoc = regexp.MustCompile(`^[a-z][a-z][a-z]?(?:_[A-Z][A-Z])?\.(.+)`)
var mblenTable = map[string]int{
"utf-8": 6,
"utf8": 6,
"jis": 8,
"eucjp": 3,
"euckr": 2,
"euccn": 2,
"sjis": 2,
"cp932": 2,
"cp51932": 2,
"cp936": 2,
"cp949": 2,
"cp950": 2,
"big5": 2,
"gbk": 2,
"gb2312": 2,
}
func isEastAsian(locale string) bool {
charset := strings.ToLower(locale)
r := reLoc.FindStringSubmatch(locale)
if len(r) == 2 {
charset = strings.ToLower(r[1])
}
if strings.HasSuffix(charset, "@cjk_narrow") {
return false
}
for pos, b := range []byte(charset) {
if b == '@' {
charset = charset[:pos]
break
}
}
max := 1
if m, ok := mblenTable[charset]; ok {
max = m
}
if max > 1 && (charset[0] != 'u' ||
strings.HasPrefix(locale, "ja") ||
strings.HasPrefix(locale, "ko") ||
strings.HasPrefix(locale, "zh")) {
return true
}
return false
}
// IsEastAsian return true if the current locale is CJK
func IsEastAsian() bool {
locale := os.Getenv("LC_CTYPE")
if locale == "" {
locale = os.Getenv("LANG")
}
// ignore C locale
if locale == "POSIX" || locale == "C" {
return false
}
if len(locale) > 1 && locale[0] == 'C' && (locale[1] == '.' || locale[1] == '-') {
return false
}
return isEastAsian(locale)
}

View File

@ -0,0 +1,25 @@
package runewidth
import (
"syscall"
)
var (
kernel32 = syscall.NewLazyDLL("kernel32")
procGetConsoleOutputCP = kernel32.NewProc("GetConsoleOutputCP")
)
// IsEastAsian return true if the current locale is CJK
func IsEastAsian() bool {
r1, _, _ := procGetConsoleOutputCP.Call()
if r1 == 0 {
return false
}
switch int(r1) {
case 932, 51932, 936, 949, 950:
return true
}
return false
}

19
vendor/github.com/olekukonko/tablewriter/LICENCE.md generated vendored Normal file
View File

@ -0,0 +1,19 @@
Copyright (C) 2014 by Oleku Konko
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

279
vendor/github.com/olekukonko/tablewriter/README.md generated vendored Normal file
View File

@ -0,0 +1,279 @@
ASCII Table Writer
=========
[![Build Status](https://travis-ci.org/olekukonko/tablewriter.png?branch=master)](https://travis-ci.org/olekukonko/tablewriter)
[![Total views](https://img.shields.io/sourcegraph/rrc/github.com/olekukonko/tablewriter.svg)](https://sourcegraph.com/github.com/olekukonko/tablewriter)
[![Godoc](https://godoc.org/github.com/olekukonko/tablewriter?status.svg)](https://godoc.org/github.com/olekukonko/tablewriter)
Generate ASCII table on the fly ... Installation is simple as
go get github.com/olekukonko/tablewriter
#### Features
- Automatic Padding
- Support Multiple Lines
- Supports Alignment
- Support Custom Separators
- Automatic Alignment of numbers & percentage
- Write directly to http , file etc via `io.Writer`
- Read directly from CSV file
- Optional row line via `SetRowLine`
- Normalise table header
- Make CSV Headers optional
- Enable or disable table border
- Set custom footer support
- Optional identical cells merging
- Set custom caption
#### Example 1 - Basic
```go
data := [][]string{
[]string{"A", "The Good", "500"},
[]string{"B", "The Very very Bad Man", "288"},
[]string{"C", "The Ugly", "120"},
[]string{"D", "The Gopher", "800"},
}
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Name", "Sign", "Rating"})
for _, v := range data {
table.Append(v)
}
table.Render() // Send output
```
##### Output 1
```
+------+-----------------------+--------+
| NAME | SIGN | RATING |
+------+-----------------------+--------+
| A | The Good | 500 |
| B | The Very very Bad Man | 288 |
| C | The Ugly | 120 |
| D | The Gopher | 800 |
+------+-----------------------+--------+
```
#### Example 2 - Without Border / Footer / Bulk Append
```go
data := [][]string{
[]string{"1/1/2014", "Domain name", "2233", "$10.98"},
[]string{"1/1/2014", "January Hosting", "2233", "$54.95"},
[]string{"1/4/2014", "February Hosting", "2233", "$51.00"},
[]string{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"},
}
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
table.SetFooter([]string{"", "", "Total", "$146.93"}) // Add Footer
table.SetBorder(false) // Set Border to false
table.AppendBulk(data) // Add Bulk Data
table.Render()
```
##### Output 2
```
DATE | DESCRIPTION | CV2 | AMOUNT
+----------+--------------------------+-------+---------+
1/1/2014 | Domain name | 2233 | $10.98
1/1/2014 | January Hosting | 2233 | $54.95
1/4/2014 | February Hosting | 2233 | $51.00
1/4/2014 | February Extra Bandwidth | 2233 | $30.00
+----------+--------------------------+-------+---------+
TOTAL | $146 93
+-------+---------+
```
#### Example 3 - CSV
```go
table, _ := tablewriter.NewCSV(os.Stdout, "test_info.csv", true)
table.SetAlignment(tablewriter.ALIGN_LEFT) // Set Alignment
table.Render()
```
##### Output 3
```
+----------+--------------+------+-----+---------+----------------+
| FIELD | TYPE | NULL | KEY | DEFAULT | EXTRA |
+----------+--------------+------+-----+---------+----------------+
| user_id | smallint(5) | NO | PRI | NULL | auto_increment |
| username | varchar(10) | NO | | NULL | |
| password | varchar(100) | NO | | NULL | |
+----------+--------------+------+-----+---------+----------------+
```
#### Example 4 - Custom Separator
```go
table, _ := tablewriter.NewCSV(os.Stdout, "test.csv", true)
table.SetRowLine(true) // Enable row line
// Change table lines
table.SetCenterSeparator("*")
table.SetColumnSeparator("‡")
table.SetRowSeparator("-")
table.SetAlignment(tablewriter.ALIGN_LEFT)
table.Render()
```
##### Output 4
```
*------------*-----------*---------*
╪ FIRST NAME ╪ LAST NAME ╪ SSN ╪
*------------*-----------*---------*
╪ John ╪ Barry ╪ 123456 ╪
*------------*-----------*---------*
╪ Kathy ╪ Smith ╪ 687987 ╪
*------------*-----------*---------*
╪ Bob ╪ McCornick ╪ 3979870 ╪
*------------*-----------*---------*
```
#### Example 5 - Markdown Format
```go
data := [][]string{
[]string{"1/1/2014", "Domain name", "2233", "$10.98"},
[]string{"1/1/2014", "January Hosting", "2233", "$54.95"},
[]string{"1/4/2014", "February Hosting", "2233", "$51.00"},
[]string{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"},
}
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
table.SetCenterSeparator("|")
table.AppendBulk(data) // Add Bulk Data
table.Render()
```
##### Output 5
```
| DATE | DESCRIPTION | CV2 | AMOUNT |
|----------|--------------------------|------|--------|
| 1/1/2014 | Domain name | 2233 | $10.98 |
| 1/1/2014 | January Hosting | 2233 | $54.95 |
| 1/4/2014 | February Hosting | 2233 | $51.00 |
| 1/4/2014 | February Extra Bandwidth | 2233 | $30.00 |
```
#### Example 6 - Identical cells merging
```go
data := [][]string{
[]string{"1/1/2014", "Domain name", "1234", "$10.98"},
[]string{"1/1/2014", "January Hosting", "2345", "$54.95"},
[]string{"1/4/2014", "February Hosting", "3456", "$51.00"},
[]string{"1/4/2014", "February Extra Bandwidth", "4567", "$30.00"},
}
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
table.SetFooter([]string{"", "", "Total", "$146.93"})
table.SetAutoMergeCells(true)
table.SetRowLine(true)
table.AppendBulk(data)
table.Render()
```
##### Output 6
```
+----------+--------------------------+-------+---------+
| DATE | DESCRIPTION | CV2 | AMOUNT |
+----------+--------------------------+-------+---------+
| 1/1/2014 | Domain name | 1234 | $10.98 |
+ +--------------------------+-------+---------+
| | January Hosting | 2345 | $54.95 |
+----------+--------------------------+-------+---------+
| 1/4/2014 | February Hosting | 3456 | $51.00 |
+ +--------------------------+-------+---------+
| | February Extra Bandwidth | 4567 | $30.00 |
+----------+--------------------------+-------+---------+
| TOTAL | $146 93 |
+----------+--------------------------+-------+---------+
```
#### Table with color
```go
data := [][]string{
[]string{"1/1/2014", "Domain name", "2233", "$10.98"},
[]string{"1/1/2014", "January Hosting", "2233", "$54.95"},
[]string{"1/4/2014", "February Hosting", "2233", "$51.00"},
[]string{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"},
}
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
table.SetFooter([]string{"", "", "Total", "$146.93"}) // Add Footer
table.SetBorder(false) // Set Border to false
table.SetHeaderColor(tablewriter.Colors{tablewriter.Bold, tablewriter.BgGreenColor},
tablewriter.Colors{tablewriter.FgHiRedColor, tablewriter.Bold, tablewriter.BgBlackColor},
tablewriter.Colors{tablewriter.BgRedColor, tablewriter.FgWhiteColor},
tablewriter.Colors{tablewriter.BgCyanColor, tablewriter.FgWhiteColor})
table.SetColumnColor(tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiBlackColor},
tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiRedColor},
tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiBlackColor},
tablewriter.Colors{tablewriter.Bold, tablewriter.FgBlackColor})
table.SetFooterColor(tablewriter.Colors{}, tablewriter.Colors{},
tablewriter.Colors{tablewriter.Bold},
tablewriter.Colors{tablewriter.FgHiRedColor})
table.AppendBulk(data)
table.Render()
```
#### Table with color Output
![Table with Color](https://cloud.githubusercontent.com/assets/6460392/21101956/bbc7b356-c0a1-11e6-9f36-dba694746efc.png)
#### Example 6 - Set table caption
```go
data := [][]string{
[]string{"A", "The Good", "500"},
[]string{"B", "The Very very Bad Man", "288"},
[]string{"C", "The Ugly", "120"},
[]string{"D", "The Gopher", "800"},
}
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Name", "Sign", "Rating"})
table.SetCaption(true, "Movie ratings.")
for _, v := range data {
table.Append(v)
}
table.Render() // Send output
```
Note: Caption text will wrap with total width of rendered table.
##### Output 6
```
+------+-----------------------+--------+
| NAME | SIGN | RATING |
+------+-----------------------+--------+
| A | The Good | 500 |
| B | The Very very Bad Man | 288 |
| C | The Ugly | 120 |
| D | The Gopher | 800 |
+------+-----------------------+--------+
Movie ratings.
```
#### TODO
- ~~Import Directly from CSV~~ - `done`
- ~~Support for `SetFooter`~~ - `done`
- ~~Support for `SetBorder`~~ - `done`
- ~~Support table with uneven rows~~ - `done`
- Support custom alignment
- General Improvement & Optimisation
- `NewHTML` Parse table from HTML

52
vendor/github.com/olekukonko/tablewriter/csv.go generated vendored Normal file
View File

@ -0,0 +1,52 @@
// Copyright 2014 Oleku Konko All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.
// This module is a Table Writer API for the Go Programming Language.
// The protocols were written in pure Go and works on windows and unix systems
package tablewriter
import (
"encoding/csv"
"io"
"os"
)
// Start A new table by importing from a CSV file
// Takes io.Writer and csv File name
func NewCSV(writer io.Writer, fileName string, hasHeader bool) (*Table, error) {
file, err := os.Open(fileName)
if err != nil {
return &Table{}, err
}
defer file.Close()
csvReader := csv.NewReader(file)
t, err := NewCSVReader(writer, csvReader, hasHeader)
return t, err
}
// Start a New Table Writer with csv.Reader
// This enables customisation such as reader.Comma = ';'
// See http://golang.org/src/pkg/encoding/csv/reader.go?s=3213:3671#L94
func NewCSVReader(writer io.Writer, csvReader *csv.Reader, hasHeader bool) (*Table, error) {
t := NewWriter(writer)
if hasHeader {
// Read the first row
headers, err := csvReader.Read()
if err != nil {
return &Table{}, err
}
t.SetHeader(headers)
}
for {
record, err := csvReader.Read()
if err == io.EOF {
break
} else if err != nil {
return &Table{}, err
}
t.Append(record)
}
return t, nil
}

798
vendor/github.com/olekukonko/tablewriter/table.go generated vendored Normal file
View File

@ -0,0 +1,798 @@
// Copyright 2014 Oleku Konko All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.
// This module is a Table Writer API for the Go Programming Language.
// The protocols were written in pure Go and works on windows and unix systems
// Create & Generate text based table
package tablewriter
import (
"bytes"
"fmt"
"io"
"regexp"
"strings"
)
const (
MAX_ROW_WIDTH = 30
)
const (
CENTER = "+"
ROW = "-"
COLUMN = "|"
SPACE = " "
NEWLINE = "\n"
)
const (
ALIGN_DEFAULT = iota
ALIGN_CENTER
ALIGN_RIGHT
ALIGN_LEFT
)
var (
decimal = regexp.MustCompile(`^-*\d*\.?\d*$`)
percent = regexp.MustCompile(`^-*\d*\.?\d*$%$`)
)
type Border struct {
Left bool
Right bool
Top bool
Bottom bool
}
type Table struct {
out io.Writer
rows [][]string
lines [][][]string
cs map[int]int
rs map[int]int
headers []string
footers []string
caption bool
captionText string
autoFmt bool
autoWrap bool
mW int
pCenter string
pRow string
pColumn string
tColumn int
tRow int
hAlign int
fAlign int
align int
newLine string
rowLine bool
autoMergeCells bool
hdrLine bool
borders Border
colSize int
headerParams []string
columnsParams []string
footerParams []string
columnsAlign []int
}
// Start New Table
// Take io.Writer Directly
func NewWriter(writer io.Writer) *Table {
t := &Table{
out: writer,
rows: [][]string{},
lines: [][][]string{},
cs: make(map[int]int),
rs: make(map[int]int),
headers: []string{},
footers: []string{},
caption: false,
captionText: "Table caption.",
autoFmt: true,
autoWrap: true,
mW: MAX_ROW_WIDTH,
pCenter: CENTER,
pRow: ROW,
pColumn: COLUMN,
tColumn: -1,
tRow: -1,
hAlign: ALIGN_DEFAULT,
fAlign: ALIGN_DEFAULT,
align: ALIGN_DEFAULT,
newLine: NEWLINE,
rowLine: false,
hdrLine: true,
borders: Border{Left: true, Right: true, Bottom: true, Top: true},
colSize: -1,
headerParams: []string{},
columnsParams: []string{},
footerParams: []string{},
columnsAlign: []int{}}
return t
}
// Render table output
func (t *Table) Render() {
if t.borders.Top {
t.printLine(true)
}
t.printHeading()
if t.autoMergeCells {
t.printRowsMergeCells()
} else {
t.printRows()
}
if !t.rowLine && t.borders.Bottom {
t.printLine(true)
}
t.printFooter()
if t.caption {
t.printCaption()
}
}
// Set table header
func (t *Table) SetHeader(keys []string) {
t.colSize = len(keys)
for i, v := range keys {
t.parseDimension(v, i, -1)
t.headers = append(t.headers, v)
}
}
// Set table Footer
func (t *Table) SetFooter(keys []string) {
//t.colSize = len(keys)
for i, v := range keys {
t.parseDimension(v, i, -1)
t.footers = append(t.footers, v)
}
}
// Set table Caption
func (t *Table) SetCaption(caption bool, captionText ...string) {
t.caption = caption
if len(captionText) == 1 {
t.captionText = captionText[0]
}
}
// Turn header autoformatting on/off. Default is on (true).
func (t *Table) SetAutoFormatHeaders(auto bool) {
t.autoFmt = auto
}
// Turn automatic multiline text adjustment on/off. Default is on (true).
func (t *Table) SetAutoWrapText(auto bool) {
t.autoWrap = auto
}
// Set the Default column width
func (t *Table) SetColWidth(width int) {
t.mW = width
}
// Set the minimal width for a column
func (t *Table) SetColMinWidth(column int, width int) {
t.cs[column] = width
}
// Set the Column Separator
func (t *Table) SetColumnSeparator(sep string) {
t.pColumn = sep
}
// Set the Row Separator
func (t *Table) SetRowSeparator(sep string) {
t.pRow = sep
}
// Set the center Separator
func (t *Table) SetCenterSeparator(sep string) {
t.pCenter = sep
}
// Set Header Alignment
func (t *Table) SetHeaderAlignment(hAlign int) {
t.hAlign = hAlign
}
// Set Footer Alignment
func (t *Table) SetFooterAlignment(fAlign int) {
t.fAlign = fAlign
}
// Set Table Alignment
func (t *Table) SetAlignment(align int) {
t.align = align
}
func (t *Table) SetColumnAlignment(keys []int) {
for _, v := range keys {
switch v {
case ALIGN_CENTER:
break
case ALIGN_LEFT:
break
case ALIGN_RIGHT:
break
default:
v = ALIGN_DEFAULT
}
t.columnsAlign = append(t.columnsAlign, v)
}
}
// Set New Line
func (t *Table) SetNewLine(nl string) {
t.newLine = nl
}
// Set Header Line
// This would enable / disable a line after the header
func (t *Table) SetHeaderLine(line bool) {
t.hdrLine = line
}
// Set Row Line
// This would enable / disable a line on each row of the table
func (t *Table) SetRowLine(line bool) {
t.rowLine = line
}
// Set Auto Merge Cells
// This would enable / disable the merge of cells with identical values
func (t *Table) SetAutoMergeCells(auto bool) {
t.autoMergeCells = auto
}
// Set Table Border
// This would enable / disable line around the table
func (t *Table) SetBorder(border bool) {
t.SetBorders(Border{border, border, border, border})
}
func (t *Table) SetBorders(border Border) {
t.borders = border
}
// Append row to table
func (t *Table) Append(row []string) {
rowSize := len(t.headers)
if rowSize > t.colSize {
t.colSize = rowSize
}
n := len(t.lines)
line := [][]string{}
for i, v := range row {
// Detect string width
// Detect String height
// Break strings into words
out := t.parseDimension(v, i, n)
// Append broken words
line = append(line, out)
}
t.lines = append(t.lines, line)
}
// Allow Support for Bulk Append
// Eliminates repeated for loops
func (t *Table) AppendBulk(rows [][]string) {
for _, row := range rows {
t.Append(row)
}
}
// NumLines to get the number of lines
func (t *Table) NumLines() int {
return len(t.lines)
}
// Clear rows
func (t *Table) ClearRows() {
t.lines = [][][]string{}
}
// Clear footer
func (t *Table) ClearFooter() {
t.footers = []string{}
}
// Print line based on row width
func (t *Table) printLine(nl bool) {
fmt.Fprint(t.out, t.pCenter)
for i := 0; i < len(t.cs); i++ {
v := t.cs[i]
fmt.Fprintf(t.out, "%s%s%s%s",
t.pRow,
strings.Repeat(string(t.pRow), v),
t.pRow,
t.pCenter)
}
if nl {
fmt.Fprint(t.out, t.newLine)
}
}
// Print line based on row width with our without cell separator
func (t *Table) printLineOptionalCellSeparators(nl bool, displayCellSeparator []bool) {
fmt.Fprint(t.out, t.pCenter)
for i := 0; i < len(t.cs); i++ {
v := t.cs[i]
if i > len(displayCellSeparator) || displayCellSeparator[i] {
// Display the cell separator
fmt.Fprintf(t.out, "%s%s%s%s",
t.pRow,
strings.Repeat(string(t.pRow), v),
t.pRow,
t.pCenter)
} else {
// Don't display the cell separator for this cell
fmt.Fprintf(t.out, "%s%s",
strings.Repeat(" ", v+2),
t.pCenter)
}
}
if nl {
fmt.Fprint(t.out, t.newLine)
}
}
// Return the PadRight function if align is left, PadLeft if align is right,
// and Pad by default
func pad(align int) func(string, string, int) string {
padFunc := Pad
switch align {
case ALIGN_LEFT:
padFunc = PadRight
case ALIGN_RIGHT:
padFunc = PadLeft
}
return padFunc
}
// Print heading information
func (t *Table) printHeading() {
// Check if headers is available
if len(t.headers) < 1 {
return
}
// Check if border is set
// Replace with space if not set
fmt.Fprint(t.out, ConditionString(t.borders.Left, t.pColumn, SPACE))
// Identify last column
end := len(t.cs) - 1
// Get pad function
padFunc := pad(t.hAlign)
// Checking for ANSI escape sequences for header
is_esc_seq := false
if len(t.headerParams) > 0 {
is_esc_seq = true
}
// Print Heading column
for i := 0; i <= end; i++ {
v := t.cs[i]
h := ""
if i < len(t.headers) {
h = t.headers[i]
}
if t.autoFmt {
h = Title(h)
}
pad := ConditionString((i == end && !t.borders.Left), SPACE, t.pColumn)
if is_esc_seq {
fmt.Fprintf(t.out, " %s %s",
format(padFunc(h, SPACE, v),
t.headerParams[i]), pad)
} else {
fmt.Fprintf(t.out, " %s %s",
padFunc(h, SPACE, v),
pad)
}
}
// Next line
fmt.Fprint(t.out, t.newLine)
if t.hdrLine {
t.printLine(true)
}
}
// Print heading information
func (t *Table) printFooter() {
// Check if headers is available
if len(t.footers) < 1 {
return
}
// Only print line if border is not set
if !t.borders.Bottom {
t.printLine(true)
}
// Check if border is set
// Replace with space if not set
fmt.Fprint(t.out, ConditionString(t.borders.Bottom, t.pColumn, SPACE))
// Identify last column
end := len(t.cs) - 1
// Get pad function
padFunc := pad(t.fAlign)
// Checking for ANSI escape sequences for header
is_esc_seq := false
if len(t.footerParams) > 0 {
is_esc_seq = true
}
// Print Heading column
for i := 0; i <= end; i++ {
v := t.cs[i]
f := t.footers[i]
if t.autoFmt {
f = Title(f)
}
pad := ConditionString((i == end && !t.borders.Top), SPACE, t.pColumn)
if len(t.footers[i]) == 0 {
pad = SPACE
}
if is_esc_seq {
fmt.Fprintf(t.out, " %s %s",
format(padFunc(f, SPACE, v),
t.footerParams[i]), pad)
} else {
fmt.Fprintf(t.out, " %s %s",
padFunc(f, SPACE, v),
pad)
}
//fmt.Fprintf(t.out, " %s %s",
// padFunc(f, SPACE, v),
// pad)
}
// Next line
fmt.Fprint(t.out, t.newLine)
//t.printLine(true)
hasPrinted := false
for i := 0; i <= end; i++ {
v := t.cs[i]
pad := t.pRow
center := t.pCenter
length := len(t.footers[i])
if length > 0 {
hasPrinted = true
}
// Set center to be space if length is 0
if length == 0 && !t.borders.Right {
center = SPACE
}
// Print first junction
if i == 0 {
fmt.Fprint(t.out, center)
}
// Pad With space of length is 0
if length == 0 {
pad = SPACE
}
// Ignore left space of it has printed before
if hasPrinted || t.borders.Left {
pad = t.pRow
center = t.pCenter
}
// Change Center start position
if center == SPACE {
if i < end && len(t.footers[i+1]) != 0 {
center = t.pCenter
}
}
// Print the footer
fmt.Fprintf(t.out, "%s%s%s%s",
pad,
strings.Repeat(string(pad), v),
pad,
center)
}
fmt.Fprint(t.out, t.newLine)
}
// Print caption text
func (t Table) printCaption() {
width := t.getTableWidth()
paragraph, _ := WrapString(t.captionText, width)
for linecount := 0; linecount < len(paragraph); linecount++ {
fmt.Fprintln(t.out, paragraph[linecount])
}
}
// Calculate the total number of characters in a row
func (t Table) getTableWidth() int {
var chars int
for _, v := range t.cs {
chars += v
}
// Add chars, spaces, seperators to calculate the total width of the table.
// ncols := t.colSize
// spaces := ncols * 2
// seps := ncols + 1
return (chars + (3 * t.colSize) + 2)
}
func (t Table) printRows() {
for i, lines := range t.lines {
t.printRow(lines, i)
}
}
func (t *Table) fillAlignment(num int) {
if len(t.columnsAlign) < num {
t.columnsAlign = make([]int, num)
for i := range t.columnsAlign {
t.columnsAlign[i] = t.align
}
}
}
// Print Row Information
// Adjust column alignment based on type
func (t *Table) printRow(columns [][]string, colKey int) {
// Get Maximum Height
max := t.rs[colKey]
total := len(columns)
// TODO Fix uneven col size
// if total < t.colSize {
// for n := t.colSize - total; n < t.colSize ; n++ {
// columns = append(columns, []string{SPACE})
// t.cs[n] = t.mW
// }
//}
// Pad Each Height
// pads := []int{}
pads := []int{}
// Checking for ANSI escape sequences for columns
is_esc_seq := false
if len(t.columnsParams) > 0 {
is_esc_seq = true
}
t.fillAlignment(total)
for i, line := range columns {
length := len(line)
pad := max - length
pads = append(pads, pad)
for n := 0; n < pad; n++ {
columns[i] = append(columns[i], " ")
}
}
//fmt.Println(max, "\n")
for x := 0; x < max; x++ {
for y := 0; y < total; y++ {
// Check if border is set
fmt.Fprint(t.out, ConditionString((!t.borders.Left && y == 0), SPACE, t.pColumn))
fmt.Fprintf(t.out, SPACE)
str := columns[y][x]
// Embedding escape sequence with column value
if is_esc_seq {
str = format(str, t.columnsParams[y])
}
// This would print alignment
// Default alignment would use multiple configuration
switch t.columnsAlign[y] {
case ALIGN_CENTER: //
fmt.Fprintf(t.out, "%s", Pad(str, SPACE, t.cs[y]))
case ALIGN_RIGHT:
fmt.Fprintf(t.out, "%s", PadLeft(str, SPACE, t.cs[y]))
case ALIGN_LEFT:
fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y]))
default:
if decimal.MatchString(strings.TrimSpace(str)) || percent.MatchString(strings.TrimSpace(str)) {
fmt.Fprintf(t.out, "%s", PadLeft(str, SPACE, t.cs[y]))
} else {
fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y]))
// TODO Custom alignment per column
//if max == 1 || pads[y] > 0 {
// fmt.Fprintf(t.out, "%s", Pad(str, SPACE, t.cs[y]))
//} else {
// fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y]))
//}
}
}
fmt.Fprintf(t.out, SPACE)
}
// Check if border is set
// Replace with space if not set
fmt.Fprint(t.out, ConditionString(t.borders.Left, t.pColumn, SPACE))
fmt.Fprint(t.out, t.newLine)
}
if t.rowLine {
t.printLine(true)
}
}
// Print the rows of the table and merge the cells that are identical
func (t *Table) printRowsMergeCells() {
var previousLine []string
var displayCellBorder []bool
var tmpWriter bytes.Buffer
for i, lines := range t.lines {
// We store the display of the current line in a tmp writer, as we need to know which border needs to be print above
previousLine, displayCellBorder = t.printRowMergeCells(&tmpWriter, lines, i, previousLine)
if i > 0 { //We don't need to print borders above first line
if t.rowLine {
t.printLineOptionalCellSeparators(true, displayCellBorder)
}
}
tmpWriter.WriteTo(t.out)
}
//Print the end of the table
if t.rowLine {
t.printLine(true)
}
}
// Print Row Information to a writer and merge identical cells.
// Adjust column alignment based on type
func (t *Table) printRowMergeCells(writer io.Writer, columns [][]string, colKey int, previousLine []string) ([]string, []bool) {
// Get Maximum Height
max := t.rs[colKey]
total := len(columns)
// Pad Each Height
pads := []int{}
for i, line := range columns {
length := len(line)
pad := max - length
pads = append(pads, pad)
for n := 0; n < pad; n++ {
columns[i] = append(columns[i], " ")
}
}
var displayCellBorder []bool
t.fillAlignment(total)
for x := 0; x < max; x++ {
for y := 0; y < total; y++ {
// Check if border is set
fmt.Fprint(writer, ConditionString((!t.borders.Left && y == 0), SPACE, t.pColumn))
fmt.Fprintf(writer, SPACE)
str := columns[y][x]
if t.autoMergeCells {
//Store the full line to merge mutli-lines cells
fullLine := strings.Join(columns[y], " ")
if len(previousLine) > y && fullLine == previousLine[y] && fullLine != "" {
// If this cell is identical to the one above but not empty, we don't display the border and keep the cell empty.
displayCellBorder = append(displayCellBorder, false)
str = ""
} else {
// First line or different content, keep the content and print the cell border
displayCellBorder = append(displayCellBorder, true)
}
}
// This would print alignment
// Default alignment would use multiple configuration
switch t.columnsAlign[y] {
case ALIGN_CENTER: //
fmt.Fprintf(writer, "%s", Pad(str, SPACE, t.cs[y]))
case ALIGN_RIGHT:
fmt.Fprintf(writer, "%s", PadLeft(str, SPACE, t.cs[y]))
case ALIGN_LEFT:
fmt.Fprintf(writer, "%s", PadRight(str, SPACE, t.cs[y]))
default:
if decimal.MatchString(strings.TrimSpace(str)) || percent.MatchString(strings.TrimSpace(str)) {
fmt.Fprintf(writer, "%s", PadLeft(str, SPACE, t.cs[y]))
} else {
fmt.Fprintf(writer, "%s", PadRight(str, SPACE, t.cs[y]))
}
}
fmt.Fprintf(writer, SPACE)
}
// Check if border is set
// Replace with space if not set
fmt.Fprint(writer, ConditionString(t.borders.Left, t.pColumn, SPACE))
fmt.Fprint(writer, t.newLine)
}
//The new previous line is the current one
previousLine = make([]string, total)
for y := 0; y < total; y++ {
previousLine[y] = strings.Join(columns[y], " ") //Store the full line for multi-lines cells
}
//Returns the newly added line and wether or not a border should be displayed above.
return previousLine, displayCellBorder
}
func (t *Table) parseDimension(str string, colKey, rowKey int) []string {
var (
raw []string
max int
)
w := DisplayWidth(str)
// Calculate Width
// Check if with is grater than maximum width
if w > t.mW {
w = t.mW
}
// Check if width exists
v, ok := t.cs[colKey]
if !ok || v < w || v == 0 {
t.cs[colKey] = w
}
if rowKey == -1 {
return raw
}
// Calculate Height
if t.autoWrap {
raw, _ = WrapString(str, t.cs[colKey])
} else {
raw = getLines(str)
}
for _, line := range raw {
if w := DisplayWidth(line); w > max {
max = w
}
}
// Make sure the with is the same length as maximum word
// Important for cases where the width is smaller than maxu word
if max > t.cs[colKey] {
t.cs[colKey] = max
}
h := len(raw)
v, ok = t.rs[rowKey]
if !ok || v < h || v == 0 {
t.rs[rowKey] = h
}
//fmt.Printf("Raw %+v %d\n", raw, len(raw))
return raw
}

View File

@ -0,0 +1,134 @@
package tablewriter
import (
"fmt"
"strconv"
"strings"
)
const ESC = "\033"
const SEP = ";"
const (
BgBlackColor int = iota + 40
BgRedColor
BgGreenColor
BgYellowColor
BgBlueColor
BgMagentaColor
BgCyanColor
BgWhiteColor
)
const (
FgBlackColor int = iota + 30
FgRedColor
FgGreenColor
FgYellowColor
FgBlueColor
FgMagentaColor
FgCyanColor
FgWhiteColor
)
const (
BgHiBlackColor int = iota + 100
BgHiRedColor
BgHiGreenColor
BgHiYellowColor
BgHiBlueColor
BgHiMagentaColor
BgHiCyanColor
BgHiWhiteColor
)
const (
FgHiBlackColor int = iota + 90
FgHiRedColor
FgHiGreenColor
FgHiYellowColor
FgHiBlueColor
FgHiMagentaColor
FgHiCyanColor
FgHiWhiteColor
)
const (
Normal = 0
Bold = 1
UnderlineSingle = 4
Italic
)
type Colors []int
func startFormat(seq string) string {
return fmt.Sprintf("%s[%sm", ESC, seq)
}
func stopFormat() string {
return fmt.Sprintf("%s[%dm", ESC, Normal)
}
// Making the SGR (Select Graphic Rendition) sequence.
func makeSequence(codes []int) string {
codesInString := []string{}
for _, code := range codes {
codesInString = append(codesInString, strconv.Itoa(code))
}
return strings.Join(codesInString, SEP)
}
// Adding ANSI escape sequences before and after string
func format(s string, codes interface{}) string {
var seq string
switch v := codes.(type) {
case string:
seq = v
case []int:
seq = makeSequence(v)
default:
return s
}
if len(seq) == 0 {
return s
}
return startFormat(seq) + s + stopFormat()
}
// Adding header colors (ANSI codes)
func (t *Table) SetHeaderColor(colors ...Colors) {
if t.colSize != len(colors) {
panic("Number of header colors must be equal to number of headers.")
}
for i := 0; i < len(colors); i++ {
t.headerParams = append(t.headerParams, makeSequence(colors[i]))
}
}
// Adding column colors (ANSI codes)
func (t *Table) SetColumnColor(colors ...Colors) {
if t.colSize != len(colors) {
panic("Number of column colors must be equal to number of headers.")
}
for i := 0; i < len(colors); i++ {
t.columnsParams = append(t.columnsParams, makeSequence(colors[i]))
}
}
// Adding column colors (ANSI codes)
func (t *Table) SetFooterColor(colors ...Colors) {
if len(t.footers) != len(colors) {
panic("Number of footer colors must be equal to number of footer.")
}
for i := 0; i < len(colors); i++ {
t.footerParams = append(t.footerParams, makeSequence(colors[i]))
}
}
func Color(colors ...int) []int {
return colors
}

4
vendor/github.com/olekukonko/tablewriter/test.csv generated vendored Normal file
View File

@ -0,0 +1,4 @@
first_name,last_name,ssn
John,Barry,123456
Kathy,Smith,687987
Bob,McCornick,3979870
1 first_name last_name ssn
2 John Barry 123456
3 Kathy Smith 687987
4 Bob McCornick 3979870

View File

@ -0,0 +1,4 @@
Field,Type,Null,Key,Default,Extra
user_id,smallint(5),NO,PRI,NULL,auto_increment
username,varchar(10),NO,,NULL,
password,varchar(100),NO,,NULL,
1 Field Type Null Key Default Extra
2 user_id smallint(5) NO PRI NULL auto_increment
3 username varchar(10) NO NULL
4 password varchar(100) NO NULL

72
vendor/github.com/olekukonko/tablewriter/util.go generated vendored Normal file
View File

@ -0,0 +1,72 @@
// Copyright 2014 Oleku Konko All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.
// This module is a Table Writer API for the Go Programming Language.
// The protocols were written in pure Go and works on windows and unix systems
package tablewriter
import (
"math"
"regexp"
"strings"
"github.com/mattn/go-runewidth"
)
var ansi = regexp.MustCompile("\033\\[(?:[0-9]{1,3}(?:;[0-9]{1,3})*)?[m|K]")
func DisplayWidth(str string) int {
return runewidth.StringWidth(ansi.ReplaceAllLiteralString(str, ""))
}
// Simple Condition for string
// Returns value based on condition
func ConditionString(cond bool, valid, inValid string) string {
if cond {
return valid
}
return inValid
}
// Format Table Header
// Replace _ , . and spaces
func Title(name string) string {
name = strings.Replace(name, "_", " ", -1)
name = strings.Replace(name, ".", " ", -1)
name = strings.TrimSpace(name)
return strings.ToUpper(name)
}
// Pad String
// Attempts to play string in the center
func Pad(s, pad string, width int) string {
gap := width - DisplayWidth(s)
if gap > 0 {
gapLeft := int(math.Ceil(float64(gap / 2)))
gapRight := gap - gapLeft
return strings.Repeat(string(pad), gapLeft) + s + strings.Repeat(string(pad), gapRight)
}
return s
}
// Pad String Right position
// This would pace string at the left side fo the screen
func PadRight(s, pad string, width int) string {
gap := width - DisplayWidth(s)
if gap > 0 {
return s + strings.Repeat(string(pad), gap)
}
return s
}
// Pad String Left position
// This would pace string at the right side fo the screen
func PadLeft(s, pad string, width int) string {
gap := width - DisplayWidth(s)
if gap > 0 {
return strings.Repeat(string(pad), gap) + s
}
return s
}

104
vendor/github.com/olekukonko/tablewriter/wrap.go generated vendored Normal file
View File

@ -0,0 +1,104 @@
// Copyright 2014 Oleku Konko All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.
// This module is a Table Writer API for the Go Programming Language.
// The protocols were written in pure Go and works on windows and unix systems
package tablewriter
import (
"math"
"strings"
"github.com/mattn/go-runewidth"
)
var (
nl = "\n"
sp = " "
)
const defaultPenalty = 1e5
// Wrap wraps s into a paragraph of lines of length lim, with minimal
// raggedness.
func WrapString(s string, lim int) ([]string, int) {
words := strings.Split(strings.Replace(s, nl, sp, -1), sp)
var lines []string
max := 0
for _, v := range words {
max = runewidth.StringWidth(v)
if max > lim {
lim = max
}
}
for _, line := range WrapWords(words, 1, lim, defaultPenalty) {
lines = append(lines, strings.Join(line, sp))
}
return lines, lim
}
// WrapWords is the low-level line-breaking algorithm, useful if you need more
// control over the details of the text wrapping process. For most uses,
// WrapString will be sufficient and more convenient.
//
// WrapWords splits a list of words into lines with minimal "raggedness",
// treating each rune as one unit, accounting for spc units between adjacent
// words on each line, and attempting to limit lines to lim units. Raggedness
// is the total error over all lines, where error is the square of the
// difference of the length of the line and lim. Too-long lines (which only
// happen when a single word is longer than lim units) have pen penalty units
// added to the error.
func WrapWords(words []string, spc, lim, pen int) [][]string {
n := len(words)
length := make([][]int, n)
for i := 0; i < n; i++ {
length[i] = make([]int, n)
length[i][i] = runewidth.StringWidth(words[i])
for j := i + 1; j < n; j++ {
length[i][j] = length[i][j-1] + spc + runewidth.StringWidth(words[j])
}
}
nbrk := make([]int, n)
cost := make([]int, n)
for i := range cost {
cost[i] = math.MaxInt32
}
for i := n - 1; i >= 0; i-- {
if length[i][n-1] <= lim {
cost[i] = 0
nbrk[i] = n
} else {
for j := i + 1; j < n; j++ {
d := lim - length[i][j-1]
c := d*d + cost[j]
if length[i][j-1] > lim {
c += pen // too-long lines get a worse penalty
}
if c < cost[i] {
cost[i] = c
nbrk[i] = j
}
}
}
}
var lines [][]string
i := 0
for i < n {
lines = append(lines, words[i:nbrk[i]])
i = nbrk[i]
}
return lines
}
// getLines decomposes a multiline string into a slice of strings.
func getLines(s string) []string {
var lines []string
for _, line := range strings.Split(s, nl) {
lines = append(lines, line)
}
return lines
}

36
vendor/vendor.json vendored
View File

@ -209,6 +209,30 @@
"revision": "4fe03583929022c9c96829401ffabc755e69782e",
"revisionTime": "2017-06-25T21:53:50Z"
},
{
"checksumSHA1": "N0jIjg2HeB8fM7dt8OED2BiRZz0=",
"path": "github.com/NaverCloudPlatform/ncloud-sdk-go/common",
"revision": "c2e73f942591b0f033a3c6df00f44badb2347c38",
"revisionTime": "2018-01-10T05:50:12Z"
},
{
"checksumSHA1": "7lT5Xmeo2MhE6CJvIInZyLBu6S8=",
"path": "github.com/NaverCloudPlatform/ncloud-sdk-go/oauth",
"revision": "c2e73f942591b0f033a3c6df00f44badb2347c38",
"revisionTime": "2018-01-10T05:50:12Z"
},
{
"checksumSHA1": "DpfU1gyyhIc1BuKZUZDRVDBkeSc=",
"path": "github.com/NaverCloudPlatform/ncloud-sdk-go/request",
"revision": "c2e73f942591b0f033a3c6df00f44badb2347c38",
"revisionTime": "2018-01-10T05:50:12Z"
},
{
"checksumSHA1": "QnZ2RMlms/zYbD8jg+lyS8ncQOY=",
"path": "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk",
"revision": "c2e73f942591b0f033a3c6df00f44badb2347c38",
"revisionTime": "2018-01-10T05:50:12Z"
},
{
"checksumSHA1": "HttiPj314X1a0i2Jen1p6lRH/vE=",
"path": "github.com/aliyun/aliyun-oss-go-sdk/oss",
@ -947,6 +971,12 @@
"path": "github.com/mattn/go-isatty",
"revision": "56b76bdf51f7708750eac80fa38b952bb9f32639"
},
{
"checksumSHA1": "cJE7dphDlam/i7PhnsyosNWtbd4=",
"path": "github.com/mattn/go-runewidth",
"revision": "97311d9f7767e3d6f422ea06661bc2c7a19e8a5d",
"revisionTime": "2017-05-10T07:48:58Z"
},
{
"checksumSHA1": "UP+pXl+ic9y6qrpZA5MqDIAuGfw=",
"path": "github.com/mitchellh/cli",
@ -1007,6 +1037,12 @@
"path": "github.com/nu7hatch/gouuid",
"revision": "179d4d0c4d8d407a32af483c2354df1d2c91e6c3"
},
{
"checksumSHA1": "WY8p2ZNGyl69P1tcVc5HFal/Ng0=",
"path": "github.com/olekukonko/tablewriter",
"revision": "96aac992fc8b1a4c83841a6c3e7178d20d989625",
"revisionTime": "2018-01-05T11:11:33Z"
},
{
"checksumSHA1": "/NoE6t3UkW4/iKAtbf59GGv6tF8=",
"path": "github.com/packer-community/winrmcp/winrmcp",

View File

@ -0,0 +1,88 @@
---
description: |
As Packer allows users to develop a custom builder as a plugin, NAVER CLOUD PLATFORM provides its own Packer builder for your convenience.
You can use NAVER CLOUD PLATFORM's Packer builder to easily create your server images.
layout: docs
page_title: 'Naver Cloud Platform - Builders'
sidebar_current: 'docs-builders-ncloud'
---
# NAVER CLOUD PLATFORM Builder
As Packer allows users to develop a custom builder as a plugin, NAVER CLOUD PLATFORM provides its own Packer builder for your convenience.
You can use NAVER CLOUD PLATFORM's Packer builder to easily create your server images.
#### Sample code of template.json
```
{
"variables": {
"ncloud_access_key": "FRxhOQRNjKVMqIz3sRLY",
"ncloud_secret_key": "xd6kTO5iNcLookBx0D8TDKmpLj2ikxqEhc06MQD2"
},
"builders": [
{
"type": "ncloud",
"access_key": "{{user `ncloud_access_key`}}",
"secret_key": "{{user `ncloud_secret_key`}}",
"server_image_product_code": "SPSW0WINNT000016",
"server_product_code": "SPSVRSSD00000011",
"member_server_image_no": "4223",
"server_image_name": "packer-test {{timestamp}}",
"server_description": "server description",
"user_data": "CreateObject(\"WScript.Shell\").run(\"cmd.exe /c powershell Set-ExecutionPolicy RemoteSigned & winrm quickconfig -q & sc config WinRM start= auto & winrm set winrm/config/service/auth @{Basic=\"\"true\"\"} & winrm set winrm/config/service @{AllowUnencrypted=\"\"true\"\"} & winrm get winrm/config/service\")",
"region": "US-West"
}
]
}
```
#### Description
* type(required): "ncloud"
* ncloud_access_key (required): User's access key. Go to [[Account Management > Authentication Key]](https://www.ncloud.com/mypage/manage/authkey) to create and view your authentication key.
* ncloud_secret_key (required): User's secret key paired with the access key. Go to [[Account Management > Authentication Key]](https://www.ncloud.com/mypage/manage/authkey) to create and view your authentication key.
* server_image_product_code: Product code of an image to create. (member_server_image_no is required if not specified)
* server_product_code (required): Product (spec) code to create.
* member_server_image_no: Previous image code. If there is an image previously created, it can be used to create a new image. (server_image_product_code is required if not specified)
* server_image_name (option): Name of an image to create.
* server_image_description (option): Description of an image to create.
* block_storage_size (option): You can add block storage ranging from 10 GB to 2000 GB, in increments of 10 GB.
* access_control_group_configuration_no: 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.
* user_data (option): Init script to run when an instance is created.
* For Linux servers, Python, Perl, and Shell scripts can be used. The path of the script to run should be included at the beginning of the script, like #!/usr/bin/env python, #!/bin/perl, or #!/bin/bash.
* For Windows servers, only Visual Basic scripts can be used.
* All scripts must be written in English.
* region (option): Name of the region where you want to create an image. (default: Korea)
* values: Korea / US-West / HongKong / Singapore / Japan / Germany
### Requirements for creating Windows images
You should include the following code in the packer configuration file for provision when creating a Windows server.
```
"builders": [
{
"type": "ncloud",
...
"user_data":
"CreateObject(\"WScript.Shell\").run(\"cmd.exe /c powershell Set-ExecutionPolicy RemoteSigned & winrm set winrm/config/service/auth @{Basic=\"\"true\"\"} & winrm set winrm/config/service @{AllowUnencrypted=\"\"true\"\"} & winrm quickconfig -q & sc config WinRM start= auto & winrm get winrm/config/service\")",
"communicator": "winrm",
"winrm_username": "Administrator"
}
],
"provisioners": [
{
"type": "powershell",
"inline": [
"$Env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /shutdown /quiet \"/unattend:C:\\Program Files (x86)\\NBP\\nserver64.xml\" "
]
}
]
```
### Note
* You can only create as many public IP addresses as the number of server instances you own. Before running Packer, please make sure that the number of public IP addresses previously created is not larger than the number of server instances (including those to be used to create server images).
* When you forcibly terminate the packer process or close the terminal (command) window where the process is running, the resources may not be cleaned up as the packer process no longer runs. In this case, you should manually clean up the resources associated with the process.