Compare commits
75 Commits
example-test
...
fix_typo
| Author | SHA1 | Date | |
|---|---|---|---|
| b448c3182c | |||
| 9230a06920 | |||
| 16658a9f47 | |||
| ceb96d061a | |||
| bb1a025f60 | |||
| 87ba7258b3 | |||
| da312e2785 | |||
| 84af0ba6da | |||
| 3c6b7841bc | |||
| c7ee5f1efd | |||
| a00846102b | |||
| 41c66d6935 | |||
| f6854f5528 | |||
| 38fe79948b | |||
| a6c5958c67 | |||
| c17f236e85 | |||
| bb5d7b6c40 | |||
| 1ea5a547e2 | |||
| 86b8ce8df0 | |||
| b51bf9250e | |||
| 2d5a32629a | |||
| 3e2db82cab | |||
| bc9dd69669 | |||
| 734e91b97c | |||
| ab0d1ee363 | |||
| cf94fd1778 | |||
| 15a2e59bba | |||
| 8c4eb5f4aa | |||
| 86788220a9 | |||
| e3bcb4f2ac | |||
| ccd0430fda | |||
| 49474f8f37 | |||
| e0614cabf4 | |||
| eaec3e5564 | |||
| eaaf22dcde | |||
| f2f33fa344 | |||
| 9189f1228e | |||
| 5e7b5729e6 | |||
| 9365c90c0b | |||
| f27bdf85f4 | |||
| 9f7bb4da25 | |||
| b1967e99c7 | |||
| 7efb41868f | |||
| 260906c3e4 | |||
| f65e1d5d55 | |||
| 23e8684aae | |||
| e22d9861aa | |||
| 1c8fc65223 | |||
| 42ca66752f | |||
| 33461126e2 | |||
| 1f834e229a | |||
| 4417f8b3bf | |||
| 8db540a935 | |||
| e8780bf7b8 | |||
| 3b0226d496 | |||
| 4c08789642 | |||
| 634bf87d99 | |||
| d566419c45 | |||
| cce1f5c1e3 | |||
| 7f26429a2a | |||
| d81c02b456 | |||
| 794e83b171 | |||
| 58fb58c2ea | |||
| 15a9e1b20a | |||
| 3f23a5ec74 | |||
| f4cbb5d7dc | |||
| fb04fa7a25 | |||
| 830140157d | |||
| cdcdf6a618 | |||
| 74434b3c3e | |||
| 3a11352dfa | |||
| 56728a937b | |||
| f044a64014 | |||
| cd370aaaad | |||
| af865b1591 |
+15
-1
@@ -1,4 +1,18 @@
|
||||
## 1.7.2 (Upcoming)
|
||||
## 1.7.3 (Upcoming)
|
||||
|
||||
## 1.7.2 (April 05, 2021)
|
||||
|
||||
### IMPROVEMENTS:
|
||||
|
||||
* builder/alicloud: Add `ramrole` configuration to ECS instance. [GH-10845]
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
* builder/proxmox: Update Proxmox Go API to ensure only the first non-loopback
|
||||
IPv4 address gets returned. [GH-10858]
|
||||
* builder/vsphere: Fix primary disk resize on clone. [GH-10848]
|
||||
* core: Fix bug where call to "packer version" sent output to stderr instead of
|
||||
stdout. [GH-10850]
|
||||
|
||||
## 1.7.1 (March 31, 2021)
|
||||
|
||||
|
||||
+2
-2
@@ -68,8 +68,8 @@
|
||||
/builder/yandex/ @GennadySpb @alexanderKhaustov @seukyaso
|
||||
/website/pages/docs/builders/yandex* @GennadySpb @alexanderKhaustov @seukyaso
|
||||
|
||||
/builder/osc/ @marinsalinas @Hakujou
|
||||
/website/pages/docs/builders/osc* @marinsalinas @Hakujou
|
||||
/builder/osc/ @marinsalinas @Hakujou @outscale-mgo
|
||||
/website/pages/docs/builders/osc* @marinsalinas @Hakujou @outscale-mgo
|
||||
|
||||
/examples/tencentcloud/ @likexian
|
||||
/builder/tencentcloud/ @likexian
|
||||
|
||||
@@ -58,8 +58,7 @@ install-gen-deps: ## Install dependencies for code generation
|
||||
# install` seems to install the last tagged version and we want to install
|
||||
# master.
|
||||
@(cd $(TEMPDIR) && GO111MODULE=on go get github.com/alvaroloes/enumer@master)
|
||||
@go install ./cmd/struct-markdown
|
||||
@go install ./cmd/mapstructure-to-hcl2
|
||||
@go install github.com/hashicorp/packer-plugin-sdk/cmd/packer-sdc@latest
|
||||
|
||||
install-lint-deps: ## Install linter dependencies
|
||||
# Pinning golangci-lint at v1.23.8 as --new-from-rev seems to work properly; the latest 1.24.0 has caused issues with memory consumption
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
// component_acc_test.go should contain acceptance tests for plugin components
|
||||
// to make sure all component types can be discovered and started.
|
||||
package plugin
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"testing"
|
||||
|
||||
amazonacc "github.com/hashicorp/packer-plugin-amazon/builder/ebs/acceptance"
|
||||
"github.com/hashicorp/packer-plugin-sdk/acctest"
|
||||
"github.com/hashicorp/packer/hcl2template/addrs"
|
||||
)
|
||||
|
||||
//go:embed test-fixtures/basic-amazon-ami-datasource.pkr.hcl
|
||||
var basicAmazonAmiDatasourceHCL2Template string
|
||||
|
||||
func TestAccInitAndBuildBasicAmazonAmiDatasource(t *testing.T) {
|
||||
plugin := addrs.Plugin{
|
||||
Hostname: "github.com",
|
||||
Namespace: "hashicorp",
|
||||
Type: "amazon",
|
||||
}
|
||||
testCase := &acctest.PluginTestCase{
|
||||
Name: "amazon-ami_basic_datasource_test",
|
||||
Setup: func() error {
|
||||
return cleanupPluginInstallation(plugin)
|
||||
},
|
||||
Teardown: func() error {
|
||||
helper := amazonacc.AWSHelper{
|
||||
Region: "us-west-2",
|
||||
AMIName: "packer-amazon-ami-test",
|
||||
}
|
||||
return helper.CleanUpAmi()
|
||||
},
|
||||
Template: basicAmazonAmiDatasourceHCL2Template,
|
||||
Type: "amazon-ami",
|
||||
Init: true,
|
||||
CheckInit: func(initCommand *exec.Cmd, logfile string) error {
|
||||
if initCommand.ProcessState != nil {
|
||||
if initCommand.ProcessState.ExitCode() != 0 {
|
||||
return fmt.Errorf("Bad exit code. Logfile: %s", logfile)
|
||||
}
|
||||
}
|
||||
logs, err := os.Open(logfile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable find %s", logfile)
|
||||
}
|
||||
defer logs.Close()
|
||||
|
||||
logsBytes, err := ioutil.ReadAll(logs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to read %s", logfile)
|
||||
}
|
||||
initOutput := string(logsBytes)
|
||||
return checkPluginInstallation(initOutput, plugin)
|
||||
},
|
||||
Check: func(buildCommand *exec.Cmd, logfile string) error {
|
||||
if buildCommand.ProcessState != nil {
|
||||
if buildCommand.ProcessState.ExitCode() != 0 {
|
||||
return fmt.Errorf("Bad exit code. Logfile: %s", logfile)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
acctest.TestPlugin(t, testCase)
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
// plugin_acc_test.go should contain acceptance tests for features related to
|
||||
// installing, discovering and running plugins.
|
||||
package plugin
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
amazonacc "github.com/hashicorp/packer-plugin-amazon/builder/ebs/acceptance"
|
||||
"github.com/hashicorp/packer-plugin-sdk/acctest"
|
||||
"github.com/hashicorp/packer-plugin-sdk/acctest/testutils"
|
||||
"github.com/hashicorp/packer/hcl2template/addrs"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
)
|
||||
|
||||
//go:embed test-fixtures/basic-amazon-ebs.pkr.hcl
|
||||
var basicAmazonEbsHCL2Template string
|
||||
|
||||
func TestAccInitAndBuildBasicAmazonEbs(t *testing.T) {
|
||||
plugin := addrs.Plugin{
|
||||
Hostname: "github.com",
|
||||
Namespace: "hashicorp",
|
||||
Type: "amazon",
|
||||
}
|
||||
testCase := &acctest.PluginTestCase{
|
||||
Name: "amazon-ebs_basic_plugin_init_and_build_test",
|
||||
Setup: func() error {
|
||||
return cleanupPluginInstallation(plugin)
|
||||
},
|
||||
Teardown: func() error {
|
||||
helper := amazonacc.AWSHelper{
|
||||
Region: "us-east-1",
|
||||
AMIName: "packer-plugin-amazon-ebs-test",
|
||||
}
|
||||
return helper.CleanUpAmi()
|
||||
},
|
||||
Template: basicAmazonEbsHCL2Template,
|
||||
Type: "amazon-ebs",
|
||||
Init: true,
|
||||
CheckInit: func(initCommand *exec.Cmd, logfile string) error {
|
||||
if initCommand.ProcessState != nil {
|
||||
if initCommand.ProcessState.ExitCode() != 0 {
|
||||
return fmt.Errorf("Bad exit code. Logfile: %s", logfile)
|
||||
}
|
||||
}
|
||||
logs, err := os.Open(logfile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable find %s", logfile)
|
||||
}
|
||||
defer logs.Close()
|
||||
|
||||
logsBytes, err := ioutil.ReadAll(logs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to read %s", logfile)
|
||||
}
|
||||
initOutput := string(logsBytes)
|
||||
return checkPluginInstallation(initOutput, plugin)
|
||||
},
|
||||
Check: func(buildCommand *exec.Cmd, logfile string) error {
|
||||
if buildCommand.ProcessState != nil {
|
||||
if buildCommand.ProcessState.ExitCode() != 0 {
|
||||
return fmt.Errorf("Bad exit code. Logfile: %s", logfile)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
acctest.TestPlugin(t, testCase)
|
||||
}
|
||||
|
||||
func cleanupPluginInstallation(plugin addrs.Plugin) error {
|
||||
home, err := homedir.Dir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pluginPath := filepath.Join(home,
|
||||
".packer.d",
|
||||
"plugins",
|
||||
plugin.Hostname,
|
||||
plugin.Namespace,
|
||||
plugin.Type)
|
||||
testutils.CleanupFiles(pluginPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkPluginInstallation(initOutput string, plugin addrs.Plugin) error {
|
||||
expectedInitLog := "Installed plugin " + plugin.String()
|
||||
if matched, _ := regexp.MatchString(expectedInitLog+".*", initOutput); !matched {
|
||||
return fmt.Errorf("logs doesn't contain expected foo value %q", initOutput)
|
||||
}
|
||||
|
||||
home, err := homedir.Dir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pluginPath := filepath.Join(home,
|
||||
".packer.d",
|
||||
"plugins",
|
||||
plugin.Hostname,
|
||||
plugin.Namespace,
|
||||
plugin.Type)
|
||||
if !testutils.FileExists(pluginPath) {
|
||||
return fmt.Errorf("%s plugin installation not found", plugin.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
packer {
|
||||
required_plugins {
|
||||
amazon = {
|
||||
version = ">= 0.0.1"
|
||||
source = "github.com/hashicorp/amazon"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data "amazon-ami" "test" {
|
||||
filters = {
|
||||
name = "ubuntu/images/*ubuntu-xenial-16.04-amd64-server-*"
|
||||
root-device-type = "ebs"
|
||||
virtualization-type = "hvm"
|
||||
}
|
||||
most_recent = true
|
||||
owners = ["099720109477"]
|
||||
}
|
||||
|
||||
source "amazon-ebs" "basic-example" {
|
||||
region = "us-west-2"
|
||||
source_ami = data.amazon-ami.test.id
|
||||
ami_name = "packer-amazon-ami-test"
|
||||
communicator = "ssh"
|
||||
instance_type = "t2.micro"
|
||||
ssh_username = "ubuntu"
|
||||
}
|
||||
|
||||
build {
|
||||
sources = [
|
||||
"source.amazon-ebs.basic-example"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
packer {
|
||||
required_plugins {
|
||||
amazon = {
|
||||
version = ">= 0.0.1"
|
||||
source = "github.com/hashicorp/amazon"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
source "amazon-ebs" "basic-test" {
|
||||
region = "us-east-1"
|
||||
instance_type = "m3.medium"
|
||||
source_ami = "ami-76b2a71e"
|
||||
ssh_username = "ubuntu"
|
||||
ami_name = "packer-plugin-amazon-ebs-test"
|
||||
}
|
||||
|
||||
build {
|
||||
sources = ["source.amazon-ebs.basic-test"]
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package acctest
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func init() {
|
||||
testTesting = true
|
||||
|
||||
if err := os.Setenv(TestEnvVar, "1"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTest_noEnv(t *testing.T) {
|
||||
// Unset the variable
|
||||
if err := os.Setenv(TestEnvVar, ""); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.Setenv(TestEnvVar, "1")
|
||||
|
||||
mt := new(mockT)
|
||||
Test(mt, TestCase{})
|
||||
|
||||
if !mt.SkipCalled {
|
||||
t.Fatal("skip not called")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTest_preCheck(t *testing.T) {
|
||||
called := false
|
||||
|
||||
mt := new(mockT)
|
||||
Test(mt, TestCase{
|
||||
PreCheck: func() { called = true },
|
||||
})
|
||||
|
||||
if !called {
|
||||
t.Fatal("precheck should be called")
|
||||
}
|
||||
}
|
||||
|
||||
// mockT implements TestT for testing
|
||||
type mockT struct {
|
||||
ErrorCalled bool
|
||||
ErrorArgs []interface{}
|
||||
FatalCalled bool
|
||||
FatalArgs []interface{}
|
||||
SkipCalled bool
|
||||
SkipArgs []interface{}
|
||||
|
||||
f bool
|
||||
}
|
||||
|
||||
func (t *mockT) Error(args ...interface{}) {
|
||||
t.ErrorCalled = true
|
||||
t.ErrorArgs = args
|
||||
t.f = true
|
||||
}
|
||||
|
||||
func (t *mockT) Fatal(args ...interface{}) {
|
||||
t.FatalCalled = true
|
||||
t.FatalArgs = args
|
||||
t.f = true
|
||||
}
|
||||
|
||||
func (t *mockT) Skip(args ...interface{}) {
|
||||
t.SkipCalled = true
|
||||
t.SkipArgs = args
|
||||
t.f = true
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:generate struct-markdown
|
||||
//go:generate packer-sdc struct-markdown
|
||||
|
||||
package ecs
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:generate mapstructure-to-hcl2 -type Config,AlicloudDiskDevice
|
||||
//go:generate packer-sdc mapstructure-to-hcl2 -type Config,AlicloudDiskDevice
|
||||
|
||||
// The alicloud contains a packersdk.Builder implementation that
|
||||
// builds ecs images for alicloud.
|
||||
@@ -135,6 +135,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
InstanceType: b.config.InstanceType,
|
||||
UserData: b.config.UserData,
|
||||
UserDataFile: b.config.UserDataFile,
|
||||
RamRoleName: b.config.RamRoleName,
|
||||
RegionId: b.config.AlicloudRegion,
|
||||
InternetChargeType: b.config.InternetChargeType,
|
||||
InternetMaxBandwidthOut: b.config.InternetMaxBandwidthOut,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by "mapstructure-to-hcl2 -type Config,AlicloudDiskDevice"; DO NOT EDIT.
|
||||
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
|
||||
|
||||
package ecs
|
||||
|
||||
@@ -88,6 +88,7 @@ type FlatConfig struct {
|
||||
AlicloudSourceImage *string `mapstructure:"source_image" required:"true" cty:"source_image" hcl:"source_image"`
|
||||
ForceStopInstance *bool `mapstructure:"force_stop_instance" required:"false" cty:"force_stop_instance" hcl:"force_stop_instance"`
|
||||
DisableStopInstance *bool `mapstructure:"disable_stop_instance" required:"false" cty:"disable_stop_instance" hcl:"disable_stop_instance"`
|
||||
RamRoleName *string `mapstructure:"ram_role_name" required:"false" cty:"ram_role_name" hcl:"ram_role_name"`
|
||||
SecurityGroupId *string `mapstructure:"security_group_id" required:"false" cty:"security_group_id" hcl:"security_group_id"`
|
||||
SecurityGroupName *string `mapstructure:"security_group_name" required:"false" cty:"security_group_name" hcl:"security_group_name"`
|
||||
UserData *string `mapstructure:"user_data" required:"false" cty:"user_data" hcl:"user_data"`
|
||||
@@ -205,6 +206,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"source_image": &hcldec.AttrSpec{Name: "source_image", Type: cty.String, Required: false},
|
||||
"force_stop_instance": &hcldec.AttrSpec{Name: "force_stop_instance", Type: cty.Bool, Required: false},
|
||||
"disable_stop_instance": &hcldec.AttrSpec{Name: "disable_stop_instance", Type: cty.Bool, Required: false},
|
||||
"ram_role_name": &hcldec.AttrSpec{Name: "ram_role_name", Type: cty.String, Required: false},
|
||||
"security_group_id": &hcldec.AttrSpec{Name: "security_group_id", Type: cty.String, Required: false},
|
||||
"security_group_name": &hcldec.AttrSpec{Name: "security_group_name", Type: cty.String, Required: false},
|
||||
"user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false},
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/aliyun/alibaba-cloud-sdk-go/services/ecs"
|
||||
builderT "github.com/hashicorp/packer-plugin-sdk/acctest"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
builderT "github.com/hashicorp/packer/acctest"
|
||||
)
|
||||
|
||||
const defaultTestRegion = "cn-beijing"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:generate struct-markdown
|
||||
//go:generate packer-sdc struct-markdown
|
||||
|
||||
package ecs
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:generate struct-markdown
|
||||
//go:generate packer-sdc struct-markdown
|
||||
|
||||
package ecs
|
||||
|
||||
@@ -47,6 +47,8 @@ type RunConfig struct {
|
||||
// E.g., Sysprep a windows which may shutdown the instance within its command.
|
||||
// The default value is false.
|
||||
DisableStopInstance bool `mapstructure:"disable_stop_instance" required:"false"`
|
||||
// Ram Role to apply when launching the instance.
|
||||
RamRoleName string `mapstructure:"ram_role_name" required:"false"`
|
||||
// ID of the security group to which a newly
|
||||
// created instance belongs. Mutual access is allowed between instances in one
|
||||
// security group. If not specified, the newly created instance will be added
|
||||
|
||||
@@ -23,6 +23,7 @@ type stepCreateAlicloudInstance struct {
|
||||
UserData string
|
||||
UserDataFile string
|
||||
instanceId string
|
||||
RamRoleName string
|
||||
RegionId string
|
||||
InternetChargeType string
|
||||
InternetMaxBandwidthOut int
|
||||
@@ -115,6 +116,7 @@ func (s *stepCreateAlicloudInstance) buildCreateInstanceRequest(state multistep.
|
||||
request.RegionId = s.RegionId
|
||||
request.InstanceType = s.InstanceType
|
||||
request.InstanceName = s.InstanceName
|
||||
request.RamRoleName = s.RamRoleName
|
||||
request.ZoneId = s.ZoneId
|
||||
|
||||
sourceImage := state.Get("source_image").(*ecs.Image)
|
||||
|
||||
@@ -30,8 +30,8 @@ import (
|
||||
|
||||
"github.com/Azure/go-autorest/autorest"
|
||||
"github.com/Azure/go-autorest/autorest/azure/auth"
|
||||
builderT "github.com/hashicorp/packer-plugin-sdk/acctest"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
builderT "github.com/hashicorp/packer/acctest"
|
||||
)
|
||||
|
||||
const DeviceLoginAcceptanceTest = "DEVICELOGIN_TEST"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//go:generate struct-markdown
|
||||
//go:generate mapstructure-to-hcl2 -type Config,SharedImageGallery,SharedImageGalleryDestination,PlanInformation
|
||||
//go:generate packer-sdc struct-markdown
|
||||
//go:generate packer-sdc mapstructure-to-hcl2 -type Config,SharedImageGallery,SharedImageGalleryDestination,PlanInformation
|
||||
|
||||
package arm
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by "mapstructure-to-hcl2 -type Config,SharedImageGallery,SharedImageGalleryDestination,PlanInformation"; DO NOT EDIT.
|
||||
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
|
||||
|
||||
package arm
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//go:generate struct-markdown
|
||||
//go:generate mapstructure-to-hcl2 -type Config
|
||||
//go:generate packer-sdc struct-markdown
|
||||
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
|
||||
|
||||
// Package chroot is able to create an Azure managed image without requiring the
|
||||
// launch of a new virtual machine for every build. It does this by attaching and
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT.
|
||||
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
|
||||
|
||||
package chroot
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//go:generate struct-markdown
|
||||
//go:generate mapstructure-to-hcl2 -type SharedImageGalleryDestination,TargetRegion
|
||||
//go:generate packer-sdc struct-markdown
|
||||
//go:generate packer-sdc mapstructure-to-hcl2 -type SharedImageGalleryDestination,TargetRegion
|
||||
|
||||
package chroot
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by "mapstructure-to-hcl2 -type SharedImageGalleryDestination,TargetRegion"; DO NOT EDIT.
|
||||
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
|
||||
|
||||
package chroot
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:generate struct-markdown
|
||||
//go:generate packer-sdc struct-markdown
|
||||
|
||||
package client
|
||||
|
||||
|
||||
@@ -181,7 +181,6 @@ func NewAzureClient(subscriptionID, resourceGroupName string,
|
||||
azureClient.GalleryImageVersionsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||
azureClient.GalleryImageVersionsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.GalleryImageVersionsClient.UserAgent)
|
||||
azureClient.GalleryImageVersionsClient.Client.PollingDuration = SharedGalleryTimeout
|
||||
azureClient.GalleryImageVersionsClient.Client.PollingDuration = PollingDuration
|
||||
|
||||
azureClient.GalleryImagesClient = newCompute.NewGalleryImagesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||
azureClient.GalleryImagesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||
|
||||
@@ -28,7 +28,7 @@ package dtl
|
||||
import (
|
||||
"testing"
|
||||
|
||||
builderT "github.com/hashicorp/packer-plugin-sdk/acctest"
|
||||
builderT "github.com/hashicorp/packer/acctest"
|
||||
)
|
||||
|
||||
const DeviceLoginAcceptanceTest = "DEVICELOGIN_TEST"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//go:generate struct-markdown
|
||||
//go:generate mapstructure-to-hcl2 -type Config,SharedImageGallery,SharedImageGalleryDestination,DtlArtifact,ArtifactParameter
|
||||
//go:generate packer-sdc struct-markdown
|
||||
//go:generate packer-sdc mapstructure-to-hcl2 -type Config,SharedImageGallery,SharedImageGalleryDestination,DtlArtifact,ArtifactParameter
|
||||
|
||||
package dtl
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by "mapstructure-to-hcl2 -type Config,SharedImageGallery,SharedImageGalleryDestination,DtlArtifact,ArtifactParameter"; DO NOT EDIT.
|
||||
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
|
||||
|
||||
package dtl
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//go:generate struct-markdown
|
||||
//go:generate mapstructure-to-hcl2 -type Config
|
||||
//go:generate packer-sdc struct-markdown
|
||||
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
|
||||
|
||||
package cloudstack
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT.
|
||||
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
|
||||
|
||||
package cloudstack
|
||||
|
||||
|
||||
@@ -80,10 +80,17 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
|
||||
// Build the steps
|
||||
steps := []multistep.Step{
|
||||
&stepCreateSSHKey{
|
||||
Debug: b.config.PackerDebug,
|
||||
DebugKeyPath: fmt.Sprintf("do_%s.pem", b.config.PackerBuildName),
|
||||
&communicator.StepSSHKeyGen{
|
||||
CommConf: &b.config.Comm,
|
||||
SSHTemporaryKeyPair: b.config.Comm.SSH.SSHTemporaryKeyPair,
|
||||
},
|
||||
multistep.If(b.config.PackerDebug && b.config.Comm.SSHPrivateKeyFile == "",
|
||||
&communicator.StepDumpSSHKey{
|
||||
Path: fmt.Sprintf("do_%s.pem", b.config.PackerBuildName),
|
||||
SSH: &b.config.Comm.SSH,
|
||||
},
|
||||
),
|
||||
&stepCreateSSHKey{},
|
||||
new(stepCreateDroplet),
|
||||
new(stepDropletInfo),
|
||||
&communicator.StepConnect{
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/digitalocean/godo"
|
||||
builderT "github.com/hashicorp/packer-plugin-sdk/acctest"
|
||||
builderT "github.com/hashicorp/packer/acctest"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//go:generate struct-markdown
|
||||
//go:generate mapstructure-to-hcl2 -type Config
|
||||
//go:generate packer-sdc struct-markdown
|
||||
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
|
||||
|
||||
package digitalocean
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT.
|
||||
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
|
||||
|
||||
package digitalocean
|
||||
|
||||
|
||||
@@ -2,26 +2,16 @@ package digitalocean
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"github.com/digitalocean/godo"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer-plugin-sdk/uuid"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
type stepCreateSSHKey struct {
|
||||
Debug bool
|
||||
DebugKeyPath string
|
||||
|
||||
keyId int
|
||||
}
|
||||
|
||||
@@ -30,31 +20,12 @@ func (s *stepCreateSSHKey) Run(ctx context.Context, state multistep.StateBag) mu
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
c := state.Get("config").(*Config)
|
||||
|
||||
ui.Say("Creating temporary ssh key for droplet...")
|
||||
|
||||
priv, err := rsa.GenerateKey(rand.Reader, 2014)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("error generating RSA key: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
if c.Comm.SSHPublicKey == nil {
|
||||
ui.Say("No public SSH key found; skipping SSH public key import...")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// ASN.1 DER encoded form
|
||||
priv_der := x509.MarshalPKCS1PrivateKey(priv)
|
||||
priv_blk := pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Headers: nil,
|
||||
Bytes: priv_der,
|
||||
}
|
||||
|
||||
// Set the private key in the config for later
|
||||
c.Comm.SSHPrivateKey = pem.EncodeToMemory(&priv_blk)
|
||||
|
||||
// Marshal the public key into SSH compatible format
|
||||
// TODO properly handle the public key error
|
||||
pub, _ := ssh.NewPublicKey(&priv.PublicKey)
|
||||
pub_sshformat := string(ssh.MarshalAuthorizedKey(pub))
|
||||
ui.Say("Importing SSH public key...")
|
||||
|
||||
// The name of the public key on DO
|
||||
name := fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID())
|
||||
@@ -62,7 +33,7 @@ func (s *stepCreateSSHKey) Run(ctx context.Context, state multistep.StateBag) mu
|
||||
// Create the key!
|
||||
key, _, err := client.Keys.Create(context.TODO(), &godo.KeyCreateRequest{
|
||||
Name: name,
|
||||
PublicKey: pub_sshformat,
|
||||
PublicKey: string(c.Comm.SSHPublicKey),
|
||||
})
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating temporary SSH key: %s", err)
|
||||
@@ -79,31 +50,6 @@ func (s *stepCreateSSHKey) Run(ctx context.Context, state multistep.StateBag) mu
|
||||
// Remember some state for the future
|
||||
state.Put("ssh_key_id", key.ID)
|
||||
|
||||
// If we're in debug mode, output the private key to the working directory.
|
||||
if s.Debug {
|
||||
ui.Message(fmt.Sprintf("Saving key for debug purposes: %s", s.DebugKeyPath))
|
||||
f, err := os.Create(s.DebugKeyPath)
|
||||
if err != nil {
|
||||
state.Put("error", fmt.Errorf("Error saving debug key: %s", err))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Write the key out
|
||||
if _, err := f.Write(pem.EncodeToMemory(&priv_blk)); err != nil {
|
||||
state.Put("error", fmt.Errorf("Error saving debug key: %s", err))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Chmod it so that it is SSH ready
|
||||
if runtime.GOOS != "windows" {
|
||||
if err := f.Chmod(0600); err != nil {
|
||||
state.Put("error", fmt.Errorf("Error setting permissions of debug key: %s", err))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
builderT "github.com/hashicorp/packer-plugin-sdk/acctest"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
builderT "github.com/hashicorp/packer/acctest"
|
||||
)
|
||||
|
||||
func TestBuilder_implBuilder(t *testing.T) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:generate mapstructure-to-hcl2 -type Config
|
||||
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
|
||||
|
||||
package file
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT.
|
||||
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
|
||||
|
||||
package file
|
||||
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
package googlecompute
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
func TestArtifact_impl(t *testing.T) {
|
||||
var _ packersdk.Artifact = new(Artifact)
|
||||
}
|
||||
|
||||
func TestArtifactState_StateData(t *testing.T) {
|
||||
expectedData := "this is the data"
|
||||
artifact := &Artifact{
|
||||
StateData: map[string]interface{}{"state_data": expectedData},
|
||||
}
|
||||
|
||||
// Valid state
|
||||
result := artifact.State("state_data")
|
||||
if result != expectedData {
|
||||
t.Fatalf("Bad: State data was %s instead of %s", result, expectedData)
|
||||
}
|
||||
|
||||
// Invalid state
|
||||
result = artifact.State("invalid_key")
|
||||
if result != nil {
|
||||
t.Fatalf("Bad: State should be nil for invalid state data name")
|
||||
}
|
||||
|
||||
// Nil StateData should not fail and should return nil
|
||||
artifact = &Artifact{}
|
||||
result = artifact.State("key")
|
||||
if result != nil {
|
||||
t.Fatalf("Bad: State should be nil for nil StateData")
|
||||
}
|
||||
}
|
||||
@@ -1,692 +0,0 @@
|
||||
package googlecompute
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/communicator"
|
||||
)
|
||||
|
||||
func TestConfigPrepare(t *testing.T) {
|
||||
cases := []struct {
|
||||
Key string
|
||||
Value interface{}
|
||||
Err bool
|
||||
}{
|
||||
{
|
||||
"unknown_key",
|
||||
"bad",
|
||||
true,
|
||||
},
|
||||
|
||||
{
|
||||
"private_key_file",
|
||||
"/tmp/i/should/not/exist",
|
||||
true,
|
||||
},
|
||||
|
||||
{
|
||||
"project_id",
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"project_id",
|
||||
"foo",
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
"source_image",
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"source_image",
|
||||
"foo",
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
"source_image_family",
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"source_image_family",
|
||||
"foo",
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
"zone",
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"zone",
|
||||
"foo",
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
"ssh_timeout",
|
||||
"SO BAD",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ssh_timeout",
|
||||
"5s",
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
"wait_to_add_ssh_keys",
|
||||
"SO BAD",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"wait_to_add_ssh_keys",
|
||||
"5s",
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
"state_timeout",
|
||||
"SO BAD",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"state_timeout",
|
||||
"5s",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"use_internal_ip",
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"use_internal_ip",
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"use_internal_ip",
|
||||
"SO VERY BAD",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"on_host_maintenance",
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"on_host_maintenance",
|
||||
"TERMINATE",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"on_host_maintenance",
|
||||
"SO VERY BAD",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"preemptible",
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"preemptible",
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"preemptible",
|
||||
"SO VERY BAD",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"image_family",
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"image_family",
|
||||
"",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"image_family",
|
||||
"foo-bar",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"image_family",
|
||||
"foo bar",
|
||||
true,
|
||||
},
|
||||
{
|
||||
// underscore is not allowed
|
||||
"image_name",
|
||||
"foo_bar",
|
||||
true,
|
||||
},
|
||||
{
|
||||
// too long
|
||||
"image_name",
|
||||
"foobar123xyz_abc-456-one-two_three_five_nine_seventeen_eleventy-seven",
|
||||
true,
|
||||
},
|
||||
{
|
||||
// starts with non-alphabetic character
|
||||
"image_name",
|
||||
"1boohoo",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"image_encryption_key",
|
||||
map[string]string{"kmsKeyName": "foo"},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"image_encryption_key",
|
||||
map[string]string{"No such key": "foo"},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"image_encryption_key",
|
||||
map[string]string{"kmsKeyName": "foo", "RawKey": "foo"},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"scopes",
|
||||
[]string{},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"scopes",
|
||||
[]string{"https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/compute", "https://www.googleapis.com/auth/devstorage.full_control", "https://www.googleapis.com/auth/sqlservice.admin"},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"scopes",
|
||||
[]string{"https://www.googleapis.com/auth/cloud-platform"},
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
"disable_default_service_account",
|
||||
"",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"disable_default_service_account",
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"disable_default_service_account",
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"disable_default_service_account",
|
||||
true,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"disable_default_service_account",
|
||||
"NOT A BOOL",
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
raw, tempfile := testConfig(t)
|
||||
defer os.Remove(tempfile)
|
||||
|
||||
if tc.Value == nil {
|
||||
delete(raw, tc.Key)
|
||||
} else {
|
||||
raw[tc.Key] = tc.Value
|
||||
}
|
||||
|
||||
var c Config
|
||||
warns, errs := c.Prepare(raw)
|
||||
|
||||
if tc.Err {
|
||||
testConfigErr(t, warns, errs, tc.Key)
|
||||
} else {
|
||||
testConfigOk(t, warns, errs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigPrepareAccelerator(t *testing.T) {
|
||||
cases := []struct {
|
||||
Keys []string
|
||||
Values []interface{}
|
||||
Err bool
|
||||
}{
|
||||
{
|
||||
[]string{"accelerator_count", "on_host_maintenance", "accelerator_type"},
|
||||
[]interface{}{1, "MIGRATE", "something_valid"},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]string{"accelerator_count", "on_host_maintenance", "accelerator_type"},
|
||||
[]interface{}{1, "TERMINATE", "something_valid"},
|
||||
false,
|
||||
},
|
||||
{
|
||||
[]string{"accelerator_count", "on_host_maintenance", "accelerator_type"},
|
||||
[]interface{}{1, "TERMINATE", nil},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]string{"accelerator_count", "on_host_maintenance", "accelerator_type"},
|
||||
[]interface{}{1, "TERMINATE", ""},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]string{"accelerator_count", "on_host_maintenance", "accelerator_type"},
|
||||
[]interface{}{1, "TERMINATE", "something_valid"},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
raw, tempfile := testConfig(t)
|
||||
defer os.Remove(tempfile)
|
||||
|
||||
errStr := ""
|
||||
for k := range tc.Keys {
|
||||
|
||||
// Create the string for error reporting
|
||||
// convert value to string if it can be converted
|
||||
errStr += fmt.Sprintf("%s:%v, ", tc.Keys[k], tc.Values[k])
|
||||
if tc.Values[k] == nil {
|
||||
delete(raw, tc.Keys[k])
|
||||
} else {
|
||||
raw[tc.Keys[k]] = tc.Values[k]
|
||||
}
|
||||
}
|
||||
|
||||
var c Config
|
||||
warns, errs := c.Prepare(raw)
|
||||
|
||||
if tc.Err {
|
||||
testConfigErr(t, warns, errs, strings.TrimRight(errStr, ", "))
|
||||
} else {
|
||||
testConfigOk(t, warns, errs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigPrepareServiceAccount(t *testing.T) {
|
||||
cases := []struct {
|
||||
Keys []string
|
||||
Values []interface{}
|
||||
Err bool
|
||||
}{
|
||||
{
|
||||
[]string{"disable_default_service_account", "service_account_email"},
|
||||
[]interface{}{true, "service@account.email.com"},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]string{"disable_default_service_account", "service_account_email"},
|
||||
[]interface{}{false, "service@account.email.com"},
|
||||
false,
|
||||
},
|
||||
{
|
||||
[]string{"disable_default_service_account", "service_account_email"},
|
||||
[]interface{}{true, ""},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
raw, tempfile := testConfig(t)
|
||||
defer os.Remove(tempfile)
|
||||
|
||||
errStr := ""
|
||||
for k := range tc.Keys {
|
||||
|
||||
// Create the string for error reporting
|
||||
// convert value to string if it can be converted
|
||||
errStr += fmt.Sprintf("%s:%v, ", tc.Keys[k], tc.Values[k])
|
||||
if tc.Values[k] == nil {
|
||||
delete(raw, tc.Keys[k])
|
||||
} else {
|
||||
raw[tc.Keys[k]] = tc.Values[k]
|
||||
}
|
||||
}
|
||||
|
||||
var c Config
|
||||
warns, errs := c.Prepare(raw)
|
||||
|
||||
if tc.Err {
|
||||
testConfigErr(t, warns, errs, strings.TrimRight(errStr, ", "))
|
||||
} else {
|
||||
testConfigOk(t, warns, errs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigPrepareStartupScriptFile(t *testing.T) {
|
||||
config := map[string]interface{}{
|
||||
"project_id": "project",
|
||||
"source_image": "foo",
|
||||
"ssh_username": "packer",
|
||||
"startup_script_file": "no-such-file",
|
||||
"zone": "us-central1-a",
|
||||
}
|
||||
|
||||
var c Config
|
||||
_, errs := c.Prepare(config)
|
||||
|
||||
if errs == nil || !strings.Contains(errs.Error(), "startup_script_file") {
|
||||
t.Fatalf("should error: startup_script_file")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigPrepareIAP_SSH(t *testing.T) {
|
||||
config := map[string]interface{}{
|
||||
"project_id": "project",
|
||||
"source_image": "foo",
|
||||
"ssh_username": "packer",
|
||||
"zone": "us-central1-a",
|
||||
"communicator": "ssh",
|
||||
"use_iap": true,
|
||||
}
|
||||
|
||||
var c Config
|
||||
_, err := c.Prepare(config)
|
||||
if err != nil {
|
||||
t.Fatalf("Shouldn't have errors. Err = %s", err)
|
||||
}
|
||||
if c.Comm.SSHHost != "localhost" {
|
||||
t.Fatalf("Should have set SSHHost")
|
||||
}
|
||||
|
||||
testIAPScript(t, &c)
|
||||
}
|
||||
|
||||
func TestConfigPrepareIAP_WinRM(t *testing.T) {
|
||||
config := map[string]interface{}{
|
||||
"project_id": "project",
|
||||
"source_image": "foo",
|
||||
"winrm_username": "packer",
|
||||
"zone": "us-central1-a",
|
||||
"communicator": "winrm",
|
||||
"use_iap": true,
|
||||
}
|
||||
|
||||
var c Config
|
||||
_, err := c.Prepare(config)
|
||||
if err != nil {
|
||||
t.Fatalf("Shouldn't have errors. Err = %s", err)
|
||||
}
|
||||
if c.Comm.WinRMHost != "localhost" {
|
||||
t.Fatalf("Should have set WinRMHost")
|
||||
}
|
||||
|
||||
testIAPScript(t, &c)
|
||||
}
|
||||
|
||||
func TestConfigPrepareIAP_failures(t *testing.T) {
|
||||
config := map[string]interface{}{
|
||||
"project_id": "project",
|
||||
"source_image": "foo",
|
||||
"winrm_username": "packer",
|
||||
"zone": "us-central1-a",
|
||||
"communicator": "none",
|
||||
"iap_hashbang": "/bin/bash",
|
||||
"iap_ext": ".ps1",
|
||||
"use_iap": true,
|
||||
}
|
||||
|
||||
var c Config
|
||||
_, errs := c.Prepare(config)
|
||||
if errs == nil {
|
||||
t.Fatalf("Should have errored because we're using none.")
|
||||
}
|
||||
if c.IAPHashBang != "/bin/bash" {
|
||||
t.Fatalf("IAP hashbang defaulted even though set.")
|
||||
}
|
||||
if c.IAPExt != ".ps1" {
|
||||
t.Fatalf("IAP tempfile defaulted even though set.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigDefaults(t *testing.T) {
|
||||
cases := []struct {
|
||||
Read func(c *Config) interface{}
|
||||
Value interface{}
|
||||
}{
|
||||
{
|
||||
func(c *Config) interface{} { return c.Comm.Type },
|
||||
"ssh",
|
||||
},
|
||||
|
||||
{
|
||||
func(c *Config) interface{} { return c.Comm.SSHPort },
|
||||
22,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
raw, tempfile := testConfig(t)
|
||||
defer os.Remove(tempfile)
|
||||
|
||||
var c Config
|
||||
warns, errs := c.Prepare(raw)
|
||||
testConfigOk(t, warns, errs)
|
||||
|
||||
actual := tc.Read(&c)
|
||||
if actual != tc.Value {
|
||||
t.Fatalf("bad: %#v", actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageName(t *testing.T) {
|
||||
raw, tempfile := testConfig(t)
|
||||
defer os.Remove(tempfile)
|
||||
|
||||
var c Config
|
||||
c.Prepare(raw)
|
||||
if !strings.HasPrefix(c.ImageName, "packer-") {
|
||||
t.Fatalf("ImageName should have 'packer-' prefix, found %s", c.ImageName)
|
||||
}
|
||||
if strings.Contains(c.ImageName, "{{timestamp}}") {
|
||||
t.Errorf("ImageName should be interpolated; found %s", c.ImageName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegion(t *testing.T) {
|
||||
raw, tempfile := testConfig(t)
|
||||
defer os.Remove(tempfile)
|
||||
|
||||
var c Config
|
||||
c.Prepare(raw)
|
||||
if c.Region != "us-east1" {
|
||||
t.Fatalf("Region should be 'us-east1' given Zone of 'us-east1-a', but is %s", c.Region)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyIAPTunnel_SSH(t *testing.T) {
|
||||
c := &communicator.Config{
|
||||
Type: "ssh",
|
||||
SSH: communicator.SSH{
|
||||
SSHHost: "example",
|
||||
SSHPort: 1234,
|
||||
},
|
||||
}
|
||||
|
||||
err := ApplyIAPTunnel(c, 8447)
|
||||
if err != nil {
|
||||
t.Fatalf("Shouldn't have errors")
|
||||
}
|
||||
if c.SSHPort != 8447 {
|
||||
t.Fatalf("Should have set SSHPort")
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyIAPTunnel_WinRM(t *testing.T) {
|
||||
c := &communicator.Config{
|
||||
Type: "winrm",
|
||||
WinRM: communicator.WinRM{
|
||||
WinRMHost: "example",
|
||||
WinRMPort: 1234,
|
||||
},
|
||||
}
|
||||
|
||||
err := ApplyIAPTunnel(c, 8447)
|
||||
if err != nil {
|
||||
t.Fatalf("Shouldn't have errors")
|
||||
}
|
||||
if c.WinRMPort != 8447 {
|
||||
t.Fatalf("Should have set WinRMPort")
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyIAPTunnel_none(t *testing.T) {
|
||||
c := &communicator.Config{
|
||||
Type: "none",
|
||||
}
|
||||
|
||||
err := ApplyIAPTunnel(c, 8447)
|
||||
if err == nil {
|
||||
t.Fatalf("Should have errors, none is not supported")
|
||||
}
|
||||
}
|
||||
|
||||
// Helper stuff below
|
||||
|
||||
func testConfig(t *testing.T) (config map[string]interface{}, tempAccountFile string) {
|
||||
tempAccountFile = testAccountFile(t)
|
||||
|
||||
config = map[string]interface{}{
|
||||
"account_file": tempAccountFile,
|
||||
"project_id": "hashicorp",
|
||||
"source_image": "foo",
|
||||
"ssh_username": "root",
|
||||
"image_family": "bar",
|
||||
"image_labels": map[string]string{
|
||||
"label-1": "value-1",
|
||||
"label-2": "value-2",
|
||||
},
|
||||
"image_licenses": []string{
|
||||
"test-license",
|
||||
},
|
||||
"image_storage_locations": []string{
|
||||
"us-east1",
|
||||
},
|
||||
"metadata_files": map[string]string{},
|
||||
"zone": "us-east1-a",
|
||||
}
|
||||
|
||||
return config, tempAccountFile
|
||||
}
|
||||
|
||||
func testConfigStruct(t *testing.T) *Config {
|
||||
raw, tempfile := testConfig(t)
|
||||
defer os.Remove(tempfile)
|
||||
|
||||
var c Config
|
||||
warns, errs := c.Prepare(raw)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", len(warns))
|
||||
}
|
||||
if errs != nil {
|
||||
t.Fatalf("bad: %#v", errs)
|
||||
}
|
||||
|
||||
return &c
|
||||
}
|
||||
|
||||
func testConfigErr(t *testing.T, warns []string, err error, extra string) {
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatalf("should error: %s", extra)
|
||||
}
|
||||
}
|
||||
|
||||
func testConfigOk(t *testing.T, warns []string, err error) {
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func testAccountFile(t *testing.T) string {
|
||||
tf, err := ioutil.TempFile("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer tf.Close()
|
||||
|
||||
if _, err := tf.Write([]byte(testAccountContent)); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
return tf.Name()
|
||||
}
|
||||
|
||||
func testIAPScript(t *testing.T, c *Config) {
|
||||
if runtime.GOOS == "windows" {
|
||||
if c.IAPExt != ".cmd" {
|
||||
t.Fatalf("IAP tempfile extension didn't default correctly to .cmd")
|
||||
}
|
||||
if c.IAPHashBang != "" {
|
||||
t.Fatalf("IAP hashbang didn't default correctly to nothing.")
|
||||
}
|
||||
} else {
|
||||
if c.IAPExt != "" {
|
||||
t.Fatalf("IAP tempfile extension should default to empty on unix mahcines")
|
||||
}
|
||||
if c.IAPHashBang != "/bin/sh" {
|
||||
t.Fatalf("IAP hashbang didn't default correctly to /bin/sh.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const testMetadataFileContent = `testMetadata`
|
||||
|
||||
func testMetadataFile(t *testing.T) string {
|
||||
tf, err := ioutil.TempFile("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer tf.Close()
|
||||
if _, err := tf.Write([]byte(testMetadataFileContent)); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
return tf.Name()
|
||||
}
|
||||
|
||||
// This is just some dummy data that doesn't actually work
|
||||
const testAccountContent = `{
|
||||
"type": "service_account",
|
||||
"project_id": "test-project-123456789",
|
||||
"private_key_id": "bananaphone",
|
||||
"private_key": "-----BEGIN PRIVATE KEY-----\nring_ring_ring_ring_ring_ring_ring_BANANAPHONE\n-----END PRIVATE KEY-----\n",
|
||||
"client_email": "raffi-compute@developer.gserviceaccount.com",
|
||||
"client_id": "1234567890",
|
||||
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
||||
"token_uri": "https://accounts.google.com/o/oauth2/token",
|
||||
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
||||
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/12345-compute%40developer.gserviceaccount.com"
|
||||
}`
|
||||
@@ -1,26 +0,0 @@
|
||||
package googlecompute
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func StubImage(name, project string, licenses []string, sizeGb int64) *Image {
|
||||
return &Image{
|
||||
Licenses: licenses,
|
||||
Name: name,
|
||||
ProjectId: project,
|
||||
SelfLink: fmt.Sprintf("https://www.googleapis.com/compute/v1/projects/%s/global/images/%s", project, name),
|
||||
SizeGb: sizeGb,
|
||||
}
|
||||
}
|
||||
|
||||
func TestImage_IsWindows(t *testing.T) {
|
||||
i := StubImage("foo", "foo-project", []string{"license-foo", "license-bar"}, 100)
|
||||
assert.False(t, i.IsWindows())
|
||||
|
||||
i = StubImage("foo", "foo-project", []string{"license-foo", "windows-license"}, 100)
|
||||
assert.True(t, i.IsWindows())
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package googlecompute
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetNetworking(t *testing.T) {
|
||||
cases := []struct {
|
||||
c *InstanceConfig
|
||||
expectedNetwork string
|
||||
expectedSubnetwork string
|
||||
error bool
|
||||
}{
|
||||
{
|
||||
c: &InstanceConfig{
|
||||
Network: "default",
|
||||
Subnetwork: "",
|
||||
NetworkProjectId: "project-id",
|
||||
Region: "region-id",
|
||||
},
|
||||
expectedNetwork: "global/networks/default",
|
||||
expectedSubnetwork: "",
|
||||
error: false,
|
||||
},
|
||||
{
|
||||
c: &InstanceConfig{
|
||||
Network: "",
|
||||
Subnetwork: "",
|
||||
NetworkProjectId: "project-id",
|
||||
Region: "region-id",
|
||||
},
|
||||
expectedNetwork: "",
|
||||
expectedSubnetwork: "",
|
||||
error: true,
|
||||
},
|
||||
{
|
||||
c: &InstanceConfig{
|
||||
Network: "some/network/path",
|
||||
Subnetwork: "some/subnetwork/path",
|
||||
NetworkProjectId: "project-id",
|
||||
Region: "region-id",
|
||||
},
|
||||
expectedNetwork: "some/network/path",
|
||||
expectedSubnetwork: "some/subnetwork/path",
|
||||
error: false,
|
||||
},
|
||||
{
|
||||
c: &InstanceConfig{
|
||||
Network: "network-value",
|
||||
Subnetwork: "subnetwork-value",
|
||||
NetworkProjectId: "project-id",
|
||||
Region: "region-id",
|
||||
},
|
||||
expectedNetwork: "projects/project-id/global/networks/network-value",
|
||||
expectedSubnetwork: "projects/project-id/regions/region-id/subnetworks/subnetwork-value",
|
||||
error: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
n, sn, err := getNetworking(tc.c)
|
||||
if n != tc.expectedNetwork {
|
||||
t.Errorf("Expected network %q but got network %q", tc.expectedNetwork, n)
|
||||
}
|
||||
if sn != tc.expectedSubnetwork {
|
||||
t.Errorf("Expected subnetwork %q but got subnetwork %q", tc.expectedSubnetwork, sn)
|
||||
}
|
||||
if !tc.error && err != nil {
|
||||
t.Errorf("Did not expect an error but got: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package googlecompute
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
)
|
||||
|
||||
func TestStepCheckExistingImage_impl(t *testing.T) {
|
||||
var _ multistep.Step = new(StepCheckExistingImage)
|
||||
}
|
||||
|
||||
func TestStepCheckExistingImage(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepCheckExistingImage)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
state.Put("instance_name", "foo")
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
driver := state.Get("driver").(*DriverMock)
|
||||
driver.ImageExistsResult = true
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// Verify state
|
||||
if driver.ImageExistsName != config.ImageName {
|
||||
t.Fatalf("bad: %#v", driver.ImageExistsName)
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package googlecompute
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestStepCreateImage_impl(t *testing.T) {
|
||||
var _ multistep.Step = new(StepCreateImage)
|
||||
}
|
||||
|
||||
func TestStepCreateImage(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepCreateImage)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
c := state.Get("config").(*Config)
|
||||
d := state.Get("driver").(*DriverMock)
|
||||
|
||||
// These are the values of the image the driver will return.
|
||||
d.CreateImageResultProjectId = "test-project"
|
||||
d.CreateImageResultSizeGb = 100
|
||||
|
||||
// run the step
|
||||
action := step.Run(context.Background(), state)
|
||||
assert.Equal(t, action, multistep.ActionContinue, "Step did not pass.")
|
||||
|
||||
uncastImage, ok := state.GetOk("image")
|
||||
assert.True(t, ok, "State does not have resulting image.")
|
||||
image, ok := uncastImage.(*Image)
|
||||
assert.True(t, ok, "Image in state is not an Image.")
|
||||
|
||||
// Verify created Image results.
|
||||
assert.Equal(t, image.Name, c.ImageName, "Created image does not match config name.")
|
||||
assert.Equal(t, image.ProjectId, d.CreateImageResultProjectId, "Created image project does not match driver project.")
|
||||
assert.Equal(t, image.SizeGb, d.CreateImageResultSizeGb, "Created image size does not match the size returned by the driver.")
|
||||
|
||||
// Verify proper args passed to driver.CreateImage.
|
||||
assert.Equal(t, d.CreateImageName, c.ImageName, "Incorrect image name passed to driver.")
|
||||
assert.Equal(t, d.CreateImageDesc, c.ImageDescription, "Incorrect image description passed to driver.")
|
||||
assert.Equal(t, d.CreateImageFamily, c.ImageFamily, "Incorrect image family passed to driver.")
|
||||
assert.Equal(t, d.CreateImageZone, c.Zone, "Incorrect image zone passed to driver.")
|
||||
assert.Equal(t, d.CreateImageDisk, c.DiskName, "Incorrect disk passed to driver.")
|
||||
assert.Equal(t, d.CreateImageLabels, c.ImageLabels, "Incorrect image_labels passed to driver.")
|
||||
assert.Equal(t, d.CreateImageLicenses, c.ImageLicenses, "Incorrect image_licenses passed to driver.")
|
||||
assert.Equal(t, d.CreateImageEncryptionKey, c.ImageEncryptionKey.ComputeType(), "Incorrect image_encryption_key passed to driver.")
|
||||
assert.Equal(t, d.CreateImageStorageLocations, c.ImageStorageLocations, "Incorrect image_storage_locations passed to driver.")
|
||||
}
|
||||
|
||||
func TestStepCreateImage_errorOnChannel(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepCreateImage)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
errCh := make(chan error, 1)
|
||||
errCh <- errors.New("error")
|
||||
|
||||
driver := state.Get("driver").(*DriverMock)
|
||||
driver.CreateImageErrCh = errCh
|
||||
|
||||
// run the step
|
||||
action := step.Run(context.Background(), state)
|
||||
assert.Equal(t, action, multistep.ActionHalt, "Step should not have passed.")
|
||||
_, ok := state.GetOk("error")
|
||||
assert.True(t, ok, "State should have an error.")
|
||||
_, ok = state.GetOk("image_name")
|
||||
assert.False(t, ok, "State should not have a resulting image.")
|
||||
}
|
||||
@@ -1,475 +0,0 @@
|
||||
package googlecompute
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestStepCreateInstance_impl(t *testing.T) {
|
||||
var _ multistep.Step = new(StepCreateInstance)
|
||||
}
|
||||
|
||||
func TestStepCreateInstance(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepCreateInstance)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
state.Put("ssh_public_key", "key")
|
||||
|
||||
c := state.Get("config").(*Config)
|
||||
d := state.Get("driver").(*DriverMock)
|
||||
d.GetImageResult = StubImage("test-image", "test-project", []string{}, 100)
|
||||
|
||||
// run the step
|
||||
assert.Equal(t, step.Run(context.Background(), state), multistep.ActionContinue, "Step should have passed and continued.")
|
||||
|
||||
// Verify state
|
||||
nameRaw, ok := state.GetOk("instance_name")
|
||||
assert.True(t, ok, "State should have an instance name.")
|
||||
|
||||
// cleanup
|
||||
step.Cleanup(state)
|
||||
|
||||
// Check args passed to the driver.
|
||||
assert.Equal(t, d.DeleteInstanceName, nameRaw.(string), "Incorrect instance name passed to driver.")
|
||||
assert.Equal(t, d.DeleteInstanceZone, c.Zone, "Incorrect instance zone passed to driver.")
|
||||
assert.Equal(t, d.DeleteDiskName, c.InstanceName, "Incorrect disk name passed to driver.")
|
||||
assert.Equal(t, d.DeleteDiskZone, c.Zone, "Incorrect disk zone passed to driver.")
|
||||
}
|
||||
|
||||
func TestStepCreateInstance_fromFamily(t *testing.T) {
|
||||
cases := []struct {
|
||||
Name string
|
||||
Family string
|
||||
Expect bool
|
||||
}{
|
||||
{"test-image", "", false},
|
||||
{"test-image", "test-family", false}, // name trumps family
|
||||
{"", "test-family", true},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
state := testState(t)
|
||||
step := new(StepCreateInstance)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
state.Put("ssh_public_key", "key")
|
||||
|
||||
c := state.Get("config").(*Config)
|
||||
c.SourceImage = tc.Name
|
||||
c.SourceImageFamily = tc.Family
|
||||
d := state.Get("driver").(*DriverMock)
|
||||
d.GetImageResult = StubImage("test-image", "test-project", []string{}, 100)
|
||||
|
||||
// run the step
|
||||
assert.Equal(t, step.Run(context.Background(), state), multistep.ActionContinue, "Step should have passed and continued.")
|
||||
|
||||
// cleanup
|
||||
step.Cleanup(state)
|
||||
|
||||
// Check args passed to the driver.
|
||||
if tc.Expect {
|
||||
assert.True(t, d.GetImageFromFamily, "Driver wasn't instructed to use an image family")
|
||||
} else {
|
||||
assert.False(t, d.GetImageFromFamily, "Driver was unexpectedly instructed to use an image family")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepCreateInstance_windowsNeedsPassword(t *testing.T) {
|
||||
|
||||
state := testState(t)
|
||||
step := new(StepCreateInstance)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
state.Put("ssh_public_key", "key")
|
||||
c := state.Get("config").(*Config)
|
||||
d := state.Get("driver").(*DriverMock)
|
||||
d.GetImageResult = StubImage("test-image", "test-project", []string{"windows"}, 100)
|
||||
c.Comm.Type = "winrm"
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// Verify state
|
||||
nameRaw, ok := state.GetOk("instance_name")
|
||||
if !ok {
|
||||
t.Fatal("should have instance name")
|
||||
}
|
||||
|
||||
createPassword, ok := state.GetOk("create_windows_password")
|
||||
|
||||
if !ok || !createPassword.(bool) {
|
||||
t.Fatal("should need to create a windows password")
|
||||
}
|
||||
|
||||
// cleanup
|
||||
step.Cleanup(state)
|
||||
|
||||
if d.DeleteInstanceName != nameRaw.(string) {
|
||||
t.Fatal("should've deleted instance")
|
||||
}
|
||||
if d.DeleteInstanceZone != c.Zone {
|
||||
t.Fatalf("bad instance zone: %#v", d.DeleteInstanceZone)
|
||||
}
|
||||
|
||||
if d.DeleteDiskName != c.InstanceName {
|
||||
t.Fatal("should've deleted disk")
|
||||
}
|
||||
if d.DeleteDiskZone != c.Zone {
|
||||
t.Fatalf("bad disk zone: %#v", d.DeleteDiskZone)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepCreateInstance_windowsPasswordSet(t *testing.T) {
|
||||
|
||||
state := testState(t)
|
||||
step := new(StepCreateInstance)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
state.Put("ssh_public_key", "key")
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
driver := state.Get("driver").(*DriverMock)
|
||||
driver.GetImageResult = StubImage("test-image", "test-project", []string{"windows"}, 100)
|
||||
config.Comm.Type = "winrm"
|
||||
config.Comm.WinRMPassword = "password"
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// Verify state
|
||||
nameRaw, ok := state.GetOk("instance_name")
|
||||
if !ok {
|
||||
t.Fatal("should have instance name")
|
||||
}
|
||||
|
||||
_, ok = state.GetOk("create_windows_password")
|
||||
|
||||
if ok {
|
||||
t.Fatal("should not need to create windows password")
|
||||
}
|
||||
|
||||
// cleanup
|
||||
step.Cleanup(state)
|
||||
|
||||
if driver.DeleteInstanceName != nameRaw.(string) {
|
||||
t.Fatal("should've deleted instance")
|
||||
}
|
||||
if driver.DeleteInstanceZone != config.Zone {
|
||||
t.Fatalf("bad instance zone: %#v", driver.DeleteInstanceZone)
|
||||
}
|
||||
|
||||
if driver.DeleteDiskName != config.InstanceName {
|
||||
t.Fatal("should've deleted disk")
|
||||
}
|
||||
if driver.DeleteDiskZone != config.Zone {
|
||||
t.Fatalf("bad disk zone: %#v", driver.DeleteDiskZone)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepCreateInstance_error(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepCreateInstance)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
state.Put("ssh_public_key", "key")
|
||||
|
||||
d := state.Get("driver").(*DriverMock)
|
||||
d.RunInstanceErr = errors.New("error")
|
||||
d.GetImageResult = StubImage("test-image", "test-project", []string{}, 100)
|
||||
|
||||
// run the step
|
||||
assert.Equal(t, step.Run(context.Background(), state), multistep.ActionHalt, "Step should have failed and halted.")
|
||||
|
||||
// Verify state
|
||||
_, ok := state.GetOk("error")
|
||||
assert.True(t, ok, "State should have an error.")
|
||||
_, ok = state.GetOk("instance_name")
|
||||
assert.False(t, ok, "State should not have an instance name.")
|
||||
}
|
||||
|
||||
func TestStepCreateInstance_errorOnChannel(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepCreateInstance)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
state.Put("ssh_public_key", "key")
|
||||
|
||||
errCh := make(chan error, 1)
|
||||
errCh <- errors.New("error")
|
||||
|
||||
d := state.Get("driver").(*DriverMock)
|
||||
d.RunInstanceErrCh = errCh
|
||||
d.GetImageResult = StubImage("test-image", "test-project", []string{}, 100)
|
||||
|
||||
// run the step
|
||||
assert.Equal(t, step.Run(context.Background(), state), multistep.ActionHalt, "Step should have failed and halted.")
|
||||
|
||||
// Verify state
|
||||
_, ok := state.GetOk("error")
|
||||
assert.True(t, ok, "State should have an error.")
|
||||
_, ok = state.GetOk("instance_name")
|
||||
assert.False(t, ok, "State should not have an instance name.")
|
||||
}
|
||||
|
||||
func TestStepCreateInstance_errorTimeout(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepCreateInstance)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
state.Put("ssh_public_key", "key")
|
||||
|
||||
errCh := make(chan error, 1)
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
config.StateTimeout = 1 * time.Millisecond
|
||||
|
||||
d := state.Get("driver").(*DriverMock)
|
||||
d.RunInstanceErrCh = errCh
|
||||
d.GetImageResult = StubImage("test-image", "test-project", []string{}, 100)
|
||||
|
||||
// run the step
|
||||
assert.Equal(t, step.Run(context.Background(), state), multistep.ActionHalt, "Step should have failed and halted.")
|
||||
|
||||
// Verify state
|
||||
_, ok := state.GetOk("error")
|
||||
assert.True(t, ok, "State should have an error.")
|
||||
_, ok = state.GetOk("instance_name")
|
||||
assert.False(t, ok, "State should not have an instance name.")
|
||||
}
|
||||
|
||||
func TestStepCreateInstance_noServiceAccount(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepCreateInstance)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
state.Put("ssh_public_key", "key")
|
||||
|
||||
c := state.Get("config").(*Config)
|
||||
c.DisableDefaultServiceAccount = true
|
||||
c.ServiceAccountEmail = ""
|
||||
d := state.Get("driver").(*DriverMock)
|
||||
d.GetImageResult = StubImage("test-image", "test-project", []string{}, 100)
|
||||
|
||||
// run the step
|
||||
assert.Equal(t, step.Run(context.Background(), state), multistep.ActionContinue, "Step should have passed and continued.")
|
||||
|
||||
// cleanup
|
||||
step.Cleanup(state)
|
||||
|
||||
// Check args passed to the driver.
|
||||
assert.Equal(t, d.RunInstanceConfig.DisableDefaultServiceAccount, c.DisableDefaultServiceAccount, "Incorrect value for DisableDefaultServiceAccount passed to driver.")
|
||||
assert.Equal(t, d.RunInstanceConfig.ServiceAccountEmail, c.ServiceAccountEmail, "Incorrect value for ServiceAccountEmail passed to driver.")
|
||||
}
|
||||
|
||||
func TestStepCreateInstance_customServiceAccount(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepCreateInstance)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
state.Put("ssh_public_key", "key")
|
||||
|
||||
c := state.Get("config").(*Config)
|
||||
c.DisableDefaultServiceAccount = true
|
||||
c.ServiceAccountEmail = "custom-service-account"
|
||||
d := state.Get("driver").(*DriverMock)
|
||||
d.GetImageResult = StubImage("test-image", "test-project", []string{}, 100)
|
||||
|
||||
// run the step
|
||||
assert.Equal(t, step.Run(context.Background(), state), multistep.ActionContinue, "Step should have passed and continued.")
|
||||
|
||||
// cleanup
|
||||
step.Cleanup(state)
|
||||
|
||||
// Check args passed to the driver.
|
||||
assert.Equal(t, d.RunInstanceConfig.DisableDefaultServiceAccount, c.DisableDefaultServiceAccount, "Incorrect value for DisableDefaultServiceAccount passed to driver.")
|
||||
assert.Equal(t, d.RunInstanceConfig.ServiceAccountEmail, c.ServiceAccountEmail, "Incorrect value for ServiceAccountEmail passed to driver.")
|
||||
}
|
||||
|
||||
func TestCreateInstanceMetadata(t *testing.T) {
|
||||
state := testState(t)
|
||||
c := state.Get("config").(*Config)
|
||||
image := StubImage("test-image", "test-project", []string{}, 100)
|
||||
key := "abcdefgh12345678"
|
||||
|
||||
// create our metadata
|
||||
_, metadataSSHKeys, err := c.createInstanceMetadata(image, key)
|
||||
|
||||
assert.True(t, err == nil, "Metadata creation should have succeeded.")
|
||||
|
||||
// ensure our key is listed
|
||||
assert.True(t, strings.Contains(metadataSSHKeys["ssh-keys"], key), "Instance metadata should contain provided key")
|
||||
}
|
||||
|
||||
func TestCreateInstanceMetadata_noPublicKey(t *testing.T) {
|
||||
state := testState(t)
|
||||
c := state.Get("config").(*Config)
|
||||
image := StubImage("test-image", "test-project", []string{}, 100)
|
||||
sshKeys := c.Metadata["ssh-keys"]
|
||||
|
||||
// create our metadata
|
||||
_, metadataSSHKeys, err := c.createInstanceMetadata(image, "")
|
||||
|
||||
assert.True(t, err == nil, "Metadata creation should have succeeded.")
|
||||
|
||||
// ensure the ssh metadata hasn't changed
|
||||
assert.Equal(t, metadataSSHKeys["ssh-keys"], sshKeys, "Instance metadata should not have been modified")
|
||||
}
|
||||
|
||||
func TestCreateInstanceMetadata_metadataFile(t *testing.T) {
|
||||
state := testState(t)
|
||||
c := state.Get("config").(*Config)
|
||||
image := StubImage("test-image", "test-project", []string{}, 100)
|
||||
content := testMetadataFileContent
|
||||
fileName := testMetadataFile(t)
|
||||
c.MetadataFiles["user-data"] = fileName
|
||||
|
||||
// create our metadata
|
||||
metadataNoSSHKeys, _, err := c.createInstanceMetadata(image, "")
|
||||
|
||||
assert.True(t, err == nil, "Metadata creation should have succeeded.")
|
||||
|
||||
// ensure the user-data key in metadata is updated with file content
|
||||
assert.Equal(t, metadataNoSSHKeys["user-data"], content, "user-data field of the instance metadata should have been updated.")
|
||||
}
|
||||
|
||||
func TestCreateInstanceMetadata_withWrapStartupScript(t *testing.T) {
|
||||
tt := []struct {
|
||||
WrapStartupScript config.Trilean
|
||||
StartupScriptContents string
|
||||
WrappedStartupScriptContents string
|
||||
WrappedStartupScriptStatus string
|
||||
}{
|
||||
{
|
||||
WrapStartupScript: config.TriUnset,
|
||||
StartupScriptContents: testMetadataFileContent,
|
||||
},
|
||||
{
|
||||
WrapStartupScript: config.TriFalse,
|
||||
StartupScriptContents: testMetadataFileContent,
|
||||
},
|
||||
{
|
||||
WrapStartupScript: config.TriTrue,
|
||||
StartupScriptContents: StartupScriptLinux,
|
||||
WrappedStartupScriptContents: testMetadataFileContent,
|
||||
WrappedStartupScriptStatus: StartupScriptStatusNotDone,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
tc := tc
|
||||
state := testState(t)
|
||||
image := StubImage("test-image", "test-project", []string{}, 100)
|
||||
c := state.Get("config").(*Config)
|
||||
c.StartupScriptFile = testMetadataFile(t)
|
||||
c.WrapStartupScriptFile = tc.WrapStartupScript
|
||||
|
||||
// create our metadata
|
||||
metadataNoSSHKeys, _, err := c.createInstanceMetadata(image, "")
|
||||
|
||||
assert.True(t, err == nil, "Metadata creation should have succeeded.")
|
||||
assert.Equal(t, tc.StartupScriptContents, metadataNoSSHKeys[StartupScriptKey], fmt.Sprintf("Instance metadata for startup script should be %q.", tc.StartupScriptContents))
|
||||
assert.Equal(t, tc.WrappedStartupScriptContents, metadataNoSSHKeys[StartupWrappedScriptKey], fmt.Sprintf("Instance metadata for wrapped startup script should be %q.", tc.WrappedStartupScriptContents))
|
||||
assert.Equal(t, tc.WrappedStartupScriptStatus, metadataNoSSHKeys[StartupScriptStatusKey], fmt.Sprintf("Instance metadata startup script status should be %q.", tc.WrappedStartupScriptStatus))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateInstanceMetadataWaitToAddSSHKeys(t *testing.T) {
|
||||
state := testState(t)
|
||||
c := state.Get("config").(*Config)
|
||||
image := StubImage("test-image", "test-project", []string{}, 100)
|
||||
key := "abcdefgh12345678"
|
||||
|
||||
var waitTime int = 4
|
||||
c.WaitToAddSSHKeys = time.Duration(waitTime) * time.Second
|
||||
c.Metadata = map[string]string{
|
||||
"metadatakey1": "xyz",
|
||||
"metadatakey2": "123",
|
||||
}
|
||||
|
||||
// create our metadata
|
||||
metadataNoSSHKeys, metadataSSHKeys, err := c.createInstanceMetadata(image, key)
|
||||
|
||||
assert.True(t, err == nil, "Metadata creation should have succeeded.")
|
||||
|
||||
// ensure our metadata is listed
|
||||
assert.True(t, strings.Contains(metadataSSHKeys["ssh-keys"], key), "Instance metadata should contain provided SSH key")
|
||||
assert.True(t, strings.Contains(metadataNoSSHKeys["metadatakey1"], "xyz"), "Instance metadata should contain provided key: metadatakey1")
|
||||
assert.True(t, strings.Contains(metadataNoSSHKeys["metadatakey2"], "123"), "Instance metadata should contain provided key: metadatakey2")
|
||||
}
|
||||
|
||||
func TestStepCreateInstanceWaitToAddSSHKeys(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepCreateInstance)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
state.Put("ssh_public_key", "key")
|
||||
|
||||
c := state.Get("config").(*Config)
|
||||
d := state.Get("driver").(*DriverMock)
|
||||
d.GetImageResult = StubImage("test-image", "test-project", []string{}, 100)
|
||||
|
||||
key := "abcdefgh12345678"
|
||||
|
||||
var waitTime int = 5
|
||||
c.WaitToAddSSHKeys = time.Duration(waitTime) * time.Second
|
||||
c.Comm.SSHPublicKey = []byte(key)
|
||||
|
||||
c.Metadata = map[string]string{
|
||||
"metadatakey1": "xyz",
|
||||
"metadatakey2": "123",
|
||||
}
|
||||
|
||||
// run the step
|
||||
assert.Equal(t, step.Run(context.Background(), state), multistep.ActionContinue, "Step should have passed and continued.")
|
||||
|
||||
// Verify state
|
||||
_, ok := state.GetOk("instance_name")
|
||||
assert.True(t, ok, "State should have an instance name.")
|
||||
|
||||
// cleanup
|
||||
step.Cleanup(state)
|
||||
}
|
||||
|
||||
func TestStepCreateInstanceNoWaitToAddSSHKeys(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepCreateInstance)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
state.Put("ssh_public_key", "key")
|
||||
|
||||
c := state.Get("config").(*Config)
|
||||
d := state.Get("driver").(*DriverMock)
|
||||
d.GetImageResult = StubImage("test-image", "test-project", []string{}, 100)
|
||||
|
||||
key := "abcdefgh12345678"
|
||||
|
||||
c.Comm.SSHPublicKey = []byte(key)
|
||||
|
||||
c.Metadata = map[string]string{
|
||||
"metadatakey1": "xyz",
|
||||
"metadatakey2": "123",
|
||||
}
|
||||
|
||||
// run the step
|
||||
assert.Equal(t, step.Run(context.Background(), state), multistep.ActionContinue, "Step should have passed and continued.")
|
||||
|
||||
// Verify state
|
||||
_, ok := state.GetOk("instance_name")
|
||||
assert.True(t, ok, "State should have an instance name.")
|
||||
|
||||
// cleanup
|
||||
step.Cleanup(state)
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
package googlecompute
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStepCreateOrResetWindowsPassword(t *testing.T) {
|
||||
state := testState(t)
|
||||
|
||||
// Step is run after the instance is created so we will have an instance name set
|
||||
state.Put("instance_name", "mock_instance")
|
||||
state.Put("create_windows_password", true)
|
||||
|
||||
step := new(StepCreateWindowsPassword)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
if password, ok := state.GetOk("winrm_password"); !ok || password.(string) != "MOCK_PASSWORD" {
|
||||
t.Fatal("should have a password", password, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepCreateOrResetWindowsPassword_passwordSet(t *testing.T) {
|
||||
state := testState(t)
|
||||
|
||||
// Step is run after the instance is created so we will have an instance name set
|
||||
state.Put("instance_name", "mock_instance")
|
||||
|
||||
c := state.Get("config").(*Config)
|
||||
|
||||
c.Comm.WinRMPassword = "password"
|
||||
|
||||
step := new(StepCreateWindowsPassword)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
if password, ok := state.GetOk("winrm_password"); !ok || password.(string) != "password" {
|
||||
t.Fatal("should have used existing password", password, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepCreateOrResetWindowsPassword_dontNeedPassword(t *testing.T) {
|
||||
state := testState(t)
|
||||
|
||||
// Step is run after the instance is created so we will have an instance name set
|
||||
state.Put("instance_name", "mock_instance")
|
||||
|
||||
step := new(StepCreateWindowsPassword)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestStepCreateOrResetWindowsPassword_debug(t *testing.T) {
|
||||
tf, err := ioutil.TempFile("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.Remove(tf.Name())
|
||||
tf.Close()
|
||||
|
||||
state := testState(t)
|
||||
// Step is run after the instance is created so we will have an instance name set
|
||||
state.Put("instance_name", "mock_instance")
|
||||
state.Put("create_windows_password", true)
|
||||
|
||||
step := new(StepCreateWindowsPassword)
|
||||
|
||||
step.Debug = true
|
||||
step.DebugKeyPath = tf.Name()
|
||||
|
||||
defer step.Cleanup(state)
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
if password, ok := state.GetOk("winrm_password"); !ok || password.(string) != "MOCK_PASSWORD" {
|
||||
t.Fatal("should have a password", password, ok)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(tf.Name()); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepCreateOrResetWindowsPassword_error(t *testing.T) {
|
||||
state := testState(t)
|
||||
|
||||
// Step is run after the instance is created so we will have an instance name set
|
||||
state.Put("instance_name", "mock_instance")
|
||||
state.Put("create_windows_password", true)
|
||||
|
||||
step := new(StepCreateWindowsPassword)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
driver := state.Get("driver").(*DriverMock)
|
||||
driver.CreateOrResetWindowsPasswordErr = errors.New("error")
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// Verify state
|
||||
if _, ok := state.GetOk("error"); !ok {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
if _, ok := state.GetOk("winrm_password"); ok {
|
||||
t.Fatal("should NOT have instance name")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepCreateOrResetWindowsPassword_errorOnChannel(t *testing.T) {
|
||||
state := testState(t)
|
||||
|
||||
// Step is run after the instance is created so we will have an instance name set
|
||||
state.Put("instance_name", "mock_instance")
|
||||
state.Put("create_windows_password", true)
|
||||
|
||||
step := new(StepCreateWindowsPassword)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
driver := state.Get("driver").(*DriverMock)
|
||||
|
||||
errCh := make(chan error, 1)
|
||||
errCh <- errors.New("error")
|
||||
|
||||
driver.CreateOrResetWindowsPasswordErrCh = errCh
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// Verify state
|
||||
if _, ok := state.GetOk("error"); !ok {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
if _, ok := state.GetOk("winrm_password"); ok {
|
||||
t.Fatal("should NOT have instance name")
|
||||
}
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
package googlecompute
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"google.golang.org/api/oauth2/v2"
|
||||
)
|
||||
|
||||
func TestStepImportOSLoginSSHKey_impl(t *testing.T) {
|
||||
var _ multistep.Step = new(StepImportOSLoginSSHKey)
|
||||
}
|
||||
|
||||
func TestStepImportOSLoginSSHKey(t *testing.T) {
|
||||
tt := []struct {
|
||||
Name string
|
||||
UseOSLogin bool
|
||||
ExpectedEmail string
|
||||
ExpectedAction multistep.StepAction
|
||||
PubKeyExpected bool
|
||||
}{
|
||||
{
|
||||
Name: "UseOSLoginDisabled",
|
||||
ExpectedAction: multistep.ActionContinue,
|
||||
},
|
||||
{
|
||||
Name: "UseOSLoginWithAccountFile",
|
||||
UseOSLogin: true,
|
||||
ExpectedAction: multistep.ActionContinue,
|
||||
ExpectedEmail: "raffi-compute@developer.gserviceaccount.com",
|
||||
PubKeyExpected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
tc := tc
|
||||
state := testState(t)
|
||||
step := new(StepImportOSLoginSSHKey)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
config.UseOSLogin = tc.UseOSLogin
|
||||
|
||||
if tc.PubKeyExpected {
|
||||
config.Comm.SSHPublicKey = []byte{'k', 'e', 'y'}
|
||||
}
|
||||
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
if step.accountEmail != tc.ExpectedEmail {
|
||||
t.Fatalf("expected accountEmail to be %q but got %q", tc.ExpectedEmail, step.accountEmail)
|
||||
}
|
||||
|
||||
if _, ok := state.GetOk("ssh_key_public_sha256"); !ok && tc.PubKeyExpected {
|
||||
t.Fatal("expected to see a public key")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepImportOSLoginSSHKey_withAccountFile(t *testing.T) {
|
||||
// default teststate contains an account file
|
||||
state := testState(t)
|
||||
step := new(StepImportOSLoginSSHKey)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
config.UseOSLogin = true
|
||||
config.Comm.SSHPublicKey = []byte{'k', 'e', 'y'}
|
||||
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
fakeAccountEmail := "raffi-compute@developer.gserviceaccount.com"
|
||||
if step.accountEmail != fakeAccountEmail {
|
||||
t.Fatalf("expected accountEmail to be %q but got %q", fakeAccountEmail, step.accountEmail)
|
||||
}
|
||||
|
||||
pubKey, ok := state.GetOk("ssh_key_public_sha256")
|
||||
if !ok {
|
||||
t.Fatal("expected to see a public key")
|
||||
}
|
||||
|
||||
sha256sum := sha256.Sum256(config.Comm.SSHPublicKey)
|
||||
if pubKey != hex.EncodeToString(sha256sum[:]) {
|
||||
t.Errorf("expected to see a matching public key, but got %q", pubKey)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepImportOSLoginSSHKey_withNoAccountFile(t *testing.T) {
|
||||
state := testState(t)
|
||||
fakeAccountEmail := "testing@packer.io"
|
||||
step := &StepImportOSLoginSSHKey{
|
||||
TokeninfoFunc: func(ctx context.Context) (*oauth2.Tokeninfo, error) {
|
||||
return &oauth2.Tokeninfo{Email: fakeAccountEmail}, nil
|
||||
},
|
||||
}
|
||||
defer step.Cleanup(state)
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
config.account = nil
|
||||
config.UseOSLogin = true
|
||||
config.Comm.SSHPublicKey = []byte{'k', 'e', 'y'}
|
||||
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
if step.accountEmail != fakeAccountEmail {
|
||||
t.Fatalf("expected accountEmail to be %q but got %q", fakeAccountEmail, step.accountEmail)
|
||||
}
|
||||
|
||||
pubKey, ok := state.GetOk("ssh_key_public_sha256")
|
||||
if !ok {
|
||||
t.Fatal("expected to see a public key")
|
||||
}
|
||||
|
||||
sha256sum := sha256.Sum256(config.Comm.SSHPublicKey)
|
||||
if pubKey != hex.EncodeToString(sha256sum[:]) {
|
||||
t.Errorf("expected to see a matching public key, but got %q", pubKey)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepImportOSLoginSSHKey_withPrivateSSHKey(t *testing.T) {
|
||||
// default teststate contains an account file
|
||||
state := testState(t)
|
||||
step := new(StepImportOSLoginSSHKey)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
config.UseOSLogin = true
|
||||
config.Comm.SSHPrivateKey = []byte{'k', 'e', 'y'}
|
||||
config.Comm.SSHPublicKey = nil
|
||||
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
if step.accountEmail != "" {
|
||||
t.Fatalf("expected accountEmail to be unset but got %q", step.accountEmail)
|
||||
}
|
||||
|
||||
pubKey, ok := state.GetOk("ssh_key_public_sha256")
|
||||
if ok {
|
||||
t.Errorf("expected to not see a public key when using a dedicated private key, but got %q", pubKey)
|
||||
}
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
package googlecompute
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
)
|
||||
|
||||
func TestStepInstanceInfo_impl(t *testing.T) {
|
||||
var _ multistep.Step = new(StepInstanceInfo)
|
||||
}
|
||||
|
||||
func TestStepInstanceInfo(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepInstanceInfo)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
state.Put("instance_name", "foo")
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
driver := state.Get("driver").(*DriverMock)
|
||||
driver.GetNatIPResult = "1.2.3.4"
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// Verify state
|
||||
if driver.WaitForInstanceState != "RUNNING" {
|
||||
t.Fatalf("bad: %#v", driver.WaitForInstanceState)
|
||||
}
|
||||
if driver.WaitForInstanceZone != config.Zone {
|
||||
t.Fatalf("bad: %#v", driver.WaitForInstanceZone)
|
||||
}
|
||||
if driver.WaitForInstanceName != "foo" {
|
||||
t.Fatalf("bad: %#v", driver.WaitForInstanceName)
|
||||
}
|
||||
|
||||
ipRaw, ok := state.GetOk("instance_ip")
|
||||
if !ok {
|
||||
t.Fatal("should have ip")
|
||||
}
|
||||
if ip, ok := ipRaw.(string); !ok {
|
||||
t.Fatal("ip is not a string")
|
||||
} else if ip != "1.2.3.4" {
|
||||
t.Fatalf("bad ip: %s", ip)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepInstanceInfo_InternalIP(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepInstanceInfo)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
state.Put("instance_name", "foo")
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
config.UseInternalIP = true
|
||||
driver := state.Get("driver").(*DriverMock)
|
||||
driver.GetNatIPResult = "1.2.3.4"
|
||||
driver.GetInternalIPResult = "5.6.7.8"
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// Verify state
|
||||
if driver.WaitForInstanceState != "RUNNING" {
|
||||
t.Fatalf("bad: %#v", driver.WaitForInstanceState)
|
||||
}
|
||||
if driver.WaitForInstanceZone != config.Zone {
|
||||
t.Fatalf("bad: %#v", driver.WaitForInstanceZone)
|
||||
}
|
||||
if driver.WaitForInstanceName != "foo" {
|
||||
t.Fatalf("bad: %#v", driver.WaitForInstanceName)
|
||||
}
|
||||
|
||||
ipRaw, ok := state.GetOk("instance_ip")
|
||||
if !ok {
|
||||
t.Fatal("should have ip")
|
||||
}
|
||||
if ip, ok := ipRaw.(string); !ok {
|
||||
t.Fatal("ip is not a string")
|
||||
} else if ip != "5.6.7.8" {
|
||||
t.Fatalf("bad ip: %s", ip)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepInstanceInfo_getNatIPError(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepInstanceInfo)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
state.Put("instance_name", "foo")
|
||||
|
||||
driver := state.Get("driver").(*DriverMock)
|
||||
driver.GetNatIPErr = errors.New("error")
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// Verify state
|
||||
if _, ok := state.GetOk("error"); !ok {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
if _, ok := state.GetOk("instance_ip"); ok {
|
||||
t.Fatal("should NOT have instance IP")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepInstanceInfo_waitError(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepInstanceInfo)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
state.Put("instance_name", "foo")
|
||||
|
||||
errCh := make(chan error, 1)
|
||||
errCh <- errors.New("error")
|
||||
|
||||
driver := state.Get("driver").(*DriverMock)
|
||||
driver.WaitForInstanceErrCh = errCh
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// Verify state
|
||||
if _, ok := state.GetOk("error"); !ok {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
if _, ok := state.GetOk("instance_ip"); ok {
|
||||
t.Fatal("should NOT have instance IP")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepInstanceInfo_errorTimeout(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepInstanceInfo)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
<-time.After(50 * time.Millisecond)
|
||||
errCh <- nil
|
||||
}()
|
||||
|
||||
state.Put("instance_name", "foo")
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
config.StateTimeout = 1 * time.Millisecond
|
||||
|
||||
driver := state.Get("driver").(*DriverMock)
|
||||
driver.WaitForInstanceErrCh = errCh
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// Verify state
|
||||
if _, ok := state.GetOk("error"); !ok {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
if _, ok := state.GetOk("instance_ip"); ok {
|
||||
t.Fatal("should NOT have instance IP")
|
||||
}
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
package googlecompute
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/communicator"
|
||||
)
|
||||
|
||||
type MockTunnelDriver struct {
|
||||
StopTunnelCalled bool
|
||||
StartTunnelCalled bool
|
||||
StartTunnelTimeout int
|
||||
}
|
||||
|
||||
func (m *MockTunnelDriver) StopTunnel() {
|
||||
m.StopTunnelCalled = true
|
||||
}
|
||||
|
||||
func (m *MockTunnelDriver) StartTunnel(_ context.Context, _ string, timeout int) error {
|
||||
m.StartTunnelCalled = true
|
||||
m.StartTunnelTimeout = timeout
|
||||
return nil
|
||||
}
|
||||
|
||||
func getTestStepStartTunnel() *StepStartTunnel {
|
||||
return &StepStartTunnel{
|
||||
IAPConf: &IAPConfig{
|
||||
IAP: true,
|
||||
IAPLocalhostPort: 0,
|
||||
IAPHashBang: "/bin/bash",
|
||||
IAPExt: "",
|
||||
},
|
||||
CommConf: &communicator.Config{
|
||||
SSH: communicator.SSH{
|
||||
SSHPort: 1234,
|
||||
},
|
||||
},
|
||||
AccountFile: "/path/to/account_file.json",
|
||||
ProjectId: "fake-project-123",
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepStartTunnel_CreateTempScript(t *testing.T) {
|
||||
s := getTestStepStartTunnel()
|
||||
|
||||
args := []string{"compute", "start-iap-tunnel", "fakeinstance-12345",
|
||||
"1234", "--local-host-port=localhost:8774", "--zone", "us-central-b",
|
||||
"--project", "fake-project-123"}
|
||||
|
||||
scriptPath, err := s.createTempGcloudScript(args)
|
||||
if err != nil {
|
||||
t.Fatalf("Shouldn't have error building script file.")
|
||||
}
|
||||
defer os.Remove(scriptPath)
|
||||
|
||||
f, err := ioutil.ReadFile(scriptPath)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't read created inventoryfile: %s", err)
|
||||
}
|
||||
|
||||
expected := `#!/bin/bash
|
||||
|
||||
gcloud auth activate-service-account --key-file='/path/to/account_file.json'
|
||||
gcloud compute start-iap-tunnel fakeinstance-12345 1234 --local-host-port=localhost:8774 --zone us-central-b --project fake-project-123
|
||||
`
|
||||
if runtime.GOOS == "windows" {
|
||||
// in real life you'd not be passing a HashBang here, but GIGO.
|
||||
expected = `#!/bin/bash
|
||||
|
||||
call gcloud auth activate-service-account --key-file "/path/to/account_file.json"
|
||||
call gcloud compute start-iap-tunnel fakeinstance-12345 1234 --local-host-port=localhost:8774 --zone us-central-b --project fake-project-123
|
||||
`
|
||||
}
|
||||
if fmt.Sprintf("%s", f) != expected {
|
||||
t.Fatalf("script didn't match expected:\n\n expected: \n%s\n; recieved: \n%s\n", expected, f)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepStartTunnel_Cleanup(t *testing.T) {
|
||||
// Check IAP true
|
||||
s := getTestStepStartTunnel()
|
||||
td := &MockTunnelDriver{}
|
||||
s.tunnelDriver = td
|
||||
|
||||
state := testState(t)
|
||||
s.Cleanup(state)
|
||||
|
||||
if !td.StopTunnelCalled {
|
||||
t.Fatalf("Should have called StopTunnel, since IAP is true")
|
||||
}
|
||||
|
||||
// Check IAP false
|
||||
s = getTestStepStartTunnel()
|
||||
td = &MockTunnelDriver{}
|
||||
s.tunnelDriver = td
|
||||
|
||||
s.IAPConf.IAP = false
|
||||
|
||||
s.Cleanup(state)
|
||||
|
||||
if td.StopTunnelCalled {
|
||||
t.Fatalf("Should not have called StopTunnel, since IAP is false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepStartTunnel_ConfigurePort_port_set_by_user(t *testing.T) {
|
||||
s := getTestStepStartTunnel()
|
||||
s.IAPConf.IAPLocalhostPort = 8447
|
||||
|
||||
ctx := context.TODO()
|
||||
err := s.ConfigureLocalHostPort(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Shouldn't have error detecting port")
|
||||
}
|
||||
if s.IAPConf.IAPLocalhostPort != 8447 {
|
||||
t.Fatalf("Shouldn't have found new port; one was configured.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepStartTunnel_ConfigurePort_port_not_set_by_user(t *testing.T) {
|
||||
s := getTestStepStartTunnel()
|
||||
s.IAPConf.IAPLocalhostPort = 0
|
||||
|
||||
ctx := context.TODO()
|
||||
err := s.ConfigureLocalHostPort(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Shouldn't have error detecting port")
|
||||
}
|
||||
if s.IAPConf.IAPLocalhostPort == 0 {
|
||||
t.Fatalf("Should have found new port; none was configured.")
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package googlecompute
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
)
|
||||
|
||||
func TestStepTeardownInstance_impl(t *testing.T) {
|
||||
var _ multistep.Step = new(StepTeardownInstance)
|
||||
}
|
||||
|
||||
func TestStepTeardownInstance(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepTeardownInstance)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
driver := state.Get("driver").(*DriverMock)
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
if driver.DeleteInstanceName != config.InstanceName {
|
||||
t.Fatal("should've deleted instance")
|
||||
}
|
||||
if driver.DeleteInstanceZone != config.Zone {
|
||||
t.Fatalf("bad zone: %#v", driver.DeleteInstanceZone)
|
||||
}
|
||||
|
||||
// cleanup
|
||||
step.Cleanup(state)
|
||||
|
||||
if driver.DeleteDiskName != config.InstanceName {
|
||||
t.Fatal("should've deleted disk")
|
||||
}
|
||||
if driver.DeleteDiskZone != config.Zone {
|
||||
t.Fatalf("bad zone: %#v", driver.DeleteDiskZone)
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package googlecompute
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
func testState(t *testing.T) multistep.StateBag {
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("config", testConfigStruct(t))
|
||||
state.Put("driver", &DriverMock{})
|
||||
state.Put("hook", &packersdk.MockHook{})
|
||||
state.Put("ui", &packersdk.BasicUi{
|
||||
Reader: new(bytes.Buffer),
|
||||
Writer: new(bytes.Buffer),
|
||||
})
|
||||
return state
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package googlecompute
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestStepWaitStartupScript(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepWaitStartupScript)
|
||||
c := state.Get("config").(*Config)
|
||||
d := state.Get("driver").(*DriverMock)
|
||||
|
||||
testZone := "test-zone"
|
||||
testInstanceName := "test-instance-name"
|
||||
|
||||
c.Zone = testZone
|
||||
state.Put("instance_name", testInstanceName)
|
||||
|
||||
// This step stops when it gets Done back from the metadata.
|
||||
d.GetInstanceMetadataResult = StartupScriptStatusDone
|
||||
|
||||
// Run the step.
|
||||
assert.Equal(t, step.Run(context.Background(), state), multistep.ActionContinue, "Step should have passed and continued.")
|
||||
|
||||
// Check that GetInstanceMetadata was called properly.
|
||||
assert.Equal(t, d.GetInstanceMetadataZone, testZone, "Incorrect zone passed to GetInstanceMetadata.")
|
||||
assert.Equal(t, d.GetInstanceMetadataName, testInstanceName, "Incorrect instance name passed to GetInstanceMetadata.")
|
||||
}
|
||||
|
||||
func TestStepWaitStartupScript_withWrapStartupScript(t *testing.T) {
|
||||
tt := []struct {
|
||||
WrapStartup config.Trilean
|
||||
Result, Zone, MetadataName string
|
||||
}{
|
||||
{WrapStartup: config.TriTrue, Result: StartupScriptStatusDone, Zone: "test-zone", MetadataName: "test-instance-name"},
|
||||
{WrapStartup: config.TriFalse},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
tc := tc
|
||||
state := testState(t)
|
||||
step := new(StepWaitStartupScript)
|
||||
c := state.Get("config").(*Config)
|
||||
d := state.Get("driver").(*DriverMock)
|
||||
|
||||
c.StartupScriptFile = "startup.sh"
|
||||
c.WrapStartupScriptFile = tc.WrapStartup
|
||||
c.Zone = "test-zone"
|
||||
state.Put("instance_name", "test-instance-name")
|
||||
|
||||
// This step stops when it gets Done back from the metadata.
|
||||
d.GetInstanceMetadataResult = tc.Result
|
||||
|
||||
// Run the step.
|
||||
assert.Equal(t, step.Run(context.Background(), state), multistep.ActionContinue, "Step should have continued.")
|
||||
|
||||
assert.Equal(t, d.GetInstanceMetadataResult, tc.Result, "MetadataResult was not the expected value.")
|
||||
assert.Equal(t, d.GetInstanceMetadataZone, tc.Zone, "Zone was not the expected value.")
|
||||
assert.Equal(t, d.GetInstanceMetadataName, tc.MetadataName, "Instance name was not the expected value.")
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package googlecompute
|
||||
|
||||
import "testing"
|
||||
|
||||
func Test_templateCleanImageName(t *testing.T) {
|
||||
vals := []struct {
|
||||
origName string
|
||||
expected string
|
||||
}{
|
||||
// test that valid name is unchanged
|
||||
{
|
||||
origName: "abcde-012345xyz",
|
||||
expected: "abcde-012345xyz",
|
||||
},
|
||||
|
||||
//test that capital letters are converted to lowercase
|
||||
{
|
||||
origName: "ABCDE-012345xyz",
|
||||
expected: "abcde-012345xyz",
|
||||
},
|
||||
// test that periods and colons are converted to hyphens
|
||||
{
|
||||
origName: "abcde-012345v1.0:0",
|
||||
expected: "abcde-012345v1-0-0",
|
||||
},
|
||||
// Name starting with number is not valid, but not in scope of this
|
||||
// function to correct
|
||||
{
|
||||
origName: "012345v1.0:0",
|
||||
expected: "012345v1-0-0",
|
||||
},
|
||||
// Name over 64 chars is not valid, but not corrected by this function.
|
||||
{
|
||||
origName: "loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong",
|
||||
expected: "loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong",
|
||||
},
|
||||
}
|
||||
|
||||
for _, v := range vals {
|
||||
name := templateCleanImageName(v.origName)
|
||||
if name != v.expected {
|
||||
t.Fatalf("template names do not match: expected %s got %s\n", v.expected, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
builderT "github.com/hashicorp/packer-plugin-sdk/acctest"
|
||||
builderT "github.com/hashicorp/packer/acctest"
|
||||
)
|
||||
|
||||
func TestBuilderAcc_basic(t *testing.T) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:generate mapstructure-to-hcl2 -type Config,imageFilter
|
||||
//go:generate packer-sdc mapstructure-to-hcl2 -type Config,imageFilter
|
||||
|
||||
package hcloud
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by "mapstructure-to-hcl2 -type Config,imageFilter"; DO NOT EDIT.
|
||||
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
|
||||
|
||||
package hcloud
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
builderT "github.com/hashicorp/packer-plugin-sdk/acctest"
|
||||
builderT "github.com/hashicorp/packer/acctest"
|
||||
)
|
||||
|
||||
func TestBuilderAcc_basic(t *testing.T) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//go:generate struct-markdown
|
||||
//go:generate mapstructure-to-hcl2 -type Config
|
||||
//go:generate packer-sdc struct-markdown
|
||||
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
|
||||
|
||||
package hyperone
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT.
|
||||
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
|
||||
|
||||
package hyperone
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:generate struct-markdown
|
||||
//go:generate packer-sdc struct-markdown
|
||||
|
||||
package common
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//go:generate struct-markdown
|
||||
//go:generate mapstructure-to-hcl2 -type OutputConfig
|
||||
//go:generate packer-sdc struct-markdown
|
||||
//go:generate packer-sdc mapstructure-to-hcl2 -type OutputConfig
|
||||
|
||||
package common
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by "mapstructure-to-hcl2 -type OutputConfig"; DO NOT EDIT.
|
||||
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
|
||||
|
||||
package common
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//go:generate struct-markdown
|
||||
//go:generate mapstructure-to-hcl2 -type Config
|
||||
//go:generate packer-sdc struct-markdown
|
||||
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
|
||||
|
||||
package iso
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT.
|
||||
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
|
||||
|
||||
package iso
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//go:generate struct-markdown
|
||||
//go:generate mapstructure-to-hcl2 -type Config
|
||||
//go:generate packer-sdc struct-markdown
|
||||
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
|
||||
|
||||
package vmcx
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT.
|
||||
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
|
||||
|
||||
package vmcx
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:generate mapstructure-to-hcl2 -type Config
|
||||
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
|
||||
|
||||
package jdcloud
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT.
|
||||
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
|
||||
|
||||
package jdcloud
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
builderT "github.com/hashicorp/packer-plugin-sdk/acctest"
|
||||
builderT "github.com/hashicorp/packer/acctest"
|
||||
)
|
||||
|
||||
func TestBuilderAcc_basic(t *testing.T) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:generate mapstructure-to-hcl2 -type Config
|
||||
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
|
||||
|
||||
package linode
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT.
|
||||
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
|
||||
|
||||
package linode
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//go:generate struct-markdown
|
||||
//go:generate mapstructure-to-hcl2 -type Config
|
||||
//go:generate packer-sdc struct-markdown
|
||||
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
|
||||
|
||||
package lxc
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT.
|
||||
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
|
||||
|
||||
package lxc
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//go:generate struct-markdown
|
||||
//go:generate mapstructure-to-hcl2 -type Config
|
||||
//go:generate packer-sdc struct-markdown
|
||||
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
|
||||
|
||||
package lxd
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT.
|
||||
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
|
||||
|
||||
package lxd
|
||||
|
||||
|
||||
+36
-54
@@ -32,7 +32,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||
}
|
||||
|
||||
func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) (packersdk.Artifact, error) {
|
||||
ui.Message("Creating Naver Cloud Platform Connection ...")
|
||||
ui.Message("Creating NAVER CLOUD PLATFORM Connection ...")
|
||||
config := Config{
|
||||
AccessKey: b.config.AccessKey,
|
||||
SecretKey: b.config.SecretKey,
|
||||
@@ -46,60 +46,42 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
b.stateBag.Put("hook", hook)
|
||||
b.stateBag.Put("ui", ui)
|
||||
|
||||
var steps []multistep.Step
|
||||
|
||||
steps = []multistep.Step{}
|
||||
|
||||
if b.config.Comm.Type == "ssh" {
|
||||
steps = []multistep.Step{
|
||||
NewStepValidateTemplate(conn, ui, &b.config),
|
||||
NewStepCreateLoginKey(conn, ui),
|
||||
NewStepCreateServerInstance(conn, ui, &b.config),
|
||||
NewStepCreateBlockStorageInstance(conn, ui, &b.config),
|
||||
NewStepGetRootPassword(conn, ui, &b.config),
|
||||
NewStepCreatePublicIPInstance(conn, ui, &b.config),
|
||||
&communicator.StepConnectSSH{
|
||||
Config: &b.config.Comm,
|
||||
Host: func(stateBag multistep.StateBag) (string, error) {
|
||||
return stateBag.Get("PublicIP").(string), nil
|
||||
},
|
||||
SSHConfig: b.config.Comm.SSHConfigFunc(),
|
||||
steps := []multistep.Step{
|
||||
NewStepValidateTemplate(conn, ui, &b.config),
|
||||
NewStepCreateLoginKey(conn, ui, &b.config),
|
||||
multistep.If(b.config.SupportVPC, NewStepCreateInitScript(conn, ui, &b.config)),
|
||||
multistep.If(b.config.SupportVPC, NewStepCreateAccessControlGroup(conn, ui, &b.config)),
|
||||
NewStepCreateServerInstance(conn, ui, &b.config),
|
||||
NewStepCreateBlockStorage(conn, ui, &b.config),
|
||||
NewStepGetRootPassword(conn, ui, &b.config),
|
||||
NewStepCreatePublicIP(conn, ui, &b.config),
|
||||
multistep.If(b.config.Comm.Type == "ssh", &communicator.StepConnectSSH{
|
||||
Config: &b.config.Comm,
|
||||
Host: func(stateBag multistep.StateBag) (string, error) {
|
||||
return stateBag.Get("public_ip").(string), nil
|
||||
},
|
||||
&commonsteps.StepProvision{},
|
||||
&commonsteps.StepCleanupTempKeys{
|
||||
Comm: &b.config.Comm,
|
||||
SSHConfig: b.config.Comm.SSHConfigFunc(),
|
||||
}),
|
||||
multistep.If(b.config.Comm.Type == "winrm", &communicator.StepConnectWinRM{
|
||||
Config: &b.config.Comm,
|
||||
Host: func(stateBag multistep.StateBag) (string, error) {
|
||||
return stateBag.Get("public_ip").(string), nil
|
||||
},
|
||||
NewStepStopServerInstance(conn, ui),
|
||||
NewStepCreateServerImage(conn, ui, &b.config),
|
||||
NewStepDeleteBlockStorageInstance(conn, ui, &b.config),
|
||||
NewStepTerminateServerInstance(conn, ui),
|
||||
}
|
||||
} else if b.config.Comm.Type == "winrm" {
|
||||
steps = []multistep.Step{
|
||||
NewStepValidateTemplate(conn, ui, &b.config),
|
||||
NewStepCreateLoginKey(conn, ui),
|
||||
NewStepCreateServerInstance(conn, ui, &b.config),
|
||||
NewStepCreateBlockStorageInstance(conn, ui, &b.config),
|
||||
NewStepGetRootPassword(conn, ui, &b.config),
|
||||
NewStepCreatePublicIPInstance(conn, ui, &b.config),
|
||||
&communicator.StepConnectWinRM{
|
||||
Config: &b.config.Comm,
|
||||
Host: func(stateBag multistep.StateBag) (string, error) {
|
||||
return stateBag.Get("PublicIP").(string), nil
|
||||
},
|
||||
WinRMConfig: func(state multistep.StateBag) (*communicator.WinRMConfig, error) {
|
||||
return &communicator.WinRMConfig{
|
||||
Username: b.config.Comm.WinRMUser,
|
||||
Password: b.config.Comm.WinRMPassword,
|
||||
}, nil
|
||||
},
|
||||
WinRMConfig: func(state multistep.StateBag) (*communicator.WinRMConfig, error) {
|
||||
return &communicator.WinRMConfig{
|
||||
Username: b.config.Comm.WinRMUser,
|
||||
Password: b.config.Comm.WinRMPassword,
|
||||
}, nil
|
||||
},
|
||||
&commonsteps.StepProvision{},
|
||||
NewStepStopServerInstance(conn, ui),
|
||||
NewStepCreateServerImage(conn, ui, &b.config),
|
||||
NewStepDeleteBlockStorageInstance(conn, ui, &b.config),
|
||||
NewStepTerminateServerInstance(conn, ui),
|
||||
}
|
||||
}),
|
||||
&commonsteps.StepProvision{},
|
||||
multistep.If(b.config.Comm.Type == "ssh", &commonsteps.StepCleanupTempKeys{
|
||||
Comm: &b.config.Comm,
|
||||
}),
|
||||
NewStepStopServerInstance(conn, ui, &b.config),
|
||||
NewStepCreateServerImage(conn, ui, &b.config),
|
||||
NewStepDeleteBlockStorage(conn, ui, &b.config),
|
||||
NewStepTerminateServerInstance(conn, ui, &b.config),
|
||||
}
|
||||
|
||||
// Run!
|
||||
@@ -107,7 +89,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
b.runner.Run(ctx, b.stateBag)
|
||||
|
||||
// If there was an error, return that
|
||||
if rawErr, ok := b.stateBag.GetOk("Error"); ok {
|
||||
if rawErr, ok := b.stateBag.GetOk("error"); ok {
|
||||
return nil, rawErr.(error)
|
||||
}
|
||||
|
||||
@@ -116,7 +98,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
StateData: map[string]interface{}{"generated_data": b.stateBag.Get("generated_data")},
|
||||
}
|
||||
|
||||
if serverImage, ok := b.stateBag.GetOk("memberServerImage"); ok {
|
||||
if serverImage, ok := b.stateBag.GetOk("member_server_image"); ok {
|
||||
artifact.MemberServerImage = serverImage.(*server.MemberServerImage)
|
||||
}
|
||||
|
||||
|
||||
+49
-24
@@ -1,5 +1,5 @@
|
||||
//go:generate struct-markdown
|
||||
//go:generate mapstructure-to-hcl2 -type Config
|
||||
//go:generate packer-sdc struct-markdown
|
||||
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
|
||||
|
||||
package ncloud
|
||||
|
||||
@@ -8,6 +8,9 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vpc"
|
||||
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
|
||||
|
||||
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/ncloud"
|
||||
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/server"
|
||||
"github.com/hashicorp/packer-plugin-sdk/common"
|
||||
@@ -50,12 +53,22 @@ type Config struct {
|
||||
BlockStorageSize int `mapstructure:"block_storage_size" required:"false"`
|
||||
// Name of the region where you want to create an image.
|
||||
// (default: Korea)
|
||||
Region string `mapstructure:"region" required:"false"`
|
||||
Region string `mapstructure:"region" required:"false"`
|
||||
RegionCode string `mapstructure:"region_code" required:"false"`
|
||||
// Deprecated
|
||||
AccessControlGroupConfigurationNo string `mapstructure:"access_control_group_configuration_no" required:"false"`
|
||||
// This is used to allow
|
||||
// winrm access when you create a Windows server. An ACG that specifies an
|
||||
// access source (0.0.0.0/0) and allowed port (5985) must be created in
|
||||
// advance.
|
||||
AccessControlGroupConfigurationNo string `mapstructure:"access_control_group_configuration_no" required:"false"`
|
||||
// advance if you use CLASSIC env. If this field is left blank,
|
||||
// Packer will create temporary ACG for automatically in VPC environment.
|
||||
AccessControlGroupNo string `mapstructure:"access_control_group_no" required:"false"`
|
||||
SupportVPC bool `mapstructure:"support_vpc" required:"false"`
|
||||
// The ID of the Subnet where you want to place the Server Instance. If this field is left blank, Packer will try to get the Public Subnet ID from the `vpc_no`.
|
||||
SubnetNo string `mapstructure:"subnet_no" required:"false"`
|
||||
// The ID of the VPC where you want to place the Server Instance. If this field is left blank, Packer will try to get the VPC ID from the `subnet_no`.
|
||||
// (You are required to least one between two parameters if u want using VPC environment: `vpc_no` or `subnet_no`)
|
||||
VpcNo string `mapstructure:"vpc_no" required:"false"`
|
||||
|
||||
Comm communicator.Config `mapstructure:",squash"`
|
||||
ctx *interpolate.Context
|
||||
@@ -63,7 +76,7 @@ type Config struct {
|
||||
|
||||
// NewConfig checks parameters
|
||||
func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
||||
warnings := []string{}
|
||||
var warnings []string
|
||||
|
||||
err := config.Decode(c, &config.DecodeOpts{
|
||||
Interpolate: true,
|
||||
@@ -81,59 +94,67 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
||||
}
|
||||
|
||||
if c.AccessKey == "" {
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("access_key is required"))
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("`access_key` is required"))
|
||||
}
|
||||
|
||||
if c.SecretKey == "" {
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("secret_key is required"))
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("`secret_key` is required"))
|
||||
}
|
||||
|
||||
if c.MemberServerImageNo == "" && c.ServerImageProductCode == "" {
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("server_image_product_code or member_server_image_no is required"))
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("`server_image_product_code` or `member_server_image_no` is required"))
|
||||
}
|
||||
|
||||
if c.MemberServerImageNo != "" && c.ServerImageProductCode != "" {
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("Only one of server_image_product_code and member_server_image_no can be set"))
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("only one of `server_image_product_code` and `member_server_image_no` can be set"))
|
||||
}
|
||||
|
||||
if c.ServerImageProductCode != "" && len(c.ServerImageProductCode) > 20 {
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("If server_image_product_code field is set, length of server_image_product_code should be max 20"))
|
||||
if c.ServerImageProductCode != "" && len(c.ServerImageProductCode) > 50 {
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("if `server_image_product_code` field is set, length of `server_image_product_code` should be max 50"))
|
||||
}
|
||||
|
||||
if c.ServerProductCode != "" && len(c.ServerProductCode) > 20 {
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("If server_product_code field is set, length of server_product_code should be max 20"))
|
||||
if c.ServerProductCode != "" && len(c.ServerProductCode) > 50 {
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("if `server_product_code` field is set, length of `server_product_code` should be max 50"))
|
||||
}
|
||||
|
||||
if c.ServerImageName != "" && (len(c.ServerImageName) < 3 || len(c.ServerImageName) > 30) {
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("If server_image_name field is set, length of server_image_name should be min 3 and max 20"))
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("if `server_image_name` field is set, length of `server_image_name` should be min 3 and max 30"))
|
||||
}
|
||||
|
||||
if c.ServerImageDescription != "" && len(c.ServerImageDescription) > 1000 {
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("If server_image_description field is set, length of server_image_description should be max 1000"))
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("if `server_image_description` field is set, length of `server_image_description` should be max 1000"))
|
||||
}
|
||||
|
||||
if c.BlockStorageSize != 0 {
|
||||
if c.BlockStorageSize < 10 || c.BlockStorageSize > 2000 {
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("The size of BlockStorageSize is at least 10 GB and up to 2000GB"))
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("the size of `block_storage_size` is at least 10 GB and up to 2000GB"))
|
||||
} else if int(c.BlockStorageSize/10)*10 != c.BlockStorageSize {
|
||||
return nil, errors.New("BlockStorageSize must be a multiple of 10 GB")
|
||||
}
|
||||
}
|
||||
|
||||
if c.UserData != "" && c.UserDataFile != "" {
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("Only one of user_data or user_data_file can be specified."))
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("only one of `user_data` or `user_data_file` can be specified."))
|
||||
} else if c.UserDataFile != "" {
|
||||
if _, err := os.Stat(c.UserDataFile); err != nil {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("user_data_file not found: %s", c.UserDataFile))
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("`user_data_file` not found: %s", c.UserDataFile))
|
||||
}
|
||||
}
|
||||
|
||||
if c.UserData != "" && len(c.UserData) > 21847 {
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("If user_data field is set, length of UserData should be max 21847"))
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("if `user_data` field is set, length of UserData should be max 21847"))
|
||||
}
|
||||
|
||||
if c.Comm.Type == "winrm" && c.AccessControlGroupConfigurationNo == "" {
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("If Communicator is winrm, access_control_group_configuration_no is required"))
|
||||
if c.AccessControlGroupConfigurationNo != "" {
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("`access_control_group_configuration_no` is deprecated, please use `access_control_group_no` instead"))
|
||||
}
|
||||
|
||||
if c.VpcNo != "" || c.SubnetNo != "" {
|
||||
c.SupportVPC = true
|
||||
}
|
||||
|
||||
if c.Comm.Type == "winrm" && c.AccessControlGroupNo == "" && !c.SupportVPC {
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("if Communicator is winrm, `access_control_group_no` (allow 5986 port) is required in `CLASSIC` environment"))
|
||||
}
|
||||
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
@@ -144,7 +165,9 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
||||
}
|
||||
|
||||
type NcloudAPIClient struct {
|
||||
server *server.APIClient
|
||||
server *server.APIClient
|
||||
vserver *vserver.APIClient
|
||||
vpc *vpc.APIClient
|
||||
}
|
||||
|
||||
func (c *Config) Client() (*NcloudAPIClient, error) {
|
||||
@@ -153,6 +176,8 @@ func (c *Config) Client() (*NcloudAPIClient, error) {
|
||||
SecretKey: c.SecretKey,
|
||||
}
|
||||
return &NcloudAPIClient{
|
||||
server: server.NewAPIClient(server.NewConfiguration(apiKey)),
|
||||
server: server.NewAPIClient(server.NewConfiguration(apiKey)),
|
||||
vserver: vserver.NewAPIClient(vserver.NewConfiguration(apiKey)),
|
||||
vpc: vpc.NewAPIClient(vpc.NewConfiguration(apiKey)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT.
|
||||
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
|
||||
|
||||
package ncloud
|
||||
|
||||
@@ -29,7 +29,12 @@ type FlatConfig struct {
|
||||
UserDataFile *string `mapstructure:"user_data_file" required:"false" cty:"user_data_file" hcl:"user_data_file"`
|
||||
BlockStorageSize *int `mapstructure:"block_storage_size" required:"false" cty:"block_storage_size" hcl:"block_storage_size"`
|
||||
Region *string `mapstructure:"region" required:"false" cty:"region" hcl:"region"`
|
||||
RegionCode *string `mapstructure:"region_code" required:"false" cty:"region_code" hcl:"region_code"`
|
||||
AccessControlGroupConfigurationNo *string `mapstructure:"access_control_group_configuration_no" required:"false" cty:"access_control_group_configuration_no" hcl:"access_control_group_configuration_no"`
|
||||
AccessControlGroupNo *string `mapstructure:"access_control_group_no" required:"false" cty:"access_control_group_no" hcl:"access_control_group_no"`
|
||||
SupportVPC *bool `mapstructure:"support_vpc" required:"false" cty:"support_vpc" hcl:"support_vpc"`
|
||||
SubnetNo *string `mapstructure:"subnet_no" required:"false" cty:"subnet_no" hcl:"subnet_no"`
|
||||
VpcNo *string `mapstructure:"vpc_no" required:"false" cty:"vpc_no" hcl:"vpc_no"`
|
||||
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
|
||||
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
|
||||
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
|
||||
@@ -112,7 +117,12 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"user_data_file": &hcldec.AttrSpec{Name: "user_data_file", Type: cty.String, Required: false},
|
||||
"block_storage_size": &hcldec.AttrSpec{Name: "block_storage_size", Type: cty.Number, Required: false},
|
||||
"region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false},
|
||||
"region_code": &hcldec.AttrSpec{Name: "region_code", Type: cty.String, Required: false},
|
||||
"access_control_group_configuration_no": &hcldec.AttrSpec{Name: "access_control_group_configuration_no", Type: cty.String, Required: false},
|
||||
"access_control_group_no": &hcldec.AttrSpec{Name: "access_control_group_no", Type: cty.String, Required: false},
|
||||
"support_vpc": &hcldec.AttrSpec{Name: "support_vpc", Type: cty.Bool, Required: false},
|
||||
"subnet_no": &hcldec.AttrSpec{Name: "subnet_no", Type: cty.String, Required: false},
|
||||
"vpc_no": &hcldec.AttrSpec{Name: "vpc_no", Type: cty.String, Required: false},
|
||||
"communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false},
|
||||
"pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false},
|
||||
"ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false},
|
||||
|
||||
@@ -119,15 +119,15 @@ func TestEmptyConfig(t *testing.T) {
|
||||
t.Error("Expected Config to require 'access_key', 'secret_key' and some mandatory fields, but it did not")
|
||||
}
|
||||
|
||||
if !strings.Contains(err.Error(), "access_key is required") {
|
||||
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") {
|
||||
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") {
|
||||
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")
|
||||
}
|
||||
}
|
||||
@@ -144,7 +144,7 @@ func TestExistsBothServerImageProductCodeAndMemberServerImageNoConfig(t *testing
|
||||
var c Config
|
||||
_, err := c.Prepare(raw)
|
||||
|
||||
if !strings.Contains(err.Error(), "Only one of server_image_product_code and member_server_image_no can be set") {
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
func processStepResult(err error, sayError func(error), state multistep.StateBag) multistep.StepAction {
|
||||
if err != nil {
|
||||
state.Put("Error", err)
|
||||
state.Put("error", err)
|
||||
sayError(err)
|
||||
|
||||
return multistep.ActionHalt
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
package ncloud
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/ncloud"
|
||||
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type StepCreateAccessControlGroup struct {
|
||||
Conn *NcloudAPIClient
|
||||
GetAccessControlGroup func(acgNo string) (*vserver.AccessControlGroup, error)
|
||||
CreateAccessControlGroup func() (string, error)
|
||||
AddAccessControlGroupRule func(acgNo string) error
|
||||
Say func(message string)
|
||||
Error func(e error)
|
||||
Config *Config
|
||||
createdAcgNo string
|
||||
}
|
||||
|
||||
func NewStepCreateAccessControlGroup(conn *NcloudAPIClient, ui packersdk.Ui, config *Config) *StepCreateAccessControlGroup {
|
||||
var step = &StepCreateAccessControlGroup{
|
||||
Conn: conn,
|
||||
Say: func(message string) { ui.Say(message) },
|
||||
Error: func(e error) { ui.Error(e.Error()) },
|
||||
Config: config,
|
||||
}
|
||||
|
||||
if config.SupportVPC {
|
||||
step.GetAccessControlGroup = step.getVpcAccessControlGroup
|
||||
step.CreateAccessControlGroup = step.createVpcAccessControlGroup
|
||||
step.AddAccessControlGroupRule = step.addVpcAccessControlGroupRule
|
||||
}
|
||||
|
||||
return step
|
||||
}
|
||||
|
||||
func (s *StepCreateAccessControlGroup) createVpcAccessControlGroup() (string, error) {
|
||||
reqParam := &vserver.CreateAccessControlGroupRequest{
|
||||
RegionCode: &s.Config.RegionCode,
|
||||
AccessControlGroupName: &s.Config.ServerImageName,
|
||||
AccessControlGroupDescription: ncloud.String("Temporary ACG for packer"),
|
||||
VpcNo: &s.Config.VpcNo,
|
||||
}
|
||||
|
||||
resp, err := s.Conn.vserver.V2Api.CreateAccessControlGroup(reqParam)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if resp != nil && *resp.TotalRows > 0 {
|
||||
return *resp.AccessControlGroupList[0].AccessControlGroupNo, nil
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (s *StepCreateAccessControlGroup) addVpcAccessControlGroupRule(acgNo string) error {
|
||||
_, err := s.Conn.vserver.V2Api.AddAccessControlGroupInboundRule(&vserver.AddAccessControlGroupInboundRuleRequest{
|
||||
RegionCode: &s.Config.RegionCode,
|
||||
AccessControlGroupNo: &acgNo,
|
||||
VpcNo: &s.Config.VpcNo,
|
||||
AccessControlGroupRuleList: []*vserver.AddAccessControlGroupRuleParameter{
|
||||
{
|
||||
IpBlock: ncloud.String("0.0.0.0/0"),
|
||||
PortRange: ncloud.String("22"),
|
||||
ProtocolTypeCode: ncloud.String("TCP"),
|
||||
},
|
||||
{
|
||||
IpBlock: ncloud.String("0.0.0.0/0"),
|
||||
PortRange: ncloud.String("3389"),
|
||||
ProtocolTypeCode: ncloud.String("TCP"),
|
||||
},
|
||||
{
|
||||
IpBlock: ncloud.String("0.0.0.0/0"),
|
||||
PortRange: ncloud.String("5985"),
|
||||
ProtocolTypeCode: ncloud.String("TCP"),
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = s.Conn.vserver.V2Api.AddAccessControlGroupOutboundRule(&vserver.AddAccessControlGroupOutboundRuleRequest{
|
||||
RegionCode: &s.Config.RegionCode,
|
||||
AccessControlGroupNo: &acgNo,
|
||||
VpcNo: &s.Config.VpcNo,
|
||||
AccessControlGroupRuleList: []*vserver.AddAccessControlGroupRuleParameter{
|
||||
{
|
||||
IpBlock: ncloud.String("0.0.0.0/0"),
|
||||
ProtocolTypeCode: ncloud.String("ICMP"),
|
||||
},
|
||||
{
|
||||
IpBlock: ncloud.String("0.0.0.0/0"),
|
||||
PortRange: ncloud.String("1-65535"),
|
||||
ProtocolTypeCode: ncloud.String("TCP"),
|
||||
},
|
||||
{
|
||||
IpBlock: ncloud.String("0.0.0.0/0"),
|
||||
PortRange: ncloud.String("1-65535"),
|
||||
ProtocolTypeCode: ncloud.String("UDP"),
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StepCreateAccessControlGroup) deleteVpcAccessControlGroup(id string) error {
|
||||
reqParam := &vserver.DeleteAccessControlGroupRequest{
|
||||
RegionCode: &s.Config.RegionCode,
|
||||
VpcNo: &s.Config.VpcNo,
|
||||
AccessControlGroupNo: ncloud.String(id),
|
||||
}
|
||||
|
||||
_, err := s.Conn.vserver.V2Api.DeleteAccessControlGroup(reqParam)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StepCreateAccessControlGroup) getVpcAccessControlGroup(id string) (*vserver.AccessControlGroup, error) {
|
||||
reqParam := &vserver.GetAccessControlGroupDetailRequest{
|
||||
RegionCode: &s.Config.RegionCode,
|
||||
AccessControlGroupNo: ncloud.String(id),
|
||||
}
|
||||
|
||||
resp, err := s.Conn.vserver.V2Api.GetAccessControlGroupDetail(reqParam)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp != nil && *resp.TotalRows > 0 {
|
||||
return resp.AccessControlGroupList[0], nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *StepCreateAccessControlGroup) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
s.Say("Create temporary ACG")
|
||||
if len(s.Config.AccessControlGroupNo) > 0 {
|
||||
acg, err := s.GetAccessControlGroup(s.Config.AccessControlGroupNo)
|
||||
if err != nil || acg == nil {
|
||||
err := fmt.Errorf("couldn't find specified ACG: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
log.Printf("Using specified ACG: %v", s.Config.AccessControlGroupNo)
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
acgNo, err := s.CreateAccessControlGroup()
|
||||
s.Say(fmt.Sprintf("Creating temporary ACG [%s]", acgNo))
|
||||
if err != nil || len(acgNo) == 0 {
|
||||
err := fmt.Errorf("couldn't create ACG for VPC: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
s.createdAcgNo = acgNo
|
||||
|
||||
s.Say(fmt.Sprintf("Creating temporary rules ACG [%s]", acgNo))
|
||||
err = s.AddAccessControlGroupRule(acgNo)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("couldn't create ACG rules for SSH or winrm: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
state.Put("access_control_group_no", acgNo)
|
||||
|
||||
return processStepResult(err, s.Error, state)
|
||||
}
|
||||
|
||||
func (s *StepCreateAccessControlGroup) Cleanup(state multistep.StateBag) {
|
||||
if s.createdAcgNo == "" {
|
||||
return
|
||||
}
|
||||
|
||||
err := s.deleteVpcAccessControlGroup(s.createdAcgNo)
|
||||
if err != nil {
|
||||
s.Error(fmt.Errorf("error cleaning up ACG. Please delete the ACG manually: err: %s; ACG No: %s", err, s.createdAcgNo))
|
||||
}
|
||||
|
||||
s.Say("Clean up temporary ACG")
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package ncloud
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
)
|
||||
|
||||
func TestStepCreateAccessControlGroupShouldFailIfOperationCreateAccessControlGroupFails(t *testing.T) {
|
||||
var testSubject = &StepCreateAccessControlGroup{
|
||||
CreateAccessControlGroup: func() (string, error) { return "", fmt.Errorf("!! Unit Test FAIL !!") },
|
||||
Say: func(message string) {},
|
||||
Error: func(e error) {},
|
||||
Config: &Config{
|
||||
Region: "Korea",
|
||||
SupportVPC: true,
|
||||
},
|
||||
}
|
||||
|
||||
stateBag := createTestStateBagStepCreateAccessControlGroup()
|
||||
|
||||
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 TestStepCreateAccessControlGroupShouldPassIfOperationCreateAccessControlGroupPasses(t *testing.T) {
|
||||
var testSubject = &StepCreateAccessControlGroup{
|
||||
CreateAccessControlGroup: func() (string, error) { return "123", nil },
|
||||
AddAccessControlGroupRule: func(acgNo string) error {
|
||||
return nil
|
||||
},
|
||||
Say: func(message string) {},
|
||||
Error: func(error) {},
|
||||
Config: &Config{
|
||||
Region: "Korea",
|
||||
SupportVPC: true,
|
||||
},
|
||||
}
|
||||
|
||||
stateBag := createTestStateBagStepCreateAccessControlGroup()
|
||||
|
||||
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 createTestStateBagStepCreateAccessControlGroup() multistep.StateBag {
|
||||
stateBag := new(multistep.BasicStateBag)
|
||||
|
||||
return stateBag
|
||||
}
|
||||
@@ -9,38 +9,54 @@ import (
|
||||
|
||||
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/ncloud"
|
||||
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/server"
|
||||
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
const (
|
||||
BlockStorageStatusAttached = "ATTAC"
|
||||
BlockStorageStatusDetached = "DETAC"
|
||||
)
|
||||
|
||||
// StepCreateBlockStorageInstance struct is for making extra block storage
|
||||
type StepCreateBlockStorageInstance struct {
|
||||
Conn *NcloudAPIClient
|
||||
CreateBlockStorageInstance func(serverInstanceNo string) (*string, error)
|
||||
Say func(message string)
|
||||
Error func(e error)
|
||||
Config *Config
|
||||
type StepCreateBlockStorage struct {
|
||||
Conn *NcloudAPIClient
|
||||
CreateBlockStorage func(serverInstanceNo string) (*string, error)
|
||||
DeleteBlockStorage func(blockStorageInstanceNo string) error
|
||||
WaiterBlockStorageStatus func(conn *NcloudAPIClient, blockStorageInstanceNo *string, status string, timeout time.Duration) error
|
||||
Say func(message string)
|
||||
Error func(e error)
|
||||
Config *Config
|
||||
}
|
||||
|
||||
// NewStepCreateBlockStorageInstance make StepCreateBlockStorage struct to make extra block storage
|
||||
func NewStepCreateBlockStorageInstance(conn *NcloudAPIClient, ui packersdk.Ui, config *Config) *StepCreateBlockStorageInstance {
|
||||
var step = &StepCreateBlockStorageInstance{
|
||||
func NewStepCreateBlockStorage(conn *NcloudAPIClient, ui packersdk.Ui, config *Config) *StepCreateBlockStorage {
|
||||
var step = &StepCreateBlockStorage{
|
||||
Conn: conn,
|
||||
Say: func(message string) { ui.Say(message) },
|
||||
Error: func(e error) { ui.Error(e.Error()) },
|
||||
Config: config,
|
||||
}
|
||||
|
||||
step.CreateBlockStorageInstance = step.createBlockStorageInstance
|
||||
if config.SupportVPC {
|
||||
step.CreateBlockStorage = step.createVpcBlockStorage
|
||||
step.DeleteBlockStorage = step.deleteVpcBlockStorage
|
||||
step.WaiterBlockStorageStatus = waiterVpcBlockStorageStatus
|
||||
} else {
|
||||
step.CreateBlockStorage = step.createClassicBlockStorage
|
||||
step.DeleteBlockStorage = step.deleteClassicBlockStorage
|
||||
step.WaiterBlockStorageStatus = waiterClassicBlockStorageStatus
|
||||
}
|
||||
|
||||
return step
|
||||
}
|
||||
|
||||
func (s *StepCreateBlockStorageInstance) createBlockStorageInstance(serverInstanceNo string) (*string, error) {
|
||||
|
||||
reqParams := new(server.CreateBlockStorageInstanceRequest)
|
||||
reqParams.BlockStorageSize = ncloud.Int64(int64(s.Config.BlockStorageSize))
|
||||
reqParams.ServerInstanceNo = &serverInstanceNo
|
||||
func (s *StepCreateBlockStorage) createClassicBlockStorage(serverInstanceNo string) (*string, error) {
|
||||
reqParams := &server.CreateBlockStorageInstanceRequest{
|
||||
BlockStorageSize: ncloud.Int64(int64(s.Config.BlockStorageSize)),
|
||||
ServerInstanceNo: &serverInstanceNo,
|
||||
}
|
||||
|
||||
resp, err := s.Conn.server.V2Api.CreateBlockStorageInstance(reqParams)
|
||||
if err != nil {
|
||||
@@ -50,31 +66,82 @@ func (s *StepCreateBlockStorageInstance) createBlockStorageInstance(serverInstan
|
||||
blockStorageInstance := resp.BlockStorageInstanceList[0]
|
||||
log.Println("Block Storage Instance information : ", blockStorageInstance.BlockStorageInstanceNo)
|
||||
|
||||
if err := waiterBlockStorageInstanceStatus(s.Conn, blockStorageInstance.BlockStorageInstanceNo, "ATTAC", 10*time.Minute); err != nil {
|
||||
if err := waiterClassicBlockStorageStatus(s.Conn, blockStorageInstance.BlockStorageInstanceNo, BlockStorageStatusAttached, 10*time.Minute); err != nil {
|
||||
return nil, errors.New("TIMEOUT : Block Storage instance status is not attached")
|
||||
}
|
||||
|
||||
return blockStorageInstance.BlockStorageInstanceNo, nil
|
||||
}
|
||||
|
||||
func (s *StepCreateBlockStorageInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
func (s *StepCreateBlockStorage) createVpcBlockStorage(serverInstanceNo string) (*string, error) {
|
||||
reqParams := &vserver.CreateBlockStorageInstanceRequest{
|
||||
RegionCode: &s.Config.RegionCode,
|
||||
BlockStorageSize: ncloud.Int32(int32(s.Config.BlockStorageSize)),
|
||||
BlockStorageDescription: nil,
|
||||
ServerInstanceNo: &serverInstanceNo,
|
||||
BlockStorageSnapshotInstanceNo: nil,
|
||||
ZoneCode: nil,
|
||||
}
|
||||
|
||||
resp, err := s.Conn.vserver.V2Api.CreateBlockStorageInstance(reqParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blockStorageInstance := resp.BlockStorageInstanceList[0]
|
||||
log.Println("Block Storage Instance information : ", blockStorageInstance.BlockStorageInstanceNo)
|
||||
|
||||
if err := s.WaiterBlockStorageStatus(s.Conn, blockStorageInstance.BlockStorageInstanceNo, BlockStorageStatusAttached, 10*time.Minute); err != nil {
|
||||
return nil, errors.New("TIMEOUT : Block Storage instance status is not attached")
|
||||
}
|
||||
|
||||
return blockStorageInstance.BlockStorageInstanceNo, nil
|
||||
}
|
||||
|
||||
func (s *StepCreateBlockStorage) deleteClassicBlockStorage(blockStorageInstanceNo string) error {
|
||||
reqParams := &server.DeleteBlockStorageInstancesRequest{
|
||||
BlockStorageInstanceNoList: []*string{&blockStorageInstanceNo},
|
||||
}
|
||||
|
||||
_, err := s.Conn.server.V2Api.DeleteBlockStorageInstances(reqParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StepCreateBlockStorage) deleteVpcBlockStorage(blockStorageInstanceNo string) error {
|
||||
reqParams := &vserver.DeleteBlockStorageInstancesRequest{
|
||||
BlockStorageInstanceNoList: []*string{&blockStorageInstanceNo},
|
||||
}
|
||||
|
||||
_, err := s.Conn.vserver.V2Api.DeleteBlockStorageInstances(reqParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StepCreateBlockStorage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
if s.Config.BlockStorageSize == 0 {
|
||||
return processStepResult(nil, s.Error, state)
|
||||
}
|
||||
|
||||
s.Say("Create extra block storage instance")
|
||||
|
||||
serverInstanceNo := state.Get("InstanceNo").(string)
|
||||
serverInstanceNo := state.Get("instance_no").(string)
|
||||
|
||||
blockStorageInstanceNo, err := s.CreateBlockStorageInstance(serverInstanceNo)
|
||||
blockStorageInstanceNo, err := s.CreateBlockStorage(serverInstanceNo)
|
||||
if err == nil {
|
||||
state.Put("BlockStorageInstanceNo", *blockStorageInstanceNo)
|
||||
state.Put("block_storage_instance_no", *blockStorageInstanceNo)
|
||||
}
|
||||
|
||||
return processStepResult(err, s.Error, state)
|
||||
}
|
||||
|
||||
func (s *StepCreateBlockStorageInstance) Cleanup(state multistep.StateBag) {
|
||||
func (s *StepCreateBlockStorage) Cleanup(state multistep.StateBag) {
|
||||
_, cancelled := state.GetOk(multistep.StateCancelled)
|
||||
_, halted := state.GetOk(multistep.StateHalted)
|
||||
|
||||
@@ -86,20 +153,17 @@ func (s *StepCreateBlockStorageInstance) Cleanup(state multistep.StateBag) {
|
||||
return
|
||||
}
|
||||
|
||||
if blockStorageInstanceNo, ok := state.GetOk("BlockStorageInstanceNo"); ok {
|
||||
if blockStorageInstanceNo, ok := state.GetOk("block_storage_instance_no"); ok {
|
||||
s.Say("Clean up Block Storage Instance")
|
||||
reqParams := server.DeleteBlockStorageInstancesRequest{
|
||||
BlockStorageInstanceNoList: []*string{blockStorageInstanceNo.(*string)},
|
||||
}
|
||||
blockStorageInstanceList, err := s.Conn.server.V2Api.DeleteBlockStorageInstances(&reqParams)
|
||||
err := s.DeleteBlockStorage(blockStorageInstanceNo.(string))
|
||||
if err != nil {
|
||||
s.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
s.Say(fmt.Sprintf("Block Storage Instance is deleted. Block Storage InstanceNo is %s", blockStorageInstanceNo.(string)))
|
||||
log.Println("Block Storage Instance information : ", blockStorageInstanceList.BlockStorageInstanceList[0])
|
||||
|
||||
if err := waiterBlockStorageInstanceStatus(s.Conn, blockStorageInstanceNo.(*string), "DETAC", time.Minute); err != nil {
|
||||
if err := s.WaiterBlockStorageStatus(s.Conn, blockStorageInstanceNo.(*string), BlockStorageStatusDetached, time.Minute); err != nil {
|
||||
s.Say("TIMEOUT : Block Storage instance status is not deattached")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,11 +10,11 @@ import (
|
||||
|
||||
func TestStepCreateBlockStorageInstanceShouldFailIfOperationCreateBlockStorageInstanceFails(t *testing.T) {
|
||||
|
||||
var testSubject = &StepCreateBlockStorageInstance{
|
||||
CreateBlockStorageInstance: func(serverInstanceNo string) (*string, error) { return nil, fmt.Errorf("!! Unit Test FAIL !!") },
|
||||
Say: func(message string) {},
|
||||
Error: func(e error) {},
|
||||
Config: new(Config),
|
||||
var testSubject = &StepCreateBlockStorage{
|
||||
CreateBlockStorage: func(serverInstanceNo string) (*string, error) { return nil, fmt.Errorf("!! Unit Test FAIL !!") },
|
||||
Say: func(message string) {},
|
||||
Error: func(e error) {},
|
||||
Config: new(Config),
|
||||
}
|
||||
|
||||
testSubject.Config.BlockStorageSize = 10
|
||||
@@ -27,18 +27,18 @@ func TestStepCreateBlockStorageInstanceShouldFailIfOperationCreateBlockStorageIn
|
||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
||||
}
|
||||
|
||||
if _, ok := stateBag.GetOk("Error"); ok == false {
|
||||
if _, ok := stateBag.GetOk("error"); ok == false {
|
||||
t.Fatal("Expected the step to set stateBag['Error'], but it was not.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepCreateBlockStorageInstanceShouldPassIfOperationCreateBlockStorageInstancePasses(t *testing.T) {
|
||||
var instanceNo = "a"
|
||||
var testSubject = &StepCreateBlockStorageInstance{
|
||||
CreateBlockStorageInstance: func(serverInstanceNo string) (*string, error) { return &instanceNo, nil },
|
||||
Say: func(message string) {},
|
||||
Error: func(e error) {},
|
||||
Config: new(Config),
|
||||
var testSubject = &StepCreateBlockStorage{
|
||||
CreateBlockStorage: func(serverInstanceNo string) (*string, error) { return &instanceNo, nil },
|
||||
Say: func(message string) {},
|
||||
Error: func(e error) {},
|
||||
Config: new(Config),
|
||||
}
|
||||
|
||||
testSubject.Config.BlockStorageSize = 10
|
||||
@@ -51,7 +51,7 @@ func TestStepCreateBlockStorageInstanceShouldPassIfOperationCreateBlockStorageIn
|
||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
||||
}
|
||||
|
||||
if _, ok := stateBag.GetOk("Error"); ok == true {
|
||||
if _, ok := stateBag.GetOk("error"); ok == true {
|
||||
t.Fatalf("Expected the step to not set stateBag['Error'], but it was.")
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,7 @@ func TestStepCreateBlockStorageInstanceShouldPassIfOperationCreateBlockStorageIn
|
||||
func createTestStateBagStepCreateBlockStorageInstance() multistep.StateBag {
|
||||
stateBag := new(multistep.BasicStateBag)
|
||||
|
||||
stateBag.Put("InstanceNo", "a")
|
||||
stateBag.Put("instance_no", "a")
|
||||
|
||||
return stateBag
|
||||
}
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
package ncloud
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/ncloud"
|
||||
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type InitScript struct {
|
||||
KeyName string
|
||||
PrivateKey string
|
||||
}
|
||||
|
||||
type StepCreateInitScript struct {
|
||||
Conn *NcloudAPIClient
|
||||
CreateInitScript func() (string, error)
|
||||
DeleteInitScript func(initScriptNo string) error
|
||||
Say func(message string)
|
||||
Error func(e error)
|
||||
Config *Config
|
||||
}
|
||||
|
||||
func NewStepCreateInitScript(conn *NcloudAPIClient, ui packersdk.Ui, config *Config) *StepCreateInitScript {
|
||||
var step = &StepCreateInitScript{
|
||||
Conn: conn,
|
||||
Say: func(message string) { ui.Say(message) },
|
||||
Error: func(e error) { ui.Error(e.Error()) },
|
||||
Config: config,
|
||||
}
|
||||
|
||||
if config.SupportVPC {
|
||||
step.CreateInitScript = step.createVpcInitScript
|
||||
step.DeleteInitScript = step.deleteVpcInitScript
|
||||
}
|
||||
|
||||
return step
|
||||
}
|
||||
|
||||
func (s *StepCreateInitScript) createVpcInitScript() (string, error) {
|
||||
name := fmt.Sprintf("packer-%d", time.Now().Unix())
|
||||
reqParams := &vserver.CreateInitScriptRequest{
|
||||
RegionCode: &s.Config.RegionCode,
|
||||
InitScriptName: &name,
|
||||
}
|
||||
|
||||
if s.Config.Comm.Type == "winrm" {
|
||||
reqParams.OsTypeCode = ncloud.String("WND")
|
||||
}
|
||||
|
||||
if s.Config.UserData != "" {
|
||||
reqParams.InitScriptContent = &s.Config.UserData
|
||||
}
|
||||
if s.Config.UserDataFile != "" {
|
||||
contents, err := ioutil.ReadFile(s.Config.UserDataFile)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Problem reading user data file: %s", err)
|
||||
}
|
||||
|
||||
reqParams.InitScriptContent = ncloud.String(string(contents))
|
||||
}
|
||||
|
||||
if reqParams.InitScriptContent == nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
resp, err := s.Conn.vserver.V2Api.CreateInitScript(reqParams)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return *resp.InitScriptList[0].InitScriptNo, nil
|
||||
}
|
||||
|
||||
func (s *StepCreateInitScript) deleteVpcInitScript(initScriptNo string) error {
|
||||
reqParams := &vserver.DeleteInitScriptsRequest{
|
||||
RegionCode: &s.Config.RegionCode,
|
||||
InitScriptNoList: []*string{&initScriptNo},
|
||||
}
|
||||
_, err := s.Conn.vserver.V2Api.DeleteInitScripts(reqParams)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StepCreateInitScript) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
if len(s.Config.UserData) == 0 && len(s.Config.UserDataFile) == 0 {
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
s.Say("Create Init script")
|
||||
initScriptNo, err := s.CreateInitScript()
|
||||
if err == nil && initScriptNo != "" {
|
||||
state.Put("init_script_no", initScriptNo)
|
||||
s.Say(fmt.Sprintf("Init script[%s] is created", initScriptNo))
|
||||
}
|
||||
|
||||
return processStepResult(err, s.Error, state)
|
||||
}
|
||||
|
||||
func (s *StepCreateInitScript) Cleanup(state multistep.StateBag) {
|
||||
if initScriptNo, ok := state.GetOk("init_script_no"); ok {
|
||||
s.Say("Cleanup Init script")
|
||||
if err := s.DeleteInitScript(initScriptNo.(string)); err != nil {
|
||||
s.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package ncloud
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
)
|
||||
|
||||
func TestStepCreateInitScriptShouldFailIfOperationCreateInitScriptFails(t *testing.T) {
|
||||
var testSubject = &StepCreateInitScript{
|
||||
CreateInitScript: func() (string, error) { return "", fmt.Errorf("!! Unit Test FAIL !!") },
|
||||
Say: func(message string) {},
|
||||
Error: func(e error) {},
|
||||
Config: &Config{
|
||||
Region: "Korea",
|
||||
SupportVPC: true,
|
||||
UserData: "test",
|
||||
},
|
||||
}
|
||||
|
||||
stateBag := createTestStateBagStepCreateInitScript()
|
||||
|
||||
var result = testSubject.Run(context.Background(), stateBag)
|
||||
|
||||
if result != multistep.ActionHalt {
|
||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
||||
}
|
||||
|
||||
if _, ok := stateBag.GetOk("error"); ok == false {
|
||||
t.Fatal("Expected the step to set stateBag['Error'], but it was not.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepCreateInitScriptShouldPassIfOperationCreateInitScriptPasses(t *testing.T) {
|
||||
var testSubject = &StepCreateInitScript{
|
||||
CreateInitScript: func() (string, error) { return "123", nil },
|
||||
Say: func(message string) {},
|
||||
Error: func(e error) {},
|
||||
Config: &Config{
|
||||
Region: "Korea",
|
||||
SupportVPC: true,
|
||||
},
|
||||
}
|
||||
|
||||
stateBag := createTestStateBagStepCreateInitScript()
|
||||
|
||||
var result = testSubject.Run(context.Background(), stateBag)
|
||||
|
||||
if result != multistep.ActionContinue {
|
||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
||||
}
|
||||
|
||||
if _, ok := stateBag.GetOk("error"); ok == true {
|
||||
t.Fatalf("Expected the step to not set stateBag['Error'], but it was.")
|
||||
}
|
||||
}
|
||||
|
||||
func createTestStateBagStepCreateInitScript() multistep.StateBag {
|
||||
stateBag := new(multistep.BasicStateBag)
|
||||
|
||||
return stateBag
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/server"
|
||||
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
@@ -18,23 +19,32 @@ type LoginKey struct {
|
||||
type StepCreateLoginKey struct {
|
||||
Conn *NcloudAPIClient
|
||||
CreateLoginKey func() (*LoginKey, error)
|
||||
DeleteLoginKey func(loginKey string) error
|
||||
Say func(message string)
|
||||
Error func(e error)
|
||||
Config *Config
|
||||
}
|
||||
|
||||
func NewStepCreateLoginKey(conn *NcloudAPIClient, ui packersdk.Ui) *StepCreateLoginKey {
|
||||
func NewStepCreateLoginKey(conn *NcloudAPIClient, ui packersdk.Ui, config *Config) *StepCreateLoginKey {
|
||||
var step = &StepCreateLoginKey{
|
||||
Conn: conn,
|
||||
Say: func(message string) { ui.Say(message) },
|
||||
Error: func(e error) { ui.Error(e.Error()) },
|
||||
Conn: conn,
|
||||
Say: func(message string) { ui.Say(message) },
|
||||
Error: func(e error) { ui.Error(e.Error()) },
|
||||
Config: config,
|
||||
}
|
||||
|
||||
step.CreateLoginKey = step.createLoginKey
|
||||
if config.SupportVPC {
|
||||
step.CreateLoginKey = step.createVpcLoginKey
|
||||
step.DeleteLoginKey = step.deleteVpcLoginKey
|
||||
} else {
|
||||
step.CreateLoginKey = step.createClassicLoginKey
|
||||
step.DeleteLoginKey = step.deleteClassicLoginKey
|
||||
}
|
||||
|
||||
return step
|
||||
}
|
||||
|
||||
func (s *StepCreateLoginKey) createLoginKey() (*LoginKey, error) {
|
||||
func (s *StepCreateLoginKey) createClassicLoginKey() (*LoginKey, error) {
|
||||
keyName := fmt.Sprintf("packer-%d", time.Now().Unix())
|
||||
reqParams := &server.CreateLoginKeyRequest{KeyName: &keyName}
|
||||
|
||||
@@ -46,12 +56,46 @@ func (s *StepCreateLoginKey) createLoginKey() (*LoginKey, error) {
|
||||
return &LoginKey{keyName, *privateKey.PrivateKey}, nil
|
||||
}
|
||||
|
||||
func (s *StepCreateLoginKey) createVpcLoginKey() (*LoginKey, error) {
|
||||
keyName := fmt.Sprintf("packer-%d", time.Now().Unix())
|
||||
reqParams := &vserver.CreateLoginKeyRequest{KeyName: &keyName}
|
||||
|
||||
privateKey, err := s.Conn.vserver.V2Api.CreateLoginKey(reqParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &LoginKey{keyName, *privateKey.PrivateKey}, nil
|
||||
}
|
||||
|
||||
func (s *StepCreateLoginKey) deleteClassicLoginKey(keyName string) error {
|
||||
reqParams := &server.DeleteLoginKeyRequest{KeyName: &keyName}
|
||||
_, err := s.Conn.server.V2Api.DeleteLoginKey(reqParams)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StepCreateLoginKey) deleteVpcLoginKey(keyName string) error {
|
||||
reqParams := &vserver.DeleteLoginKeysRequest{KeyNameList: []*string{&keyName}}
|
||||
_, err := s.Conn.vserver.V2Api.DeleteLoginKeys(reqParams)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StepCreateLoginKey) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
s.Say("Create Login Key")
|
||||
|
||||
loginKey, err := s.CreateLoginKey()
|
||||
if err == nil {
|
||||
state.Put("LoginKey", loginKey)
|
||||
state.Put("login_key", loginKey)
|
||||
s.Say(fmt.Sprintf("Login Key[%s] is created", loginKey.KeyName))
|
||||
}
|
||||
|
||||
@@ -59,9 +103,11 @@ func (s *StepCreateLoginKey) Run(ctx context.Context, state multistep.StateBag)
|
||||
}
|
||||
|
||||
func (s *StepCreateLoginKey) Cleanup(state multistep.StateBag) {
|
||||
if loginKey, ok := state.GetOk("LoginKey"); ok {
|
||||
if loginKey, ok := state.GetOk("login_key"); ok {
|
||||
s.Say("Clean up login key")
|
||||
reqParams := &server.DeleteLoginKeyRequest{KeyName: &loginKey.(*LoginKey).KeyName}
|
||||
s.Conn.server.V2Api.DeleteLoginKey(reqParams)
|
||||
if err := s.DeleteLoginKey(loginKey.(*LoginKey).KeyName); err != nil {
|
||||
s.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ func TestStepCreateLoginKeyShouldFailIfOperationCreateLoginKeyFails(t *testing.T
|
||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
||||
}
|
||||
|
||||
if _, ok := stateBag.GetOk("Error"); ok == false {
|
||||
if _, ok := stateBag.GetOk("error"); ok == false {
|
||||
t.Fatal("Expected the step to set stateBag['Error'], but it was not.")
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ func TestStepCreateLoginKeyShouldPassIfOperationCreateLoginKeyPasses(t *testing.
|
||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
||||
}
|
||||
|
||||
if _, ok := stateBag.GetOk("Error"); ok == true {
|
||||
if _, ok := stateBag.GetOk("error"); ok == true {
|
||||
t.Fatalf("Expected the step to not set stateBag['Error'], but it was.")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,36 +8,108 @@ import (
|
||||
|
||||
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/ncloud"
|
||||
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/server"
|
||||
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type StepCreatePublicIPInstance struct {
|
||||
const (
|
||||
PublicIpStatusUsed = "USED"
|
||||
PublicIpStatusCreated = "CREAT"
|
||||
PublicIpStatusRunning = "RUN"
|
||||
)
|
||||
|
||||
type StepCreatePublicIP struct {
|
||||
Conn *NcloudAPIClient
|
||||
CreatePublicIPInstance func(serverInstanceNo string) (*server.PublicIpInstance, error)
|
||||
GetPublicIP func(publicIPNo string) (*server.PublicIpInstance, error)
|
||||
CreatePublicIP func(serverInstanceNo string) (*server.PublicIpInstance, error)
|
||||
DeletePublicIp func(publicIPNo string) error
|
||||
WaiterAssociatePublicIPToServerInstance func(serverInstanceNo string, publicIP string) error
|
||||
DisassociatePublicIpFromServerInstance func(publicIPNo string) error
|
||||
Say func(message string)
|
||||
Error func(e error)
|
||||
Config *Config
|
||||
}
|
||||
|
||||
func NewStepCreatePublicIPInstance(conn *NcloudAPIClient, ui packersdk.Ui, config *Config) *StepCreatePublicIPInstance {
|
||||
var step = &StepCreatePublicIPInstance{
|
||||
func NewStepCreatePublicIP(conn *NcloudAPIClient, ui packersdk.Ui, config *Config) *StepCreatePublicIP {
|
||||
var step = &StepCreatePublicIP{
|
||||
Conn: conn,
|
||||
Say: func(message string) { ui.Say(message) },
|
||||
Error: func(e error) { ui.Error(e.Error()) },
|
||||
Config: config,
|
||||
}
|
||||
|
||||
step.CreatePublicIPInstance = step.createPublicIPInstance
|
||||
step.WaiterAssociatePublicIPToServerInstance = step.waiterAssociatePublicIPToServerInstance
|
||||
if config.SupportVPC {
|
||||
step.GetPublicIP = step.getVpcPublicIP
|
||||
step.CreatePublicIP = step.createVpcPublicIP
|
||||
step.WaiterAssociatePublicIPToServerInstance = step.waiterAssociateVpcPublicIPToServerInstance
|
||||
step.DisassociatePublicIpFromServerInstance = step.disassociateVpcPublicIpFromServerInstance
|
||||
step.DeletePublicIp = step.deleteVpcPublicIp
|
||||
} else {
|
||||
step.GetPublicIP = step.getClassicPublicIP
|
||||
step.CreatePublicIP = step.createClassicPublicIP
|
||||
step.WaiterAssociatePublicIPToServerInstance = step.waiterAssociateClassicPublicIPToServerInstance
|
||||
step.DisassociatePublicIpFromServerInstance = step.disassociateClassicPublicIpFromServerInstance
|
||||
step.DeletePublicIp = step.deleteClassicPublicIp
|
||||
}
|
||||
|
||||
return step
|
||||
}
|
||||
|
||||
func (s *StepCreatePublicIPInstance) waiterAssociatePublicIPToServerInstance(serverInstanceNo string, publicIP string) error {
|
||||
reqParams := new(server.GetServerInstanceListRequest)
|
||||
reqParams.ServerInstanceNoList = []*string{&serverInstanceNo}
|
||||
func (s *StepCreatePublicIP) getClassicPublicIP(publicIPNo string) (*server.PublicIpInstance, error) {
|
||||
reqParams := &server.GetPublicIpInstanceListRequest{
|
||||
PublicIpInstanceNoList: []*string{&publicIPNo},
|
||||
}
|
||||
|
||||
resp, err := s.Conn.server.V2Api.GetPublicIpInstanceList(reqParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp != nil && *resp.TotalRows > 0 {
|
||||
return resp.PublicIpInstanceList[0], nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *StepCreatePublicIP) getVpcPublicIP(publicIPNo string) (*server.PublicIpInstance, error) {
|
||||
reqParams := &vserver.GetPublicIpInstanceDetailRequest{
|
||||
RegionCode: &s.Config.RegionCode,
|
||||
PublicIpInstanceNo: &publicIPNo,
|
||||
}
|
||||
|
||||
resp, err := s.Conn.vserver.V2Api.GetPublicIpInstanceDetail(reqParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp != nil && *resp.TotalRows > 0 {
|
||||
inst := resp.PublicIpInstanceList[0]
|
||||
return &server.PublicIpInstance{
|
||||
PublicIpInstanceNo: inst.PublicIpInstanceNo,
|
||||
PublicIp: inst.PublicIp,
|
||||
PublicIpInstanceStatus: &server.CommonCode{
|
||||
Code: inst.PublicIpInstanceStatus.Code,
|
||||
CodeName: inst.PublicIpInstanceStatus.CodeName,
|
||||
},
|
||||
PublicIpInstanceOperation: &server.CommonCode{
|
||||
Code: inst.PublicIpInstanceOperation.Code,
|
||||
CodeName: inst.PublicIpInstanceOperation.CodeName,
|
||||
},
|
||||
ServerInstanceAssociatedWithPublicIp: &server.ServerInstance{
|
||||
ServerInstanceNo: inst.ServerInstanceNo,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *StepCreatePublicIP) waiterAssociateClassicPublicIPToServerInstance(serverInstanceNo string, publicIP string) error {
|
||||
reqParams := &server.GetServerInstanceListRequest{
|
||||
ServerInstanceNoList: []*string{&serverInstanceNo},
|
||||
}
|
||||
|
||||
c1 := make(chan error, 1)
|
||||
|
||||
@@ -68,9 +140,45 @@ func (s *StepCreatePublicIPInstance) waiterAssociatePublicIPToServerInstance(ser
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StepCreatePublicIPInstance) createPublicIPInstance(serverInstanceNo string) (*server.PublicIpInstance, error) {
|
||||
reqParams := new(server.CreatePublicIpInstanceRequest)
|
||||
reqParams.ServerInstanceNo = &serverInstanceNo
|
||||
func (s *StepCreatePublicIP) waiterAssociateVpcPublicIPToServerInstance(serverInstanceNo string, publicIP string) error {
|
||||
reqParams := &vserver.GetServerInstanceDetailRequest{
|
||||
RegionCode: &s.Config.RegionCode,
|
||||
ServerInstanceNo: &serverInstanceNo,
|
||||
}
|
||||
|
||||
c1 := make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
serverInstanceList, err := s.Conn.vserver.V2Api.GetServerInstanceDetail(reqParams)
|
||||
|
||||
if err != nil {
|
||||
c1 <- err
|
||||
return
|
||||
}
|
||||
|
||||
if publicIP == *serverInstanceList.ServerInstanceList[0].PublicIp {
|
||||
c1 <- nil
|
||||
return
|
||||
}
|
||||
|
||||
s.Say("Wait to associate public ip serverInstance")
|
||||
time.Sleep(time.Second * 3)
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case res := <-c1:
|
||||
return res
|
||||
case <-time.After(time.Second * 60):
|
||||
return fmt.Errorf("TIMEOUT : association public ip[%s] to server instance[%s] Failed", publicIP, serverInstanceNo)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StepCreatePublicIP) createClassicPublicIP(serverInstanceNo string) (*server.PublicIpInstance, error) {
|
||||
reqParams := &server.CreatePublicIpInstanceRequest{
|
||||
ServerInstanceNo: &serverInstanceNo,
|
||||
}
|
||||
|
||||
publicIPInstanceList, err := s.Conn.server.V2Api.CreatePublicIpInstance(reqParams)
|
||||
if err != nil {
|
||||
@@ -81,7 +189,7 @@ func (s *StepCreatePublicIPInstance) createPublicIPInstance(serverInstanceNo str
|
||||
publicIP := publicIPInstance.PublicIp
|
||||
s.Say(fmt.Sprintf("Public IP Instance [%s:%s] is created", *publicIPInstance.PublicIpInstanceNo, *publicIP))
|
||||
|
||||
err = s.waiterAssociatePublicIPToServerInstance(serverInstanceNo, *publicIP)
|
||||
err = s.WaiterAssociatePublicIPToServerInstance(serverInstanceNo, *publicIP)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -89,69 +197,49 @@ func (s *StepCreatePublicIPInstance) createPublicIPInstance(serverInstanceNo str
|
||||
return publicIPInstance, nil
|
||||
}
|
||||
|
||||
func (s *StepCreatePublicIPInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
s.Say("Create Public IP Instance")
|
||||
|
||||
serverInstanceNo := state.Get("InstanceNo").(string)
|
||||
|
||||
publicIPInstance, err := s.CreatePublicIPInstance(serverInstanceNo)
|
||||
if err == nil {
|
||||
state.Put("PublicIP", *publicIPInstance.PublicIp)
|
||||
state.Put("PublicIPInstance", publicIPInstance)
|
||||
// instance_id is the generic term used so that users can have access to the
|
||||
// instance id inside of the provisioners, used in step_provision.
|
||||
state.Put("instance_id", *publicIPInstance)
|
||||
func (s *StepCreatePublicIP) createVpcPublicIP(serverInstanceNo string) (*server.PublicIpInstance, error) {
|
||||
reqParams := &vserver.CreatePublicIpInstanceRequest{
|
||||
RegionCode: &s.Config.RegionCode,
|
||||
ServerInstanceNo: &serverInstanceNo,
|
||||
}
|
||||
|
||||
return processStepResult(err, s.Error, state)
|
||||
publicIPInstanceList, err := s.Conn.vserver.V2Api.CreatePublicIpInstance(reqParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
publicIPInstance := publicIPInstanceList.PublicIpInstanceList[0]
|
||||
publicIP := publicIPInstance.PublicIp
|
||||
s.Say(fmt.Sprintf("Public IP Instance [%s:%s] is created", *publicIPInstance.PublicIpInstanceNo, *publicIP))
|
||||
|
||||
err = s.WaiterAssociatePublicIPToServerInstance(serverInstanceNo, *publicIP)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &server.PublicIpInstance{
|
||||
PublicIpInstanceNo: publicIPInstance.PublicIpInstanceNo,
|
||||
PublicIp: publicIP,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *StepCreatePublicIPInstance) Cleanup(state multistep.StateBag) {
|
||||
publicIPInstance, ok := state.GetOk("PublicIPInstance")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
s.Say("Clean up Public IP Instance")
|
||||
publicIPInstanceNo := publicIPInstance.(*server.PublicIpInstance).PublicIpInstanceNo
|
||||
s.waitPublicIPInstanceStatus(publicIPInstanceNo, "USED")
|
||||
|
||||
log.Println("Disassociate Public IP Instance ", publicIPInstanceNo)
|
||||
reqParams := &server.DisassociatePublicIpFromServerInstanceRequest{PublicIpInstanceNo: publicIPInstanceNo}
|
||||
s.Conn.server.V2Api.DisassociatePublicIpFromServerInstance(reqParams)
|
||||
|
||||
s.waitPublicIPInstanceStatus(publicIPInstanceNo, "CREAT")
|
||||
|
||||
reqDeleteParams := &server.DeletePublicIpInstancesRequest{
|
||||
PublicIpInstanceNoList: ncloud.StringList([]string{*publicIPInstanceNo}),
|
||||
}
|
||||
|
||||
log.Println("Delete Public IP Instance ", publicIPInstanceNo)
|
||||
s.Conn.server.V2Api.DeletePublicIpInstances(reqDeleteParams)
|
||||
}
|
||||
|
||||
func (s *StepCreatePublicIPInstance) waitPublicIPInstanceStatus(publicIPInstanceNo *string, status string) {
|
||||
func (s *StepCreatePublicIP) waitPublicIPStatus(publicIPInstanceNo string, status string) error {
|
||||
c1 := make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
reqParams := new(server.GetPublicIpInstanceListRequest)
|
||||
reqParams.PublicIpInstanceNoList = []*string{publicIPInstanceNo}
|
||||
|
||||
for {
|
||||
resp, err := s.Conn.server.V2Api.GetPublicIpInstanceList(reqParams)
|
||||
publicIp, err := s.GetPublicIP(publicIPInstanceNo)
|
||||
if err != nil {
|
||||
log.Printf(err.Error())
|
||||
c1 <- err
|
||||
return
|
||||
}
|
||||
|
||||
if *resp.TotalRows == 0 {
|
||||
if publicIp == nil {
|
||||
c1 <- nil
|
||||
return
|
||||
}
|
||||
|
||||
instance := resp.PublicIpInstanceList[0]
|
||||
if *instance.PublicIpInstanceStatus.Code == status && *instance.PublicIpInstanceOperation.Code == "NULL" {
|
||||
if *publicIp.PublicIpInstanceStatus.Code == status && *publicIp.PublicIpInstanceOperation.Code == "NULL" {
|
||||
c1 <- nil
|
||||
return
|
||||
}
|
||||
@@ -162,8 +250,116 @@ func (s *StepCreatePublicIPInstance) waitPublicIPInstanceStatus(publicIPInstance
|
||||
|
||||
select {
|
||||
case <-c1:
|
||||
return
|
||||
return nil
|
||||
case <-time.After(time.Second * 60):
|
||||
return fmt.Errorf("TIMEOUT : wait for public ip[%s] to status[%s] Failed", publicIPInstanceNo, status)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StepCreatePublicIP) disassociateClassicPublicIpFromServerInstance(publicIPInstanceNo string) error {
|
||||
reqParams := &server.DisassociatePublicIpFromServerInstanceRequest{
|
||||
PublicIpInstanceNo: &publicIPInstanceNo,
|
||||
}
|
||||
|
||||
_, err := s.Conn.server.V2Api.DisassociatePublicIpFromServerInstance(reqParams)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *StepCreatePublicIP) disassociateVpcPublicIpFromServerInstance(publicIPInstanceNo string) error {
|
||||
reqParams := &vserver.DisassociatePublicIpFromServerInstanceRequest{
|
||||
RegionCode: &s.Config.RegionCode,
|
||||
PublicIpInstanceNo: &publicIPInstanceNo,
|
||||
}
|
||||
|
||||
_, err := s.Conn.vserver.V2Api.DisassociatePublicIpFromServerInstance(reqParams)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *StepCreatePublicIP) deleteClassicPublicIp(publicIPInstanceNo string) error {
|
||||
reqParams := &server.DeletePublicIpInstancesRequest{
|
||||
PublicIpInstanceNoList: ncloud.StringList([]string{publicIPInstanceNo}),
|
||||
}
|
||||
|
||||
_, err := s.Conn.server.V2Api.DeletePublicIpInstances(reqParams)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *StepCreatePublicIP) deleteVpcPublicIp(publicIPInstanceNo string) error {
|
||||
reqParams := &vserver.DeletePublicIpInstanceRequest{
|
||||
RegionCode: &s.Config.RegionCode,
|
||||
PublicIpInstanceNo: &publicIPInstanceNo,
|
||||
}
|
||||
|
||||
_, err := s.Conn.vserver.V2Api.DeletePublicIpInstance(reqParams)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *StepCreatePublicIP) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
s.Say("Create Public IP Instance")
|
||||
|
||||
serverInstanceNo := state.Get("instance_no").(string)
|
||||
|
||||
publicIPInstance, err := s.CreatePublicIP(serverInstanceNo)
|
||||
if err == nil {
|
||||
state.Put("public_ip", *publicIPInstance.PublicIp)
|
||||
state.Put("public_ip_instance", publicIPInstance)
|
||||
}
|
||||
|
||||
return processStepResult(err, s.Error, state)
|
||||
}
|
||||
|
||||
func (s *StepCreatePublicIP) Cleanup(state multistep.StateBag) {
|
||||
publicIPInstance, ok := state.GetOk("public_ip_instance")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
s.Say("Clean up Public IP Instance")
|
||||
publicIPInstanceNo := publicIPInstance.(*server.PublicIpInstance).PublicIpInstanceNo
|
||||
|
||||
publicIp, err := s.GetPublicIP(*publicIPInstanceNo)
|
||||
if err != nil {
|
||||
s.Error(err)
|
||||
}
|
||||
|
||||
if publicIp == nil {
|
||||
return
|
||||
}
|
||||
|
||||
publicIpUsedStatus := PublicIpStatusUsed
|
||||
publicIpCreatedStatus := PublicIpStatusCreated
|
||||
|
||||
if s.Config.SupportVPC {
|
||||
publicIpUsedStatus = PublicIpStatusRunning
|
||||
publicIpCreatedStatus = PublicIpStatusRunning
|
||||
}
|
||||
|
||||
if publicIp.ServerInstanceAssociatedWithPublicIp != nil &&
|
||||
publicIp.ServerInstanceAssociatedWithPublicIp.ServerInstanceNo != nil &&
|
||||
len(*publicIp.ServerInstanceAssociatedWithPublicIp.ServerInstanceNo) > 0 {
|
||||
if err := s.waitPublicIPStatus(*publicIPInstanceNo, publicIpUsedStatus); err != nil {
|
||||
s.Error(err)
|
||||
}
|
||||
|
||||
log.Println("Disassociate Public IP Instance ", publicIPInstanceNo)
|
||||
if err := s.DisassociatePublicIpFromServerInstance(*publicIPInstanceNo); err != nil {
|
||||
s.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.waitPublicIPStatus(*publicIPInstanceNo, publicIpCreatedStatus); err != nil {
|
||||
s.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println("Delete Public IP Instance ", publicIPInstanceNo)
|
||||
if err := s.DeletePublicIp(*publicIPInstanceNo); err != nil {
|
||||
s.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ import (
|
||||
)
|
||||
|
||||
func TestStepCreatePublicIPInstanceShouldFailIfOperationCreatePublicIPInstanceFails(t *testing.T) {
|
||||
var testSubject = &StepCreatePublicIPInstance{
|
||||
CreatePublicIPInstance: func(serverInstanceNo string) (*server.PublicIpInstance, error) {
|
||||
var testSubject = &StepCreatePublicIP{
|
||||
CreatePublicIP: func(serverInstanceNo string) (*server.PublicIpInstance, error) {
|
||||
return nil, fmt.Errorf("!! Unit Test FAIL !!")
|
||||
},
|
||||
Say: func(message string) {},
|
||||
@@ -28,7 +28,7 @@ func TestStepCreatePublicIPInstanceShouldFailIfOperationCreatePublicIPInstanceFa
|
||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
||||
}
|
||||
|
||||
if _, ok := stateBag.GetOk("Error"); ok == false {
|
||||
if _, ok := stateBag.GetOk("error"); ok == false {
|
||||
t.Fatal("Expected the step to set stateBag['Error'], but it was not.")
|
||||
}
|
||||
}
|
||||
@@ -38,8 +38,8 @@ func TestStepCreatePublicIPInstanceShouldPassIfOperationCreatePublicIPInstancePa
|
||||
c.Comm.Prepare(nil)
|
||||
c.Comm.Type = "ssh"
|
||||
|
||||
var testSubject = &StepCreatePublicIPInstance{
|
||||
CreatePublicIPInstance: func(serverInstanceNo string) (*server.PublicIpInstance, error) {
|
||||
var testSubject = &StepCreatePublicIP{
|
||||
CreatePublicIP: func(serverInstanceNo string) (*server.PublicIpInstance, error) {
|
||||
return &server.PublicIpInstance{PublicIpInstanceNo: ncloud.String("a"), PublicIp: ncloud.String("b")}, nil
|
||||
},
|
||||
Say: func(message string) {},
|
||||
@@ -55,7 +55,7 @@ func TestStepCreatePublicIPInstanceShouldPassIfOperationCreatePublicIPInstancePa
|
||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
||||
}
|
||||
|
||||
if _, ok := stateBag.GetOk("Error"); ok == true {
|
||||
if _, ok := stateBag.GetOk("error"); ok == true {
|
||||
t.Fatalf("Expected the step to not set stateBag['Error'], but it was.")
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,7 @@ func TestStepCreatePublicIPInstanceShouldPassIfOperationCreatePublicIPInstancePa
|
||||
func createTestStateBagStepCreatePublicIPInstance() multistep.StateBag {
|
||||
stateBag := new(multistep.BasicStateBag)
|
||||
|
||||
stateBag.Put("InstanceNo", "a")
|
||||
stateBag.Put("instance_no", "a")
|
||||
|
||||
return stateBag
|
||||
}
|
||||
|
||||
@@ -7,10 +7,15 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/server"
|
||||
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
const (
|
||||
ServerImageStatusCreated = "CREAT"
|
||||
)
|
||||
|
||||
type StepCreateServerImage struct {
|
||||
Conn *NcloudAPIClient
|
||||
CreateServerImage func(serverInstanceNo string) (*server.MemberServerImage, error)
|
||||
@@ -27,21 +32,26 @@ func NewStepCreateServerImage(conn *NcloudAPIClient, ui packersdk.Ui, config *Co
|
||||
Config: config,
|
||||
}
|
||||
|
||||
step.CreateServerImage = step.createServerImage
|
||||
if config.SupportVPC {
|
||||
step.CreateServerImage = step.createVpcServerImage
|
||||
} else {
|
||||
step.CreateServerImage = step.createClassicServerImage
|
||||
}
|
||||
|
||||
return step
|
||||
}
|
||||
|
||||
func (s *StepCreateServerImage) createServerImage(serverInstanceNo string) (*server.MemberServerImage, error) {
|
||||
func (s *StepCreateServerImage) createClassicServerImage(serverInstanceNo string) (*server.MemberServerImage, error) {
|
||||
// Can't create server image when status of server instance is stopping (not stopped)
|
||||
if err := waiterServerInstanceStatus(s.Conn, serverInstanceNo, "NSTOP", 1*time.Minute); err != nil {
|
||||
if err := waiterClassicServerInstanceStatus(s.Conn, serverInstanceNo, ServerInstanceStatusStopped, 1*time.Minute); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reqParams := new(server.CreateMemberServerImageRequest)
|
||||
reqParams.MemberServerImageName = &s.Config.ServerImageName
|
||||
reqParams.MemberServerImageDescription = &s.Config.ServerImageDescription
|
||||
reqParams.ServerInstanceNo = &serverInstanceNo
|
||||
reqParams := &server.CreateMemberServerImageRequest{
|
||||
MemberServerImageName: &s.Config.ServerImageName,
|
||||
MemberServerImageDescription: &s.Config.ServerImageDescription,
|
||||
ServerInstanceNo: &serverInstanceNo,
|
||||
}
|
||||
|
||||
memberServerImageList, err := s.Conn.server.V2Api.CreateMemberServerImage(reqParams)
|
||||
if err != nil {
|
||||
@@ -52,7 +62,7 @@ func (s *StepCreateServerImage) createServerImage(serverInstanceNo string) (*ser
|
||||
|
||||
s.Say(fmt.Sprintf("Server Image[%s:%s] is creating...", *serverImage.MemberServerImageName, *serverImage.MemberServerImageNo))
|
||||
|
||||
if err := waiterMemberServerImageStatus(s.Conn, *serverImage.MemberServerImageNo, "CREAT", 6*time.Hour); err != nil {
|
||||
if err := waiterClassicMemberServerImageStatus(s.Conn, *serverImage.MemberServerImageNo, ServerImageStatusCreated, 6*time.Hour); err != nil {
|
||||
return nil, errors.New("TIMEOUT : Server Image is not created")
|
||||
}
|
||||
|
||||
@@ -61,14 +71,56 @@ func (s *StepCreateServerImage) createServerImage(serverInstanceNo string) (*ser
|
||||
return serverImage, nil
|
||||
}
|
||||
|
||||
func (s *StepCreateServerImage) createVpcServerImage(serverInstanceNo string) (*server.MemberServerImage, error) {
|
||||
// Can't create server image when status of server instance is stopping (not stopped)
|
||||
if err := waiterVpcServerInstanceStatus(s.Conn, serverInstanceNo, ServerInstanceStatusStopped, 1*time.Minute); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reqParams := &vserver.CreateMemberServerImageInstanceRequest{
|
||||
MemberServerImageName: &s.Config.ServerImageName,
|
||||
MemberServerImageDescription: &s.Config.ServerImageDescription,
|
||||
ServerInstanceNo: &serverInstanceNo,
|
||||
}
|
||||
|
||||
memberServerImageList, err := s.Conn.vserver.V2Api.CreateMemberServerImageInstance(reqParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serverImage := memberServerImageList.MemberServerImageInstanceList[0]
|
||||
|
||||
s.Say(fmt.Sprintf("Server Image[%s:%s] is creating...", *serverImage.MemberServerImageName, *serverImage.MemberServerImageInstanceNo))
|
||||
|
||||
if err := waiterVpcMemberServerImageStatus(s.Conn, *serverImage.MemberServerImageInstanceNo, ServerImageStatusCreated, 6*time.Hour); err != nil {
|
||||
return nil, errors.New("TIMEOUT : Server Image is not created")
|
||||
}
|
||||
|
||||
s.Say(fmt.Sprintf("Server Image[%s:%s] is created", *serverImage.MemberServerImageName, *serverImage.MemberServerImageInstanceNo))
|
||||
|
||||
result := &server.MemberServerImage{
|
||||
MemberServerImageNo: serverImage.MemberServerImageInstanceNo,
|
||||
MemberServerImageName: serverImage.MemberServerImageName,
|
||||
}
|
||||
|
||||
if serverImage.MemberServerImageInstanceStatus != nil {
|
||||
result.MemberServerImageStatus = &server.CommonCode{
|
||||
Code: serverImage.MemberServerImageInstanceStatus.Code,
|
||||
CodeName: serverImage.MemberServerImageInstanceStatus.CodeName,
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *StepCreateServerImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
s.Say("Create Server Image")
|
||||
|
||||
serverInstanceNo := state.Get("InstanceNo").(string)
|
||||
serverInstanceNo := state.Get("instance_no").(string)
|
||||
|
||||
serverImage, err := s.CreateServerImage(serverInstanceNo)
|
||||
if err == nil {
|
||||
state.Put("memberServerImage", serverImage)
|
||||
state.Put("member_server_image", serverImage)
|
||||
}
|
||||
|
||||
return processStepResult(err, s.Error, state)
|
||||
|
||||
@@ -27,7 +27,7 @@ func TestStepCreateServerImageShouldFailIfOperationCreateServerImageFails(t *tes
|
||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
||||
}
|
||||
|
||||
if _, ok := stateBag.GetOk("Error"); ok == false {
|
||||
if _, ok := stateBag.GetOk("error"); ok == false {
|
||||
t.Fatal("Expected the step to set stateBag['Error'], but it was not.")
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ func TestStepCreateServerImageShouldPassIfOperationCreateServerImagePasses(t *te
|
||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
||||
}
|
||||
|
||||
if _, ok := stateBag.GetOk("Error"); ok == true {
|
||||
if _, ok := stateBag.GetOk("error"); ok == true {
|
||||
t.Fatalf("Expected the step to not set stateBag['Error'], but it was.")
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ func TestStepCreateServerImageShouldPassIfOperationCreateServerImagePasses(t *te
|
||||
func createTestStateBagStepCreateServerImage() multistep.StateBag {
|
||||
stateBag := new(multistep.BasicStateBag)
|
||||
|
||||
stateBag.Put("InstanceNo", "a")
|
||||
stateBag.Put("instance_no", "a")
|
||||
|
||||
return stateBag
|
||||
}
|
||||
|
||||
@@ -10,18 +10,26 @@ import (
|
||||
|
||||
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/ncloud"
|
||||
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/server"
|
||||
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
const (
|
||||
ServerInstanceStatusStopped = "NSTOP"
|
||||
ServerInstanceStatusTerminated = "TERMT"
|
||||
ServerInstanceStatusRunning = "RUN"
|
||||
)
|
||||
|
||||
type StepCreateServerInstance struct {
|
||||
Conn *NcloudAPIClient
|
||||
CreateServerInstance func(loginKeyName string, zoneNo string, feeSystemTypeCode string) (string, error)
|
||||
CheckServerInstanceStatusIsRunning func(serverInstanceNo string) error
|
||||
Say func(message string)
|
||||
Error func(e error)
|
||||
Config *Config
|
||||
serverInstanceNo string
|
||||
Conn *NcloudAPIClient
|
||||
CreateServerInstance func(loginKeyName string, feeSystemTypeCode string, state multistep.StateBag) (string, error)
|
||||
GetServerInstance func() (string, string, error)
|
||||
WaiterServerInstanceStatus func(conn *NcloudAPIClient, serverInstanceNo string, status string, timeout time.Duration) error
|
||||
Say func(message string)
|
||||
Error func(e error)
|
||||
Config *Config
|
||||
serverInstanceNo string
|
||||
}
|
||||
|
||||
func NewStepCreateServerInstance(conn *NcloudAPIClient, ui packersdk.Ui, config *Config) *StepCreateServerInstance {
|
||||
@@ -32,21 +40,32 @@ func NewStepCreateServerInstance(conn *NcloudAPIClient, ui packersdk.Ui, config
|
||||
Config: config,
|
||||
}
|
||||
|
||||
step.CreateServerInstance = step.createServerInstance
|
||||
if config.SupportVPC {
|
||||
step.CreateServerInstance = step.createVpcServerInstance
|
||||
step.WaiterServerInstanceStatus = waiterVpcServerInstanceStatus
|
||||
step.GetServerInstance = step.getVpcServerInstance
|
||||
} else {
|
||||
step.CreateServerInstance = step.createClassicServerInstance
|
||||
step.WaiterServerInstanceStatus = waiterClassicServerInstanceStatus
|
||||
step.GetServerInstance = step.getClassicServerInstance
|
||||
}
|
||||
|
||||
return step
|
||||
}
|
||||
|
||||
func (s *StepCreateServerInstance) createServerInstance(loginKeyName string, zoneNo string, feeSystemTypeCode string) (string, error) {
|
||||
reqParams := new(server.CreateServerInstancesRequest)
|
||||
reqParams.ServerProductCode = &s.Config.ServerProductCode
|
||||
reqParams.MemberServerImageNo = &s.Config.MemberServerImageNo
|
||||
func (s *StepCreateServerInstance) createClassicServerInstance(loginKeyName string, feeSystemTypeCode string, state multistep.StateBag) (string, error) {
|
||||
var zoneNo = state.Get("zone_no").(string)
|
||||
|
||||
reqParams := &server.CreateServerInstancesRequest{
|
||||
ServerProductCode: &s.Config.ServerProductCode,
|
||||
MemberServerImageNo: &s.Config.MemberServerImageNo,
|
||||
LoginKeyName: &loginKeyName,
|
||||
ZoneNo: &zoneNo,
|
||||
FeeSystemTypeCode: &feeSystemTypeCode,
|
||||
}
|
||||
if s.Config.MemberServerImageNo == "" {
|
||||
reqParams.ServerImageProductCode = &s.Config.ServerImageProductCode
|
||||
}
|
||||
reqParams.LoginKeyName = &loginKeyName
|
||||
reqParams.ZoneNo = &zoneNo
|
||||
reqParams.FeeSystemTypeCode = &feeSystemTypeCode
|
||||
|
||||
if s.Config.UserData != "" {
|
||||
reqParams.UserData = &s.Config.UserData
|
||||
@@ -61,8 +80,8 @@ func (s *StepCreateServerInstance) createServerInstance(loginKeyName string, zon
|
||||
reqParams.UserData = ncloud.String(string(contents))
|
||||
}
|
||||
|
||||
if s.Config.AccessControlGroupConfigurationNo != "" {
|
||||
reqParams.AccessControlGroupConfigurationNoList = []*string{&s.Config.AccessControlGroupConfigurationNo}
|
||||
if s.Config.AccessControlGroupNo != "" {
|
||||
reqParams.AccessControlGroupConfigurationNoList = []*string{&s.Config.AccessControlGroupNo}
|
||||
}
|
||||
|
||||
serverInstanceList, err := s.Conn.server.V2Api.CreateServerInstances(reqParams)
|
||||
@@ -74,7 +93,7 @@ func (s *StepCreateServerInstance) createServerInstance(loginKeyName string, zon
|
||||
s.Say(fmt.Sprintf("Server Instance is creating. Server InstanceNo is %s", s.serverInstanceNo))
|
||||
log.Println("Server Instance information : ", serverInstanceList.ServerInstanceList[0])
|
||||
|
||||
if err := waiterServerInstanceStatus(s.Conn, s.serverInstanceNo, "RUN", 30*time.Minute); err != nil {
|
||||
if err := s.WaiterServerInstanceStatus(s.Conn, s.serverInstanceNo, ServerInstanceStatusRunning, 30*time.Minute); err != nil {
|
||||
return "", errors.New("TIMEOUT : server instance status is not running")
|
||||
}
|
||||
|
||||
@@ -83,20 +102,104 @@ func (s *StepCreateServerInstance) createServerInstance(loginKeyName string, zon
|
||||
return s.serverInstanceNo, nil
|
||||
}
|
||||
|
||||
func (s *StepCreateServerInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
s.Say("Create Server Instance")
|
||||
func (s *StepCreateServerInstance) createVpcServerInstance(loginKeyName string, feeSystemTypeCode string, state multistep.StateBag) (string, error) {
|
||||
var initScriptNo string
|
||||
var acgNo string
|
||||
var err error
|
||||
|
||||
var loginKey = state.Get("LoginKey").(*LoginKey)
|
||||
var zoneNo = state.Get("ZoneNo").(string)
|
||||
|
||||
feeSystemTypeCode := "MTRAT"
|
||||
if _, ok := state.GetOk("FeeSystemTypeCode"); ok {
|
||||
feeSystemTypeCode = state.Get("FeeSystemTypeCode").(string)
|
||||
if v, ok := state.GetOk("init_script_no"); ok {
|
||||
initScriptNo = v.(string)
|
||||
}
|
||||
|
||||
serverInstanceNo, err := s.CreateServerInstance(loginKey.KeyName, zoneNo, feeSystemTypeCode)
|
||||
if s.Config.AccessControlGroupNo != "" {
|
||||
acgNo = s.Config.AccessControlGroupNo
|
||||
} else {
|
||||
acgNo = state.Get("access_control_group_no").(string)
|
||||
}
|
||||
|
||||
reqParams := &vserver.CreateServerInstancesRequest{
|
||||
RegionCode: &s.Config.RegionCode,
|
||||
ServerProductCode: &s.Config.ServerProductCode,
|
||||
MemberServerImageInstanceNo: &s.Config.MemberServerImageNo,
|
||||
LoginKeyName: &loginKeyName,
|
||||
FeeSystemTypeCode: &feeSystemTypeCode,
|
||||
InitScriptNo: &initScriptNo,
|
||||
VpcNo: &s.Config.VpcNo,
|
||||
SubnetNo: &s.Config.SubnetNo,
|
||||
NetworkInterfaceList: []*vserver.NetworkInterfaceParameter{{
|
||||
NetworkInterfaceOrder: ncloud.Int32(0),
|
||||
AccessControlGroupNoList: []*string{ncloud.String(acgNo)}},
|
||||
},
|
||||
}
|
||||
|
||||
if s.Config.MemberServerImageNo == "" {
|
||||
reqParams.ServerImageProductCode = &s.Config.ServerImageProductCode
|
||||
}
|
||||
|
||||
serverInstanceList, err := s.Conn.vserver.V2Api.CreateServerInstances(reqParams)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
s.serverInstanceNo = *serverInstanceList.ServerInstanceList[0].ServerInstanceNo
|
||||
s.Say(fmt.Sprintf("Server Instance is creating. Server InstanceNo is %s", s.serverInstanceNo))
|
||||
log.Println("Server Instance information : ", serverInstanceList.ServerInstanceList[0])
|
||||
|
||||
if err := s.WaiterServerInstanceStatus(s.Conn, s.serverInstanceNo, ServerInstanceStatusRunning, 30*time.Minute); err != nil {
|
||||
return "", errors.New("TIMEOUT : server instance status is not running")
|
||||
}
|
||||
|
||||
s.Say(fmt.Sprintf("Server Instance is created. Server InstanceNo is %s", s.serverInstanceNo))
|
||||
|
||||
return s.serverInstanceNo, nil
|
||||
}
|
||||
|
||||
func (s *StepCreateServerInstance) getClassicServerInstance() (string, string, error) {
|
||||
reqParams := &server.GetServerInstanceListRequest{
|
||||
ServerInstanceNoList: []*string{&s.serverInstanceNo},
|
||||
}
|
||||
|
||||
resp, err := s.Conn.server.V2Api.GetServerInstanceList(reqParams)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
if *resp.TotalRows > 0 {
|
||||
return *resp.ServerInstanceList[0].ServerInstanceNo, *resp.ServerInstanceList[0].ServerInstanceStatus.Code, nil
|
||||
} else {
|
||||
return "", "", nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StepCreateServerInstance) getVpcServerInstance() (string, string, error) {
|
||||
reqParams := &vserver.GetServerInstanceDetailRequest{
|
||||
ServerInstanceNo: &s.serverInstanceNo,
|
||||
}
|
||||
|
||||
resp, err := s.Conn.vserver.V2Api.GetServerInstanceDetail(reqParams)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
if *resp.TotalRows > 0 {
|
||||
return *resp.ServerInstanceList[0].ServerInstanceNo, *resp.ServerInstanceList[0].ServerInstanceStatus.Code, nil
|
||||
} else {
|
||||
return "", "", nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StepCreateServerInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
s.Say("Create Server Instance")
|
||||
loginKey := state.Get("login_key").(*LoginKey)
|
||||
|
||||
feeSystemTypeCode := "MTRAT"
|
||||
if _, ok := state.GetOk("fee_system_type_code"); ok {
|
||||
feeSystemTypeCode = state.Get("fee_system_type_code").(string)
|
||||
}
|
||||
|
||||
serverInstanceNo, err := s.CreateServerInstance(loginKey.KeyName, feeSystemTypeCode, state)
|
||||
if err == nil {
|
||||
state.Put("InstanceNo", serverInstanceNo)
|
||||
state.Put("instance_no", serverInstanceNo)
|
||||
// instance_id is the generic term used so that users can have access to the
|
||||
// instance id inside of the provisioners, used in step_provision.
|
||||
state.Put("instance_id", serverInstanceNo)
|
||||
@@ -108,6 +211,7 @@ func (s *StepCreateServerInstance) Run(ctx context.Context, state multistep.Stat
|
||||
func (s *StepCreateServerInstance) Cleanup(state multistep.StateBag) {
|
||||
_, cancelled := state.GetOk(multistep.StateCancelled)
|
||||
_, halted := state.GetOk(multistep.StateHalted)
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
|
||||
if !cancelled && !halted {
|
||||
return
|
||||
@@ -117,33 +221,31 @@ func (s *StepCreateServerInstance) Cleanup(state multistep.StateBag) {
|
||||
return
|
||||
}
|
||||
|
||||
reqParams := new(server.GetServerInstanceListRequest)
|
||||
reqParams.ServerInstanceNoList = []*string{&s.serverInstanceNo}
|
||||
_, status, err := s.GetServerInstance()
|
||||
|
||||
serverInstanceList, err := s.Conn.server.V2Api.GetServerInstanceList(reqParams)
|
||||
if err != nil || *serverInstanceList.TotalRows == 0 {
|
||||
if err != nil || len(status) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
s.Say("Clean up Server Instance")
|
||||
|
||||
serverInstance := serverInstanceList.ServerInstanceList[0]
|
||||
// stop server instance
|
||||
if *serverInstance.ServerInstanceStatus.Code != "NSTOP" && *serverInstance.ServerInstanceStatus.Code != "TERMT" {
|
||||
reqParams := new(server.StopServerInstancesRequest)
|
||||
reqParams.ServerInstanceNoList = []*string{&s.serverInstanceNo}
|
||||
|
||||
log.Println("Stop Server Instance")
|
||||
s.Conn.server.V2Api.StopServerInstances(reqParams)
|
||||
waiterServerInstanceStatus(s.Conn, s.serverInstanceNo, "NSTOP", time.Minute)
|
||||
if status != ServerInstanceStatusStopped && status != ServerInstanceStatusTerminated {
|
||||
stepStopServerInstance := NewStepStopServerInstance(s.Conn, ui, s.Config)
|
||||
err := stepStopServerInstance.StopServerInstance(s.serverInstanceNo)
|
||||
if err != nil {
|
||||
s.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// terminate server instance
|
||||
if *serverInstance.ServerInstanceStatus.Code != "TERMT" {
|
||||
reqParams := new(server.TerminateServerInstancesRequest)
|
||||
reqParams.ServerInstanceNoList = []*string{&s.serverInstanceNo}
|
||||
|
||||
log.Println("Terminate Server Instance")
|
||||
s.Conn.server.V2Api.TerminateServerInstances(reqParams)
|
||||
if status != ServerInstanceStatusTerminated {
|
||||
stepStopServerInstance := NewStepTerminateServerInstance(s.Conn, ui, s.Config)
|
||||
err := stepStopServerInstance.TerminateServerInstance(s.serverInstanceNo)
|
||||
if err != nil {
|
||||
s.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
func TestStepCreateServerInstanceShouldFailIfOperationCreateFails(t *testing.T) {
|
||||
var testSubject = &StepCreateServerInstance{
|
||||
CreateServerInstance: func(loginKeyName string, zoneNo string, feeSystemTypeCode string) (string, error) {
|
||||
CreateServerInstance: func(loginKeyName string, feeSystemTypeCode string, state multistep.StateBag) (string, error) {
|
||||
return "", fmt.Errorf("!! Unit Test FAIL !!")
|
||||
},
|
||||
Say: func(message string) {},
|
||||
@@ -25,16 +25,18 @@ func TestStepCreateServerInstanceShouldFailIfOperationCreateFails(t *testing.T)
|
||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
||||
}
|
||||
|
||||
if _, ok := stateBag.GetOk("Error"); ok == false {
|
||||
if _, ok := stateBag.GetOk("error"); ok == false {
|
||||
t.Fatal("Expected the step to set stateBag['Error'], but it was not.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepCreateServerInstanceShouldPassIfOperationCreatePasses(t *testing.T) {
|
||||
var testSubject = &StepCreateServerInstance{
|
||||
CreateServerInstance: func(loginKeyName string, zoneNo string, feeSystemTypeCode string) (string, error) { return "", nil },
|
||||
Say: func(message string) {},
|
||||
Error: func(e error) {},
|
||||
CreateServerInstance: func(loginKeyName string, feeSystemTypeCode string, state multistep.StateBag) (string, error) {
|
||||
return "", nil
|
||||
},
|
||||
Say: func(message string) {},
|
||||
Error: func(e error) {},
|
||||
}
|
||||
|
||||
stateBag := createTestStateBagStepCreateServerInstance()
|
||||
@@ -45,7 +47,7 @@ func TestStepCreateServerInstanceShouldPassIfOperationCreatePasses(t *testing.T)
|
||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
||||
}
|
||||
|
||||
if _, ok := stateBag.GetOk("Error"); ok == true {
|
||||
if _, ok := stateBag.GetOk("error"); ok == true {
|
||||
t.Fatalf("Expected the step to not set stateBag['Error'], but it was.")
|
||||
}
|
||||
}
|
||||
@@ -53,8 +55,8 @@ func TestStepCreateServerInstanceShouldPassIfOperationCreatePasses(t *testing.T)
|
||||
func createTestStateBagStepCreateServerInstance() multistep.StateBag {
|
||||
stateBag := new(multistep.BasicStateBag)
|
||||
|
||||
stateBag.Put("LoginKey", &LoginKey{"a", "b"})
|
||||
stateBag.Put("ZoneNo", "1")
|
||||
stateBag.Put("login_key", &LoginKey{"a", "b"})
|
||||
stateBag.Put("zone_no", "1")
|
||||
|
||||
return stateBag
|
||||
}
|
||||
|
||||
@@ -8,34 +8,40 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/server"
|
||||
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type StepDeleteBlockStorageInstance struct {
|
||||
Conn *NcloudAPIClient
|
||||
DeleteBlockStorageInstance func(blockStorageInstanceNo string) error
|
||||
Say func(message string)
|
||||
Error func(e error)
|
||||
Config *Config
|
||||
type StepDeleteBlockStorage struct {
|
||||
Conn *NcloudAPIClient
|
||||
DeleteBlockStorage func(blockStorageNo string) error
|
||||
Say func(message string)
|
||||
Error func(e error)
|
||||
Config *Config
|
||||
}
|
||||
|
||||
func NewStepDeleteBlockStorageInstance(conn *NcloudAPIClient, ui packersdk.Ui, config *Config) *StepDeleteBlockStorageInstance {
|
||||
var step = &StepDeleteBlockStorageInstance{
|
||||
func NewStepDeleteBlockStorage(conn *NcloudAPIClient, ui packersdk.Ui, config *Config) *StepDeleteBlockStorage {
|
||||
var step = &StepDeleteBlockStorage{
|
||||
Conn: conn,
|
||||
Say: func(message string) { ui.Say(message) },
|
||||
Error: func(e error) { ui.Error(e.Error()) },
|
||||
Config: config,
|
||||
}
|
||||
|
||||
step.DeleteBlockStorageInstance = step.deleteBlockStorageInstance
|
||||
if config.SupportVPC {
|
||||
step.DeleteBlockStorage = step.deleteVpcBlockStorage
|
||||
} else {
|
||||
step.DeleteBlockStorage = step.deleteClassicBlockStorage
|
||||
}
|
||||
|
||||
return step
|
||||
}
|
||||
|
||||
func (s *StepDeleteBlockStorageInstance) getBlockInstanceList(serverInstanceNo string) []*string {
|
||||
reqParams := new(server.GetBlockStorageInstanceListRequest)
|
||||
reqParams.ServerInstanceNo = &serverInstanceNo
|
||||
func (s *StepDeleteBlockStorage) getClassicBlockList(serverInstanceNo string) []*string {
|
||||
reqParams := &server.GetBlockStorageInstanceListRequest{
|
||||
ServerInstanceNo: &serverInstanceNo,
|
||||
}
|
||||
|
||||
blockStorageInstanceList, err := s.Conn.server.V2Api.GetBlockStorageInstanceList(reqParams)
|
||||
if err != nil {
|
||||
@@ -58,9 +64,35 @@ func (s *StepDeleteBlockStorageInstance) getBlockInstanceList(serverInstanceNo s
|
||||
return instanceList
|
||||
}
|
||||
|
||||
func (s *StepDeleteBlockStorageInstance) deleteBlockStorageInstance(serverInstanceNo string) error {
|
||||
blockStorageInstanceList := s.getBlockInstanceList(serverInstanceNo)
|
||||
if blockStorageInstanceList == nil || len(blockStorageInstanceList) == 0 {
|
||||
func (s *StepDeleteBlockStorage) getVpcBlockList(serverInstanceNo string) []*string {
|
||||
reqParams := &vserver.GetBlockStorageInstanceListRequest{
|
||||
ServerInstanceNo: &serverInstanceNo,
|
||||
}
|
||||
|
||||
blockStorageInstanceList, err := s.Conn.vserver.V2Api.GetBlockStorageInstanceList(reqParams)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if *blockStorageInstanceList.TotalRows == 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var instanceList []*string
|
||||
|
||||
for _, blockStorageInstance := range blockStorageInstanceList.BlockStorageInstanceList {
|
||||
log.Println(blockStorageInstance)
|
||||
if *blockStorageInstance.BlockStorageType.Code != "BASIC" {
|
||||
instanceList = append(instanceList, blockStorageInstance.BlockStorageInstanceNo)
|
||||
}
|
||||
}
|
||||
|
||||
return instanceList
|
||||
}
|
||||
|
||||
func (s *StepDeleteBlockStorage) deleteClassicBlockStorage(serverInstanceNo string) error {
|
||||
blockStorageInstanceList := s.getClassicBlockList(serverInstanceNo)
|
||||
if len(blockStorageInstanceList) == 0 {
|
||||
return nil
|
||||
}
|
||||
reqParams := server.DeleteBlockStorageInstancesRequest{
|
||||
@@ -73,26 +105,48 @@ func (s *StepDeleteBlockStorageInstance) deleteBlockStorageInstance(serverInstan
|
||||
|
||||
s.Say(fmt.Sprintf("Block Storage Instance is deleted. Block Storage Instance List is %v", blockStorageInstanceList))
|
||||
|
||||
if err := waiterDetachedBlockStorageInstance(s.Conn, serverInstanceNo, time.Minute); err != nil {
|
||||
if err := waiterClassicDetachedBlockStorage(s.Conn, serverInstanceNo, time.Minute); err != nil {
|
||||
return errors.New("TIMEOUT : Block Storage instance status is not deattached")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StepDeleteBlockStorageInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
func (s *StepDeleteBlockStorage) deleteVpcBlockStorage(serverInstanceNo string) error {
|
||||
blockStorageInstanceList := s.getVpcBlockList(serverInstanceNo)
|
||||
if len(blockStorageInstanceList) == 0 {
|
||||
return nil
|
||||
}
|
||||
reqParams := vserver.DeleteBlockStorageInstancesRequest{
|
||||
BlockStorageInstanceNoList: blockStorageInstanceList,
|
||||
}
|
||||
_, err := s.Conn.vserver.V2Api.DeleteBlockStorageInstances(&reqParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.Say(fmt.Sprintf("Block Storage Instance is deleted. Block Storage Instance List is %v", blockStorageInstanceList))
|
||||
|
||||
if err := waiterVpcDetachedBlockStorage(s.Conn, serverInstanceNo, time.Minute); err != nil {
|
||||
return errors.New("TIMEOUT : Block Storage instance status is not deattached")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StepDeleteBlockStorage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
if s.Config.BlockStorageSize == 0 {
|
||||
return processStepResult(nil, s.Error, state)
|
||||
}
|
||||
|
||||
s.Say("Delete Block Storage Instance")
|
||||
|
||||
var serverInstanceNo = state.Get("InstanceNo").(string)
|
||||
var serverInstanceNo = state.Get("instance_no").(string)
|
||||
|
||||
err := s.DeleteBlockStorageInstance(serverInstanceNo)
|
||||
err := s.DeleteBlockStorage(serverInstanceNo)
|
||||
|
||||
return processStepResult(err, s.Error, state)
|
||||
}
|
||||
|
||||
func (*StepDeleteBlockStorageInstance) Cleanup(multistep.StateBag) {
|
||||
func (*StepDeleteBlockStorage) Cleanup(multistep.StateBag) {
|
||||
}
|
||||
|
||||
@@ -9,11 +9,11 @@ import (
|
||||
)
|
||||
|
||||
func TestStepDeleteBlockStorageInstanceShouldFailIfOperationDeleteBlockStorageInstanceFails(t *testing.T) {
|
||||
var testSubject = &StepDeleteBlockStorageInstance{
|
||||
DeleteBlockStorageInstance: func(blockStorageInstanceNo string) error { return fmt.Errorf("!! Unit Test FAIL !!") },
|
||||
Say: func(message string) {},
|
||||
Error: func(e error) {},
|
||||
Config: &Config{BlockStorageSize: 10},
|
||||
var testSubject = &StepDeleteBlockStorage{
|
||||
DeleteBlockStorage: func(blockStorageNo string) error { return fmt.Errorf("!! Unit Test FAIL !!") },
|
||||
Say: func(message string) {},
|
||||
Error: func(e error) {},
|
||||
Config: &Config{BlockStorageSize: 10},
|
||||
}
|
||||
|
||||
stateBag := createTestStateBagStepDeleteBlockStorageInstance()
|
||||
@@ -24,17 +24,17 @@ func TestStepDeleteBlockStorageInstanceShouldFailIfOperationDeleteBlockStorageIn
|
||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
||||
}
|
||||
|
||||
if _, ok := stateBag.GetOk("Error"); ok == false {
|
||||
if _, ok := stateBag.GetOk("error"); ok == false {
|
||||
t.Fatal("Expected the step to set stateBag['Error'], but it was not.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepDeleteBlockStorageInstanceShouldPassIfOperationDeleteBlockStorageInstancePasses(t *testing.T) {
|
||||
var testSubject = &StepDeleteBlockStorageInstance{
|
||||
DeleteBlockStorageInstance: func(blockStorageInstanceNo string) error { return nil },
|
||||
Say: func(message string) {},
|
||||
Error: func(e error) {},
|
||||
Config: &Config{BlockStorageSize: 10},
|
||||
var testSubject = &StepDeleteBlockStorage{
|
||||
DeleteBlockStorage: func(blockStorageNo string) error { return nil },
|
||||
Say: func(message string) {},
|
||||
Error: func(e error) {},
|
||||
Config: &Config{BlockStorageSize: 10},
|
||||
}
|
||||
|
||||
stateBag := createTestStateBagStepDeleteBlockStorageInstance()
|
||||
@@ -45,7 +45,7 @@ func TestStepDeleteBlockStorageInstanceShouldPassIfOperationDeleteBlockStorageIn
|
||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
||||
}
|
||||
|
||||
if _, ok := stateBag.GetOk("Error"); ok == true {
|
||||
if _, ok := stateBag.GetOk("error"); ok == true {
|
||||
t.Fatalf("Expected the step to not set stateBag['Error'], but it was.")
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,7 @@ func TestStepDeleteBlockStorageInstanceShouldPassIfOperationDeleteBlockStorageIn
|
||||
func createTestStateBagStepDeleteBlockStorageInstance() multistep.StateBag {
|
||||
stateBag := new(multistep.BasicStateBag)
|
||||
|
||||
stateBag.Put("InstanceNo", "1")
|
||||
stateBag.Put("instance_no", "1")
|
||||
|
||||
return stateBag
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/server"
|
||||
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
@@ -25,15 +26,20 @@ func NewStepGetRootPassword(conn *NcloudAPIClient, ui packersdk.Ui, config *Conf
|
||||
Config: config,
|
||||
}
|
||||
|
||||
step.GetRootPassword = step.getRootPassword
|
||||
if config.SupportVPC {
|
||||
step.GetRootPassword = step.getVpcRootPassword
|
||||
} else {
|
||||
step.GetRootPassword = step.getClassicRootPassword
|
||||
}
|
||||
|
||||
return step
|
||||
}
|
||||
|
||||
func (s *StepGetRootPassword) getRootPassword(serverInstanceNo string, privateKey string) (string, error) {
|
||||
reqParams := new(server.GetRootPasswordRequest)
|
||||
reqParams.ServerInstanceNo = &serverInstanceNo
|
||||
reqParams.PrivateKey = &privateKey
|
||||
func (s *StepGetRootPassword) getClassicRootPassword(serverInstanceNo string, privateKey string) (string, error) {
|
||||
reqParams := &server.GetRootPasswordRequest{
|
||||
ServerInstanceNo: &serverInstanceNo,
|
||||
PrivateKey: &privateKey,
|
||||
}
|
||||
|
||||
rootPassword, err := s.Conn.server.V2Api.GetRootPassword(reqParams)
|
||||
if err != nil {
|
||||
@@ -45,11 +51,28 @@ func (s *StepGetRootPassword) getRootPassword(serverInstanceNo string, privateKe
|
||||
return *rootPassword.RootPassword, nil
|
||||
}
|
||||
|
||||
func (s *StepGetRootPassword) getVpcRootPassword(serverInstanceNo string, privateKey string) (string, error) {
|
||||
reqParams := &vserver.GetRootPasswordRequest{
|
||||
RegionCode: &s.Config.RegionCode,
|
||||
ServerInstanceNo: &serverInstanceNo,
|
||||
PrivateKey: &privateKey,
|
||||
}
|
||||
|
||||
rootPassword, err := s.Conn.vserver.V2Api.GetRootPassword(reqParams)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
s.Say(fmt.Sprintf("Root password is %s", *rootPassword.RootPassword))
|
||||
|
||||
return *rootPassword.RootPassword, nil
|
||||
}
|
||||
|
||||
func (s *StepGetRootPassword) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
s.Say("Get Root Password")
|
||||
|
||||
serverInstanceNo := state.Get("InstanceNo").(string)
|
||||
loginKey := state.Get("LoginKey").(*LoginKey)
|
||||
serverInstanceNo := state.Get("instance_no").(string)
|
||||
loginKey := state.Get("login_key").(*LoginKey)
|
||||
|
||||
rootPassword, err := s.GetRootPassword(serverInstanceNo, loginKey.PrivateKey)
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ func TestStepGetRootPasswordShouldFailIfOperationGetRootPasswordFails(t *testing
|
||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
||||
}
|
||||
|
||||
if _, ok := stateBag.GetOk("Error"); ok == false {
|
||||
if _, ok := stateBag.GetOk("error"); ok == false {
|
||||
t.Fatal("Expected the step to set stateBag['Error'], but it was not.")
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,7 @@ func TestStepGetRootPasswordShouldPassIfOperationGetRootPasswordPasses(t *testin
|
||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
||||
}
|
||||
|
||||
if _, ok := stateBag.GetOk("Error"); ok == true {
|
||||
if _, ok := stateBag.GetOk("error"); ok == true {
|
||||
t.Fatalf("Expected the step to not set stateBag['Error'], but it was.")
|
||||
}
|
||||
}
|
||||
@@ -53,8 +53,8 @@ func TestStepGetRootPasswordShouldPassIfOperationGetRootPasswordPasses(t *testin
|
||||
func DeleteTestStateBagStepGetRootPassword() multistep.StateBag {
|
||||
stateBag := new(multistep.BasicStateBag)
|
||||
|
||||
stateBag.Put("LoginKey", &LoginKey{"a", "b"})
|
||||
stateBag.Put("InstanceNo", "a")
|
||||
stateBag.Put("login_key", &LoginKey{"a", "b"})
|
||||
stateBag.Put("instance_no", "a")
|
||||
|
||||
return stateBag
|
||||
}
|
||||
|
||||
@@ -3,36 +3,46 @@ package ncloud
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/server"
|
||||
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type StepStopServerInstance struct {
|
||||
Conn *NcloudAPIClient
|
||||
StopServerInstance func(serverInstanceNo string) error
|
||||
Say func(message string)
|
||||
Error func(e error)
|
||||
Conn *NcloudAPIClient
|
||||
StopServerInstance func(serverInstanceNo string) error
|
||||
WaiterServerInstanceStatus func(conn *NcloudAPIClient, serverInstanceNo string, status string, timeout time.Duration) error
|
||||
Say func(message string)
|
||||
Error func(e error)
|
||||
Config *Config
|
||||
}
|
||||
|
||||
func NewStepStopServerInstance(conn *NcloudAPIClient, ui packersdk.Ui) *StepStopServerInstance {
|
||||
func NewStepStopServerInstance(conn *NcloudAPIClient, ui packersdk.Ui, config *Config) *StepStopServerInstance {
|
||||
var step = &StepStopServerInstance{
|
||||
Conn: conn,
|
||||
Say: func(message string) { ui.Say(message) },
|
||||
Error: func(e error) { ui.Error(e.Error()) },
|
||||
Conn: conn,
|
||||
Say: func(message string) { ui.Say(message) },
|
||||
Error: func(e error) { ui.Error(e.Error()) },
|
||||
Config: config,
|
||||
}
|
||||
|
||||
step.StopServerInstance = step.stopServerInstance
|
||||
if config.SupportVPC {
|
||||
step.StopServerInstance = step.stopVpcServerInstance
|
||||
step.WaiterServerInstanceStatus = waiterVpcServerInstanceStatus
|
||||
} else {
|
||||
step.StopServerInstance = step.stopClassicServerInstance
|
||||
step.WaiterServerInstanceStatus = waiterClassicServerInstanceStatus
|
||||
}
|
||||
|
||||
return step
|
||||
}
|
||||
|
||||
func (s *StepStopServerInstance) stopServerInstance(serverInstanceNo string) error {
|
||||
reqParams := new(server.StopServerInstancesRequest)
|
||||
reqParams.ServerInstanceNoList = []*string{&serverInstanceNo}
|
||||
func (s *StepStopServerInstance) stopClassicServerInstance(serverInstanceNo string) error {
|
||||
reqParams := &server.StopServerInstancesRequest{
|
||||
ServerInstanceNoList: []*string{&serverInstanceNo},
|
||||
}
|
||||
|
||||
serverInstanceList, err := s.Conn.server.V2Api.StopServerInstances(reqParams)
|
||||
if err != nil {
|
||||
@@ -40,9 +50,30 @@ func (s *StepStopServerInstance) stopServerInstance(serverInstanceNo string) err
|
||||
}
|
||||
|
||||
s.Say(fmt.Sprintf("Server Instance is stopping. Server InstanceNo is %s", *serverInstanceList.ServerInstanceList[0].ServerInstanceNo))
|
||||
log.Println("Server Instance information : ", serverInstanceList.ServerInstanceList[0])
|
||||
|
||||
if err := waiterServerInstanceStatus(s.Conn, serverInstanceNo, "NSTOP", 5*time.Minute); err != nil {
|
||||
if err := s.WaiterServerInstanceStatus(s.Conn, serverInstanceNo, ServerInstanceStatusStopped, 5*time.Minute); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.Say(fmt.Sprintf("Server Instance stopped. Server InstanceNo is %s", *serverInstanceList.ServerInstanceList[0].ServerInstanceNo))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StepStopServerInstance) stopVpcServerInstance(serverInstanceNo string) error {
|
||||
reqParams := &vserver.StopServerInstancesRequest{
|
||||
RegionCode: &s.Config.RegionCode,
|
||||
ServerInstanceNoList: []*string{&serverInstanceNo},
|
||||
}
|
||||
|
||||
serverInstanceList, err := s.Conn.vserver.V2Api.StopServerInstances(reqParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.Say(fmt.Sprintf("Server Instance is stopping. Server InstanceNo is %s", *serverInstanceList.ServerInstanceList[0].ServerInstanceNo))
|
||||
|
||||
if err := s.WaiterServerInstanceStatus(s.Conn, serverInstanceNo, ServerInstanceStatusStopped, 5*time.Minute); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -54,7 +85,7 @@ func (s *StepStopServerInstance) stopServerInstance(serverInstanceNo string) err
|
||||
func (s *StepStopServerInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
s.Say("Stop Server Instance")
|
||||
|
||||
var serverInstanceNo = state.Get("InstanceNo").(string)
|
||||
var serverInstanceNo = state.Get("instance_no").(string)
|
||||
|
||||
err := s.StopServerInstance(serverInstanceNo)
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user