Compare commits

..

32 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
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
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
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
1675 changed files with 88718 additions and 121678 deletions
+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
+1 -1
View File
@@ -1,4 +1,4 @@
//go:generate struct-markdown
//go:generate packer-sdc struct-markdown
package ecs
+1 -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.
+1 -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
+1 -1
View File
@@ -1,4 +1,4 @@
//go:generate struct-markdown
//go:generate packer-sdc struct-markdown
package ecs
+1 -1
View File
@@ -1,4 +1,4 @@
//go:generate struct-markdown
//go:generate packer-sdc struct-markdown
package ecs
+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
+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
+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
+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
@@ -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
+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
@@ -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)
@@ -23,7 +23,7 @@ func TestStepStopServerInstanceShouldFailIfOperationStopFails(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 TestStepStopServerInstanceShouldPassIfOperationStopPasses(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.")
}
}
@@ -51,6 +51,6 @@ func TestStepStopServerInstanceShouldPassIfOperationStopPasses(t *testing.T) {
func createTestStateBagStepStopServerInstance() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put("InstanceNo", "a")
stateBag.Put("instance_no", "a")
return stateBag
}
@@ -3,9 +3,12 @@ package ncloud
import (
"context"
"errors"
"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"
)
@@ -15,37 +18,45 @@ type StepTerminateServerInstance struct {
TerminateServerInstance func(serverInstanceNo string) error
Say func(message string)
Error func(e error)
Config *Config
}
func NewStepTerminateServerInstance(conn *NcloudAPIClient, ui packersdk.Ui) *StepTerminateServerInstance {
func NewStepTerminateServerInstance(conn *NcloudAPIClient, ui packersdk.Ui, config *Config) *StepTerminateServerInstance {
var step = &StepTerminateServerInstance{
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.TerminateServerInstance = step.terminateServerInstance
if config.SupportVPC {
step.TerminateServerInstance = step.terminateVpcServerInstance
} else {
step.TerminateServerInstance = step.terminateClassicServerInstance
}
return step
}
func (s *StepTerminateServerInstance) terminateServerInstance(serverInstanceNo string) error {
reqParams := new(server.TerminateServerInstancesRequest)
reqParams.ServerInstanceNoList = []*string{&serverInstanceNo}
func (s *StepTerminateServerInstance) terminateClassicServerInstance(serverInstanceNo string) error {
reqParams := &server.TerminateServerInstancesRequest{
ServerInstanceNoList: []*string{&serverInstanceNo},
}
_, err := s.Conn.server.V2Api.TerminateServerInstances(reqParams)
if err != nil {
return err
}
s.Say(fmt.Sprintf("Server Instance is terminating. Server InstanceNo is %s", serverInstanceNo))
c1 := make(chan error, 1)
go func() {
reqParams := new(server.GetServerInstanceListRequest)
reqParams.ServerInstanceNoList = []*string{&serverInstanceNo}
reqParams := &server.GetServerInstanceListRequest{
ServerInstanceNoList: []*string{&serverInstanceNo},
}
for {
serverInstanceList, err := s.Conn.server.V2Api.GetServerInstanceList(reqParams)
if err != nil {
c1 <- err
@@ -55,22 +66,67 @@ func (s *StepTerminateServerInstance) terminateServerInstance(serverInstanceNo s
return
}
log.Printf("Wating for terminating server instance [%s] is %s\n", serverInstanceNo, *serverInstanceList.ServerInstanceList[0].ServerInstanceStatus.Code)
time.Sleep(time.Second * 3)
}
}()
select {
case res := <-c1:
s.Say(fmt.Sprintf("Server Instance terminated. Server InstanceNo is %s", serverInstanceNo))
return res
case <-time.After(time.Second * 60):
return errors.New("TIMEOUT : Can't terminate server instance")
}
}
func (s *StepTerminateServerInstance) terminateVpcServerInstance(serverInstanceNo string) error {
reqParams := &vserver.TerminateServerInstancesRequest{
ServerInstanceNoList: []*string{&serverInstanceNo},
}
_, err := s.Conn.vserver.V2Api.TerminateServerInstances(reqParams)
if err != nil {
return err
}
s.Say(fmt.Sprintf("Server Instance is terminating. Server InstanceNo is %s", serverInstanceNo))
c1 := make(chan error, 1)
go func() {
reqParams := &vserver.GetServerInstanceListRequest{
RegionCode: &s.Config.RegionCode,
ServerInstanceNoList: []*string{&serverInstanceNo},
}
for {
serverInstanceList, err := s.Conn.vserver.V2Api.GetServerInstanceList(reqParams)
if err != nil {
c1 <- err
return
} else if *serverInstanceList.TotalRows == 0 {
c1 <- nil
return
}
log.Printf("Wating for terminating server instance [%s] is %s\n", serverInstanceNo, *serverInstanceList.ServerInstanceList[0].ServerInstanceStatus.Code)
time.Sleep(time.Second * 3)
}
}()
select {
case res := <-c1:
s.Say(fmt.Sprintf("Server Instance terminated. Server InstanceNo is %s", serverInstanceNo))
return res
case <-time.After(time.Second * 120):
return errors.New("TIMEOUT : Can't terminate server instance")
}
}
func (s *StepTerminateServerInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
s.Say("Terminate Server Instance")
var serverInstanceNo = state.Get("InstanceNo").(string)
var serverInstanceNo = state.Get("instance_no").(string)
err := s.TerminateServerInstance(serverInstanceNo)
@@ -23,7 +23,7 @@ func TestStepTerminateServerInstanceShouldFailIfOperationTerminationFails(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.")
}
}
@@ -43,7 +43,7 @@ func TestStepTerminateServerInstanceShouldPassIfOperationTerminationPasses(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.")
}
}
@@ -51,6 +51,6 @@ func TestStepTerminateServerInstanceShouldPassIfOperationTerminationPasses(t *te
func createTestStateBagStepTerminateServerInstance() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put("InstanceNo", "a")
stateBag.Put("instance_no", "a")
return stateBag
}
+264 -61
View File
@@ -9,6 +9,8 @@ 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/vpc"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/olekukonko/tablewriter"
@@ -16,14 +18,20 @@ import (
//StepValidateTemplate : struct for Validation a template
type StepValidateTemplate struct {
Conn *NcloudAPIClient
Validate func() error
Say func(message string)
Error func(e error)
Config *Config
zoneNo string
regionNo string
FeeSystemTypeCode string
Conn *NcloudAPIClient
Validate func() error
getZone func() error
getMemberServerImageList func() ([]*server.MemberServerImage, error)
getServerImageProductList func() ([]*server.Product, error)
getServerProductList func(serverImageProductCode string) ([]*server.Product, error)
Say func(message string)
Error func(e error)
Config *Config
zoneNo string
zoneCode string
regionNo string
regionCode string
FeeSystemTypeCode string
}
// NewStepValidateTemplate : function for Validation a template
@@ -35,6 +43,18 @@ func NewStepValidateTemplate(conn *NcloudAPIClient, ui packersdk.Ui, config *Con
Config: config,
}
if config.SupportVPC {
step.getZone = step.getZoneCode
step.getMemberServerImageList = step.getVpcMemberServerImageList
step.getServerImageProductList = step.getVpcServerImageProductList
step.getServerProductList = step.getVpcServerProductList
} else {
step.getZone = step.getZoneNo
step.getMemberServerImageList = step.getClassicMemberServerImageList
step.getServerImageProductList = step.getClassicServerImageProductList
step.getServerProductList = step.getClassicServerProductList
}
step.Validate = step.validateTemplate
return step
@@ -51,21 +71,19 @@ func (s *StepValidateTemplate) getZoneNo() error {
return err
}
var regionNo string
for _, region := range regionList.RegionList {
if strings.EqualFold(*region.RegionName, s.Config.Region) {
regionNo = *region.RegionNo
s.regionNo = *region.RegionNo
s.regionCode = *region.RegionCode
}
}
if regionNo == "" {
if s.regionNo == "" {
return fmt.Errorf("region %s is invalid", s.Config.Region)
}
s.regionNo = regionNo
// Get ZoneNo
resp, err := s.Conn.server.V2Api.GetZoneList(&server.GetZoneListRequest{RegionNo: &regionNo})
resp, err := s.Conn.server.V2Api.GetZoneList(&server.GetZoneListRequest{RegionNo: &s.regionNo})
if err != nil {
return err
}
@@ -77,19 +95,51 @@ func (s *StepValidateTemplate) getZoneNo() error {
return nil
}
func (s *StepValidateTemplate) validateMemberServerImage() error {
// getZoneCode : get zoneCode
func (s *StepValidateTemplate) getZoneCode() error {
if s.Config.Region == "" {
return nil
}
regionList, err := s.Conn.vserver.V2Api.GetRegionList(&vserver.GetRegionListRequest{})
if err != nil {
return err
}
for _, region := range regionList.RegionList {
if strings.EqualFold(*region.RegionName, s.Config.Region) {
s.regionCode = *region.RegionCode
s.Config.RegionCode = *region.RegionCode
}
}
if s.regionCode == "" {
return fmt.Errorf("region %s is invalid", s.Config.Region)
}
// Get ZoneNo
resp, err := s.Conn.vserver.V2Api.GetZoneList(&vserver.GetZoneListRequest{RegionCode: &s.regionCode})
if err != nil {
return err
}
if len(resp.ZoneList) > 0 {
s.zoneCode = *resp.ZoneList[0].ZoneCode
}
return nil
}
func (s *StepValidateTemplate) validateMemberServerImage(fnGetServerImageList func() ([]*server.MemberServerImage, error)) error {
var serverImageName = s.Config.ServerImageName
reqParams := new(server.GetMemberServerImageListRequest)
reqParams.RegionNo = &s.regionNo
memberServerImageList, err := s.Conn.server.V2Api.GetMemberServerImageList(reqParams)
memberServerImageList, err := fnGetServerImageList()
if err != nil {
return err
}
var isExistMemberServerImageNo = false
for _, image := range memberServerImageList.MemberServerImageList {
for _, image := range memberServerImageList {
// Check duplicate server_image_name
if *image.MemberServerImageName == serverImageName {
return fmt.Errorf("server_image_name %s is exists", serverImageName)
@@ -112,16 +162,84 @@ func (s *StepValidateTemplate) validateMemberServerImage() error {
return nil
}
func (s *StepValidateTemplate) getClassicMemberServerImageList() ([]*server.MemberServerImage, error) {
reqParams := &server.GetMemberServerImageListRequest{
RegionNo: &s.regionNo,
}
resp, err := s.Conn.server.V2Api.GetMemberServerImageList(reqParams)
if err != nil {
return nil, err
}
return resp.MemberServerImageList, nil
}
func (s *StepValidateTemplate) getVpcMemberServerImageList() ([]*server.MemberServerImage, error) {
reqParams := &vserver.GetMemberServerImageInstanceListRequest{
RegionCode: &s.regionCode,
}
memberServerImageList, err := s.Conn.vserver.V2Api.GetMemberServerImageInstanceList(reqParams)
if err != nil {
return nil, err
}
var results []*server.MemberServerImage
for _, r := range memberServerImageList.MemberServerImageInstanceList {
results = append(results, &server.MemberServerImage{
MemberServerImageNo: r.MemberServerImageInstanceNo,
MemberServerImageName: r.MemberServerImageName,
MemberServerImageDescription: r.MemberServerImageDescription,
OriginalServerInstanceNo: r.OriginalServerInstanceNo,
OriginalServerProductCode: r.OriginalServerImageProductCode,
})
}
return results, nil
}
func (s *StepValidateTemplate) getClassicServerImageProductList() ([]*server.Product, error) {
reqParams := &server.GetServerImageProductListRequest{
RegionNo: &s.regionNo,
}
resp, err := s.Conn.server.V2Api.GetServerImageProductList(reqParams)
if err != nil {
return nil, err
}
return resp.ProductList, nil
}
func (s *StepValidateTemplate) getVpcServerImageProductList() ([]*server.Product, error) {
reqParams := &vserver.GetServerImageProductListRequest{
RegionCode: &s.regionCode,
}
resp, err := s.Conn.vserver.V2Api.GetServerImageProductList(reqParams)
if err != nil {
return nil, err
}
var results []*server.Product
for _, r := range resp.ProductList {
results = append(results, &server.Product{
ProductCode: r.ProductCode,
ProductName: r.ProductName,
})
}
return results, nil
}
func (s *StepValidateTemplate) validateServerImageProduct() error {
var serverImageProductCode = s.Config.ServerImageProductCode
if serverImageProductCode == "" {
return nil
}
reqParams := new(server.GetServerImageProductListRequest)
reqParams.RegionNo = &s.regionNo
serverImageProductList, err := s.Conn.server.V2Api.GetServerImageProductList(reqParams)
serverImageProductList, err := s.getServerImageProductList()
if err != nil {
return err
}
@@ -132,7 +250,7 @@ func (s *StepValidateTemplate) validateServerImageProduct() error {
table := tablewriter.NewWriter(&buf)
table.SetHeader([]string{"Name", "Code"})
for _, product := range serverImageProductList.ProductList {
for _, product := range serverImageProductList {
// Check exist server image product code
if *product.ProductCode == serverImageProductCode {
isExistServerImage = true
@@ -143,26 +261,6 @@ func (s *StepValidateTemplate) validateServerImageProduct() error {
table.Append([]string{*product.ProductName, *product.ProductCode})
}
if !isExistServerImage {
reqParams.BlockStorageSize = ncloud.Int32(100)
serverImageProductList, err := s.Conn.server.V2Api.GetServerImageProductList(reqParams)
if err != nil {
return err
}
for _, product := range serverImageProductList.ProductList {
// Check exist server image product code
if *product.ProductCode == serverImageProductCode {
isExistServerImage = true
productName = *product.ProductName
break
}
table.Append([]string{*product.ProductName, *product.ProductCode})
}
}
if !isExistServerImage {
table.Render()
s.Say(buf.String())
@@ -177,21 +275,63 @@ func (s *StepValidateTemplate) validateServerImageProduct() error {
return nil
}
func (s *StepValidateTemplate) getClassicServerProductList(serverImageProductCode string) ([]*server.Product, error) {
reqParams := &server.GetServerProductListRequest{
ServerImageProductCode: &serverImageProductCode,
RegionNo: &s.regionNo,
}
resp, err := s.Conn.server.V2Api.GetServerProductList(reqParams)
if err != nil {
return nil, err
}
return resp.ProductList, nil
}
func (s *StepValidateTemplate) getVpcServerProductList(serverImageProductCode string) ([]*server.Product, error) {
reqParams := &vserver.GetServerProductListRequest{
ServerImageProductCode: &serverImageProductCode,
RegionCode: &s.regionCode,
}
resp, err := s.Conn.vserver.V2Api.GetServerProductList(reqParams)
if err != nil {
return nil, err
}
var results []*server.Product
for _, r := range resp.ProductList {
results = append(results, &server.Product{
ProductCode: r.ProductCode,
ProductName: r.ProductName,
ProductType: &server.CommonCode{
Code: r.ProductType.Code,
CodeName: r.ProductType.CodeName,
},
})
}
return results, nil
}
func (s *StepValidateTemplate) validateServerProductCode() error {
var serverImageProductCode = s.Config.ServerImageProductCode
var productCode = s.Config.ServerProductCode
reqParams := new(server.GetServerProductListRequest)
reqParams.ServerImageProductCode = &serverImageProductCode
reqParams.RegionNo = &s.regionNo
resp, err := s.Conn.server.V2Api.GetServerProductList(reqParams)
productList, err := s.getServerProductList(serverImageProductCode)
if err != nil {
return err
}
if productCode == "" {
return nil
}
var isExistProductCode = false
for _, product := range resp.ProductList {
for _, product := range productList {
// Check exist server image product code
if *product.ProductCode == productCode {
isExistProductCode = true
@@ -203,11 +343,6 @@ func (s *StepValidateTemplate) validateServerProductCode() error {
return errors.New("You cannot create my server image for VDS servers")
}
break
} else if productCode == "" && *product.ProductType.Code == "STAND" {
isExistProductCode = true
s.Config.ServerProductCode = *product.ProductCode
s.Say("server_product_code '" + *product.ProductCode + "' is configured automatically")
break
}
}
@@ -216,7 +351,7 @@ func (s *StepValidateTemplate) validateServerProductCode() error {
var buf bytes.Buffer
table := tablewriter.NewWriter(&buf)
table.SetHeader([]string{"Name", "Code"})
for _, product := range resp.ProductList {
for _, product := range productList {
table.Append([]string{*product.ProductName, *product.ProductCode})
}
table.Render()
@@ -229,15 +364,78 @@ func (s *StepValidateTemplate) validateServerProductCode() error {
return nil
}
func (s *StepValidateTemplate) validateVpc() error {
if !s.Config.SupportVPC {
return nil
}
if s.Config.VpcNo != "" {
reqParam := &vpc.GetVpcDetailRequest{
RegionCode: &s.Config.RegionCode,
VpcNo: &s.Config.VpcNo,
}
resp, err := s.Conn.vpc.V2Api.GetVpcDetail(reqParam)
if err != nil {
return err
}
if resp == nil || *resp.TotalRows == 0 {
return fmt.Errorf("cloud not found VPC `vpc_no` [%s]", s.Config.VpcNo)
}
}
if s.Config.SubnetNo != "" {
reqParam := &vpc.GetSubnetDetailRequest{
RegionCode: &s.Config.RegionCode,
SubnetNo: &s.Config.SubnetNo,
}
resp, err := s.Conn.vpc.V2Api.GetSubnetDetail(reqParam)
if err != nil {
return err
}
if resp != nil && *resp.TotalRows > 0 && *resp.SubnetList[0].SubnetType.Code == "PUBLIC" {
s.Config.VpcNo = *resp.SubnetList[0].VpcNo
s.Say("Set `vpc_no` is " + s.Config.VpcNo)
} else {
return fmt.Errorf("cloud not found public subnet in `subnet_no` [%s]", s.Config.SubnetNo)
}
}
if s.Config.VpcNo != "" && s.Config.SubnetNo == "" {
reqParam := &vpc.GetSubnetListRequest{
RegionCode: &s.Config.RegionCode,
VpcNo: &s.Config.VpcNo,
SubnetTypeCode: ncloud.String("PUBLIC"),
}
resp, err := s.Conn.vpc.V2Api.GetSubnetList(reqParam)
if err != nil {
return err
}
if resp != nil && *resp.TotalRows > 0 {
s.Config.SubnetNo = *resp.SubnetList[0].SubnetNo
s.Say("Set `subnet_no` is " + s.Config.SubnetNo)
} else {
return fmt.Errorf("cloud not found public subnet in `vpc_no` [%s]", s.Config.VpcNo)
}
}
return nil
}
// Check ImageName / Product Code / Server Image Product Code / Server Product Code...
func (s *StepValidateTemplate) validateTemplate() error {
// Get RegionNo, ZoneNo
if err := s.getZoneNo(); err != nil {
if err := s.getZone(); err != nil {
return err
}
// Validate member_server_image_no and member_server_image_no
if err := s.validateMemberServerImage(); err != nil {
if err := s.validateMemberServerImage(s.getMemberServerImageList); err != nil {
return err
}
@@ -246,6 +444,11 @@ func (s *StepValidateTemplate) validateTemplate() error {
return err
}
// Validate VPC
if err := s.validateVpc(); err != nil {
return err
}
// Validate server_product_code
return s.validateServerProductCode()
}
@@ -256,10 +459,10 @@ func (s *StepValidateTemplate) Run(ctx context.Context, state multistep.StateBag
err := s.Validate()
state.Put("ZoneNo", s.zoneNo)
state.Put("zone_no", s.zoneNo)
if s.FeeSystemTypeCode != "" {
state.Put("FeeSystemTypeCode", s.FeeSystemTypeCode)
state.Put("fee_system_type_code", s.FeeSystemTypeCode)
}
return processStepResult(err, s.Error, state)
@@ -23,7 +23,7 @@ func TestStepValidateTemplateShouldFailIfValidateFails(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 TestStepValidateTemplateShouldPassIfValidatePasses(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.")
}
}
@@ -6,11 +6,13 @@ import (
"time"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/server"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
)
func waiterBlockStorageInstanceStatus(conn *NcloudAPIClient, blockStorageInstanceNo *string, status string, timeout time.Duration) error {
reqParams := new(server.GetBlockStorageInstanceListRequest)
reqParams.BlockStorageInstanceNoList = []*string{blockStorageInstanceNo}
func waiterClassicBlockStorageStatus(conn *NcloudAPIClient, blockStorageInstanceNo *string, status string, timeout time.Duration) error {
reqParams := &server.GetBlockStorageInstanceListRequest{
BlockStorageInstanceNoList: []*string{blockStorageInstanceNo},
}
c1 := make(chan error, 1)
@@ -49,12 +51,54 @@ func waiterBlockStorageInstanceStatus(conn *NcloudAPIClient, blockStorageInstanc
}
}
func waiterDetachedBlockStorageInstance(conn *NcloudAPIClient, serverInstanceNo string, timeout time.Duration) error {
reqParams := new(server.GetBlockStorageInstanceListRequest)
reqParams.ServerInstanceNo = &serverInstanceNo
func waiterVpcBlockStorageStatus(conn *NcloudAPIClient, blockStorageInstanceNo *string, status string, timeout time.Duration) error {
reqParams := &vserver.GetBlockStorageInstanceListRequest{
BlockStorageInstanceNoList: []*string{blockStorageInstanceNo},
}
c1 := make(chan error, 1)
go func() {
for {
blockStorageInstanceList, err := conn.vserver.V2Api.GetBlockStorageInstanceList(reqParams)
if err != nil {
c1 <- err
return
}
if status == "DETAC" && len(blockStorageInstanceList.BlockStorageInstanceList) == 0 {
c1 <- nil
return
}
blockStorageInstance := blockStorageInstanceList.BlockStorageInstanceList[0]
code := blockStorageInstance.BlockStorageInstanceStatus.Code
operationCode := blockStorageInstance.BlockStorageInstanceOperation.Code
if *code == status && *operationCode == "NULL" {
c1 <- nil
return
}
log.Println(blockStorageInstance)
time.Sleep(time.Second * 5)
}
}()
select {
case res := <-c1:
return res
case <-time.After(timeout):
return fmt.Errorf("TIMEOUT : block storage instance status is not changed into status %s", status)
}
}
func waiterClassicDetachedBlockStorage(conn *NcloudAPIClient, serverInstanceNo string, timeout time.Duration) error {
reqParams := &server.GetBlockStorageInstanceListRequest{
ServerInstanceNo: &serverInstanceNo,
}
c1 := make(chan error, 1)
go func() {
for {
blockStorageInstanceList, err := conn.server.V2Api.GetBlockStorageInstanceList(reqParams)
@@ -79,3 +123,34 @@ func waiterDetachedBlockStorageInstance(conn *NcloudAPIClient, serverInstanceNo
return fmt.Errorf("TIMEOUT : attached block storage instance is not detached")
}
}
func waiterVpcDetachedBlockStorage(conn *NcloudAPIClient, serverInstanceNo string, timeout time.Duration) error {
reqParams := &vserver.GetBlockStorageInstanceListRequest{
ServerInstanceNo: &serverInstanceNo,
}
c1 := make(chan error, 1)
go func() {
for {
blockStorageInstanceList, err := conn.vserver.V2Api.GetBlockStorageInstanceList(reqParams)
if err != nil {
c1 <- err
return
}
if *blockStorageInstanceList.TotalRows == 1 {
c1 <- nil
return
}
time.Sleep(time.Second * 5)
}
}()
select {
case res := <-c1:
return res
case <-time.After(timeout):
return fmt.Errorf("TIMEOUT : attached block storage instance is not detached")
}
}
+39 -4
View File
@@ -6,11 +6,13 @@ import (
"time"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/server"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
)
func waiterMemberServerImageStatus(conn *NcloudAPIClient, memberServerImageNo string, status string, timeout time.Duration) error {
reqParams := new(server.GetMemberServerImageListRequest)
reqParams.MemberServerImageNoList = []*string{&memberServerImageNo}
func waiterClassicMemberServerImageStatus(conn *NcloudAPIClient, memberServerImageNo string, status string, timeout time.Duration) error {
reqParams := &server.GetMemberServerImageListRequest{
MemberServerImageNoList: []*string{&memberServerImageNo},
}
c1 := make(chan error, 1)
@@ -29,7 +31,40 @@ func waiterMemberServerImageStatus(conn *NcloudAPIClient, memberServerImageNo st
}
log.Printf("Status of member server image [%s] is %s\n", memberServerImageNo, *code)
log.Println(memberServerImageList.MemberServerImageList[0])
time.Sleep(time.Second * 5)
}
}()
select {
case res := <-c1:
return res
case <-time.After(timeout):
return fmt.Errorf("TIMEOUT : member server image status is not changed into status %s", status)
}
}
func waiterVpcMemberServerImageStatus(conn *NcloudAPIClient, memberServerImageNo string, status string, timeout time.Duration) error {
reqParams := &vserver.GetMemberServerImageInstanceDetailRequest{
MemberServerImageInstanceNo: &memberServerImageNo,
}
c1 := make(chan error, 1)
go func() {
for {
memberServerImageList, err := conn.vserver.V2Api.GetMemberServerImageInstanceDetail(reqParams)
if err != nil {
c1 <- err
return
}
code := memberServerImageList.MemberServerImageInstanceList[0].MemberServerImageInstanceStatus.Code
if *code == status {
c1 <- nil
return
}
log.Printf("Status of member server image [%s] is %s\n", memberServerImageNo, *code)
time.Sleep(time.Second * 5)
}
}()
@@ -6,9 +6,10 @@ import (
"time"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/server"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vserver"
)
func waiterServerInstanceStatus(conn *NcloudAPIClient, serverInstanceNo string, status string, timeout time.Duration) error {
func waiterClassicServerInstanceStatus(conn *NcloudAPIClient, serverInstanceNo string, status string, timeout time.Duration) error {
reqParams := new(server.GetServerInstanceListRequest)
reqParams.ServerInstanceNoList = []*string{&serverInstanceNo}
@@ -29,7 +30,40 @@ func waiterServerInstanceStatus(conn *NcloudAPIClient, serverInstanceNo string,
}
log.Printf("Status of serverInstanceNo [%s] is %s\n", serverInstanceNo, *code)
log.Println(serverInstanceList.ServerInstanceList[0])
time.Sleep(time.Second * 5)
}
}()
select {
case res := <-c1:
return res
case <-time.After(timeout):
return fmt.Errorf("TIMEOUT : server instance status is not changed into status %s", status)
}
}
func waiterVpcServerInstanceStatus(conn *NcloudAPIClient, serverInstanceNo string, status string, timeout time.Duration) error {
reqParams := &vserver.GetServerInstanceDetailRequest{
ServerInstanceNo: &serverInstanceNo,
}
c1 := make(chan error, 1)
go func() {
for {
serverInstanceList, err := conn.vserver.V2Api.GetServerInstanceDetail(reqParams)
if err != nil {
c1 <- err
return
}
code := serverInstanceList.ServerInstanceList[0].ServerInstanceStatus.Code
if *code == status {
c1 <- nil
return
}
log.Printf("Status of serverInstanceNo [%s] is %s\n", serverInstanceNo, *code)
time.Sleep(time.Second * 5)
}
}()
+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 null
+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 null
+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 oneandone
+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 oneandone
+1 -1
View File
@@ -1,4 +1,4 @@
//go:generate struct-markdown
//go:generate packer-sdc struct-markdown
package openstack
+1 -1
View File
@@ -1,4 +1,4 @@
//go:generate mapstructure-to-hcl2 -type Config,ImageFilter,ImageFilterOptions
//go:generate packer-sdc mapstructure-to-hcl2 -type Config,ImageFilter,ImageFilterOptions
// The openstack package contains a packersdk.Builder implementation that
// builds Images for openstack.
+1 -1
View File
@@ -1,4 +1,4 @@
// Code generated by "mapstructure-to-hcl2 -type Config,ImageFilter,ImageFilterOptions"; DO NOT EDIT.
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
package openstack
+1 -1
View File
@@ -1,4 +1,4 @@
//go:generate struct-markdown
//go:generate packer-sdc struct-markdown
package openstack
+1 -1
View File
@@ -1,4 +1,4 @@
//go:generate struct-markdown
//go:generate packer-sdc struct-markdown
package openstack
+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 classic
+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 classic

Some files were not shown because too many files have changed in this diff Show More