2019-05-31 08:27:41 -04:00
|
|
|
//go:generate struct-markdown
|
2019-10-14 10:43:59 -04:00
|
|
|
//go:generate mapstructure-to-hcl2 -type Config
|
2019-05-31 08:27:41 -04:00
|
|
|
|
2018-01-11 04:57:53 -05:00
|
|
|
package ncloud
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2018-01-29 04:35:43 -05:00
|
|
|
"fmt"
|
|
|
|
"os"
|
2018-01-11 04:57:53 -05:00
|
|
|
|
2020-02-03 08:55:01 -05:00
|
|
|
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/ncloud"
|
|
|
|
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/server"
|
2020-11-12 17:44:02 -05:00
|
|
|
"github.com/hashicorp/packer/packer-plugin-sdk/common"
|
2020-12-01 18:30:31 -05:00
|
|
|
"github.com/hashicorp/packer/packer-plugin-sdk/communicator"
|
2020-11-19 15:07:02 -05:00
|
|
|
packersdk "github.com/hashicorp/packer/packer-plugin-sdk/packer"
|
2020-11-18 13:34:59 -05:00
|
|
|
"github.com/hashicorp/packer/packer-plugin-sdk/template/config"
|
2020-11-11 13:21:37 -05:00
|
|
|
"github.com/hashicorp/packer/packer-plugin-sdk/template/interpolate"
|
2018-01-11 04:57:53 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
// Config is structure to use packer builder plugin for Naver Cloud Platform
|
|
|
|
type Config struct {
|
|
|
|
common.PackerConfig `mapstructure:",squash"`
|
|
|
|
|
2019-06-06 10:29:25 -04:00
|
|
|
AccessKey string `mapstructure:"access_key"`
|
|
|
|
SecretKey string `mapstructure:"secret_key"`
|
2019-05-28 11:50:58 -04:00
|
|
|
// Product code of an image to create.
|
2019-06-06 10:29:25 -04:00
|
|
|
// (member_server_image_no is required if not specified)
|
|
|
|
ServerImageProductCode string `mapstructure:"server_image_product_code" required:"true"`
|
2019-05-28 11:50:58 -04:00
|
|
|
// Product (spec) code to create.
|
2019-06-06 10:29:25 -04:00
|
|
|
ServerProductCode string `mapstructure:"server_product_code" required:"true"`
|
2019-05-28 11:50:58 -04:00
|
|
|
// Previous image code. If there is an
|
2019-06-06 10:29:25 -04:00
|
|
|
// image previously created, it can be used to create a new image.
|
|
|
|
// (server_image_product_code is required if not specified)
|
|
|
|
MemberServerImageNo string `mapstructure:"member_server_image_no" required:"false"`
|
2019-05-28 11:50:58 -04:00
|
|
|
// Name of an image to create.
|
2019-06-06 10:29:25 -04:00
|
|
|
ServerImageName string `mapstructure:"server_image_name" required:"false"`
|
2019-05-28 11:50:58 -04:00
|
|
|
// Description of an image to create.
|
2019-06-06 10:29:25 -04:00
|
|
|
ServerImageDescription string `mapstructure:"server_image_description" required:"false"`
|
2019-05-28 11:50:58 -04:00
|
|
|
// User data to apply when launching the instance. Note
|
2019-06-06 10:29:25 -04:00
|
|
|
// that you need to be careful about escaping characters due to the templates
|
|
|
|
// being JSON. It is often more convenient to use user_data_file, instead.
|
|
|
|
// Packer will not automatically wait for a user script to finish before
|
|
|
|
// shutting down the instance this must be handled in a provisioner.
|
|
|
|
UserData string `mapstructure:"user_data" required:"false"`
|
2019-05-28 11:50:58 -04:00
|
|
|
// Path to a file that will be used for the user
|
2019-06-06 10:29:25 -04:00
|
|
|
// data when launching the instance.
|
|
|
|
UserDataFile string `mapstructure:"user_data_file" required:"false"`
|
2019-05-28 11:50:58 -04:00
|
|
|
// You can add block storage ranging from 10
|
2019-06-06 10:29:25 -04:00
|
|
|
// GB to 2000 GB, in increments of 10 GB.
|
|
|
|
BlockStorageSize int `mapstructure:"block_storage_size" required:"false"`
|
2019-05-28 11:50:58 -04:00
|
|
|
// Name of the region where you want to create an image.
|
2019-06-06 10:29:25 -04:00
|
|
|
// (default: Korea)
|
|
|
|
Region string `mapstructure:"region" required:"false"`
|
2019-05-28 11:50:58 -04:00
|
|
|
// This is used to allow
|
2019-06-06 10:29:25 -04:00
|
|
|
// 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.
|
2019-05-28 11:50:58 -04:00
|
|
|
AccessControlGroupConfigurationNo string `mapstructure:"access_control_group_configuration_no" required:"false"`
|
2018-01-11 04:57:53 -05:00
|
|
|
|
|
|
|
Comm communicator.Config `mapstructure:",squash"`
|
|
|
|
ctx *interpolate.Context
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewConfig checks parameters
|
2019-12-17 05:25:56 -05:00
|
|
|
func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
2018-01-11 04:57:53 -05:00
|
|
|
warnings := []string{}
|
|
|
|
|
|
|
|
err := config.Decode(c, &config.DecodeOpts{
|
|
|
|
Interpolate: true,
|
|
|
|
InterpolateFilter: &interpolate.RenderFilter{
|
|
|
|
Exclude: []string{},
|
|
|
|
},
|
|
|
|
}, raws...)
|
|
|
|
if err != nil {
|
2019-12-17 05:25:56 -05:00
|
|
|
return warnings, err
|
2018-01-11 04:57:53 -05:00
|
|
|
}
|
|
|
|
|
2020-11-19 15:07:02 -05:00
|
|
|
var errs *packersdk.MultiError
|
2018-01-11 04:57:53 -05:00
|
|
|
if es := c.Comm.Prepare(nil); len(es) > 0 {
|
2020-11-19 15:07:02 -05:00
|
|
|
errs = packersdk.MultiErrorAppend(errs, es...)
|
2018-01-11 04:57:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if c.AccessKey == "" {
|
2020-11-19 15:07:02 -05:00
|
|
|
errs = packersdk.MultiErrorAppend(errs, errors.New("access_key is required"))
|
2018-01-11 04:57:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if c.SecretKey == "" {
|
2020-11-19 15:07:02 -05:00
|
|
|
errs = packersdk.MultiErrorAppend(errs, errors.New("secret_key is required"))
|
2018-01-11 04:57:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if c.MemberServerImageNo == "" && c.ServerImageProductCode == "" {
|
2020-11-19 15:07:02 -05:00
|
|
|
errs = packersdk.MultiErrorAppend(errs, errors.New("server_image_product_code or member_server_image_no is required"))
|
2018-01-11 04:57:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if c.MemberServerImageNo != "" && c.ServerImageProductCode != "" {
|
2020-11-19 15:07:02 -05:00
|
|
|
errs = packersdk.MultiErrorAppend(errs, errors.New("Only one of server_image_product_code and member_server_image_no can be set"))
|
2018-01-11 04:57:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if c.ServerImageProductCode != "" && len(c.ServerImageProductCode) > 20 {
|
2020-11-19 15:07:02 -05:00
|
|
|
errs = packersdk.MultiErrorAppend(errs, errors.New("If server_image_product_code field is set, length of server_image_product_code should be max 20"))
|
2018-01-11 04:57:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if c.ServerProductCode != "" && len(c.ServerProductCode) > 20 {
|
2020-11-19 15:07:02 -05:00
|
|
|
errs = packersdk.MultiErrorAppend(errs, errors.New("If server_product_code field is set, length of server_product_code should be max 20"))
|
2018-01-11 04:57:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if c.ServerImageName != "" && (len(c.ServerImageName) < 3 || len(c.ServerImageName) > 30) {
|
2020-11-19 15:07:02 -05:00
|
|
|
errs = packersdk.MultiErrorAppend(errs, errors.New("If server_image_name field is set, length of server_image_name should be min 3 and max 20"))
|
2018-01-11 04:57:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if c.ServerImageDescription != "" && len(c.ServerImageDescription) > 1000 {
|
2020-11-19 15:07:02 -05:00
|
|
|
errs = packersdk.MultiErrorAppend(errs, errors.New("If server_image_description field is set, length of server_image_description should be max 1000"))
|
2018-01-11 04:57:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if c.BlockStorageSize != 0 {
|
|
|
|
if c.BlockStorageSize < 10 || c.BlockStorageSize > 2000 {
|
2020-11-19 15:07:02 -05:00
|
|
|
errs = packersdk.MultiErrorAppend(errs, errors.New("The size of BlockStorageSize is at least 10 GB and up to 2000GB"))
|
2018-01-11 04:57:53 -05:00
|
|
|
} else if int(c.BlockStorageSize/10)*10 != c.BlockStorageSize {
|
2019-12-17 05:25:56 -05:00
|
|
|
return nil, errors.New("BlockStorageSize must be a multiple of 10 GB")
|
2018-01-11 04:57:53 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-29 04:09:14 -05:00
|
|
|
if c.UserData != "" && c.UserDataFile != "" {
|
2020-11-19 15:07:02 -05:00
|
|
|
errs = packersdk.MultiErrorAppend(errs, errors.New("Only one of user_data or user_data_file can be specified."))
|
2018-01-29 04:09:14 -05:00
|
|
|
} else if c.UserDataFile != "" {
|
|
|
|
if _, err := os.Stat(c.UserDataFile); err != nil {
|
2020-11-19 15:07:02 -05:00
|
|
|
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("user_data_file not found: %s", c.UserDataFile))
|
2018-01-29 04:09:14 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-11 04:57:53 -05:00
|
|
|
if c.UserData != "" && len(c.UserData) > 21847 {
|
2020-11-19 15:07:02 -05:00
|
|
|
errs = packersdk.MultiErrorAppend(errs, errors.New("If user_data field is set, length of UserData should be max 21847"))
|
2018-01-11 04:57:53 -05:00
|
|
|
}
|
|
|
|
|
2018-12-21 03:53:52 -05:00
|
|
|
if c.Comm.Type == "winrm" && c.AccessControlGroupConfigurationNo == "" {
|
2020-11-19 15:07:02 -05:00
|
|
|
errs = packersdk.MultiErrorAppend(errs, errors.New("If Communicator is winrm, access_control_group_configuration_no is required"))
|
2018-01-11 04:57:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if errs != nil && len(errs.Errors) > 0 {
|
2019-12-17 05:25:56 -05:00
|
|
|
return warnings, errs
|
2018-01-11 04:57:53 -05:00
|
|
|
}
|
|
|
|
|
2019-12-17 05:25:56 -05:00
|
|
|
return warnings, nil
|
2018-01-11 04:57:53 -05:00
|
|
|
}
|
2020-02-03 08:55:01 -05:00
|
|
|
|
|
|
|
type NcloudAPIClient struct {
|
|
|
|
server *server.APIClient
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Config) Client() (*NcloudAPIClient, error) {
|
|
|
|
apiKey := &ncloud.APIKey{
|
|
|
|
AccessKey: c.AccessKey,
|
|
|
|
SecretKey: c.SecretKey,
|
|
|
|
}
|
|
|
|
return &NcloudAPIClient{
|
|
|
|
server: server.NewAPIClient(server.NewConfiguration(apiKey)),
|
|
|
|
}, nil
|
|
|
|
}
|