Compare commits
74 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 00da5f634d | |||
| c9410fa1a5 | |||
| 60c961c021 | |||
| f32b67c3bb | |||
| 160d932cce | |||
| e0fe579837 | |||
| fe21ff905d | |||
| 8ee8420408 | |||
| 25ee6a19a6 | |||
| a6321ac137 | |||
| 832c0f2b2a | |||
| 33cf6bf454 | |||
| 22d373c279 | |||
| 77ce2b39d8 | |||
| 4bbeec4733 | |||
| 7ae42bbb21 | |||
| d1254a5e48 | |||
| cf65b7b494 | |||
| be7d7313c5 | |||
| 7d30a5d79d | |||
| a115b428ac | |||
| 4e22147909 | |||
| f1017a19d3 | |||
| 3ced522659 | |||
| 6c8e997d19 | |||
| 44f0be2314 | |||
| bf9bb7b5c7 | |||
| 1a5678dd86 | |||
| b2d692c33d | |||
| e8b3a0e3bf | |||
| 9944031580 | |||
| 35604a98a7 | |||
| 8bb3df7121 | |||
| d2ec658e78 | |||
| bb54b8bf10 | |||
| 1e27138857 | |||
| 3c282de6c3 | |||
| beceace7b7 | |||
| 6f23bc0d97 | |||
| edd4567096 | |||
| 9a1f2d0c97 | |||
| 125a2f1f76 | |||
| 3ac5046260 | |||
| 8c18f8fa6d | |||
| 0ea92f6369 | |||
| bf1046e970 | |||
| de50cadb5c | |||
| 4f2f9f8a1e | |||
| 68f810891f | |||
| be8b2968bd | |||
| 4c9f3eb9ca | |||
| f4caf5978f | |||
| cd93957225 | |||
| 71b815a5fd | |||
| 79481ed7ec | |||
| a101d46589 | |||
| 3058c437a3 | |||
| 10e1573930 | |||
| c3e48ebb71 | |||
| 04cbcd7ae9 | |||
| 1c3c1f17d9 | |||
| 9331afcf80 | |||
| a5b0e37d7e | |||
| 6b5a3dacd4 | |||
| 22cb31cb7b | |||
| 0637601eda | |||
| d85286e228 | |||
| d3754e3021 | |||
| ab7e89781a | |||
| 40a97e29db | |||
| 93df53a275 | |||
| 6adf1f6659 | |||
| 261abe0cae | |||
| ab4b3a8465 |
@@ -10,7 +10,7 @@ Describe the change you are making here!
|
||||
|
||||
Please include tests. Check out these examples:
|
||||
|
||||
- https://github.com/hashicorp/packer/blob/master/builder/virtualbox/common/ssh_config_test.go#L19-L37
|
||||
- https://github.com/hashicorp/packer/blob/master/builder/parallels/common/ssh_config_test.go#L34
|
||||
- https://github.com/hashicorp/packer/blob/master/post-processor/compress/post-processor_test.go#L153-L182
|
||||
|
||||
If your PR resolves any open issue(s), please indicate them like this so they will be closed when your PR is merged:
|
||||
|
||||
+13
-1
@@ -1,18 +1,30 @@
|
||||
## 1.7.1 (Upcoming)
|
||||
|
||||
### FEATURES
|
||||
### IMPROVEMENTS
|
||||
* builder/amazon: allow creation of ebs snapshots wihtout volumes. [GH-9591]
|
||||
* builder/scaleway: add support for timeout in shutdown step. [GH-10503]
|
||||
* builder/virtualbox: Add template options for chipset, firmware, nic, graphics
|
||||
controller, and audio controller. [GH-10671]
|
||||
* builder/virtualbox: Support for "virtio" storage and ISO drive. [GH-10632]
|
||||
* builder/vmware: Added "attach_snapshot" parameter to vmware vmx builder.
|
||||
[GH-10651]
|
||||
* core: Change template parsing error to include warning about file extensions.
|
||||
[GH-10652]
|
||||
* hcl2_upgrade: hcl2_upgrade command can now upgrade json var-files [GH-10676]
|
||||
|
||||
### BUG FIXES
|
||||
* builder/amazon: Update amazon SDK to fix an SSO login issue. [GH-10668]
|
||||
* builder/azure: Don't overwrite subscription id if unset. [GH-10659]
|
||||
* builder/oracle-oci: Update Oracle Go SDK to fix issue with reading key file.
|
||||
[GH-10560]
|
||||
* builder/vmware: Added a fallback file check when trying to determine the
|
||||
network-mapping configuration. [GH-10543]
|
||||
* core/hcl2_upgrade: Make hcl2_upgrade command correctly translate
|
||||
pause_before. [GH-10654]
|
||||
* core: Templates previously could not interpolate the environment variable
|
||||
PACKER_LOG_PATH. [GH-10660]
|
||||
* provisioner/chef-solo: HCL2 templates can support the json_string option.
|
||||
[GH-10655]
|
||||
|
||||
## 1.7.0 (February 17, 2021)
|
||||
|
||||
|
||||
@@ -87,7 +87,6 @@
|
||||
|
||||
/post-processor/alicloud-import/ dongxiao.zzh@alibaba-inc.com
|
||||
/post-processor/checksum/ v.tolstov@selfip.ru
|
||||
/post-processor/exoscale-import/ @falzm @mcorbin
|
||||
/post-processor/googlecompute-export/ crunkleton@google.com
|
||||
/post-processor/yandex-export/ @GennadySpb
|
||||
/post-processor/yandex-import/ @GennadySpb
|
||||
|
||||
@@ -172,7 +172,7 @@ func (c *AMIConfig) Prepare(accessConfig *AccessConfig, ctx *interpolate.Context
|
||||
|
||||
// Prevent sharing of default KMS key encrypted volumes with other aws users
|
||||
if len(c.AMIUsers) > 0 {
|
||||
if len(c.AMIKmsKeyId) == 0 && c.AMIEncryptBootVolume.True() {
|
||||
if len(c.AMIKmsKeyId) == 0 && len(c.AMIRegionKMSKeyIDs) == 0 && c.AMIEncryptBootVolume.True() {
|
||||
errs = append(errs, fmt.Errorf("Cannot share AMI encrypted with default KMS key"))
|
||||
}
|
||||
if len(c.AMIRegionKMSKeyIDs) > 0 {
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
func TestExportArtifact_impl(t *testing.T) {
|
||||
var _ packersdk.Artifact = new(ExportArtifact)
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
func TestImportArtifact_impl(t *testing.T) {
|
||||
var _ packersdk.Artifact = new(ImportArtifact)
|
||||
}
|
||||
|
||||
func TestImportArtifactBuilderId(t *testing.T) {
|
||||
a := &ImportArtifact{BuilderIdValue: "foo"}
|
||||
if a.BuilderId() != "foo" {
|
||||
t.Fatalf("bad: %#v", a.BuilderId())
|
||||
}
|
||||
}
|
||||
|
||||
func TestImportArtifactFiles(t *testing.T) {
|
||||
a := &ImportArtifact{}
|
||||
if a.Files() != nil {
|
||||
t.Fatalf("bad: %#v", a.Files())
|
||||
}
|
||||
}
|
||||
|
||||
func TestImportArtifactId(t *testing.T) {
|
||||
a := &ImportArtifact{IdValue: "foo"}
|
||||
if a.Id() != "foo" {
|
||||
t.Fatalf("bad: %#v", a.Id())
|
||||
}
|
||||
}
|
||||
|
||||
func TestImportArtifactDestroy(t *testing.T) {
|
||||
d := new(MockDriver)
|
||||
a := &ImportArtifact{
|
||||
Driver: d,
|
||||
IdValue: "foo",
|
||||
}
|
||||
|
||||
// No error
|
||||
if err := a.Destroy(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if !d.DeleteImageCalled {
|
||||
t.Fatal("delete image should be called")
|
||||
}
|
||||
if d.DeleteImageId != "foo" {
|
||||
t.Fatalf("bad: %#v", d.DeleteImageId)
|
||||
}
|
||||
|
||||
// With an error
|
||||
d.DeleteImageErr = errors.New("foo")
|
||||
if err := a.Destroy(); err != d.DeleteImageErr {
|
||||
t.Fatalf("err: %#v", err)
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
func TestBuilder_implBuilder(t *testing.T) {
|
||||
var _ packersdk.Builder = new(Builder)
|
||||
}
|
||||
@@ -1,239 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"testing"
|
||||
|
||||
builderT "github.com/hashicorp/packer-plugin-sdk/acctest"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
// RenderConfig helps create dynamic packer template configs for parsing by
|
||||
// builderT without having to write the config to a file.
|
||||
func RenderConfig(builderConfig map[string]interface{}, provisionerConfig []map[string]interface{}) string {
|
||||
// set up basic build template
|
||||
t := map[string][]map[string]interface{}{
|
||||
"builders": {
|
||||
// Setup basic docker config
|
||||
map[string]interface{}{
|
||||
"type": "test",
|
||||
"image": "ubuntu",
|
||||
"discard": true,
|
||||
},
|
||||
},
|
||||
"provisioners": []map[string]interface{}{},
|
||||
}
|
||||
// apply special builder overrides
|
||||
for k, v := range builderConfig {
|
||||
t["builders"][0][k] = v
|
||||
}
|
||||
// Apply special provisioner overrides
|
||||
t["provisioners"] = append(t["provisioners"], provisionerConfig...)
|
||||
|
||||
j, _ := json.Marshal(t)
|
||||
return string(j)
|
||||
}
|
||||
|
||||
// TestUploadDownload verifies that basic upload / download functionality works
|
||||
func TestUploadDownload(t *testing.T) {
|
||||
if os.Getenv("PACKER_ACC") == "" {
|
||||
t.Skip("This test is only run with PACKER_ACC=1")
|
||||
}
|
||||
|
||||
dockerBuilderExtraConfig := map[string]interface{}{
|
||||
"run_command": []string{"-d", "-i", "-t", "{{.Image}}", "/bin/sh"},
|
||||
}
|
||||
|
||||
dockerProvisionerConfig := []map[string]interface{}{
|
||||
{
|
||||
"type": "file",
|
||||
"source": "test-fixtures/onecakes/strawberry",
|
||||
"destination": "/strawberry-cake",
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"source": "/strawberry-cake",
|
||||
"destination": "my-strawberry-cake",
|
||||
"direction": "download",
|
||||
},
|
||||
}
|
||||
|
||||
configString := RenderConfig(dockerBuilderExtraConfig, dockerProvisionerConfig)
|
||||
|
||||
// this should be a precheck
|
||||
cmd := exec.Command("docker", "-v")
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
t.Error("docker command not found; please make sure docker is installed")
|
||||
}
|
||||
|
||||
builderT.Test(t, builderT.TestCase{
|
||||
Builder: &Builder{},
|
||||
Template: configString,
|
||||
Check: func(a []packersdk.Artifact) error {
|
||||
// Verify that the thing we downloaded is the same thing we sent up.
|
||||
// Complain loudly if it isn't.
|
||||
inputFile, err := ioutil.ReadFile("test-fixtures/onecakes/strawberry")
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to read input file: %s", err)
|
||||
}
|
||||
outputFile, err := ioutil.ReadFile("my-strawberry-cake")
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to read output file: %s", err)
|
||||
}
|
||||
if sha256.Sum256(inputFile) != sha256.Sum256(outputFile) {
|
||||
return fmt.Errorf("Input and output files do not match\n"+
|
||||
"Input:\n%s\nOutput:\n%s\n", inputFile, outputFile)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Teardown: func() error {
|
||||
// Cleanup. Honestly I don't know why you would want to get rid
|
||||
// of my strawberry cake. It's so tasty! Do you not like cake? Are you a
|
||||
// cake-hater? Or are you keeping all the cake all for yourself? So selfish!
|
||||
os.Remove("my-strawberry-cake")
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// TestLargeDownload verifies that files are the appropriate size after being
|
||||
// downloaded. This is to identify and fix the race condition in #2793. You may
|
||||
// need to use github.com/cbednarski/rerun to verify since this problem occurs
|
||||
// only intermittently.
|
||||
func TestLargeDownload(t *testing.T) {
|
||||
if os.Getenv("PACKER_ACC") == "" {
|
||||
t.Skip("This test is only run with PACKER_ACC=1")
|
||||
}
|
||||
|
||||
dockerProvisionerConfig := []map[string]interface{}{
|
||||
{
|
||||
"type": "shell",
|
||||
"inline": []string{
|
||||
"dd if=/dev/urandom of=/tmp/cupcake bs=1M count=2",
|
||||
"dd if=/dev/urandom of=/tmp/bigcake bs=1M count=100",
|
||||
"sync",
|
||||
"md5sum /tmp/cupcake /tmp/bigcake",
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"source": "/tmp/cupcake",
|
||||
"destination": "cupcake",
|
||||
"direction": "download",
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"source": "/tmp/bigcake",
|
||||
"destination": "bigcake",
|
||||
"direction": "download",
|
||||
},
|
||||
}
|
||||
|
||||
configString := RenderConfig(map[string]interface{}{}, dockerProvisionerConfig)
|
||||
|
||||
// this should be a precheck
|
||||
cmd := exec.Command("docker", "-v")
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
t.Error("docker command not found; please make sure docker is installed")
|
||||
}
|
||||
|
||||
builderT.Test(t, builderT.TestCase{
|
||||
Builder: &Builder{},
|
||||
Template: configString,
|
||||
Check: func(a []packersdk.Artifact) error {
|
||||
// Verify that the things we downloaded are the right size. Complain loudly
|
||||
// if they are not.
|
||||
//
|
||||
// cupcake should be 2097152 bytes
|
||||
// bigcake should be 104857600 bytes
|
||||
cupcake, err := os.Stat("cupcake")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to stat cupcake file: %s", err)
|
||||
}
|
||||
cupcakeExpected := int64(2097152)
|
||||
if cupcake.Size() != cupcakeExpected {
|
||||
t.Errorf("Expected cupcake to be %d bytes; found %d", cupcakeExpected, cupcake.Size())
|
||||
}
|
||||
|
||||
bigcake, err := os.Stat("bigcake")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to stat bigcake file: %s", err)
|
||||
}
|
||||
bigcakeExpected := int64(104857600)
|
||||
if bigcake.Size() != bigcakeExpected {
|
||||
t.Errorf("Expected bigcake to be %d bytes; found %d", bigcakeExpected, bigcake.Size())
|
||||
}
|
||||
|
||||
// TODO if we can, calculate a sha inside the container and compare to the
|
||||
// one we get after we pull it down. We will probably have to parse the log
|
||||
// or ui output to do this because we use /dev/urandom to create the file.
|
||||
|
||||
// if sha256.Sum256(inputFile) != sha256.Sum256(outputFile) {
|
||||
// t.Fatalf("Input and output files do not match\n"+
|
||||
// "Input:\n%s\nOutput:\n%s\n", inputFile, outputFile)
|
||||
// }
|
||||
return nil
|
||||
},
|
||||
Teardown: func() error {
|
||||
os.Remove("cupcake")
|
||||
os.Remove("bigcake")
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
// TestFixUploadOwner verifies that owner of uploaded files is the user the container is running as.
|
||||
func TestFixUploadOwner(t *testing.T) {
|
||||
if os.Getenv("PACKER_ACC") == "" {
|
||||
t.Skip("This test is only run with PACKER_ACC=1")
|
||||
}
|
||||
|
||||
cmd := exec.Command("docker", "-v")
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
t.Error("docker command not found; please make sure docker is installed")
|
||||
}
|
||||
|
||||
dockerBuilderExtraConfig := map[string]interface{}{
|
||||
"run_command": []string{"-d", "-i", "-t", "-u", "42", "{{.Image}}", "/bin/sh"},
|
||||
}
|
||||
|
||||
testFixUploadOwnerProvisionersTemplate := []map[string]interface{}{
|
||||
{
|
||||
"type": "file",
|
||||
"source": "test-fixtures/onecakes/strawberry",
|
||||
"destination": "/tmp/strawberry-cake",
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"source": "test-fixtures/manycakes",
|
||||
"destination": "/tmp/",
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"inline": "touch /tmp/testUploadOwner",
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"inline": []string{
|
||||
"[ $(stat -c %u /tmp/strawberry-cake) -eq 42 ] || (echo 'Invalid owner of /tmp/strawberry-cake' && exit 1)",
|
||||
"[ $(stat -c %u /tmp/testUploadOwner) -eq 42 ] || (echo 'Invalid owner of /tmp/testUploadOwner' && exit 1)",
|
||||
"find /tmp/manycakes | xargs -n1 -IFILE /bin/sh -c '[ $(stat -c %u FILE) -eq 42 ] || (echo \"Invalid owner of FILE\" && exit 1)'",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
configString := RenderConfig(dockerBuilderExtraConfig, testFixUploadOwnerProvisionersTemplate)
|
||||
builderT.Test(t, builderT.TestCase{
|
||||
Builder: &Builder{},
|
||||
Template: configString,
|
||||
})
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func testConfig() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"export_path": "foo",
|
||||
"image": "bar",
|
||||
}
|
||||
}
|
||||
|
||||
func testConfigStruct(t *testing.T) *Config {
|
||||
var c Config
|
||||
warns, errs := c.Prepare(testConfig())
|
||||
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) {
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("should error")
|
||||
}
|
||||
}
|
||||
|
||||
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 TestConfigPrepare_exportPath(t *testing.T) {
|
||||
td, err := ioutil.TempDir("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(td)
|
||||
|
||||
raw := testConfig()
|
||||
|
||||
// No export path. This is invalid. Previously this would not error during
|
||||
// validation and as a result the failure would happen at build time.
|
||||
delete(raw, "export_path")
|
||||
var c Config
|
||||
warns, errs := c.Prepare(raw)
|
||||
testConfigErr(t, warns, errs)
|
||||
|
||||
// Good export path
|
||||
raw["export_path"] = "good"
|
||||
warns, errs = c.Prepare(raw)
|
||||
testConfigOk(t, warns, errs)
|
||||
|
||||
// Bad export path (directory)
|
||||
raw["export_path"] = td
|
||||
warns, errs = c.Prepare(raw)
|
||||
testConfigErr(t, warns, errs)
|
||||
}
|
||||
|
||||
func TestConfigPrepare_exportPathAndCommit(t *testing.T) {
|
||||
raw := testConfig()
|
||||
|
||||
// Export but no commit (explicit default)
|
||||
raw["commit"] = false
|
||||
warns, errs := (&Config{}).Prepare(raw)
|
||||
testConfigOk(t, warns, errs)
|
||||
|
||||
// Commit AND export specified (invalid)
|
||||
raw["commit"] = true
|
||||
warns, errs = (&Config{}).Prepare(raw)
|
||||
testConfigErr(t, warns, errs)
|
||||
|
||||
// Commit but no export
|
||||
delete(raw, "export_path")
|
||||
warns, errs = (&Config{}).Prepare(raw)
|
||||
testConfigOk(t, warns, errs)
|
||||
}
|
||||
|
||||
func TestConfigPrepare_exportDiscard(t *testing.T) {
|
||||
raw := testConfig()
|
||||
|
||||
// Export but no discard (explicit default)
|
||||
raw["discard"] = false
|
||||
warns, errs := (&Config{}).Prepare(raw)
|
||||
testConfigOk(t, warns, errs)
|
||||
|
||||
// Discard AND export (invalid)
|
||||
raw["discard"] = true
|
||||
warns, errs = (&Config{}).Prepare(raw)
|
||||
testConfigErr(t, warns, errs)
|
||||
|
||||
// Discard but no export
|
||||
raw["discard"] = true
|
||||
delete(raw, "export_path")
|
||||
warns, errs = (&Config{}).Prepare(raw)
|
||||
testConfigOk(t, warns, errs)
|
||||
}
|
||||
|
||||
func TestConfigPrepare_image(t *testing.T) {
|
||||
raw := testConfig()
|
||||
|
||||
// No image
|
||||
delete(raw, "image")
|
||||
var c Config
|
||||
warns, errs := c.Prepare(raw)
|
||||
testConfigErr(t, warns, errs)
|
||||
|
||||
// Good image
|
||||
raw["image"] = "path"
|
||||
warns, errs = c.Prepare(raw)
|
||||
testConfigOk(t, warns, errs)
|
||||
}
|
||||
|
||||
func TestConfigPrepare_pull(t *testing.T) {
|
||||
raw := testConfig()
|
||||
|
||||
// No pull set
|
||||
delete(raw, "pull")
|
||||
var c Config
|
||||
warns, errs := c.Prepare(raw)
|
||||
testConfigOk(t, warns, errs)
|
||||
if !c.Pull {
|
||||
t.Fatal("should pull by default")
|
||||
}
|
||||
|
||||
// Pull set
|
||||
raw["pull"] = false
|
||||
warns, errs = c.Prepare(raw)
|
||||
testConfigOk(t, warns, errs)
|
||||
if c.Pull {
|
||||
t.Fatal("should not pull")
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package docker
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestDockerDriver_impl(t *testing.T) {
|
||||
var _ Driver = new(DockerDriver)
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package docker
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestMockDriver_impl(t *testing.T) {
|
||||
var _ Driver = new(MockDriver)
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
)
|
||||
|
||||
func testStepCommitState(t *testing.T) multistep.StateBag {
|
||||
state := testState(t)
|
||||
state.Put("container_id", "foo")
|
||||
return state
|
||||
}
|
||||
|
||||
func TestStepCommit_impl(t *testing.T) {
|
||||
var _ multistep.Step = new(StepCommit)
|
||||
}
|
||||
|
||||
func TestStepCommit(t *testing.T) {
|
||||
state := testStepCommitState(t)
|
||||
step := new(StepCommit)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
driver := state.Get("driver").(*MockDriver)
|
||||
driver.CommitImageId = "bar"
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// verify we did the right thing
|
||||
if !driver.CommitCalled {
|
||||
t.Fatal("should've called")
|
||||
}
|
||||
|
||||
// verify the ID is saved
|
||||
idRaw, ok := state.GetOk("image_id")
|
||||
if !ok {
|
||||
t.Fatal("should've saved ID")
|
||||
}
|
||||
|
||||
id := idRaw.(string)
|
||||
if id != driver.CommitImageId {
|
||||
t.Fatalf("bad: %#v", id)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepCommit_error(t *testing.T) {
|
||||
state := testStepCommitState(t)
|
||||
step := new(StepCommit)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
driver := state.Get("driver").(*MockDriver)
|
||||
driver.CommitErr = errors.New("foo")
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// verify the ID is not saved
|
||||
if _, ok := state.GetOk("image_id"); ok {
|
||||
t.Fatal("shouldn't save image ID")
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
)
|
||||
|
||||
func testStepExportState(t *testing.T) multistep.StateBag {
|
||||
state := testState(t)
|
||||
state.Put("container_id", "foo")
|
||||
return state
|
||||
}
|
||||
|
||||
func TestStepExport_impl(t *testing.T) {
|
||||
var _ multistep.Step = new(StepExport)
|
||||
}
|
||||
|
||||
func TestStepExport(t *testing.T) {
|
||||
state := testStepExportState(t)
|
||||
step := new(StepExport)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
// Create a tempfile for our output path
|
||||
tf, err := ioutil.TempFile("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
tf.Close()
|
||||
defer os.Remove(tf.Name())
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
config.ExportPath = tf.Name()
|
||||
driver := state.Get("driver").(*MockDriver)
|
||||
driver.ExportReader = bytes.NewReader([]byte("data!"))
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// verify we did the right thing
|
||||
if !driver.ExportCalled {
|
||||
t.Fatal("should've exported")
|
||||
}
|
||||
if driver.ExportID != "foo" {
|
||||
t.Fatalf("bad: %#v", driver.ExportID)
|
||||
}
|
||||
|
||||
// verify the data exported to the file
|
||||
contents, err := ioutil.ReadFile(tf.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if string(contents) != "data!" {
|
||||
t.Fatalf("bad: %#v", string(contents))
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepExport_error(t *testing.T) {
|
||||
state := testStepExportState(t)
|
||||
step := new(StepExport)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
// Create a tempfile for our output path
|
||||
tf, err := ioutil.TempFile("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
tf.Close()
|
||||
|
||||
if err := os.Remove(tf.Name()); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
config.ExportPath = tf.Name()
|
||||
driver := state.Get("driver").(*MockDriver)
|
||||
driver.ExportError = errors.New("foo")
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// verify we have an error
|
||||
if _, ok := state.GetOk("error"); !ok {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// verify we didn't make that file
|
||||
if _, err := os.Stat(tf.Name()); err == nil {
|
||||
t.Fatal("export path shouldn't exist")
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
)
|
||||
|
||||
func TestStepPull_impl(t *testing.T) {
|
||||
var _ multistep.Step = new(StepPull)
|
||||
}
|
||||
|
||||
func TestStepPull(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepPull)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
driver := state.Get("driver").(*MockDriver)
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// verify we did the right thing
|
||||
if !driver.PullCalled {
|
||||
t.Fatal("should've pulled")
|
||||
}
|
||||
if driver.PullImage != config.Image {
|
||||
t.Fatalf("bad: %#v", driver.PullImage)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepPull_error(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepPull)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
driver := state.Get("driver").(*MockDriver)
|
||||
driver.PullError = errors.New("foo")
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// verify we have an error
|
||||
if _, ok := state.GetOk("error"); !ok {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepPull_login(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepPull)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
driver := state.Get("driver").(*MockDriver)
|
||||
|
||||
config.Login = true
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// verify we pulled
|
||||
if !driver.PullCalled {
|
||||
t.Fatal("should've pulled")
|
||||
}
|
||||
|
||||
// verify we logged in
|
||||
if !driver.LoginCalled {
|
||||
t.Fatal("should've logged in")
|
||||
}
|
||||
if !driver.LogoutCalled {
|
||||
t.Fatal("should've logged out")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepPull_noPull(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepPull)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
config.Pull = false
|
||||
|
||||
driver := state.Get("driver").(*MockDriver)
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// verify we did the right thing
|
||||
if driver.PullCalled {
|
||||
t.Fatal("shouldn't have pulled")
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
)
|
||||
|
||||
func testStepRunState(t *testing.T) multistep.StateBag {
|
||||
state := testState(t)
|
||||
state.Put("temp_dir", "/foo")
|
||||
return state
|
||||
}
|
||||
|
||||
func TestStepRun_impl(t *testing.T) {
|
||||
var _ multistep.Step = new(StepRun)
|
||||
}
|
||||
|
||||
func TestStepRun(t *testing.T) {
|
||||
state := testStepRunState(t)
|
||||
step := new(StepRun)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
driver := state.Get("driver").(*MockDriver)
|
||||
driver.StartID = "foo"
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// verify we did the right thing
|
||||
if !driver.StartCalled {
|
||||
t.Fatal("should've called")
|
||||
}
|
||||
if driver.StartConfig.Image != config.Image {
|
||||
t.Fatalf("bad: %#v", driver.StartConfig.Image)
|
||||
}
|
||||
|
||||
// verify the ID is saved
|
||||
idRaw, ok := state.GetOk("container_id")
|
||||
if !ok {
|
||||
t.Fatal("should've saved ID")
|
||||
}
|
||||
|
||||
id := idRaw.(string)
|
||||
if id != "foo" {
|
||||
t.Fatalf("bad: %#v", id)
|
||||
}
|
||||
|
||||
// Verify we haven't called stop yet
|
||||
if driver.KillCalled {
|
||||
t.Fatal("should not have stopped")
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
step.Cleanup(state)
|
||||
if !driver.KillCalled {
|
||||
t.Fatal("should've stopped")
|
||||
}
|
||||
if driver.KillID != id {
|
||||
t.Fatalf("bad: %#v", driver.StopID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepRun_error(t *testing.T) {
|
||||
state := testStepRunState(t)
|
||||
step := new(StepRun)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
driver := state.Get("driver").(*MockDriver)
|
||||
driver.StartError = errors.New("foo")
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// verify the ID is not saved
|
||||
if _, ok := state.GetOk("container_id"); ok {
|
||||
t.Fatal("shouldn't save container ID")
|
||||
}
|
||||
|
||||
// Verify we haven't called stop yet
|
||||
if driver.KillCalled {
|
||||
t.Fatal("should not have stopped")
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
step.Cleanup(state)
|
||||
if driver.KillCalled {
|
||||
t.Fatal("should not have stopped")
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packerbuilderdata"
|
||||
)
|
||||
|
||||
func TestStepSetGeneratedData_Run(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepSetGeneratedData)
|
||||
step.GeneratedData = &packerbuilderdata.GeneratedData{State: state}
|
||||
driver := state.Get("driver").(*MockDriver)
|
||||
driver.Sha256Result = "80B3BB1B1696E73A9B19DEEF92F664F8979F948DF348088B61F9A3477655AF64"
|
||||
state.Put("image_id", "12345")
|
||||
|
||||
if action := step.Run(context.TODO(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("Should not halt")
|
||||
}
|
||||
if !driver.Sha256Called {
|
||||
t.Fatalf("driver.SHA256 should be called")
|
||||
}
|
||||
if driver.Sha256Id != "12345" {
|
||||
t.Fatalf("driver.SHA256 got wrong image it: %s", driver.Sha256Id)
|
||||
}
|
||||
genData := state.Get("generated_data").(map[string]interface{})
|
||||
imgSha256 := genData["ImageSha256"].(string)
|
||||
if imgSha256 != driver.Sha256Result {
|
||||
t.Fatalf("Expected ImageSha256 to be %s but was %s", driver.Sha256Result, imgSha256)
|
||||
}
|
||||
|
||||
// Image ID not implement
|
||||
state = testState(t)
|
||||
step.GeneratedData = &packerbuilderdata.GeneratedData{State: state}
|
||||
driver = state.Get("driver").(*MockDriver)
|
||||
notImplementedMsg := "ERR_IMAGE_SHA256_NOT_FOUND"
|
||||
|
||||
if action := step.Run(context.TODO(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("Should not halt")
|
||||
}
|
||||
if driver.Sha256Called {
|
||||
t.Fatalf("driver.SHA256 should not be called")
|
||||
}
|
||||
genData = state.Get("generated_data").(map[string]interface{})
|
||||
imgSha256 = genData["ImageSha256"].(string)
|
||||
if imgSha256 != notImplementedMsg {
|
||||
t.Fatalf("Expected ImageSha256 to be %s but was %s", notImplementedMsg, imgSha256)
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
)
|
||||
|
||||
func TestStepTempDir_impl(t *testing.T) {
|
||||
var _ multistep.Step = new(StepTempDir)
|
||||
}
|
||||
|
||||
func testStepTempDir_impl(t *testing.T) string {
|
||||
state := testState(t)
|
||||
step := new(StepTempDir)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
// sanity test
|
||||
if _, ok := state.GetOk("temp_dir"); ok {
|
||||
t.Fatalf("temp_dir should not be in state yet")
|
||||
}
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// Verify that we got the temp dir
|
||||
dirRaw, ok := state.GetOk("temp_dir")
|
||||
if !ok {
|
||||
t.Fatalf("should've made temp_dir")
|
||||
}
|
||||
dir := dirRaw.(string)
|
||||
|
||||
if _, err := os.Stat(dir); err != nil {
|
||||
t.Fatalf("Stat for %s failed: err: %s", err, dir)
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
step.Cleanup(state)
|
||||
if _, err := os.Stat(dir); err == nil {
|
||||
t.Fatalf("dir should be gone")
|
||||
}
|
||||
|
||||
return dir
|
||||
}
|
||||
|
||||
func TestStepTempDir(t *testing.T) {
|
||||
testStepTempDir_impl(t)
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package docker
|
||||
|
||||
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", &MockDriver{})
|
||||
state.Put("hook", &packersdk.MockHook{})
|
||||
state.Put("ui", &packersdk.BasicUi{
|
||||
Reader: new(bytes.Buffer),
|
||||
Writer: new(bytes.Buffer),
|
||||
})
|
||||
return state
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
chocolate!
|
||||
@@ -1 +0,0 @@
|
||||
vanilla!
|
||||
@@ -1 +0,0 @@
|
||||
strawberry!
|
||||
@@ -1,13 +0,0 @@
|
||||
package version
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/packer-plugin-sdk/version"
|
||||
packerVersion "github.com/hashicorp/packer/version"
|
||||
)
|
||||
|
||||
var DigitalOceanPluginVersion *version.PluginVersion
|
||||
|
||||
func init() {
|
||||
DigitalOceanPluginVersion = version.InitializePluginVersion(
|
||||
packerVersion.Version, packerVersion.VersionPrerelease)
|
||||
}
|
||||
@@ -210,6 +210,8 @@ type Config struct {
|
||||
// - The contents of the script file will be wrapped in Packer's startup script wrapper, unless `wrap_startup_script` is disabled. See `wrap_startup_script` for more details.
|
||||
// - Not supported by Windows instances. See [Startup Scripts for Windows](https://cloud.google.com/compute/docs/startupscript#providing_a_startup_script_for_windows_instances) for more details.
|
||||
StartupScriptFile string `mapstructure:"startup_script_file" required:"false"`
|
||||
// The time to wait for windows password to be retrieved. Defaults to "3m".
|
||||
WindowsPasswordTimeout time.Duration `mapstructure:"windows_password_timeout" required:"false"`
|
||||
// For backwards compatibility this option defaults to `"true"` in the future it will default to `"false"`.
|
||||
// If "true", the contents of `startup_script_file` or `"startup_script"` in the instance metadata
|
||||
// is wrapped in a Packer specific script that tracks the execution and completion of the provided
|
||||
@@ -542,6 +544,10 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
||||
c.WrapStartupScriptFile = config.TriTrue
|
||||
}
|
||||
}
|
||||
// Check windows password timeout is provided
|
||||
if c.WindowsPasswordTimeout == 0 {
|
||||
c.WindowsPasswordTimeout = 3 * time.Minute
|
||||
}
|
||||
|
||||
// Check for any errors.
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
|
||||
@@ -112,6 +112,7 @@ type FlatConfig struct {
|
||||
SourceImageFamily *string `mapstructure:"source_image_family" required:"true" cty:"source_image_family" hcl:"source_image_family"`
|
||||
SourceImageProjectId []string `mapstructure:"source_image_project_id" required:"false" cty:"source_image_project_id" hcl:"source_image_project_id"`
|
||||
StartupScriptFile *string `mapstructure:"startup_script_file" required:"false" cty:"startup_script_file" hcl:"startup_script_file"`
|
||||
WindowsPasswordTimeout *string `mapstructure:"windows_password_timeout" required:"false" cty:"windows_password_timeout" hcl:"windows_password_timeout"`
|
||||
WrapStartupScriptFile *bool `mapstructure:"wrap_startup_script" required:"false" cty:"wrap_startup_script" hcl:"wrap_startup_script"`
|
||||
Subnetwork *string `mapstructure:"subnetwork" required:"false" cty:"subnetwork" hcl:"subnetwork"`
|
||||
Tags []string `mapstructure:"tags" required:"false" cty:"tags" hcl:"tags"`
|
||||
@@ -236,6 +237,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"source_image_family": &hcldec.AttrSpec{Name: "source_image_family", Type: cty.String, Required: false},
|
||||
"source_image_project_id": &hcldec.AttrSpec{Name: "source_image_project_id", Type: cty.List(cty.String), Required: false},
|
||||
"startup_script_file": &hcldec.AttrSpec{Name: "startup_script_file", Type: cty.String, Required: false},
|
||||
"windows_password_timeout": &hcldec.AttrSpec{Name: "windows_password_timeout", Type: cty.String, Required: false},
|
||||
"wrap_startup_script": &hcldec.AttrSpec{Name: "wrap_startup_script", Type: cty.Bool, Required: false},
|
||||
"subnetwork": &hcldec.AttrSpec{Name: "subnetwork", Type: cty.String, Required: false},
|
||||
"tags": &hcldec.AttrSpec{Name: "tags", Type: cty.List(cty.String), Required: false},
|
||||
|
||||
@@ -107,13 +107,14 @@ type InstanceConfig struct {
|
||||
// WindowsPasswordConfig is the data structure that GCE needs to encrypt the created
|
||||
// windows password.
|
||||
type WindowsPasswordConfig struct {
|
||||
key *rsa.PrivateKey
|
||||
password string
|
||||
UserName string `json:"userName"`
|
||||
Modulus string `json:"modulus"`
|
||||
Exponent string `json:"exponent"`
|
||||
Email string `json:"email"`
|
||||
ExpireOn time.Time `json:"expireOn"`
|
||||
key *rsa.PrivateKey
|
||||
password string
|
||||
UserName string `json:"userName"`
|
||||
Modulus string `json:"modulus"`
|
||||
Exponent string `json:"exponent"`
|
||||
Email string `json:"email"`
|
||||
ExpireOn time.Time `json:"expireOn"`
|
||||
WindowsPasswordTimeout time.Duration `json:"timeout"`
|
||||
}
|
||||
|
||||
type windowsPasswordResponse struct {
|
||||
|
||||
@@ -253,7 +253,6 @@ func (d *driverGCE) GetImage(name string, fromFamily bool) (*Image, error) {
|
||||
"ubuntu-os-cloud",
|
||||
"windows-cloud",
|
||||
"windows-sql-cloud",
|
||||
"gce-uefi-images",
|
||||
"gce-nvme",
|
||||
// misc
|
||||
"google-containers",
|
||||
@@ -568,7 +567,7 @@ func (d *driverGCE) createWindowsPassword(errCh chan<- error, name, zone string,
|
||||
return
|
||||
}
|
||||
|
||||
timeout := time.Now().Add(time.Minute * 3)
|
||||
timeout := time.Now().Add(c.WindowsPasswordTimeout)
|
||||
hash := sha1.New()
|
||||
random := rand.Reader
|
||||
|
||||
|
||||
@@ -60,12 +60,13 @@ func (s *StepCreateWindowsPassword) Run(ctx context.Context, state multistep.Sta
|
||||
}
|
||||
|
||||
data := WindowsPasswordConfig{
|
||||
key: priv,
|
||||
UserName: c.Comm.WinRMUser,
|
||||
Modulus: base64.StdEncoding.EncodeToString(priv.N.Bytes()),
|
||||
Exponent: base64.StdEncoding.EncodeToString(buf[1:]),
|
||||
Email: email,
|
||||
ExpireOn: time.Now().Add(time.Minute * 5),
|
||||
key: priv,
|
||||
UserName: c.Comm.WinRMUser,
|
||||
Modulus: base64.StdEncoding.EncodeToString(priv.N.Bytes()),
|
||||
Exponent: base64.StdEncoding.EncodeToString(buf[1:]),
|
||||
Email: email,
|
||||
ExpireOn: time.Now().Add(time.Minute * 5),
|
||||
WindowsPasswordTimeout: c.WindowsPasswordTimeout,
|
||||
}
|
||||
|
||||
if s.Debug {
|
||||
@@ -98,7 +99,7 @@ func (s *StepCreateWindowsPassword) Run(ctx context.Context, state multistep.Sta
|
||||
ui.Message("Waiting for windows password to complete...")
|
||||
select {
|
||||
case err = <-errCh:
|
||||
case <-time.After(c.StateTimeout):
|
||||
case <-time.After(c.WindowsPasswordTimeout):
|
||||
err = errors.New("time out while waiting for the password to be created")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,7 +294,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
// configure the communicator ssh, winrm
|
||||
&communicator.StepConnect{
|
||||
Config: &b.config.SSHConfig.Comm,
|
||||
Host: hypervcommon.CommHost(b.config.SSHConfig.Comm.SSHHost),
|
||||
Host: hypervcommon.CommHost(b.config.SSHConfig.Comm.Host()),
|
||||
SSHConfig: b.config.SSHConfig.Comm.SSHConfigFunc(),
|
||||
},
|
||||
|
||||
|
||||
@@ -334,7 +334,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
// configure the communicator ssh, winrm
|
||||
&communicator.StepConnect{
|
||||
Config: &b.config.SSHConfig.Comm,
|
||||
Host: hypervcommon.CommHost(b.config.SSHConfig.Comm.SSHHost),
|
||||
Host: hypervcommon.CommHost(b.config.SSHConfig.Comm.Host()),
|
||||
SSHConfig: b.config.SSHConfig.Comm.SSHConfigFunc(),
|
||||
},
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
&communicator.StepConnect{
|
||||
Config: &b.config.RunConfig.Comm,
|
||||
Host: CommHost(
|
||||
b.config.RunConfig.Comm.SSHHost,
|
||||
b.config.RunConfig.Comm.Host(),
|
||||
computeClient,
|
||||
b.config.SSHInterface,
|
||||
b.config.SSHIPVersion),
|
||||
|
||||
@@ -238,7 +238,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
},
|
||||
&communicator.StepConnect{
|
||||
Config: &b.config.SSHConfig.Comm,
|
||||
Host: parallelscommon.CommHost(b.config.SSHConfig.Comm.SSHHost),
|
||||
Host: parallelscommon.CommHost(b.config.SSHConfig.Comm.Host()),
|
||||
SSHConfig: b.config.SSHConfig.Comm.SSHConfigFunc(),
|
||||
},
|
||||
¶llelscommon.StepUploadVersion{
|
||||
|
||||
@@ -87,7 +87,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
},
|
||||
&communicator.StepConnect{
|
||||
Config: &b.config.SSHConfig.Comm,
|
||||
Host: parallelscommon.CommHost(b.config.SSHConfig.Comm.SSHHost),
|
||||
Host: parallelscommon.CommHost(b.config.SSHConfig.Comm.Host()),
|
||||
SSHConfig: b.config.SSHConfig.Comm.SSHConfigFunc(),
|
||||
},
|
||||
¶llelscommon.StepUploadVersion{
|
||||
|
||||
@@ -48,6 +48,7 @@ func (c *CommConfig) Prepare(ctx *interpolate.Context) (warnings []string, errs
|
||||
|
||||
if c.Comm.SSHHost == "" && c.SkipNatMapping {
|
||||
c.Comm.SSHHost = "127.0.0.1"
|
||||
c.Comm.WinRMHost = "127.0.0.1"
|
||||
}
|
||||
|
||||
if c.HostPortMin == 0 {
|
||||
|
||||
@@ -41,17 +41,23 @@ func (s *StepSSHConfig) Run(ctx context.Context, state multistep.StateBag) multi
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
config.Comm.SSHHost = sshConfig.Hostname
|
||||
port, err := strconv.Atoi(sshConfig.Port)
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
if config.Comm.SSHHost == "" {
|
||||
config.Comm.SSHHost = sshConfig.Hostname
|
||||
}
|
||||
if config.Comm.SSHPort == 0 {
|
||||
port, err := strconv.Atoi(sshConfig.Port)
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
config.Comm.SSHPort = port
|
||||
}
|
||||
config.Comm.SSHPort = port
|
||||
|
||||
if config.Comm.SSHUsername != "" {
|
||||
// If user has set the username within the communicator, use the
|
||||
// auth provided there.
|
||||
// username, password, and/or keyfile auth provided there.
|
||||
log.Printf("Overriding SSH config from Vagrant with the username, " +
|
||||
"password, and private key information provided to the Packer template.")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
log.Printf("identity file is %s", sshConfig.IdentityFile)
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/communicator"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
)
|
||||
|
||||
@@ -15,6 +16,83 @@ func TestStepSSHConfig_Impl(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrepStepSSHConfig_sshOverrides(t *testing.T) {
|
||||
type testcase struct {
|
||||
name string
|
||||
inputSSHConfig communicator.SSH
|
||||
expectedSSHConfig communicator.SSH
|
||||
}
|
||||
tcs := []testcase{
|
||||
{
|
||||
// defaults to overriding with the ssh config from vagrant\
|
||||
name: "default",
|
||||
inputSSHConfig: communicator.SSH{},
|
||||
expectedSSHConfig: communicator.SSH{
|
||||
SSHHost: "127.0.0.1",
|
||||
SSHPort: 2222,
|
||||
SSHUsername: "vagrant",
|
||||
SSHPassword: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
// respects SSH host and port overrides independent of credential
|
||||
// overrides
|
||||
name: "host_override",
|
||||
inputSSHConfig: communicator.SSH{
|
||||
SSHHost: "123.45.67.8",
|
||||
SSHPort: 1234,
|
||||
},
|
||||
expectedSSHConfig: communicator.SSH{
|
||||
SSHHost: "123.45.67.8",
|
||||
SSHPort: 1234,
|
||||
SSHUsername: "vagrant",
|
||||
SSHPassword: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
// respects credential overrides
|
||||
name: "credential_override",
|
||||
inputSSHConfig: communicator.SSH{
|
||||
SSHUsername: "megan",
|
||||
SSHPassword: "SoSecure",
|
||||
},
|
||||
expectedSSHConfig: communicator.SSH{
|
||||
SSHHost: "127.0.0.1",
|
||||
SSHPort: 2222,
|
||||
SSHUsername: "megan",
|
||||
SSHPassword: "SoSecure",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
driver := &MockVagrantDriver{}
|
||||
config := &Config{
|
||||
Comm: communicator.Config{
|
||||
SSH: tc.inputSSHConfig,
|
||||
},
|
||||
}
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("driver", driver)
|
||||
state.Put("config", config)
|
||||
|
||||
step := StepSSHConfig{}
|
||||
_ = step.Run(context.Background(), state)
|
||||
|
||||
if config.Comm.SSHHost != tc.expectedSSHConfig.SSHHost {
|
||||
t.Fatalf("unexpected sshconfig host: name: %s, recieved %s", tc.name, config.Comm.SSHHost)
|
||||
}
|
||||
if config.Comm.SSHPort != tc.expectedSSHConfig.SSHPort {
|
||||
t.Fatalf("unexpected sshconfig port: name: %s, recieved %d", tc.name, config.Comm.SSHPort)
|
||||
}
|
||||
if config.Comm.SSHUsername != tc.expectedSSHConfig.SSHUsername {
|
||||
t.Fatalf("unexpected sshconfig SSHUsername: name: %s, recieved %s", tc.name, config.Comm.SSHUsername)
|
||||
}
|
||||
if config.Comm.SSHPassword != tc.expectedSSHConfig.SSHPassword {
|
||||
t.Fatalf("unexpected sshconfig SSHUsername: name: %s, recieved %s", tc.name, config.Comm.SSHPassword)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrepStepSSHConfig_GlobalID(t *testing.T) {
|
||||
driver := &MockVagrantDriver{}
|
||||
config := &Config{}
|
||||
|
||||
@@ -50,8 +50,9 @@ func (c *CommConfig) Prepare(ctx *interpolate.Context) []error {
|
||||
c.SkipNatMapping = c.SSHSkipNatMapping
|
||||
}
|
||||
|
||||
if c.Comm.SSHHost == "" {
|
||||
if c.Comm.Host() == "" {
|
||||
c.Comm.SSHHost = "127.0.0.1"
|
||||
c.Comm.WinRMHost = "127.0.0.1"
|
||||
}
|
||||
|
||||
if c.HostPortMin == 0 {
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// This step attaches the boot ISO, cd_files iso, and guest additions to the
|
||||
@@ -69,49 +71,50 @@ func (s *StepAttachISOs) Run(ctx context.Context, state multistep.StateBag) mult
|
||||
|
||||
// We have three different potential isos we can attach, so let's
|
||||
// assign each one its own spot so they don't conflict.
|
||||
var controllerName, device, port string
|
||||
var controllerName string
|
||||
var device, port int
|
||||
switch diskCategory {
|
||||
case "boot_iso":
|
||||
// figure out controller path
|
||||
controllerName = "IDE Controller"
|
||||
port = "0"
|
||||
device = "1"
|
||||
port = 0
|
||||
device = 1
|
||||
if s.ISOInterface == "sata" {
|
||||
controllerName = "SATA Controller"
|
||||
port = "1"
|
||||
device = "0"
|
||||
port = 15
|
||||
device = 0
|
||||
} else if s.ISOInterface == "virtio" {
|
||||
controllerName = "VirtIO Controller"
|
||||
port = "1"
|
||||
device = "0"
|
||||
port = 15
|
||||
device = 0
|
||||
}
|
||||
ui.Message("Mounting boot ISO...")
|
||||
case "guest_additions":
|
||||
controllerName = "IDE Controller"
|
||||
port = "1"
|
||||
device = "0"
|
||||
port = 1
|
||||
device = 0
|
||||
if s.GuestAdditionsInterface == "sata" {
|
||||
controllerName = "SATA Controller"
|
||||
port = "2"
|
||||
device = "0"
|
||||
port = 14
|
||||
device = 0
|
||||
} else if s.GuestAdditionsInterface == "virtio" {
|
||||
controllerName = "VirtIO Controller"
|
||||
port = "2"
|
||||
device = "0"
|
||||
port = 14
|
||||
device = 0
|
||||
}
|
||||
ui.Message("Mounting guest additions ISO...")
|
||||
case "cd_files":
|
||||
controllerName = "IDE Controller"
|
||||
port = "1"
|
||||
device = "1"
|
||||
port = 1
|
||||
device = 1
|
||||
if s.ISOInterface == "sata" {
|
||||
controllerName = "SATA Controller"
|
||||
port = "3"
|
||||
device = "0"
|
||||
port = 13
|
||||
device = 0
|
||||
} else if s.ISOInterface == "virtio" {
|
||||
controllerName = "VirtIO Controller"
|
||||
port = "3"
|
||||
device = "0"
|
||||
port = 13
|
||||
device = 0
|
||||
}
|
||||
ui.Message("Mounting cd_files ISO...")
|
||||
}
|
||||
@@ -120,8 +123,8 @@ func (s *StepAttachISOs) Run(ctx context.Context, state multistep.StateBag) mult
|
||||
command := []string{
|
||||
"storageattach", vmName,
|
||||
"--storagectl", controllerName,
|
||||
"--port", port,
|
||||
"--device", device,
|
||||
"--port", strconv.Itoa(port),
|
||||
"--device", strconv.Itoa(device),
|
||||
"--type", "dvddrive",
|
||||
"--medium", isoPath,
|
||||
}
|
||||
@@ -137,8 +140,8 @@ func (s *StepAttachISOs) Run(ctx context.Context, state multistep.StateBag) mult
|
||||
unmountCommand := []string{
|
||||
"storageattach", vmName,
|
||||
"--storagectl", controllerName,
|
||||
"--port", port,
|
||||
"--device", device,
|
||||
"--port", strconv.Itoa(port),
|
||||
"--device", strconv.Itoa(device),
|
||||
"--type", "dvddrive",
|
||||
"--medium", "none",
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/hashicorp/packer-plugin-sdk/bootcommand"
|
||||
@@ -52,6 +53,14 @@ type Config struct {
|
||||
// When set to bios, the firmare is BIOS. This is the default.
|
||||
// When set to efi, the firmare is EFI.
|
||||
Firmware string `mapstructure:"firmware" required:"false"`
|
||||
// Nested virtualization: false or true.
|
||||
// When set to true, nested virtualisation (VT-x/AMD-V) is enabled.
|
||||
// When set to false, nested virtualisation is disabled. This is the default.
|
||||
NestedVirt bool `mapstructure:"nested_virt" required:"false"`
|
||||
// RTC time base: UTC or local.
|
||||
// When set to true, the RTC is set as UTC time.
|
||||
// When set to false, the RTC is set as local time. This is the default.
|
||||
RTCTimeBase string `mapstructure:"rtc_time_base" required:"false"`
|
||||
// The size, in megabytes, of the hard disk to create for the VM. By
|
||||
// default, this is 40000 (about 40 GB).
|
||||
DiskSize uint `mapstructure:"disk_size" required:"false"`
|
||||
@@ -75,6 +84,17 @@ type Config struct {
|
||||
// When set to vmsvga, the graphics controller is VMware SVGA.
|
||||
// When set to none, the graphics controller is disabled.
|
||||
GfxController string `mapstructure:"gfx_controller" required:"false"`
|
||||
// The VRAM size to be used. By default, this is 4 MiB.
|
||||
GfxVramSize uint `mapstructure:"gfx_vram_size" required:"false"`
|
||||
// 3D acceleration: true or false.
|
||||
// When set to true, 3D acceleration is enabled.
|
||||
// When set to false, 3D acceleration is disabled. This is the default.
|
||||
GfxAccelerate3D bool `mapstructure:"gfx_accelerate_3d" required:"false"`
|
||||
// Screen resolution in EFI mode: WIDTHxHEIGHT.
|
||||
// When set to WIDTHxHEIGHT, it provides the given width and height as screen resolution
|
||||
// to EFI, for example 1920x1080 for Full-HD resolution. By default, no screen resolution
|
||||
// is set. Note, that this option only affects EFI boot, not the (default) BIOS boot.
|
||||
GfxEFIResolution string `mapstructure:"gfx_efi_resolution" required:"false"`
|
||||
// The guest OS type being installed. By default this is other, but you can
|
||||
// get dramatic performance improvements by setting this to the proper
|
||||
// value. To view all available values for this run VBoxManage list
|
||||
@@ -129,6 +149,12 @@ type Config struct {
|
||||
// When set to sata, the drive is attached to an AHCI SATA controller.
|
||||
// When set to virtio, the drive is attached to a VirtIO controller.
|
||||
ISOInterface string `mapstructure:"iso_interface" required:"false"`
|
||||
// Additional disks to create. Uses `vm_name` as the disk name template and
|
||||
// appends `-#` where `#` is the position in the array. `#` starts at 1 since 0
|
||||
// is the default disk. Each value represents the disk image size in MiB.
|
||||
// Each additional disk uses the same disk parameters as the default disk.
|
||||
// Unset by default.
|
||||
AdditionalDiskSize []uint `mapstructure:"disk_additional_size" required:"false"`
|
||||
// Set this to true if you would like to keep the VM registered with
|
||||
// virtualbox. Defaults to false.
|
||||
KeepRegistered bool `mapstructure:"keep_registered" required:"false"`
|
||||
@@ -212,6 +238,17 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||
errs, errors.New("firmware can only be bios or efi"))
|
||||
}
|
||||
|
||||
if b.config.RTCTimeBase == "" {
|
||||
b.config.RTCTimeBase = "local"
|
||||
}
|
||||
switch b.config.RTCTimeBase {
|
||||
case "UTC", "local":
|
||||
// do nothing
|
||||
default:
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("rtc_time_base can only be UTC or local"))
|
||||
}
|
||||
|
||||
if b.config.DiskSize == 0 {
|
||||
b.config.DiskSize = 40000
|
||||
}
|
||||
@@ -242,6 +279,24 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||
errs, errors.New("Graphics controller type can only be vboxvga, vboxsvga, vmsvga, none"))
|
||||
}
|
||||
|
||||
if b.config.GfxVramSize == 0 {
|
||||
b.config.GfxVramSize = 4
|
||||
} else {
|
||||
if b.config.GfxVramSize < 1 || b.config.GfxVramSize > 128 {
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("VGRAM size must be from 0 (use default) to 128"))
|
||||
}
|
||||
}
|
||||
|
||||
if b.config.GfxEFIResolution != "" {
|
||||
re := regexp.MustCompile(`^[\d]+x[\d]+$`)
|
||||
matched := re.MatchString(b.config.GfxEFIResolution)
|
||||
if !matched {
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("EFI resolution must be in the format WIDTHxHEIGHT, e.g. 1920x1080"))
|
||||
}
|
||||
}
|
||||
|
||||
if b.config.AudioController == "" {
|
||||
b.config.AudioController = "ac97"
|
||||
}
|
||||
@@ -400,7 +455,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
},
|
||||
&communicator.StepConnect{
|
||||
Config: &b.config.CommConfig.Comm,
|
||||
Host: vboxcommon.CommHost(b.config.CommConfig.Comm.SSHHost),
|
||||
Host: vboxcommon.CommHost(b.config.CommConfig.Comm.Host()),
|
||||
SSHConfig: b.config.CommConfig.Comm.SSHConfigFunc(),
|
||||
SSHPort: vboxcommon.CommPort,
|
||||
WinRMPort: vboxcommon.CommPort,
|
||||
|
||||
@@ -119,10 +119,15 @@ type FlatConfig struct {
|
||||
GuestAdditionsURL *string `mapstructure:"guest_additions_url" required:"false" cty:"guest_additions_url" hcl:"guest_additions_url"`
|
||||
Chipset *string `mapstructure:"chipset" required:"false" cty:"chipset" hcl:"chipset"`
|
||||
Firmware *string `mapstructure:"firmware" required:"false" cty:"firmware" hcl:"firmware"`
|
||||
NestedVirt *bool `mapstructure:"nested_virt" required:"false" cty:"nested_virt" hcl:"nested_virt"`
|
||||
RTCTimeBase *string `mapstructure:"rtc_time_base" required:"false" cty:"rtc_time_base" hcl:"rtc_time_base"`
|
||||
DiskSize *uint `mapstructure:"disk_size" required:"false" cty:"disk_size" hcl:"disk_size"`
|
||||
NICType *string `mapstructure:"nic_type" required:"false" cty:"nic_type" hcl:"nic_type"`
|
||||
AudioController *string `mapstructure:"audio_controller" required:"false" cty:"audio_controller" hcl:"audio_controller"`
|
||||
GfxController *string `mapstructure:"gfx_controller" required:"false" cty:"gfx_controller" hcl:"gfx_controller"`
|
||||
GfxVramSize *uint `mapstructure:"gfx_vram_size" required:"false" cty:"gfx_vram_size" hcl:"gfx_vram_size"`
|
||||
GfxAccelerate3D *bool `mapstructure:"gfx_accelerate_3d" required:"false" cty:"gfx_accelerate_3d" hcl:"gfx_accelerate_3d"`
|
||||
GfxEFIResolution *string `mapstructure:"gfx_efi_resolution" required:"false" cty:"gfx_efi_resolution" hcl:"gfx_efi_resolution"`
|
||||
GuestOSType *string `mapstructure:"guest_os_type" required:"false" cty:"guest_os_type" hcl:"guest_os_type"`
|
||||
HardDriveDiscard *bool `mapstructure:"hard_drive_discard" required:"false" cty:"hard_drive_discard" hcl:"hard_drive_discard"`
|
||||
HardDriveInterface *string `mapstructure:"hard_drive_interface" required:"false" cty:"hard_drive_interface" hcl:"hard_drive_interface"`
|
||||
@@ -130,6 +135,7 @@ type FlatConfig struct {
|
||||
NVMePortCount *int `mapstructure:"nvme_port_count" required:"false" cty:"nvme_port_count" hcl:"nvme_port_count"`
|
||||
HardDriveNonrotational *bool `mapstructure:"hard_drive_nonrotational" required:"false" cty:"hard_drive_nonrotational" hcl:"hard_drive_nonrotational"`
|
||||
ISOInterface *string `mapstructure:"iso_interface" required:"false" cty:"iso_interface" hcl:"iso_interface"`
|
||||
AdditionalDiskSize []uint `mapstructure:"disk_additional_size" required:"false" cty:"disk_additional_size" hcl:"disk_additional_size"`
|
||||
KeepRegistered *bool `mapstructure:"keep_registered" required:"false" cty:"keep_registered" hcl:"keep_registered"`
|
||||
SkipExport *bool `mapstructure:"skip_export" required:"false" cty:"skip_export" hcl:"skip_export"`
|
||||
VMName *string `mapstructure:"vm_name" required:"false" cty:"vm_name" hcl:"vm_name"`
|
||||
@@ -256,10 +262,15 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"guest_additions_url": &hcldec.AttrSpec{Name: "guest_additions_url", Type: cty.String, Required: false},
|
||||
"chipset": &hcldec.AttrSpec{Name: "chipset", Type: cty.String, Required: false},
|
||||
"firmware": &hcldec.AttrSpec{Name: "firmware", Type: cty.String, Required: false},
|
||||
"nested_virt": &hcldec.AttrSpec{Name: "nested_virt", Type: cty.Bool, Required: false},
|
||||
"rtc_time_base": &hcldec.AttrSpec{Name: "rtc_time_base", Type: cty.String, Required: false},
|
||||
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
|
||||
"nic_type": &hcldec.AttrSpec{Name: "nic_type", Type: cty.String, Required: false},
|
||||
"audio_controller": &hcldec.AttrSpec{Name: "audio_controller", Type: cty.String, Required: false},
|
||||
"gfx_controller": &hcldec.AttrSpec{Name: "gfx_controller", Type: cty.String, Required: false},
|
||||
"gfx_vram_size": &hcldec.AttrSpec{Name: "gfx_vram_size", Type: cty.Number, Required: false},
|
||||
"gfx_accelerate_3d": &hcldec.AttrSpec{Name: "gfx_accelerate_3d", Type: cty.Bool, Required: false},
|
||||
"gfx_efi_resolution": &hcldec.AttrSpec{Name: "gfx_efi_resolution", Type: cty.String, Required: false},
|
||||
"guest_os_type": &hcldec.AttrSpec{Name: "guest_os_type", Type: cty.String, Required: false},
|
||||
"hard_drive_discard": &hcldec.AttrSpec{Name: "hard_drive_discard", Type: cty.Bool, Required: false},
|
||||
"hard_drive_interface": &hcldec.AttrSpec{Name: "hard_drive_interface", Type: cty.String, Required: false},
|
||||
@@ -267,6 +278,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"nvme_port_count": &hcldec.AttrSpec{Name: "nvme_port_count", Type: cty.Number, Required: false},
|
||||
"hard_drive_nonrotational": &hcldec.AttrSpec{Name: "hard_drive_nonrotational", Type: cty.Bool, Required: false},
|
||||
"iso_interface": &hcldec.AttrSpec{Name: "iso_interface", Type: cty.String, Required: false},
|
||||
"disk_additional_size": &hcldec.AttrSpec{Name: "disk_additional_size", Type: cty.List(cty.Number), Required: false},
|
||||
"keep_registered": &hcldec.AttrSpec{Name: "keep_registered", Type: cty.Bool, Required: false},
|
||||
"skip_export": &hcldec.AttrSpec{Name: "skip_export", Type: cty.Bool, Required: false},
|
||||
"vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false},
|
||||
|
||||
@@ -22,31 +22,50 @@ func (s *stepCreateDisk) Run(ctx context.Context, state multistep.StateBag) mult
|
||||
driver := state.Get("driver").(vboxcommon.Driver)
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
vmName := state.Get("vmName").(string)
|
||||
|
||||
format := "VDI"
|
||||
path := filepath.Join(config.OutputDir, fmt.Sprintf("%s.%s", config.VMName, strings.ToLower(format)))
|
||||
|
||||
command := []string{
|
||||
"createhd",
|
||||
"--filename", path,
|
||||
"--size", strconv.FormatUint(uint64(config.DiskSize), 10),
|
||||
"--format", format,
|
||||
"--variant", "Standard",
|
||||
// The main disk and additional disks
|
||||
diskFullPaths := []string{}
|
||||
diskSizes := []uint{config.DiskSize}
|
||||
if len(config.AdditionalDiskSize) == 0 {
|
||||
// If there are no additional disks, use disk naming as before
|
||||
diskFullPaths = append(diskFullPaths, filepath.Join(config.OutputDir, fmt.Sprintf("%s.%s", config.VMName, strings.ToLower(format))))
|
||||
} else {
|
||||
// If there are additional disks, use consistent naming with numbers
|
||||
diskFullPaths = append(diskFullPaths, filepath.Join(config.OutputDir, fmt.Sprintf("%s-0.%s", config.VMName, strings.ToLower(format))))
|
||||
|
||||
for i, diskSize := range config.AdditionalDiskSize {
|
||||
path := filepath.Join(config.OutputDir, fmt.Sprintf("%s-%d.%s", config.VMName, i+1, strings.ToLower(format)))
|
||||
diskFullPaths = append(diskFullPaths, path)
|
||||
diskSizes = append(diskSizes, diskSize)
|
||||
}
|
||||
}
|
||||
|
||||
ui.Say("Creating hard drive...")
|
||||
err := driver.VBoxManage(command...)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating hard drive: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
// Create all required disks
|
||||
for i := range diskFullPaths {
|
||||
ui.Say(fmt.Sprintf("Creating hard drive %s with size %d MiB...", diskFullPaths[i], diskSizes[i]))
|
||||
|
||||
command := []string{
|
||||
"createhd",
|
||||
"--filename", diskFullPaths[i],
|
||||
"--size", strconv.FormatUint(uint64(diskSizes[i]), 10),
|
||||
"--format", format,
|
||||
"--variant", "Standard",
|
||||
}
|
||||
|
||||
err := driver.VBoxManage(command...)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating hard drive: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
// Add the IDE controller so we can later attach the disk.
|
||||
// When the hard disk controller is not IDE, this device is still used
|
||||
// by VirtualBox to deliver the guest extensions.
|
||||
err = driver.VBoxManage("storagectl", vmName, "--name", "IDE Controller", "--add", "ide")
|
||||
err := driver.VBoxManage("storagectl", vmName, "--name", "IDE Controller", "--add", "ide")
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating disk controller: %s", err)
|
||||
state.Put("error", err)
|
||||
@@ -116,21 +135,23 @@ func (s *stepCreateDisk) Run(ctx context.Context, state multistep.StateBag) mult
|
||||
discard = "on"
|
||||
}
|
||||
|
||||
command = []string{
|
||||
"storageattach", vmName,
|
||||
"--storagectl", controllerName,
|
||||
"--port", "0",
|
||||
"--device", "0",
|
||||
"--type", "hdd",
|
||||
"--medium", path,
|
||||
"--nonrotational", nonrotational,
|
||||
"--discard", discard,
|
||||
}
|
||||
if err := driver.VBoxManage(command...); err != nil {
|
||||
err := fmt.Errorf("Error attaching hard drive: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
for i := range diskFullPaths {
|
||||
command := []string{
|
||||
"storageattach", vmName,
|
||||
"--storagectl", controllerName,
|
||||
"--port", strconv.FormatUint(uint64(i), 10),
|
||||
"--device", "0",
|
||||
"--type", "hdd",
|
||||
"--medium", diskFullPaths[i],
|
||||
"--nonrotational", nonrotational,
|
||||
"--discard", discard,
|
||||
}
|
||||
if err := driver.VBoxManage(command...); err != nil {
|
||||
err := fmt.Errorf("Error attaching hard drive: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
|
||||
@@ -26,7 +26,7 @@ func (s *stepCreateVM) Run(ctx context.Context, state multistep.StateBag) multis
|
||||
|
||||
name := config.VMName
|
||||
|
||||
commands := make([][]string, 10)
|
||||
commands := make([][]string, 14)
|
||||
commands[0] = []string{
|
||||
"createvm", "--name", name,
|
||||
"--ostype", config.GuestOSType, "--register",
|
||||
@@ -59,7 +59,26 @@ func (s *stepCreateVM) Run(ctx context.Context, state multistep.StateBag) multis
|
||||
"--nictype6", config.NICType,
|
||||
"--nictype7", config.NICType,
|
||||
"--nictype8", config.NICType}
|
||||
commands[9] = []string{"modifyvm", name, "--graphicscontroller", config.GfxController}
|
||||
commands[9] = []string{"modifyvm", name, "--graphicscontroller", config.GfxController, "--vram", strconv.FormatUint(uint64(config.GfxVramSize), 10)}
|
||||
if config.RTCTimeBase == "UTC" {
|
||||
commands[10] = []string{"modifyvm", name, "--rtcuseutc", "on"}
|
||||
} else {
|
||||
commands[10] = []string{"modifyvm", name, "--rtcuseutc", "off"}
|
||||
}
|
||||
if config.NestedVirt == true {
|
||||
commands[11] = []string{"modifyvm", name, "--nested-hw-virt", "on"}
|
||||
} else {
|
||||
commands[11] = []string{"modifyvm", name, "--nested-hw-virt", "off"}
|
||||
}
|
||||
|
||||
if config.GfxAccelerate3D {
|
||||
commands[12] = []string{"modifyvm", name, "--accelerate3d", "on"}
|
||||
} else {
|
||||
commands[12] = []string{"modifyvm", name, "--accelerate3d", "off"}
|
||||
}
|
||||
if config.GfxEFIResolution != "" {
|
||||
commands[13] = []string{"setextradata", name, "VBoxInternal2/EfiGraphicsResolution", config.GfxEFIResolution}
|
||||
}
|
||||
|
||||
ui.Say("Creating virtual machine...")
|
||||
for _, command := range commands {
|
||||
|
||||
@@ -130,7 +130,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
},
|
||||
&communicator.StepConnect{
|
||||
Config: &b.config.CommConfig.Comm,
|
||||
Host: vboxcommon.CommHost(b.config.CommConfig.Comm.SSHHost),
|
||||
Host: vboxcommon.CommHost(b.config.CommConfig.Comm.Host()),
|
||||
SSHConfig: b.config.CommConfig.Comm.SSHConfigFunc(),
|
||||
SSHPort: vboxcommon.CommPort,
|
||||
WinRMPort: vboxcommon.CommPort,
|
||||
|
||||
@@ -114,7 +114,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
},
|
||||
&communicator.StepConnect{
|
||||
Config: &b.config.CommConfig.Comm,
|
||||
Host: vboxcommon.CommHost(b.config.CommConfig.Comm.SSHHost),
|
||||
Host: vboxcommon.CommHost(b.config.CommConfig.Comm.Host()),
|
||||
SSHConfig: b.config.CommConfig.Comm.SSHConfigFunc(),
|
||||
SSHPort: vboxcommon.CommPort,
|
||||
WinRMPort: vboxcommon.CommPort,
|
||||
|
||||
+8
-16
@@ -389,7 +389,7 @@ func TestBuild(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
defer tt.cleanup(t)
|
||||
run(t, tt.args, tt.expectedCode)
|
||||
tt.fileCheck.verify(t)
|
||||
tt.fileCheck.verify(t, "")
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -732,7 +732,7 @@ func TestHCL2PostProcessorForceFlag(t *testing.T) {
|
||||
if code := c.Run(args); code != 0 {
|
||||
fatalCommand(t, c.Meta)
|
||||
}
|
||||
fCheck.verify(t)
|
||||
fCheck.verify(t, "")
|
||||
|
||||
// Second build should override previous manifest
|
||||
UUID, _ = uuid.GenerateUUID()
|
||||
@@ -766,7 +766,7 @@ func TestHCL2PostProcessorForceFlag(t *testing.T) {
|
||||
if code := c.Run(args); code != 0 {
|
||||
fatalCommand(t, c.Meta)
|
||||
}
|
||||
fCheck.verify(t)
|
||||
fCheck.verify(t, "")
|
||||
}
|
||||
|
||||
func TestBuildCommand_HCLOnlyExceptOptions(t *testing.T) {
|
||||
@@ -887,19 +887,19 @@ func (fc fileCheck) expectedFiles() []string {
|
||||
return expected
|
||||
}
|
||||
|
||||
func (fc fileCheck) verify(t *testing.T) {
|
||||
func (fc fileCheck) verify(t *testing.T, dir string) {
|
||||
for _, f := range fc.expectedFiles() {
|
||||
if !fileExists(f) {
|
||||
t.Errorf("Expected to find %s", f)
|
||||
if _, err := os.Stat(filepath.Join(dir, f)); err != nil {
|
||||
t.Errorf("Expected to find %s: %v", f, err)
|
||||
}
|
||||
}
|
||||
for _, f := range fc.notExpected {
|
||||
if fileExists(f) {
|
||||
if _, err := os.Stat(filepath.Join(dir, f)); err == nil {
|
||||
t.Errorf("Expected to not find %s", f)
|
||||
}
|
||||
}
|
||||
for file, expectedContent := range fc.expectedContent {
|
||||
content, err := ioutil.ReadFile(file)
|
||||
content, err := ioutil.ReadFile(filepath.Join(dir, file))
|
||||
if err != nil {
|
||||
t.Fatalf("ioutil.ReadFile: %v", err)
|
||||
}
|
||||
@@ -909,14 +909,6 @@ func (fc fileCheck) verify(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// fileExists returns true if the filename is found
|
||||
func fileExists(filename string) bool {
|
||||
if _, err := os.Stat(filename); err == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// testCoreConfigBuilder creates a packer CoreConfig that has a file builder
|
||||
// available. This allows us to test a builder that writes files to disk.
|
||||
func testCoreConfigBuilder(t *testing.T) *packer.CoreConfig {
|
||||
|
||||
+2
-2
@@ -161,12 +161,12 @@ func (va *FormatArgs) AddFlagSets(flags *flag.FlagSet) {
|
||||
flags.BoolVar(&va.Check, "check", false, "check if the input is formatted")
|
||||
flags.BoolVar(&va.Diff, "diff", false, "display the diff of formatting changes")
|
||||
flags.BoolVar(&va.Write, "write", true, "overwrite source files instead of writing to stdout")
|
||||
|
||||
flags.BoolVar(&va.Recursive, "recursive", false, "Also process files in subdirectories")
|
||||
va.MetaArgs.AddFlagSets(flags)
|
||||
}
|
||||
|
||||
// FormatArgs represents a parsed cli line for `packer fmt`
|
||||
type FormatArgs struct {
|
||||
MetaArgs
|
||||
Check, Diff, Write bool
|
||||
Check, Diff, Write, Recursive bool
|
||||
}
|
||||
|
||||
@@ -87,6 +87,8 @@ func TestHelperProcess(*testing.T) {
|
||||
switch cmd {
|
||||
case "console":
|
||||
os.Exit((&ConsoleCommand{Meta: commandMeta()}).Run(args))
|
||||
case "fmt":
|
||||
os.Exit((&FormatCommand{Meta: commandMeta()}).Run(args))
|
||||
case "inspect":
|
||||
os.Exit((&InspectCommand{Meta: commandMeta()}).Run(args))
|
||||
case "build":
|
||||
|
||||
+10
-6
@@ -48,9 +48,10 @@ func (c *FormatCommand) RunContext(ctx context.Context, cla *FormatArgs) int {
|
||||
}
|
||||
|
||||
formatter := hclutils.HCL2Formatter{
|
||||
ShowDiff: cla.Diff,
|
||||
Write: cla.Write,
|
||||
Output: os.Stdout,
|
||||
ShowDiff: cla.Diff,
|
||||
Write: cla.Write,
|
||||
Output: os.Stdout,
|
||||
Recursive: cla.Recursive,
|
||||
}
|
||||
|
||||
bytesModified, diags := formatter.Format(cla.Path)
|
||||
@@ -90,6 +91,8 @@ Options:
|
||||
-write=false Don't write to source files
|
||||
(always disabled if using -check)
|
||||
|
||||
-recursive Also process files in subdirectories. By default, only the
|
||||
given directory (or current directory) is processed.
|
||||
`
|
||||
|
||||
return strings.TrimSpace(helpText)
|
||||
@@ -105,8 +108,9 @@ func (*FormatCommand) AutocompleteArgs() complete.Predictor {
|
||||
|
||||
func (*FormatCommand) AutocompleteFlags() complete.Flags {
|
||||
return complete.Flags{
|
||||
"-check": complete.PredictNothing,
|
||||
"-diff": complete.PredictNothing,
|
||||
"-write": complete.PredictNothing,
|
||||
"-check": complete.PredictNothing,
|
||||
"-diff": complete.PredictNothing,
|
||||
"-write": complete.PredictNothing,
|
||||
"-recursive": complete.PredictNothing,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -50,3 +55,122 @@ func TestFmt_unfomattedTemlateDirectory(t *testing.T) {
|
||||
fatalCommand(t, c.Meta)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
unformattedHCL = `
|
||||
ami_filter_name ="amzn2-ami-hvm-*-x86_64-gp2"
|
||||
ami_filter_owners =[ "137112412989" ]
|
||||
|
||||
`
|
||||
formattedHCL = `
|
||||
ami_filter_name = "amzn2-ami-hvm-*-x86_64-gp2"
|
||||
ami_filter_owners = ["137112412989"]
|
||||
|
||||
`
|
||||
)
|
||||
|
||||
func TestFmt_Recursive(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
formatArgs []string // arguments passed to format
|
||||
alreadyPresentContent map[string]string
|
||||
fileCheck
|
||||
}{
|
||||
{
|
||||
name: "nested formats recursively",
|
||||
formatArgs: []string{"-recursive=true"},
|
||||
alreadyPresentContent: map[string]string{
|
||||
"foo/bar/baz.pkr.hcl": unformattedHCL,
|
||||
"foo/bar/baz/woo.pkrvars.hcl": unformattedHCL,
|
||||
"potato": unformattedHCL,
|
||||
"foo/bar/potato": unformattedHCL,
|
||||
"bar.pkr.hcl": unformattedHCL,
|
||||
"-": unformattedHCL,
|
||||
},
|
||||
fileCheck: fileCheck{
|
||||
expectedContent: map[string]string{
|
||||
"foo/bar/baz.pkr.hcl": formattedHCL,
|
||||
"foo/bar/baz/woo.pkrvars.hcl": formattedHCL,
|
||||
"potato": unformattedHCL,
|
||||
"foo/bar/potato": unformattedHCL,
|
||||
"bar.pkr.hcl": formattedHCL,
|
||||
"-": unformattedHCL,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "nested no recursive format",
|
||||
formatArgs: []string{},
|
||||
alreadyPresentContent: map[string]string{
|
||||
"foo/bar/baz.pkr.hcl": unformattedHCL,
|
||||
"foo/bar/baz/woo.pkrvars.hcl": unformattedHCL,
|
||||
"bar.pkr.hcl": unformattedHCL,
|
||||
"-": unformattedHCL,
|
||||
},
|
||||
fileCheck: fileCheck{
|
||||
expectedContent: map[string]string{
|
||||
"foo/bar/baz.pkr.hcl": unformattedHCL,
|
||||
"foo/bar/baz/woo.pkrvars.hcl": unformattedHCL,
|
||||
"bar.pkr.hcl": formattedHCL,
|
||||
"-": unformattedHCL,
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
c := &FormatCommand{
|
||||
Meta: testMeta(t),
|
||||
}
|
||||
|
||||
testDir := "test-fixtures/fmt"
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tempDirectory := mustString(ioutil.TempDir(testDir, "test-dir-*"))
|
||||
defer os.RemoveAll(tempDirectory)
|
||||
|
||||
createFiles(tempDirectory, tt.alreadyPresentContent)
|
||||
|
||||
testArgs := append(tt.formatArgs, tempDirectory)
|
||||
if code := c.Run(testArgs); code != 0 {
|
||||
ui := c.Meta.Ui.(*packersdk.BasicUi)
|
||||
out := ui.Writer.(*bytes.Buffer)
|
||||
err := ui.ErrorWriter.(*bytes.Buffer)
|
||||
t.Fatalf(
|
||||
"Bad exit code for test case: %s.\n\nStdout:\n\n%s\n\nStderr:\n\n%s",
|
||||
tt.name,
|
||||
out.String(),
|
||||
err.String())
|
||||
}
|
||||
|
||||
tt.fileCheck.verify(t, tempDirectory)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_fmt_pipe(t *testing.T) {
|
||||
|
||||
tc := []struct {
|
||||
piped string
|
||||
command []string
|
||||
env []string
|
||||
expected string
|
||||
}{
|
||||
{unformattedHCL, []string{"fmt", "-"}, nil, formattedHCL},
|
||||
}
|
||||
|
||||
for _, tc := range tc {
|
||||
t.Run(fmt.Sprintf("echo %q | packer %s", tc.piped, tc.command), func(t *testing.T) {
|
||||
p := helperCommand(t, tc.command...)
|
||||
p.Stdin = strings.NewReader(tc.piped)
|
||||
p.Env = append(p.Env, tc.env...)
|
||||
fmt.Println(fmt.Sprintf("Path: %s", p.Path))
|
||||
bs, err := p.Output()
|
||||
if err != nil {
|
||||
t.Fatalf("Error occurred running command %v: %s", err, bs)
|
||||
}
|
||||
if diff := cmp.Diff(tc.expected, string(bs)); diff != "" {
|
||||
t.Fatalf("Error in diff: %s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
azuredtlbuilder "github.com/hashicorp/packer/builder/azure/dtl"
|
||||
cloudstackbuilder "github.com/hashicorp/packer/builder/cloudstack"
|
||||
digitaloceanbuilder "github.com/hashicorp/packer/builder/digitalocean"
|
||||
dockerbuilder "github.com/hashicorp/packer/builder/docker"
|
||||
filebuilder "github.com/hashicorp/packer/builder/file"
|
||||
googlecomputebuilder "github.com/hashicorp/packer/builder/googlecompute"
|
||||
hcloudbuilder "github.com/hashicorp/packer/builder/hcloud"
|
||||
@@ -73,11 +72,6 @@ import (
|
||||
checksumpostprocessor "github.com/hashicorp/packer/post-processor/checksum"
|
||||
compresspostprocessor "github.com/hashicorp/packer/post-processor/compress"
|
||||
digitaloceanimportpostprocessor "github.com/hashicorp/packer/post-processor/digitalocean-import"
|
||||
dockerimportpostprocessor "github.com/hashicorp/packer/post-processor/docker-import"
|
||||
dockerpushpostprocessor "github.com/hashicorp/packer/post-processor/docker-push"
|
||||
dockersavepostprocessor "github.com/hashicorp/packer/post-processor/docker-save"
|
||||
dockertagpostprocessor "github.com/hashicorp/packer/post-processor/docker-tag"
|
||||
exoscaleimportpostprocessor "github.com/hashicorp/packer/post-processor/exoscale-import"
|
||||
googlecomputeexportpostprocessor "github.com/hashicorp/packer/post-processor/googlecompute-export"
|
||||
googlecomputeimportpostprocessor "github.com/hashicorp/packer/post-processor/googlecompute-import"
|
||||
manifestpostprocessor "github.com/hashicorp/packer/post-processor/manifest"
|
||||
@@ -125,7 +119,6 @@ var Builders = map[string]packersdk.Builder{
|
||||
"azure-dtl": new(azuredtlbuilder.Builder),
|
||||
"cloudstack": new(cloudstackbuilder.Builder),
|
||||
"digitalocean": new(digitaloceanbuilder.Builder),
|
||||
"docker": new(dockerbuilder.Builder),
|
||||
"file": new(filebuilder.Builder),
|
||||
"googlecompute": new(googlecomputebuilder.Builder),
|
||||
"hcloud": new(hcloudbuilder.Builder),
|
||||
@@ -196,11 +189,6 @@ var PostProcessors = map[string]packersdk.PostProcessor{
|
||||
"checksum": new(checksumpostprocessor.PostProcessor),
|
||||
"compress": new(compresspostprocessor.PostProcessor),
|
||||
"digitalocean-import": new(digitaloceanimportpostprocessor.PostProcessor),
|
||||
"docker-import": new(dockerimportpostprocessor.PostProcessor),
|
||||
"docker-push": new(dockerpushpostprocessor.PostProcessor),
|
||||
"docker-save": new(dockersavepostprocessor.PostProcessor),
|
||||
"docker-tag": new(dockertagpostprocessor.PostProcessor),
|
||||
"exoscale-import": new(exoscaleimportpostprocessor.PostProcessor),
|
||||
"googlecompute-export": new(googlecomputeexportpostprocessor.PostProcessor),
|
||||
"googlecompute-import": new(googlecomputeimportpostprocessor.PostProcessor),
|
||||
"manifest": new(manifestpostprocessor.PostProcessor),
|
||||
|
||||
@@ -39,3 +39,11 @@ func (c *configDirSingleton) dir(key string) string {
|
||||
c.dirs[key] = mustString(ioutil.TempDir("", "pkr-test-cfg-dir-"+key))
|
||||
return c.dirs[key]
|
||||
}
|
||||
|
||||
// fileExists returns true if the filename is found
|
||||
func fileExists(filename string) bool {
|
||||
if _, err := os.Stat(filename); err == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
|
||||
// Previously core-bundled components, split into their own plugins but
|
||||
// still vendored with Packer for now. Importing as library instead of
|
||||
// forcing use of packer init, until packer v1.8.0
|
||||
exoscaleimportpostprocessor "github.com/exoscale/packer-plugin-exoscale/post-processor/exoscale-import"
|
||||
dockerbuilder "github.com/hashicorp/packer-plugin-docker/builder/docker"
|
||||
dockerimportpostprocessor "github.com/hashicorp/packer-plugin-docker/post-processor/docker-import"
|
||||
dockerpushpostprocessor "github.com/hashicorp/packer-plugin-docker/post-processor/docker-push"
|
||||
dockersavepostprocessor "github.com/hashicorp/packer-plugin-docker/post-processor/docker-save"
|
||||
dockertagpostprocessor "github.com/hashicorp/packer-plugin-docker/post-processor/docker-tag"
|
||||
)
|
||||
|
||||
// VendoredBuilders are builder components that were once bundled with the
|
||||
// Packer core, but are now being imported from their counterpart plugin repos
|
||||
var VendoredBuilders = map[string]packersdk.Builder{
|
||||
"docker": new(dockerbuilder.Builder),
|
||||
}
|
||||
|
||||
// VendoredProvisioners are provisioner components that were once bundled with the
|
||||
// Packer core, but are now being imported from their counterpart plugin repos
|
||||
var VendoredProvisioners = map[string]packersdk.Provisioner{}
|
||||
|
||||
// VendoredPostProcessors are post-processor components that were once bundled with the
|
||||
// Packer core, but are now being imported from their counterpart plugin repos
|
||||
var VendoredPostProcessors = map[string]packersdk.PostProcessor{
|
||||
"docker-import": new(dockerimportpostprocessor.PostProcessor),
|
||||
"docker-push": new(dockerpushpostprocessor.PostProcessor),
|
||||
"docker-save": new(dockersavepostprocessor.PostProcessor),
|
||||
"docker-tag": new(dockertagpostprocessor.PostProcessor),
|
||||
"exoscale-import": new(exoscaleimportpostprocessor.PostProcessor),
|
||||
}
|
||||
|
||||
// Upon init lets load up any plugins that were vendored manually into the default
|
||||
// set of plugins.
|
||||
func init() {
|
||||
for k, v := range VendoredBuilders {
|
||||
if _, ok := Builders[k]; ok {
|
||||
continue
|
||||
}
|
||||
Builders[k] = v
|
||||
}
|
||||
|
||||
for k, v := range VendoredProvisioners {
|
||||
if _, ok := Provisioners[k]; ok {
|
||||
continue
|
||||
}
|
||||
Provisioners[k] = v
|
||||
}
|
||||
|
||||
for k, v := range VendoredPostProcessors {
|
||||
if _, ok := PostProcessors[k]; ok {
|
||||
continue
|
||||
}
|
||||
PostProcessors[k] = v
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ require (
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/digitalocean/go-qemu v0.0.0-20201211181942-d361e7b4965f
|
||||
github.com/digitalocean/godo v1.11.1
|
||||
github.com/exoscale/egoscale v0.18.1
|
||||
github.com/exoscale/packer-plugin-exoscale v0.1.0
|
||||
github.com/fatih/camelcase v1.0.0
|
||||
github.com/fatih/structtag v1.0.0
|
||||
github.com/go-ini/ini v1.25.4
|
||||
@@ -50,6 +50,7 @@ require (
|
||||
github.com/hashicorp/go-uuid v1.0.2
|
||||
github.com/hashicorp/go-version v1.2.0
|
||||
github.com/hashicorp/hcl/v2 v2.8.0
|
||||
github.com/hashicorp/packer-plugin-docker v0.0.2
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.14
|
||||
github.com/hashicorp/vault/api v1.0.4
|
||||
github.com/hetznercloud/hcloud-go v1.15.1
|
||||
@@ -63,7 +64,6 @@ require (
|
||||
github.com/mitchellh/cli v1.1.0
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed
|
||||
github.com/mitchellh/gox v1.0.1 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.0
|
||||
github.com/mitchellh/panicwrap v1.0.0
|
||||
github.com/mitchellh/prefixedio v0.0.0-20151214002211-6e6954073784
|
||||
@@ -77,7 +77,7 @@ require (
|
||||
github.com/profitbricks/profitbricks-sdk-go v4.0.2+incompatible
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7
|
||||
github.com/shirou/gopsutil v3.21.1+incompatible
|
||||
github.com/stretchr/testify v1.6.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/tencentcloud/tencentcloud-sdk-go v3.0.222+incompatible
|
||||
github.com/ucloud/ucloud-sdk-go v0.16.3
|
||||
github.com/ufilesdk-dev/ufile-gosdk v0.0.0-20190830075812-b4dbc4ef43a6
|
||||
@@ -88,13 +88,13 @@ require (
|
||||
github.com/yandex-cloud/go-sdk v0.0.0-20200921111412-ef15ded2014c
|
||||
github.com/zclconf/go-cty v1.7.0
|
||||
github.com/zclconf/go-cty-yaml v1.0.1
|
||||
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
|
||||
golang.org/x/mobile v0.0.0-20201208152944-da85bec010a2
|
||||
golang.org/x/mod v0.3.0
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777
|
||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c
|
||||
golang.org/x/tools v0.0.0-20201111133315-69daaf961d65
|
||||
google.golang.org/api v0.32.0
|
||||
google.golang.org/grpc v1.32.0
|
||||
|
||||
@@ -62,7 +62,6 @@ github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtO
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
|
||||
github.com/Azure/go-autorest/autorest/to v0.3.0 h1:zebkZaadz7+wIQYgC7GXaz3Wb28yKYfVkkBKwc38VF8=
|
||||
github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA=
|
||||
github.com/Azure/go-autorest/autorest/validation v0.2.0 h1:15vMO4y76dehZSq7pAaOLQxC6dZYsSrj2GQpflyM/L4=
|
||||
github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI=
|
||||
github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac=
|
||||
github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E=
|
||||
@@ -70,7 +69,6 @@ github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1Gn
|
||||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4 h1:pSm8mp0T2OH2CPmPDPtwHPr3VAQaOwVF/JbllOPP4xA=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20191115201650-bad6df29494a h1:3FwiePtHk5YJrooV799oo5jIfsgRdES25VdngJM03dU=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20191115201650-bad6df29494a/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
@@ -93,11 +91,9 @@ github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190418113227-25233c783f4e h1:/8w
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190418113227-25233c783f4e/go.mod h1:T9M45xf79ahXVelWoOBmH0y4aC1t5kXO5BxwyakgIGA=
|
||||
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20170113022742-e6dbea820a9f h1:jI4DIE5Vf4oRaHfthB0oRhU+yuYuoOTurDzwAlskP00=
|
||||
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20170113022742-e6dbea820a9f/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
|
||||
github.com/antchfx/xpath v0.0.0-20170728053731-b5c552e1acbd h1:S3Fr6QnkpW9VRjiEY4psQHhhbbahASuNVj52YIce7lI=
|
||||
github.com/antchfx/xpath v0.0.0-20170728053731-b5c552e1acbd/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk=
|
||||
github.com/antchfx/xpath v1.1.11 h1:WOFtK8TVAjLm3lbgqeP0arlHpvCEeTANeWZ/csPpJkQ=
|
||||
github.com/antchfx/xpath v1.1.11/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
|
||||
github.com/antchfx/xquery v0.0.0-20170730121040-eb8c3c172607 h1:BFFG6KP8ASFBg2ptWsJn8p8RDufBjBDKIxLU7BTYGOM=
|
||||
github.com/antchfx/xquery v0.0.0-20170730121040-eb8c3c172607/go.mod h1:LzD22aAzDP8/dyiCKFp31He4m2GPjl0AFyzDtZzUu9M=
|
||||
github.com/antchfx/xquery v0.0.0-20180515051857-ad5b8c7a47b0 h1:JaCC8jz0zdMLk2m+qCCVLLLM/PL93p84w4pK3aJWj60=
|
||||
github.com/antchfx/xquery v0.0.0-20180515051857-ad5b8c7a47b0/go.mod h1:LzD22aAzDP8/dyiCKFp31He4m2GPjl0AFyzDtZzUu9M=
|
||||
@@ -114,11 +110,9 @@ github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJE
|
||||
github.com/approvals/go-approval-tests v0.0.0-20160714161514-ad96e53bea43 h1:ePCAQPf5tUc5IMcUvu6euhSGna7jzs7eiXtJXHig6Zc=
|
||||
github.com/approvals/go-approval-tests v0.0.0-20160714161514-ad96e53bea43/go.mod h1:S6puKjZ9ZeqUPBv2hEBnMZGcM2J6mOsDRQcmxkMAND0=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 h1:EFSB7Zo9Eg91v7MJPVsifUysc/wPdN+NOnVe6bWbdBM=
|
||||
github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
|
||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
@@ -127,7 +121,6 @@ github.com/aws/aws-sdk-go v1.26.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN
|
||||
github.com/aws/aws-sdk-go v1.30.8/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||
github.com/aws/aws-sdk-go v1.31.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||
github.com/aws/aws-sdk-go v1.36.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||
github.com/aws/aws-sdk-go v1.36.5 h1:SouM2ar1A8f+3DYWW622sDdqkkZAO3ha4j8GQjiPLFg=
|
||||
github.com/aws/aws-sdk-go v1.36.5/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||
github.com/aws/aws-sdk-go v1.37.15 h1:W7l7gLLMcYRlg6a+uvf3Zz4jYwdqYzhe5ymqwWoOhp4=
|
||||
github.com/aws/aws-sdk-go v1.37.15/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||
@@ -155,21 +148,21 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D
|
||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-xdr v0.0.0-20161123171359-e6a2ba005892/go.mod h1:CTDl0pzVzE5DEzZhPfvhY/9sPFMQIxaJ9VAMs9AagrE=
|
||||
github.com/deepmap/oapi-codegen v1.3.11/go.mod h1:suMvK7+rKlx3+tpa8ByptmvoXbAV70wERKTOGH3hLp0=
|
||||
github.com/deepmap/oapi-codegen v1.5.1 h1:Xx8/OynzWhQeKFWr182hg6Ja2evS1gcqdja7qMn05yU=
|
||||
github.com/deepmap/oapi-codegen v1.5.1/go.mod h1:Eb1vtV3f58zvm37CJV4UAQ1bECb0fgAVvTdonC1ftJg=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/digitalocean/go-libvirt v0.0.0-20190626172931-4d226dd6c437 h1:phR13shVFOIpa1pnLBmewI9p16NEladLPvVylLPeexo=
|
||||
github.com/digitalocean/go-libvirt v0.0.0-20190626172931-4d226dd6c437/go.mod h1:PRcPVAAma6zcLpFd4GZrjR/MRpood3TamjKI2m/z/Uw=
|
||||
github.com/digitalocean/go-libvirt v0.0.0-20201209184759-e2a69bcd5bd1 h1:j6vGflaQ2T7yOWqVgPdiRF73j/U2Zmpbbzab8nyDCRQ=
|
||||
github.com/digitalocean/go-libvirt v0.0.0-20201209184759-e2a69bcd5bd1/go.mod h1:QS1XzqZLcDniNYrN7EZefq3wIyb/M2WmJbql4ZKoc1Q=
|
||||
github.com/digitalocean/go-libvirt v0.0.0-20210108193637-3a8ae49ba8cd h1:+96Lbk3f8glkOcsRdy3Nubga8pE40kor2OgxDzVGNZM=
|
||||
github.com/digitalocean/go-libvirt v0.0.0-20210108193637-3a8ae49ba8cd/go.mod h1:gtar3MgGsIO64GgphCHw1cbyxSI6qEuTIm9+izMmlfk=
|
||||
github.com/digitalocean/go-libvirt v0.0.0-20210112203132-25518eb2c840 h1:F3RVNV8SLLNhkNFcbDTgD3wAPMcrMJW6xjjI0JXy9z8=
|
||||
github.com/digitalocean/go-libvirt v0.0.0-20210112203132-25518eb2c840/go.mod h1:gtar3MgGsIO64GgphCHw1cbyxSI6qEuTIm9+izMmlfk=
|
||||
github.com/digitalocean/go-qemu v0.0.0-20181112162955-dd7bb9c771b8 h1:N7nH2py78LcMqYY3rZjjrsX6N7uCN7sjvaosgpXN9Ow=
|
||||
github.com/digitalocean/go-qemu v0.0.0-20181112162955-dd7bb9c771b8/go.mod h1:/YnlngP1PARC0SKAZx6kaAEMOp8bNTQGqS+Ka3MctNI=
|
||||
github.com/digitalocean/go-qemu v0.0.0-20201211181942-d361e7b4965f h1:BYkBJhHxUJJn27mhqfqWycWaEOWv9JQqLgQ2pOFJMqE=
|
||||
github.com/digitalocean/go-qemu v0.0.0-20201211181942-d361e7b4965f/go.mod h1:y4Eq3ZfZQFWQwVyW0qvgo5seXUIq2C7BlHsdE+xtXL4=
|
||||
@@ -187,29 +180,36 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/exoscale/egoscale v0.18.1 h1:1FNZVk8jHUx0AvWhOZxLEDNlacTU0chMXUUNkm9EZaI=
|
||||
github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSYdCF3rnBKXE=
|
||||
github.com/exoscale/egoscale v0.43.1 h1:Lhr0UOfg3t3Y56yh1DsYCjQuUHqFvsC8iUVqvub8+0Q=
|
||||
github.com/exoscale/egoscale v0.43.1/go.mod h1:mpEXBpROAa/2i5GC0r33rfxG+TxSEka11g1PIXt9+zc=
|
||||
github.com/exoscale/packer-plugin-exoscale v0.1.0 h1:p4ymqF1tNiTuxgSdnEjGqXehMdDQbV7BPaLsMxGav24=
|
||||
github.com/exoscale/packer-plugin-exoscale v0.1.0/go.mod h1:ZmJRkxsAlmEsVYOMxYPupDkax54uZ+ph0h3W59aIMZ8=
|
||||
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
|
||||
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/fatih/structtag v1.0.0 h1:pTHj65+u3RKWYPSGaU290FpI/dXxTaHdVwVwbcPKmEc=
|
||||
github.com/fatih/structtag v1.0.0/go.mod h1:IKitwq45uXL/yqi5mYghiD3w9H6eTOvI9vnk8tXMphA=
|
||||
github.com/getkin/kin-openapi v0.13.0/go.mod h1:WGRs2ZMM1Q8LR1QBEwUxC6RJEfaBcD0s+pcEVXFuAjw=
|
||||
github.com/getkin/kin-openapi v0.37.0/go.mod h1:ZJSfy1PxJv2QQvH9EdBj3nupRTVvV42mkW6zKUlRBwk=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-chi/chi v1.5.1/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k=
|
||||
github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-ini/ini v1.25.4 h1:Mujh4R/dH6YL8bxuISne3xX2+qcQ9p0IxKAP6ExWoUo=
|
||||
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
|
||||
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
|
||||
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
|
||||
github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY=
|
||||
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8=
|
||||
github.com/go-resty/resty/v2 v2.3.0 h1:JOOeAvjSlapTT92p8xiS19Zxev1neGikoHsXJeOq8So=
|
||||
github.com/go-resty/resty/v2 v2.3.0/go.mod h1:UpN9CgLZNsv4e9XG50UU8xdI0F43UQ4HmxLBDwaroHU=
|
||||
@@ -221,8 +221,9 @@ github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/gofrs/flock v0.7.3 h1:I0EKY9l8HZCXTMYC4F80vwT6KNypV9uYKP3Alm/hjmQ=
|
||||
github.com/gofrs/flock v0.7.3/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
|
||||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 h1:zN2lZNZRflqFyxVaTIU61KNKQ9C0055u9CAfpmqUvo4=
|
||||
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3/go.mod h1:nPpo7qLxd6XL3hWJG/O60sR8ZKfMCiIoNap5GvD12KU=
|
||||
@@ -255,6 +256,7 @@ github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
@@ -300,7 +302,6 @@ github.com/gophercloud/gophercloud v0.12.0 h1:mZrie07npp6ODiwHZolTicr5jV8Ogn43Av
|
||||
github.com/gophercloud/gophercloud v0.12.0/go.mod h1:gmC5oQqMDOMO1t1gq5DquX/yAU808e/4mzjjDA76+Ss=
|
||||
github.com/gophercloud/utils v0.0.0-20200508015959-b0167b94122c h1:iawx2ojEQA7c+GmkaVO5sN+k8YONibXyDO8RlsC+1bs=
|
||||
github.com/gophercloud/utils v0.0.0-20200508015959-b0167b94122c/go.mod h1:ehWUbLQJPqS0Ep+CxeD559hsm9pthPXadJNKwZkp43w=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
@@ -336,11 +337,9 @@ github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9
|
||||
github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v0.12.0 h1:d4QkX8FRTYaKaCZBoXYY8zJX2BXjWxurN/GA2tkrmZM=
|
||||
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-immutable-radix v1.1.0 h1:vN9wG1D6KG6YHRTWr8512cxGOVgTMEfgEdSj/hr8MPc=
|
||||
github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI=
|
||||
github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
@@ -353,7 +352,6 @@ github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn
|
||||
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.4 h1:1BZvpawXoJCWX6pNtow9+rpEj+3itIlutiqnntI6jOE=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/hashicorp/go-rootcerts v1.0.1 h1:DMo4fmknnz0E0evoNYnV48RjWndOsmd6OW+09R3cEP8=
|
||||
github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
@@ -373,7 +371,6 @@ github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+d
|
||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
|
||||
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
@@ -385,7 +382,6 @@ github.com/hashicorp/hcl/v2 v2.8.0/go.mod h1:bQTN5mpo+jewjJgh8jr0JUguIi7qPHUF6yI
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
|
||||
github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/memberlist v0.2.2 h1:5+RffWKwqJ71YPu9mWsF7ZOscZmwfasdA8kbdC7AO2g=
|
||||
github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
|
||||
@@ -397,23 +393,17 @@ github.com/hashicorp/packer v1.6.7-0.20210125170305-539638b0f951/go.mod h1:Z3eun
|
||||
github.com/hashicorp/packer v1.6.7-0.20210126105722-aef4ced967ec/go.mod h1:2+Vo/c/fA+TD9yFc/h9jQMFm4yG+IymQIr0OdJJOPiE=
|
||||
github.com/hashicorp/packer v1.6.7-0.20210208125835-f616955ebcb6/go.mod h1:7f5ZpTTRG53rQ58BcTADuTnpiBcB3wapuxl4sF2sGMM=
|
||||
github.com/hashicorp/packer v1.6.7-0.20210217093213-201869d627bf/go.mod h1:+EWPPcqee4h8S/y913Dnta1eJkgiqsGXBQgB75A2qV0=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.6 h1:BN2G4APXSMvDURFdnk+6DspwsU83pZeMsbEur7NmGsA=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.6/go.mod h1:Nvh28f+Jmpp2rcaN79bULTouNkGNDRfHckhHKTAXtyU=
|
||||
github.com/hashicorp/packer-plugin-docker v0.0.2 h1:j/hQTogaN2pZfZohlZTRu5YvNZg2/qtYYHkxPBxv2Oo=
|
||||
github.com/hashicorp/packer-plugin-docker v0.0.2/go.mod h1:A2p9qztS4n88KsNF+qBM7BWw2HndW636GpFIjNSvbKM=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.6/go.mod h1:Nvh28f+Jmpp2rcaN79bULTouNkGNDRfHckhHKTAXtyU=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.7-0.20210111224258-fd30ebb797f0/go.mod h1:YdWTt5w6cYfaQG7IOi5iorL+3SXnz8hI0gJCi8Db/LI=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.7-0.20210120105339-f6fd68d2570a h1:QbS+UBmK9DZuEDPodi1pCiS66dLYI3rmUX/cowNopsk=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.7-0.20210120105339-f6fd68d2570a/go.mod h1:exN0C+Pe+3zu18l4nxueNjX5cfmslxUX/m/xk4IVmZQ=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.7-0.20210122130548-45a6ca0a9365 h1:u7DeYY9ukhSZpLE11qCFU8pxnO+YM2/85wwVXHJZdRE=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.7-0.20210122130548-45a6ca0a9365/go.mod h1:K7VsU0lfJBDyiUrSNnS/j+zMxSRwwH9WC9QvHv32KsU=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.10-0.20210126105622-8e1648006d93 h1:3wFACjFiBkF5sZrai0zvcWv2fHIgLa4g6ZXxbqngBhs=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.10-0.20210126105622-8e1648006d93/go.mod h1:AtWQLNfpn7cgH2SmZ1PTedwqNOhiPvzcuKfH5sDvIQ0=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.11 h1:nUurEGaJtpVDyg94bKC2xOXWf1TqQ+++GovFUnP5REI=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.11/go.mod h1:GNb0WNs7zibb8vzUZce1As64z2AW0FEMwhe2J7/NW5I=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.12 h1:eagxsrborfhc0E8zvDVCiRE9pQkjBw/6jm23jp0D8RU=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.12/go.mod h1:hs82OYeufirGG6KRENMpjBWomnIlte99X6wXAPThJ5I=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.14 h1:42WOZLmIbAYYC1WXxtlrQZN+fFdysVvTmj3jtoI6gOU=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.14/go.mod h1:tNb3XzJPnjMl3QuUdKmF47B5ImerdTakalHzUAvW0aw=
|
||||
github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hashicorp/serf v0.9.2 h1:yJoyfZXo4Pk2p/M/viW+YLibBFiIbKoP79gu7kDAFP0=
|
||||
github.com/hashicorp/serf v0.9.2/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
|
||||
@@ -429,6 +419,9 @@ github.com/hetznercloud/hcloud-go v1.15.1/go.mod h1:8lR3yHBHZWy2uGcUi9Ibt4UOoop2
|
||||
github.com/hyperonecom/h1-client-go v0.0.0-20191203060043-b46280e4c4a4 h1:mSmyzhwBeQt2TlHbsXYLona9pwjWAvYGwQJ2Cq/k3VE=
|
||||
github.com/hyperonecom/h1-client-go v0.0.0-20191203060043-b46280e4c4a4/go.mod h1:yNUVHSleURKSaYUKq4Wx0i/vjCen2aq7CvPyHd/Vj2Q=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jarcoal/httpmock v1.0.6/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
||||
github.com/jarcoal/httpmock v1.0.8 h1:8kI16SoO6LQKgPE7PvQuV+YuD/inwHd7fOOe2zMbo4k=
|
||||
github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
||||
github.com/jdcloud-api/jdcloud-sdk-go v1.9.1-0.20190605102154-3d81a50ca961 h1:a2/K4HRhg31A5vafiz5yYiGMjaCxwRpyjJStfVquKds=
|
||||
github.com/jdcloud-api/jdcloud-sdk-go v1.9.1-0.20190605102154-3d81a50ca961/go.mod h1:UrKjuULIWLjHFlG6aSPunArE5QX57LftMmStAZJBEX8=
|
||||
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4=
|
||||
@@ -442,28 +435,22 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62 h1:JHCT6xuyPUrbbgAPE/3dqlvUKzRHMNuTBKKUb6OeR/k=
|
||||
github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA=
|
||||
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE=
|
||||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v0.0.0-20160131094358-f86d2e6d8a77 h1:rJnR80lkojFgjdg/oQPhbZoY8t8uM51XMz8DrJrjabk=
|
||||
github.com/klauspost/compress v0.0.0-20160131094358-f86d2e6d8a77/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.11.6 h1:EgWPCW6O3n1D5n99Zq3xXBt9uCwRGvpwGOusOLNBRSQ=
|
||||
github.com/klauspost/compress v1.11.6/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.11.7 h1:0hzRabrMN4tSTvMfnL3SCv1ZGeAP23ynzodBgaHeMeg=
|
||||
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/cpuid v0.0.0-20160106104451-349c67577817 h1:/7pPahIC+GoCm/euDCi2Pm29bAj9tc6TcK4Zcc8D3WI=
|
||||
github.com/klauspost/cpuid v0.0.0-20160106104451-349c67577817/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/crc32 v0.0.0-20160114101742-999f3125931f h1:UD9YLTi2aBhdOOThzatodQ/pGd9nd5255swS+UzHZj4=
|
||||
github.com/klauspost/crc32 v0.0.0-20160114101742-999f3125931f/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg=
|
||||
github.com/klauspost/crc32 v1.2.0 h1:0VuyqOCruD33/lJ/ojXNvzVyl8Zr5zdTmj9l9qLZ86I=
|
||||
github.com/klauspost/crc32 v1.2.0/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg=
|
||||
@@ -471,11 +458,9 @@ github.com/klauspost/pgzip v0.0.0-20151221113845-47f36e165cec h1:PYqF3Tiz2W2Ag0e
|
||||
github.com/klauspost/pgzip v0.0.0-20151221113845-47f36e165cec/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.0.0-20131111012553-2788f0dbd169 h1:YUrU1/jxRqnt0PSrKj1Uj/wEjk/fjnE80QFfi2Zlj7Q=
|
||||
github.com/kr/fs v0.0.0-20131111012553-2788f0dbd169/go.mod h1:glhvuHOU9Hy7/8PwwdtnarXqLagOX0b/TbZx2zLMqEg=
|
||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
@@ -484,37 +469,41 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4=
|
||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||
github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g=
|
||||
github.com/labstack/echo/v4 v4.1.17/go.mod h1:Tn2yRQL/UclUalpb5rPdXDevbkJ+lp/2svdyFBg6CHQ=
|
||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
||||
github.com/linode/linodego v0.14.0 h1:0APKMjiVGyry2TTUVDiok72H6cWpFNMMrFWBFn14aFU=
|
||||
github.com/linode/linodego v0.14.0/go.mod h1:2ce3S00NrDqJfp4i55ZuSlT0U3cKNELNYACWBPI8Tnw=
|
||||
github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9 h1:SmVbOZFWAlyQshuMfOkiAx1f5oUTsOGG5IXplAEYeeM=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc=
|
||||
github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 h1:2ZKn+w/BJeL43sCxI2jhPLRv73oVVOjEKZjKkflyqxg=
|
||||
github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc=
|
||||
github.com/masterzen/winrm v0.0.0-20200615185753-c42b5136ff88/go.mod h1:a2HXwefeat3evJHxFXSayvRHpYEPJYtErl4uIzfaUqY=
|
||||
github.com/masterzen/winrm v0.0.0-20201030141608-56ca5c5f2380 h1:uKhPH5dYpx3Z8ZAnaTGfGZUiHOWa5p5mdG8wZlh+tLo=
|
||||
github.com/masterzen/winrm v0.0.0-20201030141608-56ca5c5f2380/go.mod h1:a2HXwefeat3evJHxFXSayvRHpYEPJYtErl4uIzfaUqY=
|
||||
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
||||
github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
|
||||
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
|
||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-tty v0.0.0-20191112051231-74040eebce08 h1:8YAWbq7rJqfbc6IaAvA2eCQuOQvf6Bs4vHKcOyWw//E=
|
||||
github.com/mattn/go-tty v0.0.0-20191112051231-74040eebce08/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.26 h1:gPxPSwALAeHJSjarOs00QjVdV9QoBvc1D2ujQUr5BzU=
|
||||
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
@@ -528,7 +517,6 @@ github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-testing-interface v1.0.3 h1:gqwbsGvc0jbhAPW/26WfEoSiPANAVlR49AAVdvaTjI4=
|
||||
github.com/mitchellh/go-testing-interface v1.0.3/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
@@ -538,7 +526,6 @@ github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZX
|
||||
github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/gox v1.0.1 h1:x0jD3dcHk9a9xPSDN6YEL4xL6Qz0dvNYm8yZqui5chI=
|
||||
github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4=
|
||||
github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
@@ -553,11 +540,9 @@ github.com/mitchellh/prefixedio v0.0.0-20151214002211-6e6954073784 h1:+DAetXqxv/
|
||||
github.com/mitchellh/prefixedio v0.0.0-20151214002211-6e6954073784/go.mod h1:kB1naBgV9ORnkiTVeyJOI1DavaJkG4oNIq0Af6ZVKUo=
|
||||
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
|
||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
@@ -567,7 +552,6 @@ github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQ
|
||||
github.com/olekukonko/tablewriter v0.0.0-20180105111133-96aac992fc8b h1:LGItPaClbzopugAomw5VFKnG3h1dUr9QW5KOU+m8gu0=
|
||||
github.com/olekukonko/tablewriter v0.0.0-20180105111133-96aac992fc8b/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/oracle/oci-go-sdk v18.0.0+incompatible h1:FLV4KixsVfF3rwyVTMI6Ryp/Q+OSb9sR5TawbfjFLN4=
|
||||
github.com/oracle/oci-go-sdk v18.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
|
||||
github.com/oracle/oci-go-sdk v24.3.0+incompatible h1:x4mcfb4agelf1O4/1/auGlZ1lr97jXRSSN5MxTgG/zU=
|
||||
github.com/oracle/oci-go-sdk v24.3.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
|
||||
@@ -601,6 +585,7 @@ github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R
|
||||
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
||||
@@ -613,34 +598,32 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUt
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shirou/gopsutil v2.18.12+incompatible h1:1eaJvGomDnH74/5cF4CTmTbLHAriGFsTZppLXDX93OM=
|
||||
github.com/shirou/gopsutil v2.18.12+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shirou/gopsutil v3.21.1+incompatible h1:2LwXWdbjXwyDgq26Yy/OT4xozlpmssQfy/rtfhWb0bY=
|
||||
github.com/shirou/gopsutil v3.21.1+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 h1:udFKJ0aHUL60LboW/A+DfgoHVedieIzIXE8uylPue0U=
|
||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
|
||||
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w=
|
||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As=
|
||||
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go v3.0.222+incompatible h1:bs+0lcG4RELNbE8PsBC9oaPP0/qExr0DuEGnZyocm84=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go v3.0.222+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
@@ -648,7 +631,6 @@ github.com/ucloud/ucloud-sdk-go v0.16.3 h1:DCh4A5vSxFr3EvtvJL+g0Ehy4hSlEkMpQmEvx
|
||||
github.com/ucloud/ucloud-sdk-go v0.16.3/go.mod h1:dyLmFHmUfgb4RZKYQP9IArlvQ2pxzFthfhwxRzOEPIw=
|
||||
github.com/ufilesdk-dev/ufile-gosdk v0.0.0-20190830075812-b4dbc4ef43a6 h1:FAWNiqocJ04wC4Znj7Ax4PGWstZijayO6ifuHHvb+vI=
|
||||
github.com/ufilesdk-dev/ufile-gosdk v0.0.0-20190830075812-b4dbc4ef43a6/go.mod h1:R5FMQxkQ+QK/9Vz+jfnJP4rZIktYrRcWmuAnbOSkROI=
|
||||
github.com/ugorji/go v0.0.0-20151218193438-646ae4a518c1 h1:U6ufy3mLDgg9RYupntOvAF7xCmNNquyKaYaaVHo1Nnk=
|
||||
github.com/ugorji/go v0.0.0-20151218193438-646ae4a518c1/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
|
||||
github.com/ugorji/go v1.2.4 h1:cTciPbZ/VSOzCLKclmssnfQ/jyoVyOcJ3aoJyUV1Urc=
|
||||
github.com/ugorji/go v1.2.4/go.mod h1:EuaSCk8iZMdIspsu6HXH7X2UGKw1ezO4wCfGszGmmo4=
|
||||
@@ -656,6 +638,10 @@ github.com/ugorji/go/codec v1.2.4 h1:C5VurWRRCKjuENsbM6GYVw8W++WVW9rSxoACKIvxzz8
|
||||
github.com/ugorji/go/codec v1.2.4/go.mod h1:bWBu1+kIRWcF8uMklKaJrR6fTWQOwAlrIzX22pHwryA=
|
||||
github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok=
|
||||
github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||
github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
||||
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
|
||||
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
|
||||
@@ -695,14 +681,18 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200422194213-44a606286825/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9 h1:sYNJzB4J8toYPQTM6pAkcmBRgw9SnQKP9oXCHfgy604=
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -756,6 +746,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@@ -775,8 +766,9 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R
|
||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11 h1:lwlPPsmjDKK0J6eG6xDWd5XPehI0R024zxjDnw3esPA=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -812,12 +804,14 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -836,11 +830,13 @@ golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
@@ -849,8 +845,9 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
||||
@@ -972,7 +969,6 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200831141814-d751682dd103/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200914193844-75d14daec038 h1:SnvTpXhVDJGFxzZiHbMUZTh3VjU2Vx2feJ7Zfl5+OIY=
|
||||
google.golang.org/genproto v0.0.0-20200914193844-75d14daec038/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200918140846-d0d605568037 h1:ujwz1DPMeHwCvo36rK5shXhAzc4GMRecrqQFaMZJBKQ=
|
||||
google.golang.org/genproto v0.0.0-20200918140846-d0d605568037/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
@@ -1012,7 +1008,6 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.27 h1:kJdccidYzt3CaHD1crCFTS1hxyhSi059NhOFUf03YFo=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=
|
||||
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
|
||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
@@ -1024,12 +1019,12 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
||||
+64
-39
@@ -7,6 +7,8 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/hclparse"
|
||||
@@ -14,9 +16,9 @@ import (
|
||||
)
|
||||
|
||||
type HCL2Formatter struct {
|
||||
ShowDiff, Write bool
|
||||
Output io.Writer
|
||||
parser *hclparse.Parser
|
||||
ShowDiff, Write, Recursive bool
|
||||
Output io.Writer
|
||||
parser *hclparse.Parser
|
||||
}
|
||||
|
||||
// NewHCL2Formatter creates a new formatter, ready to format configuration files.
|
||||
@@ -26,55 +28,77 @@ func NewHCL2Formatter() *HCL2Formatter {
|
||||
}
|
||||
}
|
||||
|
||||
func isHcl2FileOrVarFile(path string) bool {
|
||||
if strings.HasSuffix(path, hcl2FileExt) || strings.HasSuffix(path, hcl2VarFileExt) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *HCL2Formatter) formatFile(path string, diags hcl.Diagnostics, bytesModified int) (int, hcl.Diagnostics) {
|
||||
data, err := f.processFile(path)
|
||||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: fmt.Sprintf("encountered an error while formatting %s", path),
|
||||
Detail: err.Error(),
|
||||
})
|
||||
}
|
||||
bytesModified += len(data)
|
||||
return bytesModified, diags
|
||||
}
|
||||
|
||||
// Format all HCL2 files in path and return the total bytes formatted.
|
||||
// If any error is encountered, zero bytes will be returned.
|
||||
//
|
||||
// Path can be a directory or a file.
|
||||
func (f *HCL2Formatter) Format(path string) (int, hcl.Diagnostics) {
|
||||
var diags hcl.Diagnostics
|
||||
var bytesModified int
|
||||
|
||||
var allHclFiles []string
|
||||
var diags []*hcl.Diagnostic
|
||||
|
||||
if path == "-" {
|
||||
allHclFiles = []string{"-"}
|
||||
} else {
|
||||
hclFiles, _, diags := GetHCL2Files(path, hcl2FileExt, hcl2JsonFileExt)
|
||||
if diags.HasErrors() {
|
||||
return 0, diags
|
||||
}
|
||||
|
||||
hclVarFiles, _, diags := GetHCL2Files(path, hcl2VarFileExt, hcl2VarJsonFileExt)
|
||||
if diags.HasErrors() {
|
||||
return 0, diags
|
||||
}
|
||||
|
||||
allHclFiles = append(hclFiles, hclVarFiles...)
|
||||
|
||||
if len(allHclFiles) == 0 {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: fmt.Sprintf("Cannot tell whether %s contains HCL2 configuration data", path),
|
||||
})
|
||||
|
||||
return 0, diags
|
||||
}
|
||||
if path == "" {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "path is empty, cannot format",
|
||||
Detail: "path is empty, cannot format",
|
||||
})
|
||||
return bytesModified, diags
|
||||
}
|
||||
|
||||
if f.parser == nil {
|
||||
f.parser = hclparse.NewParser()
|
||||
}
|
||||
|
||||
var bytesModified int
|
||||
for _, fn := range allHclFiles {
|
||||
data, err := f.processFile(fn)
|
||||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: fmt.Sprintf("encountered an error while formatting %s", fn),
|
||||
Detail: err.Error(),
|
||||
})
|
||||
if s, err := os.Stat(path); err != nil || !s.IsDir() {
|
||||
return f.formatFile(path, diags, bytesModified)
|
||||
}
|
||||
|
||||
fileInfos, err := ioutil.ReadDir(path)
|
||||
if err != nil {
|
||||
diag := &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Cannot read hcl directory",
|
||||
Detail: err.Error(),
|
||||
}
|
||||
diags = append(diags, diag)
|
||||
return bytesModified, diags
|
||||
}
|
||||
|
||||
for _, fileInfo := range fileInfos {
|
||||
filename := filepath.Join(path, fileInfo.Name())
|
||||
if fileInfo.IsDir() {
|
||||
if f.Recursive {
|
||||
var tempDiags hcl.Diagnostics
|
||||
var tempBytesModified int
|
||||
tempBytesModified, tempDiags = f.Format(filename)
|
||||
bytesModified += tempBytesModified
|
||||
diags = diags.Extend(tempDiags)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if isHcl2FileOrVarFile(filename) {
|
||||
bytesModified, diags = f.formatFile(filename, diags, bytesModified)
|
||||
}
|
||||
bytesModified += len(data)
|
||||
}
|
||||
|
||||
return bytesModified, diags
|
||||
@@ -84,6 +108,7 @@ func (f *HCL2Formatter) Format(path string) (int, hcl.Diagnostics) {
|
||||
// overwriting the contents of the original when the f.Write is true; a diff of the changes
|
||||
// will be outputted if f.ShowDiff is true.
|
||||
func (f *HCL2Formatter) processFile(filename string) ([]byte, error) {
|
||||
|
||||
if f.Output == nil {
|
||||
f.Output = os.Stdout
|
||||
}
|
||||
|
||||
@@ -32,11 +32,9 @@ func TestHCL2Formatter_Format(t *testing.T) {
|
||||
if diags.HasErrors() {
|
||||
t.Fatalf("the call to Format failed unexpectedly %s", diags.Error())
|
||||
}
|
||||
|
||||
if buf.String() != "" && tc.FormatExpected == false {
|
||||
t.Errorf("Format(%q) should contain the name of the formatted file(s), but got %q", tc.Path, buf.String())
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -90,6 +90,11 @@ func (p *Parser) Parse(filename string, varFiles []string, argVars map[string]st
|
||||
if filename != "" {
|
||||
hclFiles, jsonFiles, moreDiags := GetHCL2Files(filename, hcl2FileExt, hcl2JsonFileExt)
|
||||
diags = append(diags, moreDiags...)
|
||||
if moreDiags.HasErrors() {
|
||||
// here this probably means that the file was not found, let's
|
||||
// simply leave early.
|
||||
return nil, diags
|
||||
}
|
||||
if len(hclFiles)+len(jsonFiles) == 0 {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
|
||||
@@ -57,7 +57,6 @@ func GetHCL2Files(filename, hclSuffix, jsonSuffix string) (hclFiles, jsonFiles [
|
||||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Cannot tell wether " + filename + " is a directory",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return nil, nil, diags
|
||||
|
||||
@@ -152,7 +152,7 @@ func wrappedMain() int {
|
||||
// passed into commands like `packer build`
|
||||
config, err := loadConfig()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error loading configuration: \n\n%s\n", err)
|
||||
fmt.Fprintf(os.Stdout, "%s Error loading configuration: \n\n%s\n", ErrorPrefix, err)
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ func wrappedMain() int {
|
||||
|
||||
cacheDir, err := packersdk.CachePath()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error preparing cache directory: \n\n%s\n", err)
|
||||
fmt.Fprintf(os.Stdout, "%s Error preparing cache directory: \n\n%s\n", ErrorPrefix, err)
|
||||
return 1
|
||||
}
|
||||
log.Printf("[INFO] Setting cache directory: %s", cacheDir)
|
||||
@@ -187,7 +187,7 @@ func wrappedMain() int {
|
||||
// Set this so that we don't get colored output in our machine-
|
||||
// readable UI.
|
||||
if err := os.Setenv("PACKER_NO_COLOR", "1"); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Packer failed to initialize UI: %s\n", err)
|
||||
fmt.Fprintf(os.Stdout, "%s Packer failed to initialize UI: %s\n", ErrorPrefix, err)
|
||||
return 1
|
||||
}
|
||||
} else {
|
||||
@@ -202,13 +202,13 @@ func wrappedMain() int {
|
||||
currentPID := os.Getpid()
|
||||
backgrounded, err := checkProcess(currentPID)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "cannot determine if process is in "+
|
||||
"background: %s\n", err)
|
||||
fmt.Fprintf(os.Stdout, "%s cannot determine if process is in "+
|
||||
"background: %s\n", ErrorPrefix, err)
|
||||
}
|
||||
if backgrounded {
|
||||
fmt.Fprint(os.Stderr, "Running in background, not using a TTY\n")
|
||||
fmt.Fprintf(os.Stdout, "%s Running in background, not using a TTY\n", ErrorPrefix)
|
||||
} else if TTY, err := openTTY(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "No tty available: %s\n", err)
|
||||
fmt.Fprintf(os.Stdout, "%s No tty available: %s\n", ErrorPrefix, err)
|
||||
} else {
|
||||
basicUi.TTY = TTY
|
||||
basicUi.PB = &packer.UiProgressBar{}
|
||||
@@ -246,7 +246,7 @@ func wrappedMain() int {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error executing CLI: %s\n", err)
|
||||
fmt.Fprintf(os.Stdout, "%s Error executing CLI: %s\n", ErrorPrefix, err)
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
@@ -305,12 +305,12 @@ func (e ChecksumFileEntry) Arch() string { return e.arch }
|
||||
//
|
||||
func (e *ChecksumFileEntry) init(req *Requirement) (err error) {
|
||||
filename := e.Filename
|
||||
res := strings.TrimLeft(filename, req.FilenamePrefix())
|
||||
res := strings.TrimPrefix(filename, req.FilenamePrefix())
|
||||
// res now looks like v0.2.12_x5.0_freebsd_amd64.zip
|
||||
|
||||
e.ext = filepath.Ext(res)
|
||||
|
||||
res = strings.TrimRight(res, e.ext)
|
||||
res = strings.TrimSuffix(res, e.ext)
|
||||
// res now looks like v0.2.12_x5.0_freebsd_amd64
|
||||
|
||||
parts := strings.Split(res, "_")
|
||||
@@ -326,7 +326,7 @@ func (e *ChecksumFileEntry) init(req *Requirement) (err error) {
|
||||
|
||||
func (e *ChecksumFileEntry) validate(expectedVersion string, installOpts BinaryInstallationOptions) error {
|
||||
if e.binVersion != expectedVersion {
|
||||
return fmt.Errorf("wrong version, expected %s ", expectedVersion)
|
||||
return fmt.Errorf("wrong version: '%s' does not match expected %s ", e.binVersion, expectedVersion)
|
||||
}
|
||||
if e.os != installOpts.OS || e.arch != installOpts.ARCH {
|
||||
return fmt.Errorf("wrong system, expected %s_%s ", installOpts.OS, installOpts.ARCH)
|
||||
|
||||
@@ -27,6 +27,32 @@ var (
|
||||
pluginFolderWrongChecksums = filepath.Join("testdata", "wrong_checksums")
|
||||
)
|
||||
|
||||
func TestChecksumFileEntry_init(t *testing.T) {
|
||||
expectedVersion := "v0.3.0"
|
||||
req := &Requirement{
|
||||
Identifier: &addrs.Plugin{
|
||||
Hostname: "github.com",
|
||||
Namespace: "ddelnano",
|
||||
Type: "xenserver",
|
||||
},
|
||||
}
|
||||
|
||||
checkSum := &ChecksumFileEntry{
|
||||
Filename: fmt.Sprintf("packer-plugin-xenserver_%s_x5.0_darwin_amd64.zip", expectedVersion),
|
||||
Checksum: "0f5969b069b9c0a58f2d5786c422341c70dfe17bd68f896fcbd46677e8c913f1",
|
||||
}
|
||||
|
||||
err := checkSum.init(req)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("ChecksumFileEntry.init failure: %v", err)
|
||||
}
|
||||
|
||||
if checkSum.binVersion != expectedVersion {
|
||||
t.Errorf("failed to parse ChecksumFileEntry properly expected version '%s' but found '%s'", expectedVersion, checkSum.binVersion)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlugin_ListInstallations(t *testing.T) {
|
||||
|
||||
type fields struct {
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
package dockerimport
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
func TestPostProcessor_ImplementsPostProcessor(t *testing.T) {
|
||||
var _ packersdk.PostProcessor = new(PostProcessor)
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package version
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/packer-plugin-sdk/version"
|
||||
packerVersion "github.com/hashicorp/packer/version"
|
||||
)
|
||||
|
||||
var DockerImportPluginVersion *version.PluginVersion
|
||||
|
||||
func init() {
|
||||
DockerImportPluginVersion = version.InitializePluginVersion(
|
||||
packerVersion.Version, packerVersion.VersionPrerelease)
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
package dockerpush
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer/builder/docker"
|
||||
dockerimport "github.com/hashicorp/packer/post-processor/docker-import"
|
||||
)
|
||||
|
||||
func testUi() *packersdk.BasicUi {
|
||||
return &packersdk.BasicUi{
|
||||
Reader: new(bytes.Buffer),
|
||||
Writer: new(bytes.Buffer),
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostProcessor_ImplementsPostProcessor(t *testing.T) {
|
||||
var _ packersdk.PostProcessor = new(PostProcessor)
|
||||
}
|
||||
|
||||
func TestPostProcessor_PostProcess(t *testing.T) {
|
||||
driver := &docker.MockDriver{}
|
||||
p := &PostProcessor{Driver: driver}
|
||||
artifact := &packersdk.MockArtifact{
|
||||
BuilderIdValue: dockerimport.BuilderId,
|
||||
IdValue: "foo/bar",
|
||||
}
|
||||
|
||||
result, keep, forceOverride, err := p.PostProcess(context.Background(), testUi(), artifact)
|
||||
if _, ok := result.(packersdk.Artifact); !ok {
|
||||
t.Fatal("should be instance of Artifact")
|
||||
}
|
||||
if !keep {
|
||||
t.Fatal("should keep")
|
||||
}
|
||||
if forceOverride {
|
||||
t.Fatal("Should default to keep, but not override user wishes")
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if !driver.PushCalled {
|
||||
t.Fatal("should call push")
|
||||
}
|
||||
if driver.PushName != "foo/bar" {
|
||||
t.Fatal("bad name")
|
||||
}
|
||||
if result.Id() != "foo/bar" {
|
||||
t.Fatal("bad image id")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostProcessor_PostProcess_portInName(t *testing.T) {
|
||||
driver := &docker.MockDriver{}
|
||||
p := &PostProcessor{Driver: driver}
|
||||
artifact := &packersdk.MockArtifact{
|
||||
BuilderIdValue: dockerimport.BuilderId,
|
||||
IdValue: "localhost:5000/foo/bar",
|
||||
}
|
||||
|
||||
result, keep, forceOverride, err := p.PostProcess(context.Background(), testUi(), artifact)
|
||||
if _, ok := result.(packersdk.Artifact); !ok {
|
||||
t.Fatal("should be instance of Artifact")
|
||||
}
|
||||
if !keep {
|
||||
t.Fatal("should keep")
|
||||
}
|
||||
if forceOverride {
|
||||
t.Fatal("Should default to keep, but not override user wishes")
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if !driver.PushCalled {
|
||||
t.Fatal("should call push")
|
||||
}
|
||||
if driver.PushName != "localhost:5000/foo/bar" {
|
||||
t.Fatal("bad name")
|
||||
}
|
||||
if result.Id() != "localhost:5000/foo/bar" {
|
||||
t.Fatal("bad image id")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostProcessor_PostProcess_tags(t *testing.T) {
|
||||
driver := &docker.MockDriver{}
|
||||
p := &PostProcessor{Driver: driver}
|
||||
artifact := &packersdk.MockArtifact{
|
||||
BuilderIdValue: dockerimport.BuilderId,
|
||||
IdValue: "hashicorp/ubuntu:precise",
|
||||
}
|
||||
|
||||
result, keep, forceOverride, err := p.PostProcess(context.Background(), testUi(), artifact)
|
||||
if _, ok := result.(packersdk.Artifact); !ok {
|
||||
t.Fatal("should be instance of Artifact")
|
||||
}
|
||||
if !keep {
|
||||
t.Fatal("should keep")
|
||||
}
|
||||
if forceOverride {
|
||||
t.Fatal("Should default to keep, but not override user wishes")
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if !driver.PushCalled {
|
||||
t.Fatal("should call push")
|
||||
}
|
||||
if driver.PushName != "hashicorp/ubuntu:precise" {
|
||||
t.Fatalf("bad name: %s", driver.PushName)
|
||||
}
|
||||
if result.Id() != "hashicorp/ubuntu:precise" {
|
||||
t.Fatal("bad image id")
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package version
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/packer-plugin-sdk/version"
|
||||
packerVersion "github.com/hashicorp/packer/version"
|
||||
)
|
||||
|
||||
var DockerPushPluginVersion *version.PluginVersion
|
||||
|
||||
func init() {
|
||||
DockerPushPluginVersion = version.InitializePluginVersion(
|
||||
packerVersion.Version, packerVersion.VersionPrerelease)
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package dockersave
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
func TestPostProcessor_ImplementsPostProcessor(t *testing.T) {
|
||||
var _ packersdk.PostProcessor = new(PostProcessor)
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package version
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/packer-plugin-sdk/version"
|
||||
packerVersion "github.com/hashicorp/packer/version"
|
||||
)
|
||||
|
||||
var DockerSavePluginVersion *version.PluginVersion
|
||||
|
||||
func init() {
|
||||
DockerSavePluginVersion = version.InitializePluginVersion(
|
||||
packerVersion.Version, packerVersion.VersionPrerelease)
|
||||
}
|
||||
@@ -1,196 +0,0 @@
|
||||
package dockertag
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer/builder/docker"
|
||||
dockerimport "github.com/hashicorp/packer/post-processor/docker-import"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func testConfig() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"repository": "foo",
|
||||
"tag": "bar,buzz",
|
||||
}
|
||||
}
|
||||
|
||||
func testPP(t *testing.T) *PostProcessor {
|
||||
var p PostProcessor
|
||||
if err := p.Configure(testConfig()); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
return &p
|
||||
}
|
||||
|
||||
func testUi() *packersdk.BasicUi {
|
||||
return &packersdk.BasicUi{
|
||||
Reader: new(bytes.Buffer),
|
||||
Writer: new(bytes.Buffer),
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostProcessor_ImplementsPostProcessor(t *testing.T) {
|
||||
var _ packersdk.PostProcessor = new(PostProcessor)
|
||||
}
|
||||
|
||||
func TestPostProcessor_PostProcess(t *testing.T) {
|
||||
driver := &docker.MockDriver{}
|
||||
p := &PostProcessor{Driver: driver}
|
||||
if err := p.Configure(testConfig()); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
artifact := &packersdk.MockArtifact{
|
||||
BuilderIdValue: dockerimport.BuilderId,
|
||||
IdValue: "1234567890abcdef",
|
||||
}
|
||||
|
||||
result, keep, forceOverride, err := p.PostProcess(context.Background(), testUi(), artifact)
|
||||
if _, ok := result.(packersdk.Artifact); !ok {
|
||||
t.Fatal("should be instance of Artifact")
|
||||
}
|
||||
if !keep {
|
||||
t.Fatal("should keep")
|
||||
}
|
||||
if !forceOverride {
|
||||
t.Fatal("Should force keep no matter what user sets.")
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if driver.TagImageCalled != 2 {
|
||||
t.Fatal("should call TagImage")
|
||||
}
|
||||
if driver.TagImageImageId != "1234567890abcdef" {
|
||||
t.Fatal("bad image id")
|
||||
}
|
||||
|
||||
if driver.TagImageRepo[0] != "foo:bar" {
|
||||
t.Fatal("bad repo")
|
||||
}
|
||||
|
||||
if driver.TagImageRepo[1] != "foo:buzz" {
|
||||
t.Fatal("bad repo")
|
||||
}
|
||||
|
||||
if driver.TagImageForce {
|
||||
t.Fatal("bad force. force=false in default")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostProcessor_PostProcess_Force(t *testing.T) {
|
||||
driver := &docker.MockDriver{}
|
||||
p := &PostProcessor{Driver: driver}
|
||||
c := testConfig()
|
||||
c["force"] = true
|
||||
if err := p.Configure(c); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
artifact := &packersdk.MockArtifact{
|
||||
BuilderIdValue: dockerimport.BuilderId,
|
||||
IdValue: "1234567890abcdef",
|
||||
}
|
||||
|
||||
result, keep, forceOverride, err := p.PostProcess(context.Background(), testUi(), artifact)
|
||||
if _, ok := result.(packersdk.Artifact); !ok {
|
||||
t.Fatal("should be instance of Artifact")
|
||||
}
|
||||
if !keep {
|
||||
t.Fatal("should keep")
|
||||
}
|
||||
if !forceOverride {
|
||||
t.Fatal("Should force keep no matter what user sets.")
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if driver.TagImageCalled != 2 {
|
||||
t.Fatal("should call TagImage")
|
||||
}
|
||||
if driver.TagImageImageId != "1234567890abcdef" {
|
||||
t.Fatal("bad image id")
|
||||
}
|
||||
if driver.TagImageRepo[0] != "foo:bar" {
|
||||
t.Fatal("bad repo")
|
||||
}
|
||||
if driver.TagImageRepo[1] != "foo:buzz" {
|
||||
t.Fatal("bad repo")
|
||||
}
|
||||
if !driver.TagImageForce {
|
||||
t.Fatal("bad force")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostProcessor_PostProcess_NoTag(t *testing.T) {
|
||||
driver := &docker.MockDriver{}
|
||||
p := &PostProcessor{Driver: driver}
|
||||
c := testConfig()
|
||||
delete(c, "tag")
|
||||
if err := p.Configure(c); err != nil {
|
||||
t.Fatalf("err %s", err)
|
||||
}
|
||||
|
||||
artifact := &packersdk.MockArtifact{BuilderIdValue: dockerimport.BuilderId, IdValue: "1234567890abcdef"}
|
||||
|
||||
result, keep, forceOverride, err := p.PostProcess(context.Background(), testUi(), artifact)
|
||||
if _, ok := result.(packersdk.Artifact); !ok {
|
||||
t.Fatal("should be instance of Artifact")
|
||||
}
|
||||
if !keep {
|
||||
t.Fatal("should keep")
|
||||
}
|
||||
if !forceOverride {
|
||||
t.Fatal("Should force keep no matter what user sets.")
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if driver.TagImageCalled != 1 {
|
||||
t.Fatal("should call TagImage")
|
||||
}
|
||||
if driver.TagImageImageId != "1234567890abcdef" {
|
||||
t.Fatal("bad image id")
|
||||
}
|
||||
if driver.TagImageRepo[0] != "foo" {
|
||||
t.Fatal("bad repo")
|
||||
}
|
||||
if driver.TagImageForce {
|
||||
t.Fatal("bad force")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostProcessor_PostProcess_Tag_vs_Tags(t *testing.T) {
|
||||
testCases := []map[string]interface{}{
|
||||
{
|
||||
"tag": "bar,buzz",
|
||||
"tags": []string{"bang"},
|
||||
},
|
||||
{
|
||||
"tag": []string{"bar", "buzz"},
|
||||
"tags": []string{"bang"},
|
||||
},
|
||||
{
|
||||
"tag": []string{"bar"},
|
||||
"tags": []string{"buzz", "bang"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
var p PostProcessor
|
||||
if err := p.Configure(tc); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
assert.ElementsMatchf(t, p.config.Tags, []string{"bar", "buzz", "bang"},
|
||||
"tag and tags fields should be combined into tags fields. Recieved: %#v",
|
||||
p.config.Tags)
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package version
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/packer-plugin-sdk/version"
|
||||
packerVersion "github.com/hashicorp/packer/version"
|
||||
)
|
||||
|
||||
var DockerTagPluginVersion *version.PluginVersion
|
||||
|
||||
func init() {
|
||||
DockerTagPluginVersion = version.InitializePluginVersion(
|
||||
packerVersion.Version, packerVersion.VersionPrerelease)
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package exoscaleimport
|
||||
|
||||
const BuilderId = "packer.post-processor.exoscale-import"
|
||||
|
||||
type Artifact struct {
|
||||
id string
|
||||
}
|
||||
|
||||
func (a *Artifact) BuilderId() string {
|
||||
return BuilderId
|
||||
}
|
||||
|
||||
func (a *Artifact) Id() string {
|
||||
return a.id
|
||||
}
|
||||
|
||||
func (a *Artifact) Files() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Artifact) String() string {
|
||||
return a.id
|
||||
}
|
||||
|
||||
func (a *Artifact) State(name string) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Artifact) Destroy() error {
|
||||
return nil
|
||||
}
|
||||
@@ -1,270 +0,0 @@
|
||||
//go:generate mapstructure-to-hcl2 -type Config
|
||||
|
||||
package exoscaleimport
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
"github.com/aws/aws-sdk-go/service/s3/s3manager"
|
||||
"github.com/exoscale/egoscale"
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/hashicorp/packer-plugin-sdk/common"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/config"
|
||||
"github.com/hashicorp/packer/builder/file"
|
||||
"github.com/hashicorp/packer/builder/qemu"
|
||||
"github.com/hashicorp/packer/post-processor/artifice"
|
||||
"github.com/hashicorp/packer/post-processor/exoscale-import/version"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultTemplateZone = "ch-gva-2"
|
||||
defaultAPIEndpoint = "https://api.exoscale.com/compute"
|
||||
defaultSOSEndpoint = "https://sos-" + defaultTemplateZone + ".exo.io"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
SkipClean bool `mapstructure:"skip_clean"`
|
||||
|
||||
SOSEndpoint string `mapstructure:"sos_endpoint"`
|
||||
APIEndpoint string `mapstructure:"api_endpoint"`
|
||||
APIKey string `mapstructure:"api_key"`
|
||||
APISecret string `mapstructure:"api_secret"`
|
||||
ImageBucket string `mapstructure:"image_bucket"`
|
||||
TemplateZone string `mapstructure:"template_zone"`
|
||||
TemplateName string `mapstructure:"template_name"`
|
||||
TemplateDescription string `mapstructure:"template_description"`
|
||||
TemplateUsername string `mapstructure:"template_username"`
|
||||
TemplateDisablePassword bool `mapstructure:"template_disable_password"`
|
||||
TemplateDisableSSHKey bool `mapstructure:"template_disable_sshkey"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
egoscale.UserAgent = "Packer-Exoscale/" + version.ExoscaleImportPluginVersion.FormattedVersion() + " " + egoscale.UserAgent
|
||||
}
|
||||
|
||||
type PostProcessor struct {
|
||||
config Config
|
||||
}
|
||||
|
||||
func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() }
|
||||
|
||||
func (p *PostProcessor) Configure(raws ...interface{}) error {
|
||||
p.config.TemplateZone = defaultTemplateZone
|
||||
p.config.APIEndpoint = defaultAPIEndpoint
|
||||
p.config.SOSEndpoint = defaultSOSEndpoint
|
||||
|
||||
if err := config.Decode(&p.config, nil, raws...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.config.APIKey == "" {
|
||||
p.config.APIKey = os.Getenv("EXOSCALE_API_KEY")
|
||||
}
|
||||
|
||||
if p.config.APISecret == "" {
|
||||
p.config.APISecret = os.Getenv("EXOSCALE_API_SECRET")
|
||||
}
|
||||
|
||||
requiredArgs := map[string]*string{
|
||||
"api_key": &p.config.APIKey,
|
||||
"api_secret": &p.config.APISecret,
|
||||
"api_endpoint": &p.config.APIEndpoint,
|
||||
"sos_endpoint": &p.config.SOSEndpoint,
|
||||
"image_bucket": &p.config.ImageBucket,
|
||||
"template_zone": &p.config.TemplateZone,
|
||||
"template_name": &p.config.TemplateName,
|
||||
"template_description": &p.config.TemplateDescription,
|
||||
}
|
||||
|
||||
errs := new(packersdk.MultiError)
|
||||
for k, v := range requiredArgs {
|
||||
if *v == "" {
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, fmt.Errorf("%s must be set", k))
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs.Errors) > 0 {
|
||||
return errs
|
||||
}
|
||||
|
||||
packersdk.LogSecretFilter.Set(p.config.APIKey, p.config.APISecret)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *PostProcessor) PostProcess(ctx context.Context, ui packersdk.Ui, a packersdk.Artifact) (packersdk.Artifact, bool, bool, error) {
|
||||
switch a.BuilderId() {
|
||||
case qemu.BuilderId, file.BuilderId, artifice.BuilderId:
|
||||
break
|
||||
|
||||
default:
|
||||
err := fmt.Errorf(
|
||||
"Unknown artifact type: %s\nCan only import from QEMU/file builders and Artifice post-processor artifacts.",
|
||||
a.BuilderId())
|
||||
return nil, false, false, err
|
||||
}
|
||||
|
||||
ui.Message("Uploading template image")
|
||||
url, md5sum, err := p.uploadImage(ctx, ui, a)
|
||||
if err != nil {
|
||||
return nil, false, false, fmt.Errorf("unable to upload image: %s", err)
|
||||
}
|
||||
|
||||
ui.Message("Registering template")
|
||||
id, err := p.registerTemplate(ctx, ui, url, md5sum)
|
||||
if err != nil {
|
||||
return nil, false, false, fmt.Errorf("unable to register template: %s", err)
|
||||
}
|
||||
|
||||
if !p.config.SkipClean {
|
||||
ui.Message("Deleting uploaded template image")
|
||||
if err = p.deleteImage(ctx, ui, a); err != nil {
|
||||
return nil, false, false, fmt.Errorf("unable to delete uploaded template image: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return &Artifact{id}, false, false, nil
|
||||
}
|
||||
|
||||
func (p *PostProcessor) uploadImage(ctx context.Context, ui packersdk.Ui, a packersdk.Artifact) (string, string, error) {
|
||||
var (
|
||||
imageFile = a.Files()[0]
|
||||
bucketFile = filepath.Base(imageFile)
|
||||
)
|
||||
|
||||
f, err := os.Open(imageFile)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
fileInfo, err := f.Stat()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// For tracking image file upload progress
|
||||
pf := ui.TrackProgress(imageFile, 0, fileInfo.Size(), f)
|
||||
defer pf.Close()
|
||||
|
||||
hash := md5.New()
|
||||
if _, err := io.Copy(hash, f); err != nil {
|
||||
return "", "", fmt.Errorf("image checksumming failed: %s", err)
|
||||
}
|
||||
if _, err := f.Seek(0, 0); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
sess := session.Must(session.NewSessionWithOptions(session.Options{Config: aws.Config{
|
||||
Region: aws.String(p.config.TemplateZone),
|
||||
Endpoint: aws.String(p.config.SOSEndpoint),
|
||||
Credentials: credentials.NewStaticCredentials(p.config.APIKey, p.config.APISecret, "")}}))
|
||||
|
||||
uploader := s3manager.NewUploader(sess)
|
||||
output, err := uploader.UploadWithContext(ctx, &s3manager.UploadInput{
|
||||
Body: pf,
|
||||
Bucket: aws.String(p.config.ImageBucket),
|
||||
Key: aws.String(bucketFile),
|
||||
ContentMD5: aws.String(base64.StdEncoding.EncodeToString(hash.Sum(nil))),
|
||||
ACL: aws.String("public-read"),
|
||||
})
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return output.Location, fmt.Sprintf("%x", hash.Sum(nil)), nil
|
||||
}
|
||||
|
||||
func (p *PostProcessor) deleteImage(ctx context.Context, ui packersdk.Ui, a packersdk.Artifact) error {
|
||||
var (
|
||||
imageFile = a.Files()[0]
|
||||
bucketFile = filepath.Base(imageFile)
|
||||
)
|
||||
|
||||
sess := session.Must(session.NewSessionWithOptions(session.Options{Config: aws.Config{
|
||||
Region: aws.String(p.config.TemplateZone),
|
||||
Endpoint: aws.String(p.config.SOSEndpoint),
|
||||
Credentials: credentials.NewStaticCredentials(p.config.APIKey, p.config.APISecret, "")}}))
|
||||
|
||||
svc := s3.New(sess)
|
||||
if _, err := svc.DeleteObject(&s3.DeleteObjectInput{
|
||||
Bucket: aws.String(p.config.ImageBucket),
|
||||
Key: aws.String(bucketFile),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *PostProcessor) registerTemplate(ctx context.Context, ui packersdk.Ui, url, md5sum string) (string, error) {
|
||||
var (
|
||||
passwordEnabled = !p.config.TemplateDisablePassword
|
||||
sshkeyEnabled = !p.config.TemplateDisableSSHKey
|
||||
regErr error
|
||||
)
|
||||
|
||||
exo := egoscale.NewClient(p.config.APIEndpoint, p.config.APIKey, p.config.APISecret)
|
||||
exo.RetryStrategy = egoscale.FibonacciRetryStrategy
|
||||
|
||||
zone := egoscale.Zone{Name: p.config.TemplateZone}
|
||||
if resp, err := exo.GetWithContext(ctx, &zone); err != nil {
|
||||
return "", fmt.Errorf("template zone lookup failed: %s", err)
|
||||
} else {
|
||||
zone.ID = resp.(*egoscale.Zone).ID
|
||||
}
|
||||
|
||||
req := egoscale.RegisterCustomTemplate{
|
||||
URL: url,
|
||||
ZoneID: zone.ID,
|
||||
Name: p.config.TemplateName,
|
||||
Displaytext: p.config.TemplateDescription,
|
||||
PasswordEnabled: &passwordEnabled,
|
||||
SSHKeyEnabled: &sshkeyEnabled,
|
||||
Details: map[string]string{"username": p.config.TemplateUsername},
|
||||
Checksum: md5sum,
|
||||
}
|
||||
|
||||
res := make([]egoscale.Template, 0)
|
||||
|
||||
exo.AsyncRequestWithContext(ctx, req, func(jobRes *egoscale.AsyncJobResult, err error) bool {
|
||||
if err != nil {
|
||||
regErr = fmt.Errorf("request failed: %s", err)
|
||||
return false
|
||||
} else if jobRes.JobStatus == egoscale.Pending {
|
||||
// Job is not completed yet
|
||||
ui.Message("template registration in progress")
|
||||
return true
|
||||
}
|
||||
|
||||
if err := jobRes.Result(&res); err != nil {
|
||||
regErr = err
|
||||
return false
|
||||
}
|
||||
|
||||
if len(res) != 1 {
|
||||
regErr = fmt.Errorf("unexpected response from API (expected 1 item, got %d)", len(res))
|
||||
return false
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
if regErr != nil {
|
||||
return "", regErr
|
||||
}
|
||||
|
||||
return res[0].ID.String(), nil
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package version
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/packer-plugin-sdk/version"
|
||||
packerVersion "github.com/hashicorp/packer/version"
|
||||
)
|
||||
|
||||
var ExoscaleImportPluginVersion *version.PluginVersion
|
||||
|
||||
func init() {
|
||||
ExoscaleImportPluginVersion = version.InitializePluginVersion(
|
||||
packerVersion.Version, packerVersion.VersionPrerelease)
|
||||
}
|
||||
@@ -11,9 +11,9 @@ import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-docker/builder/docker"
|
||||
builderT "github.com/hashicorp/packer-plugin-sdk/acctest"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer/builder/docker"
|
||||
"github.com/hashicorp/packer/provisioner/file"
|
||||
)
|
||||
|
||||
|
||||
@@ -138,12 +138,12 @@ func (p *Provisioner) ProvisionDownload(ui packersdk.Ui, comm packersdk.Communic
|
||||
return fmt.Errorf("Error interpolating destination: %s", err)
|
||||
}
|
||||
for _, src := range p.config.Sources {
|
||||
dst := dst
|
||||
src, err := interpolate.Render(src, &p.config.ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error interpolating source: %s", err)
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Downloading %s => %s", src, dst))
|
||||
// ensure destination dir exists. p.config.Destination may either be a file or a dir.
|
||||
dir := dst
|
||||
// if it doesn't end with a /, set dir as the parent dir
|
||||
@@ -152,6 +152,8 @@ func (p *Provisioner) ProvisionDownload(ui packersdk.Ui, comm packersdk.Communic
|
||||
} else if !strings.HasSuffix(src, "/") && !strings.HasSuffix(src, "*") {
|
||||
dst = filepath.Join(dst, filepath.Base(src))
|
||||
}
|
||||
ui.Say(fmt.Sprintf("Downloading %s => %s", src, dst))
|
||||
|
||||
if dir != "" {
|
||||
err := os.MkdirAll(dir, os.FileMode(0755))
|
||||
if err != nil {
|
||||
|
||||
@@ -272,6 +272,69 @@ func TestProvisionerProvision_SendsFileMultipleDirs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionerProvision_DownloadsMultipleFilesToFolder(t *testing.T) {
|
||||
var p Provisioner
|
||||
|
||||
tf1, err := ioutil.TempFile("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("error tempfile: %s", err)
|
||||
}
|
||||
defer os.Remove(tf1.Name())
|
||||
|
||||
if _, err = tf1.Write([]byte("hello")); err != nil {
|
||||
t.Fatalf("error writing tempfile: %s", err)
|
||||
}
|
||||
|
||||
tf2, err := ioutil.TempFile("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("error tempfile: %s", err)
|
||||
}
|
||||
defer os.Remove(tf2.Name())
|
||||
|
||||
if _, err = tf2.Write([]byte("hello")); err != nil {
|
||||
t.Fatalf("error writing tempfile: %s", err)
|
||||
}
|
||||
|
||||
config := map[string]interface{}{
|
||||
"sources": []string{tf1.Name(), tf2.Name()},
|
||||
"destination": "something/",
|
||||
"direction": "download",
|
||||
}
|
||||
|
||||
if err := p.Prepare(config); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
b := bytes.NewBuffer(nil)
|
||||
ui := &packersdk.BasicUi{
|
||||
Writer: b,
|
||||
PB: &packersdk.NoopProgressTracker{},
|
||||
}
|
||||
comm := &packersdk.MockCommunicator{}
|
||||
err = p.Provision(context.Background(), ui, comm, make(map[string]interface{}))
|
||||
if err != nil {
|
||||
t.Fatalf("should successfully provision: %s", err)
|
||||
}
|
||||
|
||||
if !strings.Contains(b.String(), tf1.Name()) {
|
||||
t.Errorf("should print source filenam '%s'e; output: \n%s", tf1.Name(), b.String())
|
||||
}
|
||||
|
||||
if !strings.Contains(b.String(), tf2.Name()) {
|
||||
t.Errorf("should second source filename '%s'; output: \n%s", tf2.Name(), b.String())
|
||||
}
|
||||
|
||||
dst1 := filepath.Join("something", filepath.Base(tf1.Name()))
|
||||
if !strings.Contains(b.String(), dst1) {
|
||||
t.Errorf("should print destination filename '%s'; output: \n%s", dst1, b.String())
|
||||
}
|
||||
|
||||
dst2 := filepath.Join("something", filepath.Base(tf2.Name()))
|
||||
if !strings.Contains(b.String(), dst2) {
|
||||
t.Errorf("should print destination filename '%s'; output: \n%s", dst2, b.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionerProvision_SendsFileMultipleFilesToFolder(t *testing.T) {
|
||||
var p Provisioner
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unicode"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
@@ -59,6 +60,7 @@ type Config struct {
|
||||
LocalPort int `mapstructure:"local_port"`
|
||||
SSHHostKeyFile string `mapstructure:"ssh_host_key_file"`
|
||||
SSHAuthorizedKeyFile string `mapstructure:"ssh_authorized_key_file"`
|
||||
ValidExitCodes []int `mapstructure:"valid_exit_codes"`
|
||||
}
|
||||
|
||||
type Provisioner struct {
|
||||
@@ -157,6 +159,10 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("user: could not determine current user from environment."))
|
||||
}
|
||||
|
||||
if p.config.ValidExitCodes == nil {
|
||||
p.config.ValidExitCodes = []int{0, 101}
|
||||
}
|
||||
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
return errs
|
||||
}
|
||||
@@ -170,7 +176,7 @@ func (p *Provisioner) getVersion() error {
|
||||
"Error running \"%s version\": %s", p.config.Command, err.Error())
|
||||
}
|
||||
|
||||
versionRe := regexp.MustCompile(`\w (\d+\.\d+[.\d+]*)`)
|
||||
versionRe := regexp.MustCompile(`(\d+\.\d+[.\d+]*)`)
|
||||
matches := versionRe.FindStringSubmatch(string(out))
|
||||
if matches == nil {
|
||||
return fmt.Errorf(
|
||||
@@ -412,9 +418,33 @@ func (p *Provisioner) executeInspec(ui packersdk.Ui, comm packersdk.Communicator
|
||||
return err
|
||||
}
|
||||
wg.Wait()
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Non-zero exit status: %s", err)
|
||||
|
||||
if err := cmd.Wait(); err != nil {
|
||||
if exiterr, ok := err.(*exec.ExitError); ok {
|
||||
// The program has exited with an exit code != 0
|
||||
|
||||
// This works on both Unix and Windows. Although package
|
||||
// syscall is generally platform dependent, WaitStatus is
|
||||
// defined for both Unix and Windows and in both cases has
|
||||
// an ExitStatus() method with the same signature.
|
||||
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
|
||||
exitStatus := status.ExitStatus()
|
||||
// Check exit code against allowed codes (likely just 0)
|
||||
validExitCode := false
|
||||
for _, v := range p.config.ValidExitCodes {
|
||||
if exitStatus == v {
|
||||
validExitCode = true
|
||||
}
|
||||
}
|
||||
if !validExitCode {
|
||||
return fmt.Errorf(
|
||||
"Inspec exited with unexpected exit status: %d. Expected exit codes are: %v",
|
||||
exitStatus, p.config.ValidExitCodes)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("Unable to get exit status: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -30,6 +30,7 @@ type FlatConfig struct {
|
||||
LocalPort *int `mapstructure:"local_port" cty:"local_port" hcl:"local_port"`
|
||||
SSHHostKeyFile *string `mapstructure:"ssh_host_key_file" cty:"ssh_host_key_file" hcl:"ssh_host_key_file"`
|
||||
SSHAuthorizedKeyFile *string `mapstructure:"ssh_authorized_key_file" cty:"ssh_authorized_key_file" hcl:"ssh_authorized_key_file"`
|
||||
ValidExitCodes []int `mapstructure:"valid_exit_codes" cty:"valid_exit_codes" hcl:"valid_exit_codes"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatConfig.
|
||||
@@ -64,6 +65,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"local_port": &hcldec.AttrSpec{Name: "local_port", Type: cty.Number, Required: false},
|
||||
"ssh_host_key_file": &hcldec.AttrSpec{Name: "ssh_host_key_file", Type: cty.String, Required: false},
|
||||
"ssh_authorized_key_file": &hcldec.AttrSpec{Name: "ssh_authorized_key_file", Type: cty.String, Required: false},
|
||||
"valid_exit_codes": &hcldec.AttrSpec{Name: "valid_exit_codes", Type: cty.List(cty.Number), Required: false},
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
package inspec
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -268,12 +271,17 @@ func TestProvisionerPrepare_LocalPort(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInspecGetVersion(t *testing.T) {
|
||||
if os.Getenv("PACKER_ACC") == "" {
|
||||
t.Skip("This test is only run with PACKER_ACC=1 and it requires InSpec to be installed")
|
||||
var p Provisioner
|
||||
if os.Getenv("PACKER_ACC") == "1" {
|
||||
p.config.Command = "inspec"
|
||||
} else {
|
||||
p.config.Command = "./test-fixtures/inspec_version.sh"
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("This test is only run with PACKER_ACC=1 and it requires InSpec to be installed")
|
||||
}
|
||||
}
|
||||
|
||||
var p Provisioner
|
||||
p.config.Command = "inspec exec"
|
||||
p.config.Backend = "local"
|
||||
err := p.getVersion()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
@@ -291,3 +299,32 @@ func TestInspecGetVersionError(t *testing.T) {
|
||||
t.Fatal("Error message should include command name")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInspecValidExitCodes(t *testing.T) {
|
||||
var p Provisioner
|
||||
if os.Getenv("PACKER_ACC") == "1" {
|
||||
p.config.Command = "inspec"
|
||||
} else {
|
||||
p.config.Command = "./test-fixtures/valid_exit_codes.sh"
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("This test is only run with PACKER_ACC=1 and it requires InSpec to be installed")
|
||||
}
|
||||
}
|
||||
p.config.Backend = "local"
|
||||
p.config.Profile = "test-fixtures/skip_control.rb"
|
||||
err := p.Prepare()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
comm := &packersdk.MockCommunicator{}
|
||||
ui := &packersdk.BasicUi{
|
||||
Reader: new(bytes.Buffer),
|
||||
Writer: new(bytes.Buffer),
|
||||
}
|
||||
|
||||
err = p.Provision(context.Background(), ui, comm, make(map[string]interface{}))
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
cat <<EOB
|
||||
4.26.13
|
||||
EOB
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,13 @@
|
||||
# copyright: 2018, The Authors
|
||||
|
||||
title "skip control"
|
||||
|
||||
control "skip-1.0" do
|
||||
impact 1.0
|
||||
title "skip control"
|
||||
desc "skip control to generate a 101 return code"
|
||||
only_if { 1 == 2 }
|
||||
describe file("/tmp") do
|
||||
it { should be_directory }
|
||||
end
|
||||
end
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
|
||||
cat <<EOB
|
||||
Profile: tests from test-fixtures/skip_control.rb (tests from test-fixtures/valid_exit_codes.sh)
|
||||
Version: (not specified)
|
||||
Target: local://
|
||||
|
||||
[38;5;247m ↺ skip-1.0: skip control[0m
|
||||
[38;5;247m ↺ Skipped control due to only_if condition.[0m
|
||||
|
||||
|
||||
Profile Summary: 0 successful controls, 0 control failures, [38;5;247m1 control skipped[0m
|
||||
Test Summary: 0 successful, 0 failures, [38;5;247m1 skipped[0m
|
||||
EOB
|
||||
|
||||
exit 101
|
||||
@@ -101,7 +101,7 @@ var guestOSTypeConfigs = map[string]guestOSTypeConfig{
|
||||
tempDir: "/tmp/salt",
|
||||
stateRoot: "/srv/salt",
|
||||
pillarRoot: "/srv/pillar",
|
||||
bootstrapFetchCmd: "curl -L https://bootstrap.saltstack.com -o /tmp/install_salt.sh || wget -O /tmp/install_salt.sh https://bootstrap.saltstack.com",
|
||||
bootstrapFetchCmd: "curl -L https://bootstrap.saltproject.io -o /tmp/install_salt.sh || wget -O /tmp/install_salt.sh https://bootstrap.saltproject.io",
|
||||
bootstrapRunCmd: "sh /tmp/install_salt.sh",
|
||||
},
|
||||
guestexec.WindowsOSType: {
|
||||
@@ -109,7 +109,7 @@ var guestOSTypeConfigs = map[string]guestOSTypeConfig{
|
||||
tempDir: "C:/Windows/Temp/salt/",
|
||||
stateRoot: "C:/salt/state",
|
||||
pillarRoot: "C:/salt/pillar/",
|
||||
bootstrapFetchCmd: "powershell Invoke-WebRequest -Uri 'https://raw.githubusercontent.com/saltstack/salt-bootstrap/stable/bootstrap-salt.ps1' -OutFile 'C:/Windows/Temp/bootstrap-salt.ps1'",
|
||||
bootstrapFetchCmd: "powershell -Command \"[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]'Tls,Tls11,Tls12'; Invoke-WebRequest -Uri 'https://winbootstrap.saltproject.io' -OutFile 'C:/Windows/Temp/bootstrap-salt.ps1'\"",
|
||||
bootstrapRunCmd: "Powershell C:/Windows/Temp/bootstrap-salt.ps1",
|
||||
},
|
||||
}
|
||||
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
// Copyright 2021 DeepMap, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
package runtime
|
||||
|
||||
// Binder is the interface implemented by types that can be bound to a query string or a parameter string
|
||||
// The input can be assumed to be a valid string. If you define a Bind method you are responsible for all
|
||||
// data being completely bound to the type.
|
||||
//
|
||||
// By convention, to approximate the behavior of Bind functions themselves,
|
||||
// Binder implements Bind("") as a no-op.
|
||||
type Binder interface {
|
||||
Bind(src string) error
|
||||
}
|
||||
+463
@@ -0,0 +1,463 @@
|
||||
// Copyright 2019 DeepMap, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/deepmap/oapi-codegen/pkg/types"
|
||||
)
|
||||
|
||||
// This function binds a parameter as described in the Path Parameters
|
||||
// section here to a Go object:
|
||||
// https://swagger.io/docs/specification/serialization/
|
||||
func BindStyledParameter(style string, explode bool, paramName string,
|
||||
value string, dest interface{}) error {
|
||||
|
||||
if value == "" {
|
||||
return fmt.Errorf("parameter '%s' is empty, can't bind its value", paramName)
|
||||
}
|
||||
|
||||
// Everything comes in by pointer, dereference it
|
||||
v := reflect.Indirect(reflect.ValueOf(dest))
|
||||
|
||||
// This is the basic type of the destination object.
|
||||
t := v.Type()
|
||||
|
||||
if t.Kind() == reflect.Struct {
|
||||
// We've got a destination object, we'll create a JSON representation
|
||||
// of the input value, and let the json library deal with the unmarshaling
|
||||
parts, err := splitStyledParameter(style, explode, true, paramName, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return bindSplitPartsToDestinationStruct(paramName, parts, explode, dest)
|
||||
}
|
||||
|
||||
if t.Kind() == reflect.Slice {
|
||||
// Chop up the parameter into parts based on its style
|
||||
parts, err := splitStyledParameter(style, explode, false, paramName, value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error splitting input '%s' into parts: %s", value, err)
|
||||
}
|
||||
|
||||
return bindSplitPartsToDestinationArray(parts, dest)
|
||||
}
|
||||
|
||||
// Try to bind the remaining types as a base type.
|
||||
return BindStringToObject(value, dest)
|
||||
}
|
||||
|
||||
// This is a complex set of operations, but each given parameter style can be
|
||||
// packed together in multiple ways, using different styles of separators, and
|
||||
// different packing strategies based on the explode flag. This function takes
|
||||
// as input any parameter format, and unpacks it to a simple list of strings
|
||||
// or key-values which we can then treat generically.
|
||||
// Why, oh why, great Swagger gods, did you have to make this so complicated?
|
||||
func splitStyledParameter(style string, explode bool, object bool, paramName string, value string) ([]string, error) {
|
||||
switch style {
|
||||
case "simple":
|
||||
// In the simple case, we always split on comma
|
||||
parts := strings.Split(value, ",")
|
||||
return parts, nil
|
||||
case "label":
|
||||
// In the label case, it's more tricky. In the no explode case, we have
|
||||
// /users/.3,4,5 for arrays
|
||||
// /users/.role,admin,firstName,Alex for objects
|
||||
// in the explode case, we have:
|
||||
// /users/.3.4.5
|
||||
// /users/.role=admin.firstName=Alex
|
||||
if explode {
|
||||
// In the exploded case, split everything on periods.
|
||||
parts := strings.Split(value, ".")
|
||||
// The first part should be an empty string because we have a
|
||||
// leading period.
|
||||
if parts[0] != "" {
|
||||
return nil, fmt.Errorf("invalid format for label parameter '%s', should start with '.'", paramName)
|
||||
}
|
||||
return parts[1:], nil
|
||||
|
||||
} else {
|
||||
// In the unexploded case, we strip off the leading period.
|
||||
if value[0] != '.' {
|
||||
return nil, fmt.Errorf("invalid format for label parameter '%s', should start with '.'", paramName)
|
||||
}
|
||||
// The rest is comma separated.
|
||||
return strings.Split(value[1:], ","), nil
|
||||
}
|
||||
|
||||
case "matrix":
|
||||
if explode {
|
||||
// In the exploded case, we break everything up on semicolon
|
||||
parts := strings.Split(value, ";")
|
||||
// The first part should always be empty string, since we started
|
||||
// with ;something
|
||||
if parts[0] != "" {
|
||||
return nil, fmt.Errorf("invalid format for matrix parameter '%s', should start with ';'", paramName)
|
||||
}
|
||||
parts = parts[1:]
|
||||
// Now, if we have an object, we just have a list of x=y statements.
|
||||
// for a non-object, like an array, we have id=x, id=y. id=z, etc,
|
||||
// so we need to strip the prefix from each of them.
|
||||
if !object {
|
||||
prefix := paramName + "="
|
||||
for i := range parts {
|
||||
parts[i] = strings.TrimPrefix(parts[i], prefix)
|
||||
}
|
||||
}
|
||||
return parts, nil
|
||||
} else {
|
||||
// In the unexploded case, parameters will start with ;paramName=
|
||||
prefix := ";" + paramName + "="
|
||||
if !strings.HasPrefix(value, prefix) {
|
||||
return nil, fmt.Errorf("expected parameter '%s' to start with %s", paramName, prefix)
|
||||
}
|
||||
str := strings.TrimPrefix(value, prefix)
|
||||
return strings.Split(str, ","), nil
|
||||
}
|
||||
case "form":
|
||||
var parts []string
|
||||
if explode {
|
||||
parts = strings.Split(value, "&")
|
||||
if !object {
|
||||
prefix := paramName + "="
|
||||
for i := range parts {
|
||||
parts[i] = strings.TrimPrefix(parts[i], prefix)
|
||||
}
|
||||
}
|
||||
return parts, nil
|
||||
} else {
|
||||
parts = strings.Split(value, ",")
|
||||
prefix := paramName + "="
|
||||
for i := range parts {
|
||||
parts[i] = strings.TrimPrefix(parts[i], prefix)
|
||||
}
|
||||
}
|
||||
return parts, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unhandled parameter style: %s", style)
|
||||
}
|
||||
|
||||
// Given a set of values as a slice, create a slice to hold them all, and
|
||||
// assign to each one by one.
|
||||
func bindSplitPartsToDestinationArray(parts []string, dest interface{}) error {
|
||||
// Everything comes in by pointer, dereference it
|
||||
v := reflect.Indirect(reflect.ValueOf(dest))
|
||||
|
||||
// This is the basic type of the destination object.
|
||||
t := v.Type()
|
||||
|
||||
// We've got a destination array, bind each object one by one.
|
||||
// This generates a slice of the correct element type and length to
|
||||
// hold all the parts.
|
||||
newArray := reflect.MakeSlice(t, len(parts), len(parts))
|
||||
for i, p := range parts {
|
||||
err := BindStringToObject(p, newArray.Index(i).Addr().Interface())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error setting array element: %s", err)
|
||||
}
|
||||
}
|
||||
v.Set(newArray)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Given a set of chopped up parameter parts, bind them to a destination
|
||||
// struct. The exploded parameter controls whether we send key value pairs
|
||||
// in the exploded case, or a sequence of values which are interpreted as
|
||||
// tuples.
|
||||
// Given the struct Id { firstName string, role string }, as in the canonical
|
||||
// swagger examples, in the exploded case, we would pass
|
||||
// ["firstName=Alex", "role=admin"], where in the non-exploded case, we would
|
||||
// pass "firstName", "Alex", "role", "admin"]
|
||||
//
|
||||
// We punt the hard work of binding these values to the object to the json
|
||||
// library. We'll turn those arrays into JSON strings, and unmarshal
|
||||
// into the struct.
|
||||
func bindSplitPartsToDestinationStruct(paramName string, parts []string, explode bool, dest interface{}) error {
|
||||
// We've got a destination object, we'll create a JSON representation
|
||||
// of the input value, and let the json library deal with the unmarshaling
|
||||
var fields []string
|
||||
if explode {
|
||||
fields = make([]string, len(parts))
|
||||
for i, property := range parts {
|
||||
propertyParts := strings.Split(property, "=")
|
||||
if len(propertyParts) != 2 {
|
||||
return fmt.Errorf("parameter '%s' has invalid exploded format", paramName)
|
||||
}
|
||||
fields[i] = "\"" + propertyParts[0] + "\":\"" + propertyParts[1] + "\""
|
||||
}
|
||||
} else {
|
||||
if len(parts)%2 != 0 {
|
||||
return fmt.Errorf("parameter '%s' has invalid format, property/values need to be pairs", paramName)
|
||||
}
|
||||
fields = make([]string, len(parts)/2)
|
||||
for i := 0; i < len(parts); i += 2 {
|
||||
key := parts[i]
|
||||
value := parts[i+1]
|
||||
fields[i/2] = "\"" + key + "\":\"" + value + "\""
|
||||
}
|
||||
}
|
||||
jsonParam := "{" + strings.Join(fields, ",") + "}"
|
||||
err := json.Unmarshal([]byte(jsonParam), dest)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error binding parameter %s fields: %s", paramName, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// This works much like BindStyledParameter, however it takes a query argument
|
||||
// input array from the url package, since query arguments come through a
|
||||
// different path than the styled arguments. They're also exceptionally fussy.
|
||||
// For example, consider the exploded and unexploded form parameter examples:
|
||||
// (exploded) /users?role=admin&firstName=Alex
|
||||
// (unexploded) /users?id=role,admin,firstName,Alex
|
||||
//
|
||||
// In the first case, we can pull the "id" parameter off the context,
|
||||
// and unmarshal via json as an intermediate. Easy. In the second case, we
|
||||
// don't have the id QueryParam present, but must find "role", and "firstName".
|
||||
// what if there is another parameter similar to "ID" named "role"? We can't
|
||||
// tell them apart. This code tries to fail, but the moral of the story is that
|
||||
// you shouldn't pass objects via form styled query arguments, just use
|
||||
// the Content parameter form.
|
||||
func BindQueryParameter(style string, explode bool, required bool, paramName string,
|
||||
queryParams url.Values, dest interface{}) error {
|
||||
|
||||
// dv = destination value.
|
||||
dv := reflect.Indirect(reflect.ValueOf(dest))
|
||||
|
||||
// intermediate value form which is either dv or dv dereferenced.
|
||||
v := dv
|
||||
|
||||
// inner code will bind the string's value to this interface.
|
||||
var output interface{}
|
||||
|
||||
if required {
|
||||
// If the parameter is required, then the generated code will pass us
|
||||
// a pointer to it: &int, &object, and so forth. We can directly set
|
||||
// them.
|
||||
output = dest
|
||||
} else {
|
||||
// For optional parameters, we have an extra indirect. An optional
|
||||
// parameter of type "int" will be *int on the struct. We pass that
|
||||
// in by pointer, and have **int.
|
||||
|
||||
// If the destination, is a nil pointer, we need to allocate it.
|
||||
if v.IsNil() {
|
||||
t := v.Type()
|
||||
newValue := reflect.New(t.Elem())
|
||||
// for now, hang onto the output buffer separately from destination,
|
||||
// as we don't want to write anything to destination until we can
|
||||
// unmarshal successfully, and check whether a field is required.
|
||||
output = newValue.Interface()
|
||||
} else {
|
||||
// If the destination isn't nil, just use that.
|
||||
output = v.Interface()
|
||||
}
|
||||
|
||||
// Get rid of that extra indirect as compared to the required case,
|
||||
// so the code below doesn't have to care.
|
||||
v = reflect.Indirect(reflect.ValueOf(output))
|
||||
}
|
||||
|
||||
// This is the basic type of the destination object.
|
||||
t := v.Type()
|
||||
k := t.Kind()
|
||||
|
||||
switch style {
|
||||
case "form":
|
||||
var parts []string
|
||||
if explode {
|
||||
// ok, the explode case in query arguments is very, very annoying,
|
||||
// because an exploded object, such as /users?role=admin&firstName=Alex
|
||||
// isn't actually present in the parameter array. We have to do
|
||||
// different things based on destination type.
|
||||
values, found := queryParams[paramName]
|
||||
var err error
|
||||
|
||||
switch k {
|
||||
case reflect.Slice:
|
||||
// In the slice case, we simply use the arguments provided by
|
||||
// http library.
|
||||
if !found {
|
||||
if required {
|
||||
return fmt.Errorf("query parameter '%s' is required", paramName)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
err = bindSplitPartsToDestinationArray(values, output)
|
||||
case reflect.Struct:
|
||||
// This case is really annoying, and error prone, but the
|
||||
// form style object binding doesn't tell us which arguments
|
||||
// in the query string correspond to the object's fields. We'll
|
||||
// try to bind field by field.
|
||||
err = bindParamsToExplodedObject(paramName, queryParams, output)
|
||||
default:
|
||||
// Primitive object case. We expect to have 1 value to
|
||||
// unmarshal.
|
||||
if len(values) == 0 {
|
||||
if required {
|
||||
return fmt.Errorf("query parameter '%s' is required", paramName)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if len(values) != 1 {
|
||||
return fmt.Errorf("multiple values for single value parameter '%s'", paramName)
|
||||
}
|
||||
err = BindStringToObject(values[0], output)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// If the parameter is required, and we've successfully unmarshaled
|
||||
// it, this assigns the new object to the pointer pointer.
|
||||
if !required {
|
||||
dv.Set(reflect.ValueOf(output))
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
values, found := queryParams[paramName]
|
||||
if !found {
|
||||
if required {
|
||||
return fmt.Errorf("query parameter '%s' is required", paramName)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if len(values) != 1 {
|
||||
return fmt.Errorf("parameter '%s' is not exploded, but is specified multiple times", paramName)
|
||||
}
|
||||
parts = strings.Split(values[0], ",")
|
||||
}
|
||||
var err error
|
||||
switch k {
|
||||
case reflect.Slice:
|
||||
err = bindSplitPartsToDestinationArray(parts, output)
|
||||
case reflect.Struct:
|
||||
err = bindSplitPartsToDestinationStruct(paramName, parts, explode, output)
|
||||
default:
|
||||
if len(parts) == 0 {
|
||||
if required {
|
||||
return fmt.Errorf("query parameter '%s' is required", paramName)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if len(parts) != 1 {
|
||||
return fmt.Errorf("multiple values for single value parameter '%s'", paramName)
|
||||
}
|
||||
err = BindStringToObject(parts[0], output)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !required {
|
||||
dv.Set(reflect.ValueOf(output))
|
||||
}
|
||||
return nil
|
||||
case "deepObject":
|
||||
if !explode {
|
||||
return errors.New("deepObjects must be exploded")
|
||||
}
|
||||
return UnmarshalDeepObject(dest, paramName, queryParams)
|
||||
case "spaceDelimited", "pipeDelimited":
|
||||
return fmt.Errorf("query arguments of style '%s' aren't yet supported", style)
|
||||
default:
|
||||
return fmt.Errorf("style '%s' on parameter '%s' is invalid", style, paramName)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// This function reflects the destination structure, and pulls the value for
|
||||
// each settable field from the given parameters map. This is to deal with the
|
||||
// exploded form styled object which may occupy any number of parameter names.
|
||||
// We don't try to be smart here, if the field exists as a query argument,
|
||||
// set its value.
|
||||
func bindParamsToExplodedObject(paramName string, values url.Values, dest interface{}) error {
|
||||
// Dereference pointers to their destination values
|
||||
binder, v, t := indirect(dest)
|
||||
if binder != nil {
|
||||
return BindStringToObject(values.Get(paramName), dest)
|
||||
}
|
||||
if t.Kind() != reflect.Struct {
|
||||
return fmt.Errorf("unmarshaling query arg '%s' into wrong type", paramName)
|
||||
}
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
fieldT := t.Field(i)
|
||||
|
||||
// Skip unsettable fields, such as internal ones.
|
||||
if !v.Field(i).CanSet() {
|
||||
continue
|
||||
}
|
||||
|
||||
// Find the json annotation on the field, and use the json specified
|
||||
// name if available, otherwise, just the field name.
|
||||
tag := fieldT.Tag.Get("json")
|
||||
fieldName := fieldT.Name
|
||||
if tag != "" {
|
||||
tagParts := strings.Split(tag, ",")
|
||||
name := tagParts[0]
|
||||
if name != "" {
|
||||
fieldName = name
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, we look up field name in the parameter list.
|
||||
fieldVal, found := values[fieldName]
|
||||
if found {
|
||||
if len(fieldVal) != 1 {
|
||||
return fmt.Errorf("field '%s' specified multiple times for param '%s'", fieldName, paramName)
|
||||
}
|
||||
err := BindStringToObject(fieldVal[0], v.Field(i).Addr().Interface())
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not bind query arg '%s' to request object: %s'", paramName, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// indirect
|
||||
func indirect(dest interface{}) (interface{}, reflect.Value, reflect.Type) {
|
||||
v := reflect.ValueOf(dest)
|
||||
if v.Type().NumMethod() > 0 && v.CanInterface() {
|
||||
if u, ok := v.Interface().(Binder); ok {
|
||||
return u, reflect.Value{}, nil
|
||||
}
|
||||
}
|
||||
v = reflect.Indirect(v)
|
||||
t := v.Type()
|
||||
// special handling for custom types which might look like an object. We
|
||||
// don't want to use object binding on them, but rather treat them as
|
||||
// primitive types. time.Time{} is a unique case since we can't add a Binder
|
||||
// to it without changing the underlying generated code.
|
||||
if t.ConvertibleTo(reflect.TypeOf(time.Time{})) {
|
||||
return dest, reflect.Value{}, nil
|
||||
}
|
||||
if t.ConvertibleTo(reflect.TypeOf(types.Date{})) {
|
||||
return dest, reflect.Value{}, nil
|
||||
}
|
||||
return nil, v, t
|
||||
}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
// Copyright 2019 DeepMap, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/deepmap/oapi-codegen/pkg/types"
|
||||
)
|
||||
|
||||
// This function takes a string, and attempts to assign it to the destination
|
||||
// interface via whatever type conversion is necessary. We have to do this
|
||||
// via reflection instead of a much simpler type switch so that we can handle
|
||||
// type aliases. This function was the easy way out, the better way, since we
|
||||
// know the destination type each place that we use this, is to generate code
|
||||
// to read each specific type.
|
||||
func BindStringToObject(src string, dst interface{}) error {
|
||||
var err error
|
||||
|
||||
v := reflect.ValueOf(dst)
|
||||
t := reflect.TypeOf(dst)
|
||||
|
||||
// We need to dereference pointers
|
||||
if t.Kind() == reflect.Ptr {
|
||||
v = reflect.Indirect(v)
|
||||
t = v.Type()
|
||||
}
|
||||
|
||||
// The resulting type must be settable. reflect will catch issues like
|
||||
// passing the destination by value.
|
||||
if !v.CanSet() {
|
||||
return errors.New("destination is not settable")
|
||||
}
|
||||
|
||||
switch t.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
var val int64
|
||||
val, err = strconv.ParseInt(src, 10, 64)
|
||||
if err == nil {
|
||||
v.SetInt(val)
|
||||
}
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
var val uint64
|
||||
val, err = strconv.ParseUint(src, 10, 64)
|
||||
if err == nil {
|
||||
v.SetUint(val)
|
||||
}
|
||||
case reflect.String:
|
||||
v.SetString(src)
|
||||
err = nil
|
||||
case reflect.Float64, reflect.Float32:
|
||||
var val float64
|
||||
val, err = strconv.ParseFloat(src, 64)
|
||||
if err == nil {
|
||||
v.SetFloat(val)
|
||||
}
|
||||
case reflect.Bool:
|
||||
var val bool
|
||||
val, err = strconv.ParseBool(src)
|
||||
if err == nil {
|
||||
v.SetBool(val)
|
||||
}
|
||||
case reflect.Struct:
|
||||
// if this is not of type Time or of type Date look to see if this is of type Binder.
|
||||
if dstType, ok := dst.(Binder); ok {
|
||||
return dstType.Bind(src)
|
||||
}
|
||||
|
||||
if t.ConvertibleTo(reflect.TypeOf(time.Time{})) {
|
||||
// Don't fail on empty string.
|
||||
if src == "" {
|
||||
return nil
|
||||
}
|
||||
// Time is a special case of a struct that we handle
|
||||
parsedTime, err := time.Parse(time.RFC3339Nano, src)
|
||||
if err != nil {
|
||||
parsedTime, err = time.Parse(types.DateFormat, src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing '%s' as RFC3339 or 2006-01-02 time: %s", src, err)
|
||||
}
|
||||
}
|
||||
// So, assigning this gets a little fun. We have a value to the
|
||||
// dereference destination. We can't do a conversion to
|
||||
// time.Time because the result isn't assignable, so we need to
|
||||
// convert pointers.
|
||||
if t != reflect.TypeOf(time.Time{}) {
|
||||
vPtr := v.Addr()
|
||||
vtPtr := vPtr.Convert(reflect.TypeOf(&time.Time{}))
|
||||
v = reflect.Indirect(vtPtr)
|
||||
}
|
||||
v.Set(reflect.ValueOf(parsedTime))
|
||||
return nil
|
||||
}
|
||||
|
||||
if t.ConvertibleTo(reflect.TypeOf(types.Date{})) {
|
||||
// Don't fail on empty string.
|
||||
if src == "" {
|
||||
return nil
|
||||
}
|
||||
parsedTime, err := time.Parse(types.DateFormat, src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing '%s' as date: %s", src, err)
|
||||
}
|
||||
parsedDate := types.Date{Time: parsedTime}
|
||||
|
||||
// We have to do the same dance here to assign, just like with times
|
||||
// above.
|
||||
if t != reflect.TypeOf(types.Date{}) {
|
||||
vPtr := v.Addr()
|
||||
vtPtr := vPtr.Convert(reflect.TypeOf(&types.Date{}))
|
||||
v = reflect.Indirect(vtPtr)
|
||||
}
|
||||
v.Set(reflect.ValueOf(parsedDate))
|
||||
return nil
|
||||
}
|
||||
|
||||
// We fall through to the error case below if we haven't handled the
|
||||
// destination type above.
|
||||
fallthrough
|
||||
default:
|
||||
// We've got a bunch of types unimplemented, don't fail silently.
|
||||
err = fmt.Errorf("can not bind to destination of type: %s", t.Kind())
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("error binding string parameter: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+357
@@ -0,0 +1,357 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/deepmap/oapi-codegen/pkg/types"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func marshalDeepObject(in interface{}, path []string) ([]string, error) {
|
||||
var result []string
|
||||
|
||||
switch t := in.(type) {
|
||||
case []interface{}:
|
||||
// For the array, we will use numerical subscripts of the form [x],
|
||||
// in the same order as the array.
|
||||
for i, iface := range t {
|
||||
newPath := append(path, strconv.Itoa(i))
|
||||
fields, err := marshalDeepObject(iface, newPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error traversing array")
|
||||
}
|
||||
result = append(result, fields...)
|
||||
}
|
||||
case map[string]interface{}:
|
||||
// For a map, each key (field name) becomes a member of the path, and
|
||||
// we recurse. First, sort the keys.
|
||||
keys := make([]string, len(t))
|
||||
i := 0
|
||||
for k := range t {
|
||||
keys[i] = k
|
||||
i++
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
// Now, for each key, we recursively marshal it.
|
||||
for _, k := range keys {
|
||||
newPath := append(path, k)
|
||||
fields, err := marshalDeepObject(t[k], newPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error traversing map")
|
||||
}
|
||||
result = append(result, fields...)
|
||||
}
|
||||
default:
|
||||
// Now, for a concrete value, we will turn the path elements
|
||||
// into a deepObject style set of subscripts. [a, b, c] turns into
|
||||
// [a][b][c]
|
||||
prefix := "[" + strings.Join(path, "][") + "]"
|
||||
result = []string{
|
||||
prefix + fmt.Sprintf("=%v", t),
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func MarshalDeepObject(i interface{}, paramName string) (string, error) {
|
||||
// We're going to marshal to JSON and unmarshal into an interface{},
|
||||
// which will use the json pkg to deal with all the field annotations. We
|
||||
// can then walk the generic object structure to produce a deepObject. This
|
||||
// isn't efficient and it would be more efficient to reflect on our own,
|
||||
// but it's complicated, error-prone code.
|
||||
buf, err := json.Marshal(i)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to marshal input to JSON")
|
||||
}
|
||||
var i2 interface{}
|
||||
err = json.Unmarshal(buf, &i2)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to unmarshal JSON")
|
||||
}
|
||||
fields, err := marshalDeepObject(i2, nil)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "error traversing JSON structure")
|
||||
}
|
||||
|
||||
// Prefix the param name to each subscripted field.
|
||||
for i := range fields {
|
||||
fields[i] = paramName + fields[i]
|
||||
}
|
||||
return strings.Join(fields, "&"), nil
|
||||
}
|
||||
|
||||
type fieldOrValue struct {
|
||||
fields map[string]fieldOrValue
|
||||
value string
|
||||
}
|
||||
|
||||
func (f *fieldOrValue) appendPathValue(path []string, value string) {
|
||||
fieldName := path[0]
|
||||
if len(path) == 1 {
|
||||
f.fields[fieldName] = fieldOrValue{value: value}
|
||||
return
|
||||
}
|
||||
|
||||
pv, found := f.fields[fieldName]
|
||||
if !found {
|
||||
pv = fieldOrValue{
|
||||
fields: make(map[string]fieldOrValue),
|
||||
}
|
||||
f.fields[fieldName] = pv
|
||||
}
|
||||
pv.appendPathValue(path[1:], value)
|
||||
}
|
||||
|
||||
func makeFieldOrValue(paths [][]string, values []string) fieldOrValue {
|
||||
|
||||
f := fieldOrValue{
|
||||
fields: make(map[string]fieldOrValue),
|
||||
}
|
||||
for i := range paths {
|
||||
path := paths[i]
|
||||
value := values[i]
|
||||
f.appendPathValue(path, value)
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func UnmarshalDeepObject(dst interface{}, paramName string, params url.Values) error {
|
||||
// Params are all the query args, so we need those that look like
|
||||
// "paramName["...
|
||||
var fieldNames []string
|
||||
var fieldValues []string
|
||||
searchStr := paramName + "["
|
||||
for pName, pValues := range params {
|
||||
if strings.HasPrefix(pName, searchStr) {
|
||||
// trim the parameter name from the full name.
|
||||
pName = pName[len(paramName):]
|
||||
fieldNames = append(fieldNames, pName)
|
||||
if len(pValues) != 1 {
|
||||
return fmt.Errorf("%s has multiple values", pName)
|
||||
}
|
||||
fieldValues = append(fieldValues, pValues[0])
|
||||
}
|
||||
}
|
||||
|
||||
// Now, for each field, reconstruct its subscript path and value
|
||||
paths := make([][]string, len(fieldNames))
|
||||
for i, path := range fieldNames {
|
||||
path = strings.TrimLeft(path, "[")
|
||||
path = strings.TrimRight(path, "]")
|
||||
paths[i] = strings.Split(path, "][")
|
||||
}
|
||||
|
||||
fieldPaths := makeFieldOrValue(paths, fieldValues)
|
||||
err := assignPathValues(dst, fieldPaths)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error assigning value to destination")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// This returns a field name, either using the variable name, or the json
|
||||
// annotation if that exists.
|
||||
func getFieldName(f reflect.StructField) string {
|
||||
n := f.Name
|
||||
tag, found := f.Tag.Lookup("json")
|
||||
if found {
|
||||
// If we have a json field, and the first part of it before the
|
||||
// first comma is non-empty, that's our field name.
|
||||
parts := strings.Split(tag, ",")
|
||||
if parts[0] != "" {
|
||||
n = parts[0]
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Create a map of field names that we'll see in the deepObject to reflect
|
||||
// field indices on the given type.
|
||||
func fieldIndicesByJsonTag(i interface{}) (map[string]int, error) {
|
||||
t := reflect.TypeOf(i)
|
||||
if t.Kind() != reflect.Struct {
|
||||
return nil, errors.New("expected a struct as input")
|
||||
}
|
||||
|
||||
n := t.NumField()
|
||||
fieldMap := make(map[string]int)
|
||||
for i := 0; i < n; i++ {
|
||||
field := t.Field(i)
|
||||
fieldName := getFieldName(field)
|
||||
fieldMap[fieldName] = i
|
||||
}
|
||||
return fieldMap, nil
|
||||
}
|
||||
|
||||
func assignPathValues(dst interface{}, pathValues fieldOrValue) error {
|
||||
//t := reflect.TypeOf(dst)
|
||||
v := reflect.ValueOf(dst)
|
||||
|
||||
iv := reflect.Indirect(v)
|
||||
it := iv.Type()
|
||||
|
||||
switch it.Kind() {
|
||||
case reflect.Slice:
|
||||
sliceLength := len(pathValues.fields)
|
||||
dstSlice := reflect.MakeSlice(it, sliceLength, sliceLength)
|
||||
err := assignSlice(dstSlice, pathValues)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error assigning slice")
|
||||
}
|
||||
iv.Set(dstSlice)
|
||||
return nil
|
||||
case reflect.Struct:
|
||||
// Some special types we care about are structs. Handle them
|
||||
// here. They may be redefined, so we need to do some hoop
|
||||
// jumping. If the types are aliased, we need to type convert
|
||||
// the pointer, then set the value of the dereference pointer.
|
||||
|
||||
// We check to see if the object implements the Binder interface first.
|
||||
if dst, isBinder := v.Interface().(Binder); isBinder {
|
||||
return dst.Bind(pathValues.value)
|
||||
}
|
||||
// Then check the legacy types
|
||||
if it.ConvertibleTo(reflect.TypeOf(types.Date{})) {
|
||||
var date types.Date
|
||||
var err error
|
||||
date.Time, err = time.Parse(types.DateFormat, pathValues.value)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "invalid date format")
|
||||
}
|
||||
dst := iv
|
||||
if it != reflect.TypeOf(types.Date{}) {
|
||||
// Types are aliased, convert the pointers.
|
||||
ivPtr := iv.Addr()
|
||||
aPtr := ivPtr.Convert(reflect.TypeOf(&types.Date{}))
|
||||
dst = reflect.Indirect(aPtr)
|
||||
}
|
||||
dst.Set(reflect.ValueOf(date))
|
||||
}
|
||||
if it.ConvertibleTo(reflect.TypeOf(time.Time{})) {
|
||||
var tm time.Time
|
||||
var err error
|
||||
tm, err = time.Parse(time.RFC3339Nano, pathValues.value)
|
||||
if err != nil {
|
||||
// Fall back to parsing it as a date.
|
||||
tm, err = time.Parse(types.DateFormat, pathValues.value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing tim as RFC3339 or 2006-01-02 time: %s", err)
|
||||
}
|
||||
return errors.Wrap(err, "invalid date format")
|
||||
}
|
||||
dst := iv
|
||||
if it != reflect.TypeOf(time.Time{}) {
|
||||
// Types are aliased, convert the pointers.
|
||||
ivPtr := iv.Addr()
|
||||
aPtr := ivPtr.Convert(reflect.TypeOf(&time.Time{}))
|
||||
dst = reflect.Indirect(aPtr)
|
||||
}
|
||||
dst.Set(reflect.ValueOf(tm))
|
||||
}
|
||||
fieldMap, err := fieldIndicesByJsonTag(iv.Interface())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed enumerating fields")
|
||||
}
|
||||
for _, fieldName := range sortedFieldOrValueKeys(pathValues.fields) {
|
||||
fieldValue := pathValues.fields[fieldName]
|
||||
fieldIndex, found := fieldMap[fieldName]
|
||||
if !found {
|
||||
return fmt.Errorf("field [%s] is not present in destination object", fieldName)
|
||||
}
|
||||
field := iv.Field(fieldIndex)
|
||||
err = assignPathValues(field.Addr().Interface(), fieldValue)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error assigning field [%s]", fieldName)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
case reflect.Ptr:
|
||||
// If we have a pointer after redirecting, it means we're dealing with
|
||||
// an optional field, such as *string, which was passed in as &foo. We
|
||||
// will allocate it if necessary, and call ourselves with a different
|
||||
// interface.
|
||||
dstVal := reflect.New(it.Elem())
|
||||
dstPtr := dstVal.Interface()
|
||||
err := assignPathValues(dstPtr, pathValues)
|
||||
iv.Set(dstVal)
|
||||
return err
|
||||
case reflect.Bool:
|
||||
val, err := strconv.ParseBool(pathValues.value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("expected a valid bool, got %s", pathValues.value)
|
||||
}
|
||||
iv.SetBool(val)
|
||||
return nil
|
||||
case reflect.Float32:
|
||||
val, err := strconv.ParseFloat(pathValues.value, 32)
|
||||
if err != nil {
|
||||
return fmt.Errorf("expected a valid float, got %s", pathValues.value)
|
||||
}
|
||||
iv.SetFloat(val)
|
||||
return nil
|
||||
case reflect.Float64:
|
||||
val, err := strconv.ParseFloat(pathValues.value, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("expected a valid float, got %s", pathValues.value)
|
||||
}
|
||||
iv.SetFloat(val)
|
||||
return nil
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
val, err := strconv.ParseInt(pathValues.value, 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("expected a valid int, got %s", pathValues.value)
|
||||
}
|
||||
iv.SetInt(val)
|
||||
return nil
|
||||
case reflect.String:
|
||||
iv.SetString(pathValues.value)
|
||||
return nil
|
||||
default:
|
||||
return errors.New("unhandled type: " + it.String())
|
||||
}
|
||||
}
|
||||
|
||||
func assignSlice(dst reflect.Value, pathValues fieldOrValue) error {
|
||||
// Gather up the values
|
||||
nValues := len(pathValues.fields)
|
||||
values := make([]string, nValues)
|
||||
// We expect to have consecutive array indices in the map
|
||||
for i := 0; i < nValues; i++ {
|
||||
indexStr := strconv.Itoa(i)
|
||||
fv, found := pathValues.fields[indexStr]
|
||||
if !found {
|
||||
return errors.New("array deepObjects must have consecutive indices")
|
||||
}
|
||||
values[i] = fv.value
|
||||
}
|
||||
|
||||
// This could be cleaner, but we can call into assignPathValues to
|
||||
// avoid recreating this logic.
|
||||
for i := 0; i < nValues; i++ {
|
||||
dstElem := dst.Index(i).Addr()
|
||||
err := assignPathValues(dstElem.Interface(), fieldOrValue{value: values[i]})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error binding array")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func sortedFieldOrValueKeys(m map[string]fieldOrValue) []string {
|
||||
keys := make([]string, 0, len(m))
|
||||
for k := range m {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
||||
+352
@@ -0,0 +1,352 @@
|
||||
// Copyright 2019 DeepMap, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/deepmap/oapi-codegen/pkg/types"
|
||||
)
|
||||
|
||||
// Given an input value, such as a primitive type, array or object, turn it
|
||||
// into a parameter based on style/explode definition.
|
||||
func StyleParam(style string, explode bool, paramName string, value interface{}) (string, error) {
|
||||
t := reflect.TypeOf(value)
|
||||
v := reflect.ValueOf(value)
|
||||
|
||||
// Things may be passed in by pointer, we need to dereference, so return
|
||||
// error on nil.
|
||||
if t.Kind() == reflect.Ptr {
|
||||
if v.IsNil() {
|
||||
return "", fmt.Errorf("value is a nil pointer")
|
||||
}
|
||||
v = reflect.Indirect(v)
|
||||
t = v.Type()
|
||||
}
|
||||
|
||||
switch t.Kind() {
|
||||
case reflect.Slice:
|
||||
n := v.Len()
|
||||
sliceVal := make([]interface{}, n)
|
||||
for i := 0; i < n; i++ {
|
||||
sliceVal[i] = v.Index(i).Interface()
|
||||
}
|
||||
return styleSlice(style, explode, paramName, sliceVal)
|
||||
case reflect.Struct:
|
||||
return styleStruct(style, explode, paramName, value)
|
||||
case reflect.Map:
|
||||
return styleMap(style, explode, paramName, value)
|
||||
default:
|
||||
return stylePrimitive(style, explode, paramName, value)
|
||||
}
|
||||
}
|
||||
|
||||
func styleSlice(style string, explode bool, paramName string, values []interface{}) (string, error) {
|
||||
if style == "deepObject" {
|
||||
if !explode {
|
||||
return "", errors.New("deepObjects must be exploded")
|
||||
}
|
||||
return MarshalDeepObject(values, paramName)
|
||||
}
|
||||
|
||||
var prefix string
|
||||
var separator string
|
||||
|
||||
switch style {
|
||||
case "simple":
|
||||
separator = ","
|
||||
case "label":
|
||||
prefix = "."
|
||||
if explode {
|
||||
separator = "."
|
||||
} else {
|
||||
separator = ","
|
||||
}
|
||||
case "matrix":
|
||||
prefix = fmt.Sprintf(";%s=", paramName)
|
||||
if explode {
|
||||
separator = prefix
|
||||
} else {
|
||||
separator = ","
|
||||
}
|
||||
case "form":
|
||||
prefix = fmt.Sprintf("%s=", paramName)
|
||||
if explode {
|
||||
separator = "&" + prefix
|
||||
} else {
|
||||
separator = ","
|
||||
}
|
||||
case "spaceDelimited":
|
||||
prefix = fmt.Sprintf("%s=", paramName)
|
||||
if explode {
|
||||
separator = "&" + prefix
|
||||
} else {
|
||||
separator = " "
|
||||
}
|
||||
case "pipeDelimited":
|
||||
prefix = fmt.Sprintf("%s=", paramName)
|
||||
if explode {
|
||||
separator = "&" + prefix
|
||||
} else {
|
||||
separator = "|"
|
||||
}
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported style '%s'", style)
|
||||
}
|
||||
|
||||
// We're going to assume here that the array is one of simple types.
|
||||
var err error
|
||||
parts := make([]string, len(values))
|
||||
for i, v := range values {
|
||||
parts[i], err = primitiveToString(v)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error formatting '%s': %s", paramName, err)
|
||||
}
|
||||
}
|
||||
return prefix + strings.Join(parts, separator), nil
|
||||
}
|
||||
|
||||
func sortedKeys(strMap map[string]string) []string {
|
||||
keys := make([]string, len(strMap))
|
||||
i := 0
|
||||
for k := range strMap {
|
||||
keys[i] = k
|
||||
i++
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
||||
|
||||
// This is a special case. The struct may be a date or time, in
|
||||
// which case, marshal it in correct format.
|
||||
func marshalDateTimeValue(value interface{}) (string, bool) {
|
||||
v := reflect.Indirect(reflect.ValueOf(value))
|
||||
t := v.Type()
|
||||
|
||||
if t.ConvertibleTo(reflect.TypeOf(time.Time{})) {
|
||||
tt := v.Convert(reflect.TypeOf(time.Time{}))
|
||||
timeVal := tt.Interface().(time.Time)
|
||||
return timeVal.Format(time.RFC3339Nano), true
|
||||
}
|
||||
|
||||
if t.ConvertibleTo(reflect.TypeOf(types.Date{})) {
|
||||
d := v.Convert(reflect.TypeOf(types.Date{}))
|
||||
dateVal := d.Interface().(types.Date)
|
||||
return dateVal.Format(types.DateFormat), true
|
||||
}
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
||||
func styleStruct(style string, explode bool, paramName string, value interface{}) (string, error) {
|
||||
|
||||
if timeVal, ok := marshalDateTimeValue(value); ok {
|
||||
styledVal, err := stylePrimitive(style, explode, paramName, timeVal)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to style time")
|
||||
}
|
||||
return styledVal, nil
|
||||
}
|
||||
|
||||
if style == "deepObject" {
|
||||
if !explode {
|
||||
return "", errors.New("deepObjects must be exploded")
|
||||
}
|
||||
return MarshalDeepObject(value, paramName)
|
||||
}
|
||||
|
||||
// Otherwise, we need to build a dictionary of the struct's fields. Each
|
||||
// field may only be a primitive value.
|
||||
v := reflect.ValueOf(value)
|
||||
t := reflect.TypeOf(value)
|
||||
fieldDict := make(map[string]string)
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
fieldT := t.Field(i)
|
||||
// Find the json annotation on the field, and use the json specified
|
||||
// name if available, otherwise, just the field name.
|
||||
tag := fieldT.Tag.Get("json")
|
||||
fieldName := fieldT.Name
|
||||
if tag != "" {
|
||||
tagParts := strings.Split(tag, ",")
|
||||
name := tagParts[0]
|
||||
if name != "" {
|
||||
fieldName = name
|
||||
}
|
||||
}
|
||||
f := v.Field(i)
|
||||
|
||||
// Unset optional fields will be nil pointers, skip over those.
|
||||
if f.Type().Kind() == reflect.Ptr && f.IsNil() {
|
||||
continue
|
||||
}
|
||||
str, err := primitiveToString(f.Interface())
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error formatting '%s': %s", paramName, err)
|
||||
}
|
||||
fieldDict[fieldName] = str
|
||||
}
|
||||
|
||||
return processFieldDict(style, explode, paramName, fieldDict)
|
||||
}
|
||||
|
||||
func styleMap(style string, explode bool, paramName string, value interface{}) (string, error) {
|
||||
if style == "deepObject" {
|
||||
if !explode {
|
||||
return "", errors.New("deepObjects must be exploded")
|
||||
}
|
||||
return MarshalDeepObject(value, paramName)
|
||||
}
|
||||
|
||||
dict, ok := value.(map[string]interface{})
|
||||
if !ok {
|
||||
return "", errors.New("map not of type map[string]interface{}")
|
||||
}
|
||||
|
||||
fieldDict := make(map[string]string)
|
||||
for fieldName, value := range dict {
|
||||
str, err := primitiveToString(value)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error formatting '%s': %s", paramName, err)
|
||||
}
|
||||
fieldDict[fieldName] = str
|
||||
}
|
||||
|
||||
return processFieldDict(style, explode, paramName, fieldDict)
|
||||
}
|
||||
|
||||
func processFieldDict(style string, explode bool, paramName string, fieldDict map[string]string) (string, error) {
|
||||
var parts []string
|
||||
|
||||
// This works for everything except deepObject. We'll handle that one
|
||||
// separately.
|
||||
if style != "deepObject" {
|
||||
if explode {
|
||||
for _, k := range sortedKeys(fieldDict) {
|
||||
v := fieldDict[k]
|
||||
parts = append(parts, k+"="+v)
|
||||
}
|
||||
} else {
|
||||
for _, k := range sortedKeys(fieldDict) {
|
||||
v := fieldDict[k]
|
||||
parts = append(parts, k)
|
||||
parts = append(parts, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var prefix string
|
||||
var separator string
|
||||
|
||||
switch style {
|
||||
case "simple":
|
||||
separator = ","
|
||||
case "label":
|
||||
prefix = "."
|
||||
if explode {
|
||||
separator = prefix
|
||||
} else {
|
||||
separator = ","
|
||||
}
|
||||
case "matrix":
|
||||
if explode {
|
||||
separator = ";"
|
||||
prefix = ";"
|
||||
} else {
|
||||
separator = ","
|
||||
prefix = fmt.Sprintf(";%s=", paramName)
|
||||
}
|
||||
case "form":
|
||||
if explode {
|
||||
separator = "&"
|
||||
} else {
|
||||
prefix = fmt.Sprintf("%s=", paramName)
|
||||
separator = ","
|
||||
}
|
||||
case "deepObject":
|
||||
{
|
||||
if !explode {
|
||||
return "", fmt.Errorf("deepObject parameters must be exploded")
|
||||
}
|
||||
for _, k := range sortedKeys(fieldDict) {
|
||||
v := fieldDict[k]
|
||||
part := fmt.Sprintf("%s[%s]=%s", paramName, k, v)
|
||||
parts = append(parts, part)
|
||||
}
|
||||
separator = "&"
|
||||
}
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported style '%s'", style)
|
||||
}
|
||||
|
||||
return prefix + strings.Join(parts, separator), nil
|
||||
}
|
||||
|
||||
func stylePrimitive(style string, explode bool, paramName string, value interface{}) (string, error) {
|
||||
strVal, err := primitiveToString(value)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var prefix string
|
||||
switch style {
|
||||
case "simple":
|
||||
case "label":
|
||||
prefix = "."
|
||||
case "matrix":
|
||||
prefix = fmt.Sprintf(";%s=", paramName)
|
||||
case "form":
|
||||
prefix = fmt.Sprintf("%s=", paramName)
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported style '%s'", style)
|
||||
}
|
||||
return prefix + url.QueryEscape(strVal), nil
|
||||
}
|
||||
|
||||
// Converts a primitive value to a string. We need to do this based on the
|
||||
// Kind of an interface, not the Type to work with aliased types.
|
||||
func primitiveToString(value interface{}) (string, error) {
|
||||
var output string
|
||||
|
||||
// Values may come in by pointer for optionals, so make sure to dereferene.
|
||||
v := reflect.Indirect(reflect.ValueOf(value))
|
||||
t := v.Type()
|
||||
kind := t.Kind()
|
||||
|
||||
switch kind {
|
||||
case reflect.Int8, reflect.Int32, reflect.Int64, reflect.Int:
|
||||
output = strconv.FormatInt(v.Int(), 10)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
output = strconv.FormatFloat(v.Float(), 'f', -1, 64)
|
||||
case reflect.Bool:
|
||||
if v.Bool() {
|
||||
output = "true"
|
||||
} else {
|
||||
output = "false"
|
||||
}
|
||||
case reflect.String:
|
||||
output = v.String()
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported type %s", reflect.TypeOf(value).String())
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
const DateFormat = "2006-01-02"
|
||||
|
||||
type Date struct {
|
||||
time.Time
|
||||
}
|
||||
|
||||
func (d Date) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(d.Time.Format(DateFormat))
|
||||
}
|
||||
|
||||
func (d *Date) UnmarshalJSON(data []byte) error {
|
||||
var dateStr string
|
||||
err := json.Unmarshal(data, &dateStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parsed, err := time.Parse(DateFormat, dateStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.Time = parsed
|
||||
return nil
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
)
|
||||
|
||||
type Email string
|
||||
|
||||
func (e Email) MarshalJSON() ([]byte, error) {
|
||||
if !emailRegex.MatchString(string(e)) {
|
||||
return nil, errors.New("email: failed to pass regex validation")
|
||||
}
|
||||
return json.Marshal(string(e))
|
||||
}
|
||||
|
||||
func (e *Email) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(data, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
if !emailRegex.MatchString(s) {
|
||||
return errors.New("email: failed to pass regex validation")
|
||||
}
|
||||
*e = Email(s)
|
||||
return nil
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package types
|
||||
|
||||
import "regexp"
|
||||
|
||||
const (
|
||||
emailRegexString = "^(?:(?:(?:(?:[a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(?:\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|(?:(?:\\x22)(?:(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(?:\\x20|\\x09)+)?(?:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(\\x20|\\x09)+)?(?:\\x22))))@(?:(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$"
|
||||
)
|
||||
|
||||
var (
|
||||
emailRegex = regexp.MustCompile(emailRegexString)
|
||||
)
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
version: "2"
|
||||
|
||||
checks:
|
||||
argument-count:
|
||||
enabled: false
|
||||
complex-logic:
|
||||
enabled: false
|
||||
file-lines:
|
||||
enabled: false
|
||||
method-complexity:
|
||||
enabled: false
|
||||
method-count:
|
||||
enabled: false
|
||||
method-lines:
|
||||
enabled: false
|
||||
nested-control-flow:
|
||||
enabled: false
|
||||
return-statements:
|
||||
enabled: false
|
||||
similar-code:
|
||||
enabled: false
|
||||
identical-code:
|
||||
enabled: false
|
||||
|
||||
plugins:
|
||||
gofmt:
|
||||
enabled: true
|
||||
golint:
|
||||
enabled: true
|
||||
govet:
|
||||
enabled: true
|
||||
+4
-1
@@ -1,3 +1,6 @@
|
||||
run:
|
||||
timeout: 10m
|
||||
|
||||
linters-settings:
|
||||
golint:
|
||||
min-confidence: 0.3
|
||||
@@ -9,8 +12,8 @@ linters-settings:
|
||||
linters:
|
||||
enable:
|
||||
- deadcode
|
||||
- dupl
|
||||
- errcheck
|
||||
- gocritic
|
||||
- gocyclo
|
||||
- goimports
|
||||
- golint
|
||||
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"Test": false,
|
||||
"Enable": [
|
||||
"deadcode",
|
||||
"errcheck",
|
||||
"golint",
|
||||
"gosimple",
|
||||
"gotype",
|
||||
"ineffassign",
|
||||
"interfacer",
|
||||
"misspell",
|
||||
"structcheck",
|
||||
"unconvert",
|
||||
"varcheck",
|
||||
"vet",
|
||||
"vetshadow"
|
||||
],
|
||||
"Disable": [
|
||||
"goconst",
|
||||
"gocyclo",
|
||||
"gosec",
|
||||
"maligned"
|
||||
],
|
||||
"Skip": ["vendor"]
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
language: go
|
||||
|
||||
dist: xenial
|
||||
sudo: required
|
||||
|
||||
go:
|
||||
- "1.7.x"
|
||||
- "1.8.x"
|
||||
- "1.9.x"
|
||||
- "1.10.x"
|
||||
- "1.11.x"
|
||||
- "1.12.x"
|
||||
- tip
|
||||
|
||||
env:
|
||||
- GOLANGCI_LINT_VERSION=1.15.0 GO111MODULES=on
|
||||
|
||||
cache: apt
|
||||
|
||||
addons:
|
||||
apt:
|
||||
update: true
|
||||
packages:
|
||||
- rpm
|
||||
|
||||
install:
|
||||
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $GOPATH/bin v${GOLANGCI_LINT_VERSION}
|
||||
- npm i codeclimate-test-reporter
|
||||
- '[ "$(echo "$TRAVIS_GO_VERSION" | perl -pe "s/\\.[x\\d]+$//")" = "1.11" ] && go mod vendor || go get -u github.com/gofrs/uuid'
|
||||
|
||||
before_script:
|
||||
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
||||
- chmod +x ./cc-test-reporter
|
||||
- ./cc-test-reporter before-build
|
||||
|
||||
script:
|
||||
- go test -race -coverprofile=c.out -covermode=atomic .
|
||||
|
||||
after_script:
|
||||
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- stage: golangci-lint
|
||||
go: 1.10.x
|
||||
if: type = pull_request
|
||||
script:
|
||||
- go get -u github.com/gofrs/uuid
|
||||
- golangci-lint run .
|
||||
+249
@@ -1,6 +1,255 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
0.43.1
|
||||
------
|
||||
|
||||
- change: in `NewClient()`, the `v2.Client` embedded in the `Client` struct doesn't inherit the custom `http.Client` set using `WithHTTPClient()`.
|
||||
|
||||
0.43.0
|
||||
------
|
||||
|
||||
- change: [Exoscale API V2](https://openapi-v2.exoscale.com/) related code has been relocated under the `github.com/exoscale/egoscale/v2` package.
|
||||
Note: `egoscale.Client` embeds a `v2.Client` initialized implicitly as a convenience.
|
||||
|
||||
0.42.0
|
||||
------
|
||||
|
||||
- feature: new `SKSNodepool.AntiAffinityGroupIDs` field
|
||||
- change: `SKSCluster.Level` field renamed as `SKSCluster.ServiceLevel`
|
||||
|
||||
0.41.0
|
||||
------
|
||||
|
||||
- feature: new method `ListZones()`
|
||||
|
||||
0.40.1
|
||||
------
|
||||
|
||||
- Improve API v2 async job tests and error reporting (#466)
|
||||
|
||||
0.40.0
|
||||
------
|
||||
|
||||
- feature: new method `UpgradeSKSCluster()`
|
||||
- feature: new fields `SKSCluster.Level` and `SKSCluster.CNI`
|
||||
- change: `SKSCluster.EnableExoscaleCloudController` replaced with `SKSCluster.AddOns`
|
||||
|
||||
0.39.1
|
||||
------
|
||||
|
||||
- fix: add missing `UpdateVirtualMachineSecurityGroups` operation metadata
|
||||
|
||||
0.39.0
|
||||
------
|
||||
|
||||
- feature: add `UpdateVirtualMachineSecurityGroups` operation (#464)
|
||||
|
||||
0.38.0
|
||||
------
|
||||
|
||||
- feature: add `SKSCluster.EvictNodepoolMembers()` and `ListSKSClusterVersions()` methods
|
||||
|
||||
0.37.1
|
||||
------
|
||||
|
||||
- fix: `UpdateIPAddress.HealthcheckTLSSkipVerify` field always set to `false` (#462)
|
||||
|
||||
0.37.0
|
||||
------
|
||||
|
||||
- feature: `NewClient()` now accepts options (460)
|
||||
- fix: NLB service healthcheck TLS SNI bug (#461)
|
||||
|
||||
0.36.2
|
||||
------
|
||||
|
||||
- fix: `CreateInstancePool.AntiAffinityGroupIDs` field is optional (#459)
|
||||
|
||||
0.36.1
|
||||
------
|
||||
|
||||
- feature: add support for Exoscale Cloud Controller in SKS clusters
|
||||
- fix: add missing tests for SKS Nodepools Security Groups
|
||||
|
||||
0.36.0
|
||||
------
|
||||
|
||||
- feature: add support for Anti-Affinity Groups to Instance Pools
|
||||
- feature: add support for Security Groups to SKS Nodepools
|
||||
|
||||
0.35.3
|
||||
------
|
||||
|
||||
- Fix typo in version.go
|
||||
|
||||
0.35.2
|
||||
------
|
||||
|
||||
- Improve API v2 errors handling (#455)
|
||||
|
||||
0.35.1
|
||||
------
|
||||
|
||||
- fix: various SKS-related bugs (#454)
|
||||
|
||||
0.35.0
|
||||
------
|
||||
|
||||
- feature: add support for SKS resources (#453)
|
||||
|
||||
0.34.0
|
||||
------
|
||||
|
||||
- change: `BucketUsage.Usage` is now an `int64` (#451)
|
||||
|
||||
0.33.2
|
||||
------
|
||||
|
||||
- fix: make `GetWithContext` return more relevant errors (#450)
|
||||
|
||||
0.33.1
|
||||
------
|
||||
|
||||
- fix: `UpdateNetworkLoadBalancer` call panicking following a public API change
|
||||
|
||||
0.33.0
|
||||
------
|
||||
|
||||
- feature: add support for Network Load Balancer service HTTPS health checking (#449)
|
||||
|
||||
0.32.0
|
||||
------
|
||||
|
||||
- feature: add support for Instance Pool root disk size update (#448)
|
||||
|
||||
0.31.2
|
||||
------
|
||||
|
||||
- fix: add missing TLS-specific parameters to `AssociateIPAddress`
|
||||
|
||||
0.31.1
|
||||
------
|
||||
|
||||
- fix: Instance Pool IPv6 flag handling
|
||||
|
||||
0.31.0
|
||||
------
|
||||
|
||||
- feature: add support for IPv6 in Instance Pools (#446)
|
||||
|
||||
0.30.0
|
||||
------
|
||||
|
||||
- feature: add new TLS-specific parameters to managed EIP
|
||||
|
||||
0.29.0
|
||||
------
|
||||
|
||||
- feature: `ListVirtualMachines` call to allow searching by `ManagerID` (#442)
|
||||
- fix: remove duplicate `User-Agent` HTTP header in Runstatus calls
|
||||
- tests: `*NetworkLoadBalancer*` calls are now tested using HTTP mocks
|
||||
- codegen: `internal/v2` updated
|
||||
|
||||
0.28.1
|
||||
------
|
||||
|
||||
- fix: Fix `ListVolumes` call to allow searching by ID (#440)
|
||||
|
||||
0.28.0
|
||||
------
|
||||
|
||||
- feature: add `Manager`/`ManagerID` fields to `VirtualMachine` structure (#438)
|
||||
- fix: HTTP request User Agent header handling (#439)
|
||||
|
||||
0.27.0
|
||||
------
|
||||
|
||||
- feature: Add `evictInstancePoolMembers` call to Instance Pool (#437)
|
||||
|
||||
0.26.6
|
||||
------
|
||||
|
||||
- change: Add support for Compute instance templates boot mode (#436)
|
||||
|
||||
0.26.5
|
||||
------
|
||||
|
||||
- fix: bug in the ListNetworkLoadBalancers call (#435)
|
||||
|
||||
0.26.4
|
||||
------
|
||||
|
||||
- Fixing typo in previous release
|
||||
|
||||
0.26.3
|
||||
------
|
||||
|
||||
- change: updated API V2 async operation code (#434)
|
||||
|
||||
0.26.2
|
||||
------
|
||||
|
||||
- change: updated OpenAPI code-generated API V2 bindings
|
||||
|
||||
0.26.1
|
||||
------
|
||||
|
||||
- change: the `DisplayText` property of `RegisterCustomTemplate` is now optional (#433)
|
||||
|
||||
0.26.0
|
||||
------
|
||||
|
||||
- feature: Add support for Network Load Balancer resources (#432)
|
||||
|
||||
0.25.0
|
||||
------
|
||||
|
||||
- feature: Add support for `listBucketsUsage` (#431)
|
||||
- change: Switch CI to Github Actions (#430)
|
||||
|
||||
0.24.0
|
||||
------
|
||||
|
||||
- feature: Add export snapshot implementation (#427)
|
||||
- feature: Add support for public API V2 (#425)
|
||||
- change: Switch module to Go 1.14 (#429)
|
||||
- change: Travis CI: set minimum Go version to 1.13
|
||||
- doc: Annotate API doc regarding use of tags (#423)
|
||||
- tests: fix request client timeout handling (#422)
|
||||
|
||||
0.23.0
|
||||
------
|
||||
|
||||
- change: Add `Resources` field to `APIKey` (#420)
|
||||
|
||||
0.22.0
|
||||
------
|
||||
|
||||
- change: Remove all references to Network Offerings (#418)
|
||||
|
||||
0.21.0
|
||||
------
|
||||
|
||||
- feature: add const `NotFound` 404 on type `ErrorCode` (#417)
|
||||
|
||||
0.20.1
|
||||
------
|
||||
|
||||
- fix: update the `ListAPIKeysResponse` field (#415)
|
||||
|
||||
0.20.0
|
||||
------
|
||||
|
||||
- feature: Add Instance pool implementation (#410)
|
||||
- feature: Add IAM implementation (#411)
|
||||
|
||||
0.19.0
|
||||
------
|
||||
|
||||
- feature: add field `Description` on type `IPAddress` (#413)
|
||||
- change: add Json tag `omitempty` on field `TemplateFilter` in type `ListTemplates` (#412)
|
||||
|
||||
0.18.1
|
||||
------
|
||||
|
||||
|
||||
+2
-1
@@ -5,7 +5,8 @@ description: the Go library for Exoscale
|
||||
|
||||
<a href="https://gopherize.me/gopher/9c1bc7cfe1d84cf43e477dbfc4aa86332065f1fd"><img src="gopher.png" align="right" alt=""></a>
|
||||
|
||||
[](https://travis-ci.org/exoscale/egoscale) [](https://codeclimate.com/github/exoscale/egoscale/maintainability) [](https://codeclimate.com/github/exoscale/egoscale/test_coverage) [](https://godoc.org/github.com/exoscale/egoscale) [](https://goreportcard.com/report/github.com/exoscale/egoscale)
|
||||
[](https://github.com/exoscale/egoscale/actions?query=workflow%3ACI+branch%3Amaster)
|
||||
[](https://godoc.org/github.com/exoscale/egoscale) [](https://goreportcard.com/report/github.com/exoscale/egoscale)
|
||||
|
||||
A wrapper for the [Exoscale public cloud](https://www.exoscale.com) API.
|
||||
|
||||
|
||||
+35
-26
@@ -8,13 +8,15 @@ import (
|
||||
|
||||
// Healthcheck represents an Healthcheck attached to an IP
|
||||
type Healthcheck struct {
|
||||
Interval int64 `json:"interval,omitempty" doc:"healthcheck definition: time in seconds to wait for each check. Default: 10, minimum: 5"`
|
||||
Mode string `json:"mode,omitempty" doc:"healthcheck definition: healthcheck mode can be either 'tcp' or 'http'"`
|
||||
Path string `json:"path,omitempty" doc:"healthcheck definition: the path against which the 'http' healthcheck will be performed. Required if mode is 'http', ignored otherwise."`
|
||||
Port int64 `json:"port,omitempty" doc:"healthcheck definition: the port against which the healthcheck will be performed. Required if a 'mode' is provided."`
|
||||
StrikesFail int64 `json:"strikes-fail,omitempty" doc:"healthcheck definition: number of times to retry before declaring the healthcheck 'dead'. Default: 3"`
|
||||
StrikesOk int64 `json:"strikes-ok,omitempty" doc:"healthcheck definition: number of times to retry before declaring the healthcheck 'alive'. Default: 2"`
|
||||
Timeout int64 `json:"timeout,omitempty" doc:"healthcheck definition: time in seconds to wait for each check. Default: 2, cannot be greater than interval."`
|
||||
Interval int64 `json:"interval,omitempty" doc:"healthcheck definition: time in seconds to wait for each check. Default: 10, minimum: 5"`
|
||||
Mode string `json:"mode,omitempty" doc:"healthcheck definition: healthcheck mode can be either 'tcp' or 'http'"`
|
||||
Path string `json:"path,omitempty" doc:"healthcheck definition: the path against which the 'http' healthcheck will be performed. Required if mode is 'http', ignored otherwise."`
|
||||
Port int64 `json:"port,omitempty" doc:"healthcheck definition: the port against which the healthcheck will be performed. Required if a 'mode' is provided."`
|
||||
StrikesFail int64 `json:"strikes-fail,omitempty" doc:"healthcheck definition: number of times to retry before declaring the healthcheck 'dead'. Default: 3"`
|
||||
StrikesOk int64 `json:"strikes-ok,omitempty" doc:"healthcheck definition: number of times to retry before declaring the healthcheck 'alive'. Default: 2"`
|
||||
Timeout int64 `json:"timeout,omitempty" doc:"healthcheck definition: time in seconds to wait for each check. Default: 2, cannot be greater than interval."`
|
||||
TLSSNI string `json:"tls-sni,omitempty" doc:"healthcheck definition: server name to present for HTTPS checks"`
|
||||
TLSSkipVerify bool `json:"tls-skip-verify" doc:"healthcheck definition: bypass certificate chain verification for HTTPS checks"`
|
||||
}
|
||||
|
||||
// IPAddress represents an IP Address
|
||||
@@ -23,6 +25,7 @@ type IPAddress struct {
|
||||
Associated string `json:"associated,omitempty" doc:"date the public IP address was associated"`
|
||||
AssociatedNetworkID *UUID `json:"associatednetworkid,omitempty" doc:"the ID of the Network associated with the IP address"`
|
||||
AssociatedNetworkName string `json:"associatednetworkname,omitempty" doc:"the name of the Network associated with the IP address"`
|
||||
Description string `json:"description,omitempty" doc:"The IP address description."`
|
||||
ForVirtualNetwork bool `json:"forvirtualnetwork,omitempty" doc:"the virtual network for the IP address"`
|
||||
Healthcheck *Healthcheck `json:"healthcheck,omitempty" doc:"The IP healthcheck configuration"`
|
||||
ID *UUID `json:"id,omitempty" doc:"public IP address id"`
|
||||
@@ -89,15 +92,18 @@ func (ipaddress IPAddress) Delete(ctx context.Context, client *Client) error {
|
||||
|
||||
// AssociateIPAddress (Async) represents the IP creation
|
||||
type AssociateIPAddress struct {
|
||||
HealthcheckInterval int64 `json:"interval,omitempty" doc:"healthcheck definition: time in seconds to wait for each check. Default: 10, minimum: 5"`
|
||||
HealthcheckMode string `json:"mode,omitempty" doc:"healthcheck definition: healthcheck mode can be either 'tcp' or 'http'"`
|
||||
HealthcheckPath string `json:"path,omitempty" doc:"healthcheck definition: the path against which the 'http' healthcheck will be performed. Required if mode is 'http', ignored otherwise."`
|
||||
HealthcheckPort int64 `json:"port,omitempty" doc:"healthcheck definition: the port against which the healthcheck will be performed. Required if a 'mode' is provided."`
|
||||
HealthcheckStrikesFail int64 `json:"strikes-fail,omitempty" doc:"healthcheck definition: number of times to retry before declaring the healthcheck 'dead'. Default: 3"`
|
||||
HealthcheckStrikesOk int64 `json:"strikes-ok,omitempty" doc:"healthcheck definition: number of times to retry before declaring the healthcheck 'alive'. Default: 2"`
|
||||
HealthcheckTimeout int64 `json:"timeout,omitempty" doc:"healthcheck definition: time in seconds to wait for each check. Default: 2, cannot be greater than interval."`
|
||||
ZoneID *UUID `json:"zoneid,omitempty" doc:"the ID of the availability zone you want to acquire a public IP address from"`
|
||||
_ bool `name:"associateIpAddress" description:"Acquires and associates a public IP to an account."`
|
||||
Description string `json:"description,omitempty" doc:"The IP address description."`
|
||||
HealthcheckInterval int64 `json:"interval,omitempty" doc:"healthcheck definition: time in seconds to wait for each check. Default: 10, minimum: 5"`
|
||||
HealthcheckMode string `json:"mode,omitempty" doc:"healthcheck definition: healthcheck mode can be either 'tcp', 'http', or 'https'"`
|
||||
HealthcheckPath string `json:"path,omitempty" doc:"healthcheck definition: the path against which the 'http' healthcheck will be performed. Required if mode is 'http' or 'https', ignored otherwise."`
|
||||
HealthcheckPort int64 `json:"port,omitempty" doc:"healthcheck definition: the port against which the healthcheck will be performed. Required if a 'mode' is provided."`
|
||||
HealthcheckStrikesFail int64 `json:"strikes-fail,omitempty" doc:"healthcheck definition: number of times to retry before declaring the healthcheck 'dead'. Default: 3"`
|
||||
HealthcheckStrikesOk int64 `json:"strikes-ok,omitempty" doc:"healthcheck definition: number of times to retry before declaring the healthcheck 'alive'. Default: 2"`
|
||||
HealthcheckTimeout int64 `json:"timeout,omitempty" doc:"healthcheck definition: time in seconds to wait for each check. Default: 2, cannot be greater than interval."`
|
||||
HealthcheckTLSSkipVerify bool `json:"tls-skip-verify,omitempty" doc:"healthcheck definition: skip TLS verification for HTTPS checks. Default: false"`
|
||||
HealthcheckTLSSNI string `json:"tls-sni,omitempty" doc:"healthcheck definition: server name to present for HTTPS checks. Default: no server name is presented"`
|
||||
ZoneID *UUID `json:"zoneid,omitempty" doc:"the ID of the availability zone you want to acquire a public IP address from"`
|
||||
_ bool `name:"associateIpAddress" description:"Acquires and associates a public IP to an account."`
|
||||
}
|
||||
|
||||
// Response returns the struct to unmarshal
|
||||
@@ -128,15 +134,18 @@ func (DisassociateIPAddress) AsyncResponse() interface{} {
|
||||
|
||||
// UpdateIPAddress (Async) represents the IP modification
|
||||
type UpdateIPAddress struct {
|
||||
HealthcheckInterval int64 `json:"interval,omitempty" doc:"healthcheck definition: time in seconds to wait for each check. Default: 10, minimum: 5"`
|
||||
HealthcheckMode string `json:"mode,omitempty" doc:"healthcheck definition: healthcheck mode can be either 'tcp' or 'http'"`
|
||||
HealthcheckPath string `json:"path,omitempty" doc:"healthcheck definition: the path against which the 'http' healthcheck will be performed. Required if mode is 'http', ignored otherwise."`
|
||||
HealthcheckPort int64 `json:"port,omitempty" doc:"healthcheck definition: the port against which the healthcheck will be performed. Required if a 'mode' is provided."`
|
||||
HealthcheckStrikesFail int64 `json:"strikes-fail,omitempty" doc:"healthcheck definition: number of times to retry before declaring the healthcheck 'dead'. Default: 3"`
|
||||
HealthcheckStrikesOk int64 `json:"strikes-ok,omitempty" doc:"healthcheck definition: number of times to retry before declaring the healthcheck 'alive'. Default: 2"`
|
||||
HealthcheckTimeout int64 `json:"timeout,omitempty" doc:"healthcheck definition: time in seconds to wait for each check. Default: 2, cannot be greater than interval."`
|
||||
ID *UUID `json:"id" doc:"the id of the public IP address to update"`
|
||||
_ bool `name:"updateIpAddress" description:"Updates an IP address"`
|
||||
Description string `json:"description,omitempty" doc:"The IP address description."`
|
||||
HealthcheckInterval int64 `json:"interval,omitempty" doc:"healthcheck definition: time in seconds to wait for each check. Default: 10, minimum: 5"`
|
||||
HealthcheckMode string `json:"mode,omitempty" doc:"healthcheck definition: healthcheck mode can be either 'tcp', 'http', or 'https'"`
|
||||
HealthcheckPath string `json:"path,omitempty" doc:"healthcheck definition: the path against which the 'http' healthcheck will be performed. Required if mode is 'http', ignored otherwise."`
|
||||
HealthcheckPort int64 `json:"port,omitempty" doc:"healthcheck definition: the port against which the healthcheck will be performed. Required if a 'mode' is provided."`
|
||||
HealthcheckStrikesFail int64 `json:"strikes-fail,omitempty" doc:"healthcheck definition: number of times to retry before declaring the healthcheck 'dead'. Default: 3"`
|
||||
HealthcheckStrikesOk int64 `json:"strikes-ok,omitempty" doc:"healthcheck definition: number of times to retry before declaring the healthcheck 'alive'. Default: 2"`
|
||||
HealthcheckTimeout int64 `json:"timeout,omitempty" doc:"healthcheck definition: time in seconds to wait for each check. Default: 2, cannot be greater than interval."`
|
||||
HealthcheckTLSSNI string `json:"tls-sni,omitempty" doc:"healthcheck definition: server name to present for HTTPS checks"`
|
||||
HealthcheckTLSSkipVerify bool `json:"tls-skip-verify,omitempty" doc:"healthcheck definition: bypass certificate chain verification for HTTPS checks"`
|
||||
ID *UUID `json:"id" doc:"the id of the public IP address to update"`
|
||||
_ bool `name:"updateIpAddress" description:"Updates an IP address"`
|
||||
}
|
||||
|
||||
// Response returns the struct to unmarshal
|
||||
@@ -166,7 +175,7 @@ type ListPublicIPAddresses struct {
|
||||
Page int `json:"page,omitempty"`
|
||||
PageSize int `json:"pagesize,omitempty"`
|
||||
PhysicalNetworkID *UUID `json:"physicalnetworkid,omitempty" doc:"lists all public IP addresses by physical network id"`
|
||||
Tags []ResourceTag `json:"tags,omitempty" doc:"List resources by tags (key/value pairs)"`
|
||||
Tags []ResourceTag `json:"tags,omitempty" doc:"List resources by tags (key/value pairs). Note: multiple tags are OR'ed, not AND'ed."`
|
||||
VlanID *UUID `json:"vlanid,omitempty" doc:"lists all public IP addresses by VLAN ID"`
|
||||
ZoneID *UUID `json:"zoneid,omitempty" doc:"lists all public IP addresses by Zone ID"`
|
||||
_ bool `name:"listPublicIpAddresses" description:"Lists all public ip addresses"`
|
||||
|
||||
+44
-44
@@ -6,20 +6,20 @@ import (
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// AffinityGroup represents an (anti-)affinity group
|
||||
// AffinityGroup represents an Affinity Group.
|
||||
//
|
||||
// Affinity and Anti-Affinity groups provide a way to influence where VMs should run.
|
||||
// Affinity and Anti-Affinity Groups provide a way to influence where VMs should run.
|
||||
// See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/stable/virtual_machines.html#affinity-groups
|
||||
type AffinityGroup struct {
|
||||
Account string `json:"account,omitempty" doc:"the account owning the affinity group"`
|
||||
Description string `json:"description,omitempty" doc:"the description of the affinity group"`
|
||||
ID *UUID `json:"id,omitempty" doc:"the ID of the affinity group"`
|
||||
Name string `json:"name,omitempty" doc:"the name of the affinity group"`
|
||||
Type string `json:"type,omitempty" doc:"the type of the affinity group"`
|
||||
VirtualMachineIDs []UUID `json:"virtualmachineIds,omitempty" doc:"virtual machine Ids associated with this affinity group"`
|
||||
Account string `json:"account,omitempty" doc:"the account owning the Affinity Group"`
|
||||
Description string `json:"description,omitempty" doc:"the description of the Affinity Group"`
|
||||
ID *UUID `json:"id,omitempty" doc:"the ID of the Affinity Group"`
|
||||
Name string `json:"name,omitempty" doc:"the name of the Affinity Group"`
|
||||
Type string `json:"type,omitempty" doc:"the type of the Affinity Group"`
|
||||
VirtualMachineIDs []UUID `json:"virtualmachineIds,omitempty" doc:"virtual machine IDs associated with this Affinity Group"`
|
||||
}
|
||||
|
||||
// ListRequest builds the ListAffinityGroups request
|
||||
// ListRequest builds the ListAffinityGroups request.
|
||||
func (ag AffinityGroup) ListRequest() (ListCommand, error) {
|
||||
return &ListAffinityGroups{
|
||||
ID: ag.ID,
|
||||
@@ -27,7 +27,7 @@ func (ag AffinityGroup) ListRequest() (ListCommand, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Delete removes the given Affinity Group
|
||||
// Delete deletes the given Affinity Group.
|
||||
func (ag AffinityGroup) Delete(ctx context.Context, client *Client) error {
|
||||
if ag.ID == nil && ag.Name == "" {
|
||||
return fmt.Errorf("an Affinity Group may only be deleted using ID or Name")
|
||||
@@ -44,114 +44,114 @@ func (ag AffinityGroup) Delete(ctx context.Context, client *Client) error {
|
||||
return client.BooleanRequestWithContext(ctx, req)
|
||||
}
|
||||
|
||||
// AffinityGroupType represent an affinity group type
|
||||
// AffinityGroupType represent an Affinity Group type.
|
||||
type AffinityGroupType struct {
|
||||
Type string `json:"type,omitempty" doc:"the type of the affinity group"`
|
||||
Type string `json:"type,omitempty" doc:"the type of the Affinity Group"`
|
||||
}
|
||||
|
||||
// CreateAffinityGroup (Async) represents a new (anti-)affinity group
|
||||
// CreateAffinityGroup (Async) represents a new Affinity Group.
|
||||
type CreateAffinityGroup struct {
|
||||
Description string `json:"description,omitempty" doc:"Optional description of the affinity group"`
|
||||
Name string `json:"name,omitempty" doc:"Name of the affinity group"`
|
||||
Type string `json:"type" doc:"Type of the affinity group from the available affinity/anti-affinity group types"`
|
||||
_ bool `name:"createAffinityGroup" description:"Creates an affinity/anti-affinity group"`
|
||||
Description string `json:"description,omitempty" doc:"Optional description of the Affinity Group"`
|
||||
Name string `json:"name" doc:"Name of the Affinity Group"`
|
||||
Type string `json:"type" doc:"Type of the Affinity Group from the available Affinity Group Group types"`
|
||||
_ bool `name:"createAffinityGroup" description:"Creates an Affinity Group Group"`
|
||||
}
|
||||
|
||||
func (req CreateAffinityGroup) onBeforeSend(params url.Values) error {
|
||||
// Name must be set, but can be empty
|
||||
// Name must be set, but can be empty.
|
||||
if req.Name == "" {
|
||||
params.Set("name", "")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Response returns the struct to unmarshal
|
||||
// Response returns the struct to unmarshal.
|
||||
func (CreateAffinityGroup) Response() interface{} {
|
||||
return new(AsyncJobResult)
|
||||
}
|
||||
|
||||
// AsyncResponse returns the struct to unmarshal the async job
|
||||
// AsyncResponse returns the struct to unmarshal the async job.
|
||||
func (CreateAffinityGroup) AsyncResponse() interface{} {
|
||||
return new(AffinityGroup)
|
||||
}
|
||||
|
||||
// UpdateVMAffinityGroup (Async) represents a modification of a (anti-)affinity group
|
||||
// UpdateVMAffinityGroup (Async) represents a modification of an Affinity Group.
|
||||
type UpdateVMAffinityGroup struct {
|
||||
ID *UUID `json:"id" doc:"The ID of the virtual machine"`
|
||||
AffinityGroupIDs []UUID `json:"affinitygroupids,omitempty" doc:"comma separated list of affinity groups id that are going to be applied to the virtual machine. Should be passed only when vm is created from a zone with Basic Network support. Mutually exclusive with securitygroupnames parameter"`
|
||||
AffinityGroupNames []string `json:"affinitygroupnames,omitempty" doc:"comma separated list of affinity groups names that are going to be applied to the virtual machine. Should be passed only when vm is created from a zone with Basic Network support. Mutually exclusive with securitygroupids parameter"`
|
||||
_ bool `name:"updateVMAffinityGroup" description:"Updates the affinity/anti-affinity group associations of a virtual machine. The VM has to be stopped and restarted for the new properties to take effect."`
|
||||
AffinityGroupIDs []UUID `json:"affinitygroupids,omitempty" doc:"comma separated list of Affinity Groups id that are going to be applied to the virtual machine. Should be passed only when vm is created from a zone with Basic Network support. Mutually exclusive with securitygroupnames parameter"`
|
||||
AffinityGroupNames []string `json:"affinitygroupnames,omitempty" doc:"comma separated list of Affinity Groups names that are going to be applied to the virtual machine. Should be passed only when vm is created from a zone with Basic Network support. Mutually exclusive with securitygroupids parameter"`
|
||||
_ bool `name:"updateVMAffinityGroup" description:"Updates the Affinity Group Group associations of a virtual machine. The VM has to be stopped and restarted for the new properties to take effect."`
|
||||
}
|
||||
|
||||
func (req UpdateVMAffinityGroup) onBeforeSend(params url.Values) error {
|
||||
// Either AffinityGroupIDs or AffinityGroupNames must be set
|
||||
// Either AffinityGroupIDs or AffinityGroupNames must be set.
|
||||
if len(req.AffinityGroupIDs) == 0 && len(req.AffinityGroupNames) == 0 {
|
||||
params.Set("affinitygroupids", "")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Response returns the struct to unmarshal
|
||||
// Response returns the struct to unmarshal.
|
||||
func (UpdateVMAffinityGroup) Response() interface{} {
|
||||
return new(AsyncJobResult)
|
||||
}
|
||||
|
||||
// AsyncResponse returns the struct to unmarshal the async job
|
||||
// AsyncResponse returns the struct to unmarshal the async job.
|
||||
func (UpdateVMAffinityGroup) AsyncResponse() interface{} {
|
||||
return new(VirtualMachine)
|
||||
}
|
||||
|
||||
// DeleteAffinityGroup (Async) represents an (anti-)affinity group to be deleted
|
||||
// DeleteAffinityGroup (Async) represents an Affinity Group to be deleted.
|
||||
type DeleteAffinityGroup struct {
|
||||
ID *UUID `json:"id,omitempty" doc:"The ID of the affinity group. Mutually exclusive with name parameter"`
|
||||
Name string `json:"name,omitempty" doc:"The name of the affinity group. Mutually exclusive with ID parameter"`
|
||||
_ bool `name:"deleteAffinityGroup" description:"Deletes affinity group"`
|
||||
ID *UUID `json:"id,omitempty" doc:"The ID of the Affinity Group. Mutually exclusive with name parameter"`
|
||||
Name string `json:"name,omitempty" doc:"The name of the Affinity Group. Mutually exclusive with ID parameter"`
|
||||
_ bool `name:"deleteAffinityGroup" description:"Deletes Affinity Group"`
|
||||
}
|
||||
|
||||
// Response returns the struct to unmarshal
|
||||
// Response returns the struct to unmarshal.
|
||||
func (DeleteAffinityGroup) Response() interface{} {
|
||||
return new(AsyncJobResult)
|
||||
}
|
||||
|
||||
// AsyncResponse returns the struct to unmarshal the async job
|
||||
// AsyncResponse returns the struct to unmarshal the async job.
|
||||
func (DeleteAffinityGroup) AsyncResponse() interface{} {
|
||||
return new(BooleanResponse)
|
||||
}
|
||||
|
||||
//go:generate go run generate/main.go -interface=Listable ListAffinityGroups
|
||||
|
||||
// ListAffinityGroups represents an (anti-)affinity groups search
|
||||
// ListAffinityGroups represents an Affinity Groups search.
|
||||
type ListAffinityGroups struct {
|
||||
ID *UUID `json:"id,omitempty" doc:"List the affinity group by the ID provided"`
|
||||
ID *UUID `json:"id,omitempty" doc:"List the Affinity Group by the ID provided"`
|
||||
Keyword string `json:"keyword,omitempty" doc:"List by keyword"`
|
||||
Name string `json:"name,omitempty" doc:"Lists affinity groups by name"`
|
||||
Name string `json:"name,omitempty" doc:"Lists Affinity Groups by name"`
|
||||
Page int `json:"page,omitempty"`
|
||||
PageSize int `json:"pagesize,omitempty"`
|
||||
Type string `json:"type,omitempty" doc:"Lists affinity groups by type"`
|
||||
VirtualMachineID *UUID `json:"virtualmachineid,omitempty" doc:"Lists affinity groups by virtual machine ID"`
|
||||
_ bool `name:"listAffinityGroups" description:"Lists affinity groups"`
|
||||
Type string `json:"type,omitempty" doc:"Lists Affinity Groups by type"`
|
||||
VirtualMachineID *UUID `json:"virtualmachineid,omitempty" doc:"Lists Affinity Groups by virtual machine ID"`
|
||||
_ bool `name:"listAffinityGroups" description:"Lists Affinity Groups"`
|
||||
}
|
||||
|
||||
// ListAffinityGroupsResponse represents a list of (anti-)affinity groups
|
||||
// ListAffinityGroupsResponse represents a list of Affinity Groups.
|
||||
type ListAffinityGroupsResponse struct {
|
||||
Count int `json:"count"`
|
||||
AffinityGroup []AffinityGroup `json:"affinitygroup"`
|
||||
}
|
||||
|
||||
// ListAffinityGroupTypes represents an (anti-)affinity groups search
|
||||
// ListAffinityGroupTypes represents an Affinity Groups types search.
|
||||
type ListAffinityGroupTypes struct {
|
||||
Keyword string `json:"keyword,omitempty" doc:"List by keyword"`
|
||||
Page int `json:"page,omitempty"`
|
||||
PageSize int `json:"pagesize,omitempty"`
|
||||
_ bool `name:"listAffinityGroupTypes" description:"Lists affinity group types available"`
|
||||
_ bool `name:"listAffinityGroupTypes" description:"Lists Affinity Group types available"`
|
||||
}
|
||||
|
||||
// Response returns the struct to unmarshal
|
||||
// Response returns the struct to unmarshal.
|
||||
func (ListAffinityGroupTypes) Response() interface{} {
|
||||
return new(ListAffinityGroupTypesResponse)
|
||||
}
|
||||
|
||||
// ListAffinityGroupTypesResponse represents a list of (anti-)affinity group types
|
||||
// ListAffinityGroupTypesResponse represents a list of Affinity Group types.
|
||||
type ListAffinityGroupTypesResponse struct {
|
||||
Count int `json:"count"`
|
||||
AffinityGroupType []AffinityGroupType `json:"affinitygrouptype"`
|
||||
|
||||
+5
-5
@@ -4,12 +4,12 @@ package egoscale
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Response returns the struct to unmarshal
|
||||
// Response returns the struct to unmarshal.
|
||||
func (ListAffinityGroups) Response() interface{} {
|
||||
return new(ListAffinityGroupsResponse)
|
||||
}
|
||||
|
||||
// ListRequest returns itself
|
||||
// ListRequest returns itself.
|
||||
func (ls *ListAffinityGroups) ListRequest() (ListCommand, error) {
|
||||
if ls == nil {
|
||||
return nil, fmt.Errorf("%T cannot be nil", ls)
|
||||
@@ -17,17 +17,17 @@ func (ls *ListAffinityGroups) ListRequest() (ListCommand, error) {
|
||||
return ls, nil
|
||||
}
|
||||
|
||||
// SetPage sets the current apge
|
||||
// SetPage sets the current page.
|
||||
func (ls *ListAffinityGroups) SetPage(page int) {
|
||||
ls.Page = page
|
||||
}
|
||||
|
||||
// SetPageSize sets the page size
|
||||
// SetPageSize sets the page size.
|
||||
func (ls *ListAffinityGroups) SetPageSize(pageSize int) {
|
||||
ls.PageSize = pageSize
|
||||
}
|
||||
|
||||
// Each triggers the callback for each, valid answer or any non 404 issue
|
||||
// Each triggers the callback for each, valid answer or any non 404 issue.
|
||||
func (ListAffinityGroups) Each(resp interface{}, callback IterateItemFunc) {
|
||||
items, ok := resp.(*ListAffinityGroupsResponse)
|
||||
if !ok {
|
||||
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
package egoscale
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// AntiAffinityGroup represents an Anti-Affinity Group.
|
||||
type AntiAffinityGroup struct {
|
||||
Account string `json:"account,omitempty" doc:"the account owning the Anti-Affinity Group"`
|
||||
Description string `json:"description,omitempty" doc:"the description of the Anti-Affinity Group"`
|
||||
ID *UUID `json:"id,omitempty" doc:"the ID of the Anti-Affinity Group"`
|
||||
Name string `json:"name,omitempty" doc:"the name of the Anti-Affinity Group"`
|
||||
Type string `json:"type,omitempty" doc:"the type of the Anti-Affinity Group"`
|
||||
VirtualMachineIDs []UUID `json:"virtualmachineIds,omitempty" doc:"virtual machine IDs associated with this Anti-Affinity Group"`
|
||||
}
|
||||
|
||||
// ListRequest builds the ListAntiAffinityGroups request.
|
||||
func (ag AntiAffinityGroup) ListRequest() (ListCommand, error) {
|
||||
return &ListAffinityGroups{
|
||||
ID: ag.ID,
|
||||
Name: ag.Name,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Delete deletes the given Anti-Affinity Group.
|
||||
func (ag AntiAffinityGroup) Delete(ctx context.Context, client *Client) error {
|
||||
if ag.ID == nil && ag.Name == "" {
|
||||
return fmt.Errorf("an Anti-Affinity Group may only be deleted using ID or Name")
|
||||
}
|
||||
|
||||
req := &DeleteAffinityGroup{}
|
||||
|
||||
if ag.ID != nil {
|
||||
req.ID = ag.ID
|
||||
} else {
|
||||
req.Name = ag.Name
|
||||
}
|
||||
|
||||
return client.BooleanRequestWithContext(ctx, req)
|
||||
}
|
||||
|
||||
// CreateAntiAffinityGroup represents an Anti-Affinity Group creation.
|
||||
type CreateAntiAffinityGroup struct {
|
||||
Name string `json:"name" doc:"Name of the Anti-Affinity Group"`
|
||||
Description string `json:"description,omitempty" doc:"Optional description of the Anti-Affinity Group"`
|
||||
_ bool `name:"createAntiAffinityGroup" description:"Creates an Anti-Affinity Group"`
|
||||
}
|
||||
|
||||
func (req CreateAntiAffinityGroup) onBeforeSend(params url.Values) error {
|
||||
// Name must be set, but can be empty.
|
||||
if req.Name == "" {
|
||||
params.Set("name", "")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Response returns the struct to unmarshal.
|
||||
func (CreateAntiAffinityGroup) Response() interface{} {
|
||||
return new(AsyncJobResult)
|
||||
}
|
||||
|
||||
// AsyncResponse returns the struct to unmarshal the async job.
|
||||
func (CreateAntiAffinityGroup) AsyncResponse() interface{} {
|
||||
return new(AffinityGroup)
|
||||
}
|
||||
|
||||
//go:generate go run generate/main.go -interface=Listable ListAntiAffinityGroups
|
||||
|
||||
// ListAntiAffinityGroups represents an Anti-Affinity Groups search.
|
||||
type ListAntiAffinityGroups struct {
|
||||
ID *UUID `json:"id,omitempty" doc:"List the Anti-Affinity Group by the ID provided"`
|
||||
Keyword string `json:"keyword,omitempty" doc:"List by keyword"`
|
||||
Name string `json:"name,omitempty" doc:"Lists Anti-Affinity Groups by name"`
|
||||
Page int `json:"page,omitempty"`
|
||||
PageSize int `json:"pagesize,omitempty"`
|
||||
VirtualMachineID *UUID `json:"virtualmachineid,omitempty" doc:"Lists Anti-Affinity Groups by virtual machine ID"`
|
||||
_ bool `name:"listAntiAffinityGroups" description:"Lists Anti-Affinity Groups"`
|
||||
}
|
||||
|
||||
// ListAntiAffinityGroupsResponse represents a list of Anti-Affinity Groups.
|
||||
type ListAntiAffinityGroupsResponse struct {
|
||||
Count int `json:"count"`
|
||||
AntiAffinityGroup []AffinityGroup `json:"antiaffinitygroup"`
|
||||
}
|
||||
|
||||
// DeleteAntiAffinityGroup (Async) represents an Anti-Affinity Group to be deleted.
|
||||
type DeleteAntiAffinityGroup struct {
|
||||
ID *UUID `json:"id,omitempty" doc:"The ID of the Anti-Affinity Group. Mutually exclusive with name parameter"`
|
||||
Name string `json:"name,omitempty" doc:"The name of the Anti-Affinity Group. Mutually exclusive with ID parameter"`
|
||||
_ bool `name:"deleteAntiAffinityGroup" description:"Deletes Anti-Affinity Group"`
|
||||
}
|
||||
|
||||
// Response returns the struct to unmarshal.
|
||||
func (DeleteAntiAffinityGroup) Response() interface{} {
|
||||
return new(AsyncJobResult)
|
||||
}
|
||||
|
||||
// AsyncResponse returns the struct to unmarshal the async job.
|
||||
func (DeleteAntiAffinityGroup) AsyncResponse() interface{} {
|
||||
return new(BooleanResponse)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user