Compare commits

...

75 Commits

Author SHA1 Message Date
sylviamoss b448c3182c fix TEMPATE to TEMPLATE in fmt cmd 2021-04-19 14:07:22 +02:00
Adrien Delorme 9230a06920 move googlecompute plugin to github.com/hashicorp/packer-plugin-googlecompute (#10890) 2021-04-19 11:10:15 +02:00
Sylvia Moss 16658a9f47 Extract virtualbox plugin (#10910) 2021-04-16 17:38:02 +02:00
Wilken Rivera ceb96d061a Extract ansible plugins (#10912)
* Remove ansible components and docs

* Vendored packer-plugin-ansible

* Add remote ansible docs
2021-04-16 10:31:09 -04:00
Romain Lecat bb1a025f60 Add outscale-mgo to osc codeowners (#10917) 2021-04-16 15:25:44 +02:00
Adrien Delorme 87ba7258b3 Use packer-sdc in packer + remove mapstructure-to-hcl2 & struct-markdown (#10913)
* start using `go:generate packer-sdc struct-markdown`

* Update Makefile

remove @go install ./cmd/struct-markdown

* run go generate for struct-markdown

* use //go:generate packer-sdc mapstructure-to-hcl2

* run go generate for mapstructure-to-hcl2

* remove struct-markdown and mapstructure-to-hcl2

* vendor vendors
2021-04-16 11:52:03 +02:00
Megan Marsh da312e2785 Merge pull request #10896 from hashicorp/extract_vsphere
Extract vSphere plugin
2021-04-15 16:30:27 -07:00
Megan Marsh 84af0ba6da go mod tidy 2021-04-15 16:25:58 -07:00
sylviamoss 3c6b7841bc fix vsphere link 2021-04-15 16:25:36 -07:00
sylviamoss c7ee5f1efd update packer-plugin-vsphere and sdk 2021-04-15 16:25:36 -07:00
sylviamoss a00846102b add vsphere to docs-remote-plugins.json 2021-04-15 16:25:36 -07:00
sylviamoss 41c66d6935 vendor vsphere plugin 2021-04-15 16:25:31 -07:00
sylviamoss f6854f5528 update go vendor 2021-04-15 16:24:57 -07:00
sylviamoss 38fe79948b remove vsphere components and docs 2021-04-15 16:24:57 -07:00
Daniel Finneran a6c5958c67 Adds bzip2 support to post-processor (#10867)
* compress post processor: add bzip2 + tests

* post-processor/compress/post-processor_test.go: refactor tests and add tests for bzip2

* post-processor_test.go: test write/read for all compression algos

* check artifact.Destroy() errors

* close archive before deleting it

Co-authored-by: Adrien Delorme <azr@users.noreply.github.com>
2021-04-15 18:05:09 +02:00
Jeff Escalante c17f236e85 Upgrade Downloads Page (#10907)
* upgrade downloads page

* fix syntax errors on builders/ncloud page
2021-04-14 14:51:29 -04:00
Megan Marsh bb5d7b6c40 Merge pull request #10870 from NaverCloudPlatform/master
Support ncloud vpc version
2021-04-13 10:32:16 -07:00
sangkyu-kim 1ea5a547e2 Merge branch 'master' into master 2021-04-13 13:46:48 +09:00
Megan Marsh 86b8ce8df0 Postprocessor only docs (#10899)
* add a note for only/except from cli to the post-processor template section

* typo; missing space

* Update website/content/docs/templates/hcl_templates/blocks/build/post-processor.mdx

Co-authored-by: Wilken Rivera <dev@wilkenrivera.com>

* tweak wording

Co-authored-by: Wilken Rivera <dev@wilkenrivera.com>
2021-04-12 15:39:27 -04:00
Megan Marsh b51bf9250e Merge pull request #10900 from hashicorp/digitalocean-import-docs-fix
Update URL to custom-images overview page
2021-04-12 12:18:00 -07:00
Wilken Rivera 2d5a32629a Update URL to custom-images overview page 2021-04-12 10:30:25 -04:00
Kerim Satirli 3e2db82cab fixes typo (#10894) 2021-04-09 11:59:08 +02:00
Megan Marsh bc9dd69669 Merge pull request #10880 from hashicorp/amazon_acc_test
Add plugin acceptance test using the Amazon plugin
2021-04-08 13:31:32 -07:00
Megan Marsh 734e91b97c Merge pull request #10878 from hashicorp/rewrite_acctests
Move acctest pkg from the SDK to core and update acceptance tests
2021-04-08 13:21:18 -07:00
Zachary Shilton ab0d1ee363 website: fix edit links for remote plugins (#10884)
* website: fix issue with edits links, use branch name, not version

* website: patch layout shift issue related to global style

* website: update plugin config docs with sourceBranch

* website: tweak spacing above plugin tier label

* website: add note on default value for sourceBranch
2021-04-08 10:09:58 -04:00
Kerim Satirli cf94fd1778 switches JSON and HCL2 tabs (#10888)
* switches JSON and HCL2 tabs for all provisioners

* corrects `packer` to `Packer`

* corrects `http` to `HTTP`

* corrects typos and highlighting consistency issues

* corrects typos and highlighting consistency issues

* corrects typos and highlighting consistency issues

* `ansible` -> `Ansible`

* `packer fmt` for HCL2 blocks in provisioners

* linting and spelling

* fixes formatting

* fixes formatting

* Update website/content/docs/provisioners/ansible.mdx

Co-authored-by: Adrien Delorme <azr@users.noreply.github.com>

* fixes formatting

* improves example

* generate stuff

Co-authored-by: Adrien Delorme <azr@users.noreply.github.com>
2021-04-08 15:02:57 +02:00
George Wilson 15a2e59bba Autogenerated docs for ansible local provisioner (#10829) 2021-04-08 12:18:49 +02:00
Kerim Satirli 8c4eb5f4aa corrects default value and adds highlighting (#10886)
* value is expected to be `ssh` not `SSH`

* highlighting default values
2021-04-08 12:02:05 +02:00
Kendall Strautman 86788220a9 Merge pull request #10875 from hashicorp/ks.style/branding-refresh
style(website): upgrade react-components, colors, logos
2021-04-07 08:02:13 -07:00
Kerim Satirli e3bcb4f2ac adds highlighting to locals stanza (#10881) 2021-04-07 16:38:23 +02:00
Kendall Strautman ccd0430fda Update website/pages/downloads/style.css
Co-authored-by: Zachary Shilton <4624598+zchsh@users.noreply.github.com>
2021-04-07 07:35:05 -07:00
sylviamoss 49474f8f37 add plugin acceptance test using amazon plugin 2021-04-07 16:21:15 +02:00
sylviamoss e0614cabf4 move acctest pkg from sdk to core and update acceptance tests 2021-04-07 11:52:19 +02:00
Shaun McEvoy eaec3e5564 Add Image Storage Locations field to Google Compute Import post-processor (#10864)
* add image storage locations to Google Compute Import
2021-04-07 10:36:08 +02:00
James eaaf22dcde builder/digitalocean: support ecdsa, ed25519, dsa temporary key types via packer-plugin-sdk/communicator step… (#10856)
* support ecdsa, ed25519 temporary key algos via temporary_key_pair_algorith config

* builder/digitalocean: improve public key marshalling error handling

* builder/digitalocean: use packer-plugin-sdk to manage temporary ssh keys

* builder/digitalocean: clean up unused properties

Co-authored-by: tserkov <tserkov@penguin>
2021-04-07 10:33:05 +02:00
Qingchuan Hao f2f33fa344 Correct SIG timout (#10816) 2021-04-07 10:00:17 +02:00
Kendall Strautman 9189f1228e chore: adds comment 2021-04-06 15:37:05 -07:00
Kendall Strautman 5e7b5729e6 style: override product-downloader colors 2021-04-06 15:35:23 -07:00
Megan Marsh 9365c90c0b revendor 2021-04-06 13:52:07 -07:00
Megan Marsh f27bdf85f4 upgrade exoscale dependency 2021-04-06 13:51:14 -07:00
Kendall Strautman 9f7bb4da25 chore: fix deps 2021-04-06 13:48:00 -07:00
Kendall Strautman b1967e99c7 chore: upgrade react-components, colors, logos 2021-04-06 13:47:58 -07:00
Brandon Romano 7efb41868f Upgrade StackMenu to latest (#10874) 2021-04-06 16:40:48 -04:00
Wilken Rivera 260906c3e4 Add redirect for Docker post-processor pages (#10872)
Remote plugin docs such as Docker now fall under a top level path named
after the provider (e.g https://packer.io/docs/docker/...). This change
adds a redirect for the old URLs to the new location.
2021-04-06 10:37:05 -04:00
Roshan Padaki f65e1d5d55 Fix tiny typo in hcl2_upgrade.mdx (#10868) 2021-04-06 11:51:10 +02:00
sangkyu.kim 23e8684aae fix lint, fmt, generate 2021-04-06 11:40:41 +09:00
sangkyu.kim e22d9861aa update modules 2021-04-06 10:56:16 +09:00
sangkyu-kim 1c8fc65223 Merge branch 'master' into master 2021-04-06 10:35:58 +09:00
sangkyu-kim 42ca66752f Merge pull request #1 from NaverCloudPlatform/ncloud_vpc
Implement VPC
2021-04-06 10:27:50 +09:00
packer-ci 33461126e2 Putting source back into Dev Mode 2021-04-05 23:32:25 +00:00
packer-ci 1f834e229a Cut version 1.7.2 2021-04-05 22:55:12 +00:00
packer-ci 4417f8b3bf cut version 1.7.2 2021-04-05 22:55:11 +00:00
packer-ci 8db540a935 update changelog 2021-04-05 22:55:11 +00:00
Megan Marsh e8780bf7b8 add massive warning about error logging to WrappedMain 2021-04-05 15:03:39 -07:00
Megan Marsh 3b0226d496 update changelog 2021-04-05 11:16:39 -07:00
Megan Marsh 4c08789642 Merge pull request #10850 from hashicorp/ui_fix
Switch to using ui once it is initialized
2021-04-05 11:14:20 -07:00
Wilken Rivera 634bf87d99 Update CHANGELOG 2021-04-05 12:51:05 -04:00
Wilken Rivera d566419c45 Update unmaintained-plugins partial 2021-04-05 08:14:43 -04:00
Recai Oktaş cce1f5c1e3 Update index.mdx (#10865)
Fix a minor typo.
2021-04-05 06:40:32 -04:00
elsnepal 7f26429a2a feature[alicloud]: add ramrole to ecs instance (#10845)
* add RamRole support for ecs instance

* ordering of attributes

* run make generate
2021-04-02 15:02:13 -04:00
Sylvia Moss d81c02b456 Fix primary disk resize on clone and add tests (#10848)
* Fix primary disk resize on clone and add tests

* remove commented tests
2021-04-02 14:41:11 -04:00
mmassez 794e83b171 Proxmox builder return first ipv4 address (#10858)
* Check if IP address is IPv4 before returning it

Returns the first IPv4 address instead of the first IP address which is an IPv6 for Windows VMs

* Updated the go module

* Reversed the order of checks

First check if it's a loopback and check for ipv4 afterwards
2021-04-02 14:37:20 -04:00
Zachary Shilton 58fb58c2ea website: fix issue with bloated static props (#10860)
* website: fix issue with bloated static props

* website: remove script to check static props size
2021-04-02 11:29:31 -04:00
sangkyu.kim 15a9e1b20a skip validate product_code if empty 2021-04-02 18:18:59 +09:00
sangkyu.kim 3f23a5ec74 Remove getClassicServerImageProductList within block storage size 100GB 2021-04-02 16:38:58 +09:00
sangkyu.kim f4cbb5d7dc fix length validation message 2021-04-02 14:55:05 +09:00
Brian Choy fb04fa7a25 Fix vault function docs example (#10851)
The given example is missing a `,`.
2021-04-01 15:18:06 +02:00
Zachary Shilton 830140157d website: remove obselete nav data (#10811)
* website: remove obselete sidebar_title frontmatter from docs

* website: bump to latest docs-page

* website: update plugin creation and registration docs

* website: fix broken links
2021-03-31 15:07:00 -04:00
sangkyu.kim cdcdf6a618 fix checking subnet type 2021-03-31 18:22:22 +09:00
sangkyu.kim 74434b3c3e create temporary ACG for VPC 2021-03-31 15:15:57 +09:00
sangkyu.kim 3a11352dfa Fix ncloud builder unhandled buildvar type 2021-03-30 13:59:59 +09:00
sangkyu.kim 56728a937b update ncloud guide 2021-03-30 11:53:42 +09:00
sangkyu.kim f044a64014 fix test code 2021-03-29 22:51:04 +09:00
sangkyu.kim cd370aaaad implement vpc environment 2021-03-29 22:51:04 +09:00
sangkyu.kim af865b1591 update ncloud-sdk-go-v2 vendor 2021-03-29 22:51:03 +09:00
2323 changed files with 192257 additions and 123144 deletions
+15 -1
View File
@@ -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
View File
@@ -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
+1 -2
View File
@@ -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
+71
View File
@@ -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)
}
+112
View File
@@ -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"]
}
+72
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
//go:generate struct-markdown
//go:generate packer-sdc struct-markdown
package ecs
+2 -1
View File
@@ -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,
+3 -1
View File
@@ -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},
+1 -1
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
//go:generate struct-markdown
//go:generate packer-sdc struct-markdown
package ecs
+3 -1
View File
@@ -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)
+1 -1
View File
@@ -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"
+2 -2
View File
@@ -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 -1
View File
@@ -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
+2 -2
View File
@@ -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 -1
View 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 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 -1
View File
@@ -1,4 +1,4 @@
//go:generate struct-markdown
//go:generate packer-sdc struct-markdown
package client
-1
View File
@@ -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)
+1 -1
View File
@@ -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"
+2 -2
View File
@@ -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 -1
View File
@@ -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
+2 -2
View File
@@ -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 -1
View 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 cloudstack
+10 -3
View File
@@ -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{
+1 -1
View File
@@ -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"
)
+2 -2
View File
@@ -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 -1
View 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 digitalocean
+5 -59
View File
@@ -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
}
+1 -1
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
//go:generate mapstructure-to-hcl2 -type Config
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
package file
+1 -1
View 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
-37
View 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")
}
}
-692
View File
@@ -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"
}`
-26
View File
@@ -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())
}
-72
View File
@@ -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)
}
}
-21
View File
@@ -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-----
+1 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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
+1 -1
View File
@@ -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) {
+2 -2
View File
@@ -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 -1
View 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 hyperone
+1 -1
View File
@@ -1,4 +1,4 @@
//go:generate struct-markdown
//go:generate packer-sdc struct-markdown
package common
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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 -1
View 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 iso
+2 -2
View File
@@ -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 -1
View 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 vmcx
+1 -1
View File
@@ -1,4 +1,4 @@
//go:generate mapstructure-to-hcl2 -type Config
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
package jdcloud
+1 -1
View 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 jdcloud
+1 -1
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
//go:generate mapstructure-to-hcl2 -type Config
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
package linode
+1 -1
View 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 linode
+2 -2
View File
@@ -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 -1
View 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 lxc
+2 -2
View File
@@ -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 -1
View 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 lxd
+36 -54
View File
@@ -32,7 +32,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
}
func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) (packersdk.Artifact, error) {
ui.Message("Creating Naver Cloud Platform Connection ...")
ui.Message("Creating NAVER CLOUD PLATFORM Connection ...")
config := Config{
AccessKey: b.config.AccessKey,
SecretKey: b.config.SecretKey,
@@ -46,60 +46,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
View File
@@ -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
}
+11 -1
View 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 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},
+4 -4
View File
@@ -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")
}
}
+1 -1
View File
@@ -6,7 +6,7 @@ import (
func processStepResult(err error, sayError func(error), state multistep.StateBag) multistep.StepAction {
if err != nil {
state.Put("Error", err)
state.Put("error", err)
sayError(err)
return multistep.ActionHalt
@@ -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
}
+116
View File
@@ -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
}
+56 -10
View File
@@ -6,6 +6,7 @@ import (
"time"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/server"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
@@ -18,23 +19,32 @@ type LoginKey struct {
type StepCreateLoginKey struct {
Conn *NcloudAPIClient
CreateLoginKey func() (*LoginKey, error)
DeleteLoginKey func(loginKey string) error
Say func(message string)
Error func(e error)
Config *Config
}
func NewStepCreateLoginKey(conn *NcloudAPIClient, ui packersdk.Ui) *StepCreateLoginKey {
func NewStepCreateLoginKey(conn *NcloudAPIClient, ui packersdk.Ui, config *Config) *StepCreateLoginKey {
var step = &StepCreateLoginKey{
Conn: conn,
Say: func(message string) { ui.Say(message) },
Error: func(e error) { ui.Error(e.Error()) },
Conn: conn,
Say: func(message string) { ui.Say(message) },
Error: func(e error) { ui.Error(e.Error()) },
Config: config,
}
step.CreateLoginKey = step.createLoginKey
if config.SupportVPC {
step.CreateLoginKey = step.createVpcLoginKey
step.DeleteLoginKey = step.deleteVpcLoginKey
} else {
step.CreateLoginKey = step.createClassicLoginKey
step.DeleteLoginKey = step.deleteClassicLoginKey
}
return step
}
func (s *StepCreateLoginKey) createLoginKey() (*LoginKey, error) {
func (s *StepCreateLoginKey) createClassicLoginKey() (*LoginKey, error) {
keyName := fmt.Sprintf("packer-%d", time.Now().Unix())
reqParams := &server.CreateLoginKeyRequest{KeyName: &keyName}
@@ -46,12 +56,46 @@ func (s *StepCreateLoginKey) createLoginKey() (*LoginKey, error) {
return &LoginKey{keyName, *privateKey.PrivateKey}, nil
}
func (s *StepCreateLoginKey) createVpcLoginKey() (*LoginKey, error) {
keyName := fmt.Sprintf("packer-%d", time.Now().Unix())
reqParams := &vserver.CreateLoginKeyRequest{KeyName: &keyName}
privateKey, err := s.Conn.vserver.V2Api.CreateLoginKey(reqParams)
if err != nil {
return nil, err
}
return &LoginKey{keyName, *privateKey.PrivateKey}, nil
}
func (s *StepCreateLoginKey) deleteClassicLoginKey(keyName string) error {
reqParams := &server.DeleteLoginKeyRequest{KeyName: &keyName}
_, err := s.Conn.server.V2Api.DeleteLoginKey(reqParams)
if err != nil {
return err
}
return nil
}
func (s *StepCreateLoginKey) deleteVpcLoginKey(keyName string) error {
reqParams := &vserver.DeleteLoginKeysRequest{KeyNameList: []*string{&keyName}}
_, err := s.Conn.vserver.V2Api.DeleteLoginKeys(reqParams)
if err != nil {
return err
}
return nil
}
func (s *StepCreateLoginKey) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
s.Say("Create Login Key")
loginKey, err := s.CreateLoginKey()
if err == nil {
state.Put("LoginKey", loginKey)
state.Put("login_key", loginKey)
s.Say(fmt.Sprintf("Login Key[%s] is created", loginKey.KeyName))
}
@@ -59,9 +103,11 @@ func (s *StepCreateLoginKey) Run(ctx context.Context, state multistep.StateBag)
}
func (s *StepCreateLoginKey) Cleanup(state multistep.StateBag) {
if loginKey, ok := state.GetOk("LoginKey"); ok {
if loginKey, ok := state.GetOk("login_key"); ok {
s.Say("Clean up login key")
reqParams := &server.DeleteLoginKeyRequest{KeyName: &loginKey.(*LoginKey).KeyName}
s.Conn.server.V2Api.DeleteLoginKey(reqParams)
if err := s.DeleteLoginKey(loginKey.(*LoginKey).KeyName); err != nil {
s.Error(err)
return
}
}
}
+2 -2
View File
@@ -23,7 +23,7 @@ func TestStepCreateLoginKeyShouldFailIfOperationCreateLoginKeyFails(t *testing.T
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
}
if _, ok := stateBag.GetOk("Error"); ok == false {
if _, ok := stateBag.GetOk("error"); ok == false {
t.Fatal("Expected the step to set stateBag['Error'], but it was not.")
}
}
@@ -43,7 +43,7 @@ func TestStepCreateLoginKeyShouldPassIfOperationCreateLoginKeyPasses(t *testing.
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
}
if _, ok := stateBag.GetOk("Error"); ok == true {
if _, ok := stateBag.GetOk("error"); ok == true {
t.Fatalf("Expected the step to not set stateBag['Error'], but it was.")
}
}
+256 -60
View File
@@ -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
}
+62 -10
View File
@@ -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
}
+148 -46
View File
@@ -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
}
+30 -7
View File
@@ -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)
+4 -4
View File
@@ -24,7 +24,7 @@ func TestStepGetRootPasswordShouldFailIfOperationGetRootPasswordFails(t *testing
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
}
if _, ok := stateBag.GetOk("Error"); ok == false {
if _, ok := stateBag.GetOk("error"); ok == false {
t.Fatal("Expected the step to set stateBag['Error'], but it was not.")
}
}
@@ -45,7 +45,7 @@ func TestStepGetRootPasswordShouldPassIfOperationGetRootPasswordPasses(t *testin
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
}
if _, ok := stateBag.GetOk("Error"); ok == true {
if _, ok := stateBag.GetOk("error"); ok == true {
t.Fatalf("Expected the step to not set stateBag['Error'], but it was.")
}
}
@@ -53,8 +53,8 @@ func TestStepGetRootPasswordShouldPassIfOperationGetRootPasswordPasses(t *testin
func DeleteTestStateBagStepGetRootPassword() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put("LoginKey", &LoginKey{"a", "b"})
stateBag.Put("InstanceNo", "a")
stateBag.Put("login_key", &LoginKey{"a", "b"})
stateBag.Put("instance_no", "a")
return stateBag
}
+47 -16
View File
@@ -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