From 59dac451a5a0c62e6507579b5eed279283ecc7ca Mon Sep 17 00:00:00 2001 From: nouney Date: Thu, 15 Sep 2016 22:15:56 +0200 Subject: [PATCH 0001/1216] provisioner/file: add "generated" key to allow files created on-the-fly. --- provisioner/file/provisioner.go | 5 ++++- provisioner/file/provisioner_test.go | 25 ++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/provisioner/file/provisioner.go b/provisioner/file/provisioner.go index 2b9a6b5fe..207885bfe 100644 --- a/provisioner/file/provisioner.go +++ b/provisioner/file/provisioner.go @@ -26,6 +26,9 @@ type Config struct { // Direction Direction string + // False if the sources have to exist. + Generated bool + ctx interpolate.Context } @@ -61,7 +64,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { if p.config.Direction == "upload" { for _, src := range p.config.Sources { - if _, err := os.Stat(src); err != nil { + if _, err := os.Stat(src); p.config.Generated == false && err != nil { errs = packer.MultiErrorAppend(errs, fmt.Errorf("Bad source '%s': %s", src, err)) } diff --git a/provisioner/file/provisioner_test.go b/provisioner/file/provisioner_test.go index 53dc315d4..5a2b67037 100644 --- a/provisioner/file/provisioner_test.go +++ b/provisioner/file/provisioner_test.go @@ -45,6 +45,12 @@ func TestProvisionerPrepare_InvalidSource(t *testing.T) { if err == nil { t.Fatalf("should require existing file") } + + config["generated"] = false + err = p.Prepare(config) + if err == nil { + t.Fatalf("should required existing file") + } } func TestProvisionerPrepare_ValidSource(t *testing.T) { @@ -58,11 +64,28 @@ func TestProvisionerPrepare_ValidSource(t *testing.T) { config := testConfig() config["source"] = tf.Name() - err = p.Prepare(config) if err != nil { t.Fatalf("should allow valid file: %s", err) } + + config["generated"] = false + err = p.Prepare(config) + if err != nil { + t.Fatalf("should allow valid file: %s", err) + } +} + +func TestProvisionerPrepare_GeneratedSource(t *testing.T) { + var p Provisioner + + config := testConfig() + config["source"] = "/this/should/not/exist" + config["generated"] = true + err := p.Prepare(config) + if err != nil { + t.Fatalf("should allow non-existing file: %s", err) + } } func TestProvisionerPrepare_EmptyDestination(t *testing.T) { From ed32b6e3c6da1b79eb54d959be22e6cdb06177a2 Mon Sep 17 00:00:00 2001 From: nouney Date: Thu, 15 Sep 2016 22:46:10 +0200 Subject: [PATCH 0002/1216] provisioner/file: document "generated" key. --- website/source/docs/provisioners/file.html.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/website/source/docs/provisioners/file.html.md b/website/source/docs/provisioners/file.html.md index bb66fc220..fb932b512 100644 --- a/website/source/docs/provisioners/file.html.md +++ b/website/source/docs/provisioners/file.html.md @@ -46,6 +46,9 @@ The available configuration options are listed below. All elements are required. "upload." If it is set to "download" then the file "source" in the machine will be downloaded locally to "destination" +- `generated` (boolean) - If true, check the file existence only before uploading. + This allows to upload files created on-the-fly. This defaults to false. + ## Directory Uploads The file provisioner is also able to upload a complete directory to the remote From 5dc6b183655d4eabfe4c9fb6dc22a2e9522e2ac4 Mon Sep 17 00:00:00 2001 From: YAMADA Tsuyoshi Date: Thu, 10 Nov 2016 18:50:31 +0900 Subject: [PATCH 0003/1216] googlecompute-export: use application default credential as same as googlecompute builder --- .../googlecompute-export/post-processor.go | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/post-processor/googlecompute-export/post-processor.go b/post-processor/googlecompute-export/post-processor.go index f198ab7c8..51a61c9a3 100644 --- a/post-processor/googlecompute-export/post-processor.go +++ b/post-processor/googlecompute-export/post-processor.go @@ -2,7 +2,6 @@ package googlecomputeexport import ( "fmt" - "io/ioutil" "strings" "github.com/mitchellh/multistep" @@ -83,13 +82,12 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac exporterConfig.CalcTimeout() // Set up credentials and GCE driver. - b, err := ioutil.ReadFile(accountKeyFilePath) - if err != nil { - err = fmt.Errorf("Error fetching account credentials: %s", err) - return nil, p.config.KeepOriginalImage, err + if accountKeyFilePath != "" { + err := googlecompute.ProcessAccountFile(&exporterConfig.Account, accountKeyFilePath) + if err != nil { + return nil, p.config.KeepOriginalImage, err + } } - accountKeyContents := string(b) - googlecompute.ProcessAccountFile(&exporterConfig.Account, accountKeyContents) driver, err := googlecompute.NewDriverGCE(ui, projectId, &exporterConfig.Account) if err != nil { return nil, p.config.KeepOriginalImage, err From 86c0c859c58b4e981aaeb816495c6e4406530c46 Mon Sep 17 00:00:00 2001 From: Marc Carmier Date: Wed, 15 Feb 2017 22:04:28 +0100 Subject: [PATCH 0004/1216] Validate the remote_type value for builder/vmware-iso --- builder/vmware/iso/builder.go | 4 +++ builder/vmware/iso/builder_test.go | 40 ++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) mode change 100755 => 100644 builder/vmware/iso/builder.go diff --git a/builder/vmware/iso/builder.go b/builder/vmware/iso/builder.go old mode 100755 new mode 100644 index 10fce5911..ac4c2c853 --- a/builder/vmware/iso/builder.go +++ b/builder/vmware/iso/builder.go @@ -162,6 +162,10 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { errs = packer.MultiErrorAppend(errs, fmt.Errorf("remote_host must be specified")) } + if b.config.RemoteType != "esx5" { + errs = packer.MultiErrorAppend(errs, + fmt.Errorf("Only 'esx5' value is accepted for remote_type")) + } } if b.config.Format != "" { diff --git a/builder/vmware/iso/builder_test.go b/builder/vmware/iso/builder_test.go index 5c1ca5d11..785251e6e 100644 --- a/builder/vmware/iso/builder_test.go +++ b/builder/vmware/iso/builder_test.go @@ -139,6 +139,46 @@ func TestBuilderPrepare_InvalidFloppies(t *testing.T) { } } +func TestBuilderPrepare_RemoteType(t *testing.T) { + var b Builder + config := testConfig() + + config["format"] = "ovf" + config["remote_host"] = "foobar.example.com" + // Bad + config["remote_type"] = "foobar" + warns, err := b.Prepare(config) + if len(warns) > 0 { + t.Fatalf("bad: %#v", warns) + } + if err == nil { + t.Fatal("should have error") + } + + config["remote_host"] = "" + config["remote_type"] = "esx5" + // Bad + warns, err = b.Prepare(config) + if len(warns) > 0 { + t.Fatalf("bad: %#v", warns) + } + if err == nil { + t.Fatal("should have error") + } + + // Good + config["remote_type"] = "esx5" + config["remote_host"] = "foobar.example.com" + b = Builder{} + warns, err = b.Prepare(config) + if len(warns) > 0 { + t.Fatalf("bad: %#v", warns) + } + if err != nil { + t.Fatalf("should not have error: %s", err) + } +} + func TestBuilderPrepare_Format(t *testing.T) { var b Builder config := testConfig() From 5c11a2e59421a9e12655d3331ccfd8d0895e2f4b Mon Sep 17 00:00:00 2001 From: Marc Carmier Date: Wed, 15 Feb 2017 22:11:27 +0100 Subject: [PATCH 0005/1216] Add test for empty remote_type value --- builder/vmware/iso/builder_test.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/builder/vmware/iso/builder_test.go b/builder/vmware/iso/builder_test.go index 785251e6e..78ad79adc 100644 --- a/builder/vmware/iso/builder_test.go +++ b/builder/vmware/iso/builder_test.go @@ -156,7 +156,7 @@ func TestBuilderPrepare_RemoteType(t *testing.T) { } config["remote_host"] = "" - config["remote_type"] = "esx5" + config["remote_type"] = "" // Bad warns, err = b.Prepare(config) if len(warns) > 0 { @@ -177,6 +177,18 @@ func TestBuilderPrepare_RemoteType(t *testing.T) { if err != nil { t.Fatalf("should not have error: %s", err) } + + // Good + config["remote_type"] = "esx5" + config["remote_host"] = "foobar.example.com" + b = Builder{} + warns, err = b.Prepare(config) + if len(warns) > 0 { + t.Fatalf("bad: %#v", warns) + } + if err != nil { + t.Fatalf("should not have error: %s", err) + } } func TestBuilderPrepare_Format(t *testing.T) { From d189676ff079ed5c3815d11461c12a053c32436e Mon Sep 17 00:00:00 2001 From: Anton Kvashenkin Date: Sun, 12 Mar 2017 12:48:08 +0300 Subject: [PATCH 0006/1216] Website/docs: make windows-restart provisioner description more accurate As per https://github.com/mitchellh/packer/blob/master/provisioner/windows-restart/provisioner.go#L16 windows-restart doesn't stop winrm prior to restart host. --- website/source/docs/provisioners/windows-restart.html.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/website/source/docs/provisioners/windows-restart.html.md b/website/source/docs/provisioners/windows-restart.html.md index 3f736c94e..2b3f481a7 100644 --- a/website/source/docs/provisioners/windows-restart.html.md +++ b/website/source/docs/provisioners/windows-restart.html.md @@ -37,9 +37,7 @@ The reference of available configuration options is listed below. Optional parameters: - `restart_command` (string) - The command to execute to initiate the - restart. By default this is `shutdown /r /c "packer restart" /t 5 && net - stop winrm`. A key action of this is to stop WinRM so that Packer can - detect it is rebooting. + restart. By default this is `shutdown /r /f /t 0 /c "packer restart"`. - `restart_check_command` (string) - A command to execute to check if the restart succeeded. This will be done in a loop. From 41d0adfbdd1599e40a862aa2e48b0bb42b9617c1 Mon Sep 17 00:00:00 2001 From: Zbigniew Kostrzewa Date: Wed, 28 Jun 2017 12:33:53 +0200 Subject: [PATCH 0007/1216] Add playbook_files to execute multiple ansible playbooks. --- provisioner/ansible-local/provisioner.go | 120 ++++++++++++++++-- provisioner/ansible-local/provisioner_test.go | 44 +++++++ 2 files changed, 150 insertions(+), 14 deletions(-) diff --git a/provisioner/ansible-local/provisioner.go b/provisioner/ansible-local/provisioner.go index 2f6a873f9..a25aca36c 100644 --- a/provisioner/ansible-local/provisioner.go +++ b/provisioner/ansible-local/provisioner.go @@ -38,6 +38,9 @@ type Config struct { // The main playbook file to execute. PlaybookFile string `mapstructure:"playbook_file"` + // The playbook files to execute. + PlaybookFiles []string `mapstructure:"playbook_files"` + // An array of local paths of playbook files to upload. PlaybookPaths []string `mapstructure:"playbook_paths"` @@ -63,6 +66,8 @@ type Config struct { type Provisioner struct { config Config + + playbookFiles []string } func (p *Provisioner) Prepare(raws ...interface{}) error { @@ -77,6 +82,9 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return err } + // Reset the state. + p.playbookFiles = make([]string, 0, len(p.config.PlaybookFiles)) + // Defaults if p.config.Command == "" { p.config.Command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 ansible-playbook" @@ -91,9 +99,32 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { // Validation var errs *packer.MultiError - err = validateFileConfig(p.config.PlaybookFile, "playbook_file", true) - if err != nil { - errs = packer.MultiErrorAppend(errs, err) + + // Check that either playbook_file or playbook_files is specified + if len(p.config.PlaybookFiles) != 0 && p.config.PlaybookFile != "" { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("Either playbook_file or playbook_files can be specified, not both")) + } + if len(p.config.PlaybookFiles) == 0 && p.config.PlaybookFile == "" { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("Either playbook_file or playbook_files must be specified")) + } + if p.config.PlaybookFile != "" { + err = validateFileConfig(p.config.PlaybookFile, "playbook_file", true) + if err != nil { + errs = packer.MultiErrorAppend(errs, err) + } + } + + for _, playbookFile := range p.config.PlaybookFiles { + if err := validateFileConfig(playbookFile, "playbook_files", true); err != nil { + errs = packer.MultiErrorAppend(errs, err) + } else { + playbookFile, err := filepath.Abs(playbookFile) + if err != nil { + errs = packer.MultiErrorAppend(errs, err) + } else { + p.playbookFiles = append(p.playbookFiles, playbookFile) + } + } } // Check that the inventory file exists, if configured @@ -166,11 +197,15 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { } } - ui.Message("Uploading main Playbook file...") - src := p.config.PlaybookFile - dst := filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(src))) - if err := p.uploadFile(ui, comm, dst, src); err != nil { - return fmt.Errorf("Error uploading main playbook: %s", err) + if p.config.PlaybookFile != "" { + ui.Message("Uploading main Playbook file...") + src := p.config.PlaybookFile + dst := filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(src))) + if err := p.uploadFile(ui, comm, dst, src); err != nil { + return fmt.Errorf("Error uploading main playbook: %s", err) + } + } else if err := p.provisionPlaybookFiles(ui, comm); err != nil { + return err } if len(p.config.InventoryFile) == 0 { @@ -201,16 +236,16 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { if len(p.config.GalaxyFile) > 0 { ui.Message("Uploading galaxy file...") - src = p.config.GalaxyFile - dst = filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(src))) + src := p.config.GalaxyFile + dst := filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(src))) if err := p.uploadFile(ui, comm, dst, src); err != nil { return fmt.Errorf("Error uploading galaxy file: %s", err) } } ui.Message("Uploading inventory file...") - src = p.config.InventoryFile - dst = filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(src))) + src := p.config.InventoryFile + dst := filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(src))) if err := p.uploadFile(ui, comm, dst, src); err != nil { return fmt.Errorf("Error uploading inventory file: %s", err) } @@ -269,6 +304,44 @@ func (p *Provisioner) Cancel() { os.Exit(0) } +func (p *Provisioner) provisionPlaybookFiles(ui packer.Ui, comm packer.Communicator) error { + var playbookDir string + if p.config.PlaybookDir != "" { + var err error + playbookDir, err = filepath.Abs(p.config.PlaybookDir) + if err != nil { + return err + } + } + for index, playbookFile := range p.playbookFiles { + if playbookDir != "" && strings.HasPrefix(playbookFile, playbookDir) { + p.playbookFiles[index] = strings.TrimPrefix(playbookFile, playbookDir) + continue + } + if err := p.provisionPlaybookFile(ui, comm, playbookFile); err != nil { + return err + } + } + return nil +} + +func (p *Provisioner) provisionPlaybookFile(ui packer.Ui, comm packer.Communicator, playbookFile string) error { + ui.Message(fmt.Sprintf("Uploading playbook file: %s", playbookFile)) + + remoteDir := filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Dir(playbookFile))) + remotePlaybookFile := filepath.ToSlash(filepath.Join(p.config.StagingDir, playbookFile)) + + if err := p.createDir(ui, comm, remoteDir); err != nil { + return fmt.Errorf("Error uploading playbook file: %s [%s]", playbookFile, err) + } + + if err := p.uploadFile(ui, comm, remotePlaybookFile, playbookFile); err != nil { + return fmt.Errorf("Error uploading playbook: %s [%s]", playbookFile, err) + } + + return nil +} + func (p *Provisioner) executeGalaxy(ui packer.Ui, comm packer.Communicator) error { rolesDir := filepath.ToSlash(filepath.Join(p.config.StagingDir, "roles")) galaxyFile := filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(p.config.GalaxyFile))) @@ -291,7 +364,6 @@ func (p *Provisioner) executeGalaxy(ui packer.Ui, comm packer.Communicator) erro } func (p *Provisioner) executeAnsible(ui packer.Ui, comm packer.Communicator) error { - playbook := filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(p.config.PlaybookFile))) inventory := filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(p.config.InventoryFile))) extraArgs := fmt.Sprintf(" --extra-vars \"packer_build_name=%s packer_builder_type=%s packer_http_addr=%s\" ", @@ -307,8 +379,28 @@ func (p *Provisioner) executeAnsible(ui packer.Ui, comm packer.Communicator) err } } + if p.config.PlaybookFile != "" { + playbookFile := filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(p.config.PlaybookFile))) + if err := p.executeAnsiblePlaybook(ui, comm, playbookFile, extraArgs, inventory); err != nil { + return err + } + } + + for _, playbookFile := range p.playbookFiles { + playbookFile = filepath.ToSlash(filepath.Join(p.config.StagingDir, playbookFile)) + if err := p.executeAnsiblePlaybook(ui, comm, playbookFile, extraArgs, inventory); err != nil { + return err + } + } + return nil +} + +func (p *Provisioner) executeAnsiblePlaybook( + ui packer.Ui, comm packer.Communicator, playbookFile, extraArgs, inventory string, +) error { command := fmt.Sprintf("cd %s && %s %s%s -c local -i %s", - p.config.StagingDir, p.config.Command, playbook, extraArgs, inventory) + p.config.StagingDir, p.config.Command, playbookFile, extraArgs, inventory, + ) ui.Message(fmt.Sprintf("Executing Ansible: %s", command)) cmd := &packer.RemoteCmd{ Command: command, diff --git a/provisioner/ansible-local/provisioner_test.go b/provisioner/ansible-local/provisioner_test.go index 2195b7107..a074e1331 100644 --- a/provisioner/ansible-local/provisioner_test.go +++ b/provisioner/ansible-local/provisioner_test.go @@ -73,6 +73,50 @@ func TestProvisionerPrepare_PlaybookFile(t *testing.T) { } } +func TestProvisionerPrepare_PlaybookFiles(t *testing.T) { + var p Provisioner + config := testConfig() + + err := p.Prepare(config) + if err == nil { + t.Fatal("should have error") + } + + config["playbook_file"] = "" + config["playbook_files"] = []string{} + err = p.Prepare(config) + if err == nil { + t.Fatal("should have error") + } + + playbook_file, err := ioutil.TempFile("", "playbook") + if err != nil { + t.Fatalf("err: %s", err) + } + defer os.Remove(playbook_file.Name()) + + config["playbook_file"] = playbook_file.Name() + config["playbook_files"] = []string{"some_other_file"} + err = p.Prepare(config) + if err == nil { + t.Fatal("should have error") + } + + config["playbook_file"] = playbook_file.Name() + config["playbook_files"] = []string{} + err = p.Prepare(config) + if err != nil { + t.Fatalf("err: %s", err) + } + + config["playbook_file"] = "" + config["playbook_files"] = []string{playbook_file.Name()} + err = p.Prepare(config) + if err != nil { + t.Fatalf("err: %s", err) + } +} + func TestProvisionerPrepare_InventoryFile(t *testing.T) { var p Provisioner config := testConfig() From f68204534507fa7846b6b6bf477f91def3297c18 Mon Sep 17 00:00:00 2001 From: localghost Date: Mon, 3 Jul 2017 23:44:10 +0200 Subject: [PATCH 0008/1216] Test for ansible-local playbook_files with mocked Communicator. --- .../ansible-local/communicator_mock.go | 38 +++++++++ provisioner/ansible-local/provisioner_test.go | 84 +++++++++++++++++++ provisioner/ansible-local/ui_stub.go | 15 ++++ 3 files changed, 137 insertions(+) create mode 100644 provisioner/ansible-local/communicator_mock.go create mode 100644 provisioner/ansible-local/ui_stub.go diff --git a/provisioner/ansible-local/communicator_mock.go b/provisioner/ansible-local/communicator_mock.go new file mode 100644 index 000000000..8ed59e9bb --- /dev/null +++ b/provisioner/ansible-local/communicator_mock.go @@ -0,0 +1,38 @@ +package ansiblelocal + +import ( + "github.com/hashicorp/packer/packer" + "io" + "os" +) + +type communicatorMock struct { + startCommand []string + uploadDestination []string +} + +func (c *communicatorMock) Start(cmd *packer.RemoteCmd) error { + c.startCommand = append(c.startCommand, cmd.Command) + cmd.SetExited(0) + return nil +} + +func (c *communicatorMock) Upload(dst string, _ io.Reader, _ *os.FileInfo) error { + c.uploadDestination = append(c.uploadDestination, dst) + return nil +} + +func (c *communicatorMock) UploadDir(dst, src string, exclude []string) error { + return nil +} + +func (c *communicatorMock) Download(src string, dst io.Writer) error { + return nil +} + +func (c *communicatorMock) DownloadDir(src, dst string, exclude []string) error { + return nil +} + +func (c *communicatorMock) verify() { +} diff --git a/provisioner/ansible-local/provisioner_test.go b/provisioner/ansible-local/provisioner_test.go index a074e1331..1e81b1bd6 100644 --- a/provisioner/ansible-local/provisioner_test.go +++ b/provisioner/ansible-local/provisioner_test.go @@ -7,6 +7,7 @@ import ( "strings" "testing" + "fmt" "github.com/hashicorp/packer/packer" ) @@ -15,6 +16,37 @@ func testConfig() map[string]interface{} { return m } +func createTempFile() string { + file, err := ioutil.TempFile("", "") + if err != nil { + panic(fmt.Sprintf("err: %s", err)) + } + return file.Name() +} + +func createTempFiles(numFiles int) []string { + files := make([]string, 0, numFiles) + defer func() { + // Cleanup the files if not all were created. + if len(files) < numFiles { + for _, file := range files { + os.Remove(file) + } + } + }() + + for i := 0; i < numFiles; i++ { + files = append(files, createTempFile()) + } + return files +} + +func removeFiles(files ...string) { + for _, file := range files { + os.Remove(file) + } +} + func TestProvisioner_Impl(t *testing.T) { var raw interface{} raw = &Provisioner{} @@ -117,6 +149,58 @@ func TestProvisionerPrepare_PlaybookFiles(t *testing.T) { } } +func assertPlaybooksExecuted(comm *communicatorMock, playbooks []string) { + cmdIndex := 0 + for _, playbook := range playbooks { + for ; cmdIndex < len(comm.startCommand); cmdIndex++ { + cmd := comm.startCommand[cmdIndex] + if strings.Contains(cmd, "ansible-playbook") && strings.Contains(cmd, playbook) { + break + } + } + if cmdIndex == len(comm.startCommand) { + panic(fmt.Sprintf("Playbook %s was not executed", playbook)) + } + } +} + +func assertPlaybooksUploaded(comm *communicatorMock, playbooks []string) { + uploadIndex := 0 + for _, playbook := range playbooks { + for ; uploadIndex < len(comm.uploadDestination); uploadIndex++ { + dest := comm.uploadDestination[uploadIndex] + if strings.HasSuffix(dest, playbook) { + break + } + } + if uploadIndex == len(comm.uploadDestination) { + panic(fmt.Sprintf("Playbook %s was not uploaded", playbook)) + } + } +} + +func TestProvisionerProvision_PlaybookFiles(t *testing.T) { + var p Provisioner + config := testConfig() + + playbooks := createTempFiles(3) + defer removeFiles(playbooks...) + + config["playbook_files"] = playbooks + err := p.Prepare(config) + if err != nil { + t.Fatalf("err: %s", err) + } + + comm := &communicatorMock{} + if err := p.Provision(&uiStub{}, comm); err != nil { + t.Fatalf("err: %s", err) + } + + assertPlaybooksUploaded(comm, playbooks) + assertPlaybooksExecuted(comm, playbooks) +} + func TestProvisionerPrepare_InventoryFile(t *testing.T) { var p Provisioner config := testConfig() diff --git a/provisioner/ansible-local/ui_stub.go b/provisioner/ansible-local/ui_stub.go new file mode 100644 index 000000000..4faa2a215 --- /dev/null +++ b/provisioner/ansible-local/ui_stub.go @@ -0,0 +1,15 @@ +package ansiblelocal + +type uiStub struct{} + +func (su *uiStub) Ask(string) (string, error) { + return "", nil +} + +func (su *uiStub) Error(string) {} + +func (su *uiStub) Machine(string, ...string) {} + +func (su *uiStub) Message(string) {} + +func (su *uiStub) Say(msg string) {} From 9ea6313b68593b0c15e351f80fdb6f5ef9e1a0ea Mon Sep 17 00:00:00 2001 From: localghost Date: Thu, 6 Jul 2017 21:02:46 +0200 Subject: [PATCH 0009/1216] Fix playbook_files test on Windows. --- provisioner/ansible-local/provisioner_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/provisioner/ansible-local/provisioner_test.go b/provisioner/ansible-local/provisioner_test.go index 1e81b1bd6..5fdcdfe8e 100644 --- a/provisioner/ansible-local/provisioner_test.go +++ b/provisioner/ansible-local/provisioner_test.go @@ -152,6 +152,7 @@ func TestProvisionerPrepare_PlaybookFiles(t *testing.T) { func assertPlaybooksExecuted(comm *communicatorMock, playbooks []string) { cmdIndex := 0 for _, playbook := range playbooks { + playbook = filepath.ToSlash(playbook) for ; cmdIndex < len(comm.startCommand); cmdIndex++ { cmd := comm.startCommand[cmdIndex] if strings.Contains(cmd, "ansible-playbook") && strings.Contains(cmd, playbook) { @@ -167,6 +168,7 @@ func assertPlaybooksExecuted(comm *communicatorMock, playbooks []string) { func assertPlaybooksUploaded(comm *communicatorMock, playbooks []string) { uploadIndex := 0 for _, playbook := range playbooks { + playbook = filepath.ToSlash(playbook) for ; uploadIndex < len(comm.uploadDestination); uploadIndex++ { dest := comm.uploadDestination[uploadIndex] if strings.HasSuffix(dest, playbook) { From 1bd32d3876df0e0a10f6d57f363e0d124d509bdc Mon Sep 17 00:00:00 2001 From: localghost Date: Sun, 9 Jul 2017 20:04:01 +0200 Subject: [PATCH 0010/1216] Add documentation about playbook_files option of ansible-local provisioner. --- website/source/docs/provisioners/ansible-local.html.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/website/source/docs/provisioners/ansible-local.html.md b/website/source/docs/provisioners/ansible-local.html.md index 3ca775cf4..3f9500534 100644 --- a/website/source/docs/provisioners/ansible-local.html.md +++ b/website/source/docs/provisioners/ansible-local.html.md @@ -43,7 +43,12 @@ Required: - `playbook_file` (string) - The playbook file to be executed by ansible. This file must exist on your local system and will be uploaded to the - remote machine. + remote machine. This option is exclusive with `playbook_files`. + +- `playbook_files` (array of strings) - The playbook files to be executed by ansible. + These files must exist on your local system. If the files don't exist in the `playbook_dir` + or you don't set `playbook_dir` they will be uploaded to the remote machine. This option + is exclusive with `playbook_file`. Optional: From 33ae9cb2bb9134942609d178fe5421507d817c23 Mon Sep 17 00:00:00 2001 From: Zbigniew Kostrzewa Date: Mon, 10 Jul 2017 08:19:29 +0200 Subject: [PATCH 0011/1216] Add test for playbook_files using docker builder. --- provisioner/ansible-local/provisioner_test.go | 175 ++++++++++++++---- .../ansible-local/test-fixtures/hello.yml | 5 + .../ansible-local/test-fixtures/world.yml | 5 + 3 files changed, 149 insertions(+), 36 deletions(-) create mode 100644 provisioner/ansible-local/test-fixtures/hello.yml create mode 100644 provisioner/ansible-local/test-fixtures/world.yml diff --git a/provisioner/ansible-local/provisioner_test.go b/provisioner/ansible-local/provisioner_test.go index 5fdcdfe8e..bd6568afc 100644 --- a/provisioner/ansible-local/provisioner_test.go +++ b/provisioner/ansible-local/provisioner_test.go @@ -8,45 +8,13 @@ import ( "testing" "fmt" + "github.com/hashicorp/packer/builder/docker" "github.com/hashicorp/packer/packer" + "github.com/hashicorp/packer/provisioner/file" + "github.com/hashicorp/packer/template" + "os/exec" ) -func testConfig() map[string]interface{} { - m := make(map[string]interface{}) - return m -} - -func createTempFile() string { - file, err := ioutil.TempFile("", "") - if err != nil { - panic(fmt.Sprintf("err: %s", err)) - } - return file.Name() -} - -func createTempFiles(numFiles int) []string { - files := make([]string, 0, numFiles) - defer func() { - // Cleanup the files if not all were created. - if len(files) < numFiles { - for _, file := range files { - os.Remove(file) - } - } - }() - - for i := 0; i < numFiles; i++ { - files = append(files, createTempFile()) - } - return files -} - -func removeFiles(files ...string) { - for _, file := range files { - os.Remove(file) - } -} - func TestProvisioner_Impl(t *testing.T) { var raw interface{} raw = &Provisioner{} @@ -318,3 +286,138 @@ func TestProvisionerPrepare_Dirs(t *testing.T) { t.Fatalf("err: %s", err) } } + +func TestProvisionerProvisionDocker_PlaybookFiles(t *testing.T) { + if os.Getenv("PACKER_ACC") == "" { + t.Skip("This test is only run with PACKER_ACC=1") + } + + ui := packer.TestUi(t) + cache := &packer.FileCache{CacheDir: os.TempDir()} + + tpl, err := template.Parse(strings.NewReader(playbookFilesDockerConfig)) + if err != nil { + t.Fatalf("Unable to parse config: %s", err) + } + + // Check if docker executable can be found. + _, err = exec.LookPath("docker") + if err != nil { + t.Error("docker command not found; please make sure docker is installed") + } + + // Setup the builder + builder := &docker.Builder{} + warnings, err := builder.Prepare(tpl.Builders["docker"].Config) + if err != nil { + t.Fatalf("Error preparing configuration %s", err) + } + if len(warnings) > 0 { + t.Fatal("Encountered configuration warnings; aborting") + } + + ansible := &Provisioner{} + err = ansible.Prepare(tpl.Provisioners[0].Config) + if err != nil { + t.Fatalf("Error preparing ansible-local provisioner: %s", err) + } + + download := &file.Provisioner{} + err = download.Prepare(tpl.Provisioners[1].Config) + if err != nil { + t.Fatalf("Error preparing download: %s", err) + } + defer os.Remove("hello_world") + + // Add hooks so the provisioners run during the build + hooks := map[string][]packer.Hook{} + hooks[packer.HookProvision] = []packer.Hook{ + &packer.ProvisionHook{ + Provisioners: []packer.Provisioner{ + ansible, + download, + }, + ProvisionerTypes: []string{tpl.Provisioners[0].Type, tpl.Provisioners[1].Type}, + }, + } + hook := &packer.DispatchHook{Mapping: hooks} + + artifact, err := builder.Run(ui, hook, cache) + if err != nil { + t.Fatalf("Error running build %s", err) + } + defer artifact.Destroy() + + actualContent, err := ioutil.ReadFile("hello_world") + if err != nil { + t.Fatalf("Expected file not found: %s", err) + } + + expectedContent := "Hello world!" + if string(actualContent) != expectedContent { + t.Fatalf(`Unexpected file content: expected="%s", actual="%s"`, expectedContent, actualContent) + } +} + +func testConfig() map[string]interface{} { + m := make(map[string]interface{}) + return m +} + +func createTempFile() string { + file, err := ioutil.TempFile("", "") + if err != nil { + panic(fmt.Sprintf("err: %s", err)) + } + return file.Name() +} + +func createTempFiles(numFiles int) []string { + files := make([]string, 0, numFiles) + defer func() { + // Cleanup the files if not all were created. + if len(files) < numFiles { + for _, file := range files { + os.Remove(file) + } + } + }() + + for i := 0; i < numFiles; i++ { + files = append(files, createTempFile()) + } + return files +} + +func removeFiles(files ...string) { + for _, file := range files { + os.Remove(file) + } +} + +const playbookFilesDockerConfig = ` +{ + "builders": [ + { + "type": "docker", + "image": "williamyeh/ansible:centos7", + "discard": true + } + ], + "provisioners": [ + { + "type": "ansible-local", + "playbook_files": [ + "test-fixtures/hello.yml", + "test-fixtures/world.yml" + ] + }, + { + "type": "file", + "source": "/tmp/hello_world", + "destination": "hello_world", + "direction": "download" + } + ] +} +` diff --git a/provisioner/ansible-local/test-fixtures/hello.yml b/provisioner/ansible-local/test-fixtures/hello.yml new file mode 100644 index 000000000..6bb8797d8 --- /dev/null +++ b/provisioner/ansible-local/test-fixtures/hello.yml @@ -0,0 +1,5 @@ +--- +- hosts: all + tasks: + - name: write Hello + shell: echo -n "Hello" >> /tmp/hello_world \ No newline at end of file diff --git a/provisioner/ansible-local/test-fixtures/world.yml b/provisioner/ansible-local/test-fixtures/world.yml new file mode 100644 index 000000000..98a205c7b --- /dev/null +++ b/provisioner/ansible-local/test-fixtures/world.yml @@ -0,0 +1,5 @@ +--- +- hosts: all + tasks: + - name: write world! + shell: echo -n " world!" >> /tmp/hello_world \ No newline at end of file From 079cbc263fa631ada838d9fab11f7771e51e1b76 Mon Sep 17 00:00:00 2001 From: localghost Date: Mon, 10 Jul 2017 21:58:46 +0200 Subject: [PATCH 0012/1216] Add tests for playbook_files with playbook_dir. --- provisioner/ansible-local/provisioner_test.go | 161 +++++++++++++----- 1 file changed, 122 insertions(+), 39 deletions(-) diff --git a/provisioner/ansible-local/provisioner_test.go b/provisioner/ansible-local/provisioner_test.go index bd6568afc..461e53539 100644 --- a/provisioner/ansible-local/provisioner_test.go +++ b/provisioner/ansible-local/provisioner_test.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/provisioner/file" "github.com/hashicorp/packer/template" + "github.com/moby/moby/pkg/ioutils" "os/exec" ) @@ -117,43 +118,11 @@ func TestProvisionerPrepare_PlaybookFiles(t *testing.T) { } } -func assertPlaybooksExecuted(comm *communicatorMock, playbooks []string) { - cmdIndex := 0 - for _, playbook := range playbooks { - playbook = filepath.ToSlash(playbook) - for ; cmdIndex < len(comm.startCommand); cmdIndex++ { - cmd := comm.startCommand[cmdIndex] - if strings.Contains(cmd, "ansible-playbook") && strings.Contains(cmd, playbook) { - break - } - } - if cmdIndex == len(comm.startCommand) { - panic(fmt.Sprintf("Playbook %s was not executed", playbook)) - } - } -} - -func assertPlaybooksUploaded(comm *communicatorMock, playbooks []string) { - uploadIndex := 0 - for _, playbook := range playbooks { - playbook = filepath.ToSlash(playbook) - for ; uploadIndex < len(comm.uploadDestination); uploadIndex++ { - dest := comm.uploadDestination[uploadIndex] - if strings.HasSuffix(dest, playbook) { - break - } - } - if uploadIndex == len(comm.uploadDestination) { - panic(fmt.Sprintf("Playbook %s was not uploaded", playbook)) - } - } -} - func TestProvisionerProvision_PlaybookFiles(t *testing.T) { var p Provisioner config := testConfig() - playbooks := createTempFiles(3) + playbooks := createTempFiles("", 3) defer removeFiles(playbooks...) config["playbook_files"] = playbooks @@ -171,6 +140,40 @@ func TestProvisionerProvision_PlaybookFiles(t *testing.T) { assertPlaybooksExecuted(comm, playbooks) } +func TestProvisionerProvision_PlaybookFilesWithPlaybookDir(t *testing.T) { + var p Provisioner + config := testConfig() + + playbook_dir, err := ioutils.TempDir("", "") + if err != nil { + t.Fatalf("Failed to create playbook_dir: %s", err) + } + defer os.RemoveAll(playbook_dir) + playbooks := createTempFiles(playbook_dir, 3) + + playbookNames := make([]string, 0, len(playbooks)) + playbooksInPlaybookDir := make([]string, 0, len(playbooks)) + for _, playbook := range playbooks { + playbooksInPlaybookDir = append(playbooksInPlaybookDir, strings.TrimPrefix(playbook, playbook_dir)) + playbookNames = append(playbookNames, filepath.Base(playbook)) + } + + config["playbook_files"] = playbooks + config["playbook_dir"] = playbook_dir + err = p.Prepare(config) + if err != nil { + t.Fatalf("err: %s", err) + } + + comm := &communicatorMock{} + if err := p.Provision(&uiStub{}, comm); err != nil { + t.Fatalf("err: %s", err) + } + + assertPlaybooksNotUploaded(comm, playbookNames) + assertPlaybooksExecuted(comm, playbooksInPlaybookDir) +} + func TestProvisionerPrepare_InventoryFile(t *testing.T) { var p Provisioner config := testConfig() @@ -288,6 +291,14 @@ func TestProvisionerPrepare_Dirs(t *testing.T) { } func TestProvisionerProvisionDocker_PlaybookFiles(t *testing.T) { + testProvisionerProvisionDockerWithPlaybookFiles(t, playbookFilesDockerTemplate) +} + +func TestProvisionerProvisionDocker_PlaybookFilesWithPlaybookDir(t *testing.T) { + testProvisionerProvisionDockerWithPlaybookFiles(t, playbookFilesWithPlaybookDirDockerTemplate) +} + +func testProvisionerProvisionDockerWithPlaybookFiles(t *testing.T, templateString string) { if os.Getenv("PACKER_ACC") == "" { t.Skip("This test is only run with PACKER_ACC=1") } @@ -295,7 +306,7 @@ func TestProvisionerProvisionDocker_PlaybookFiles(t *testing.T) { ui := packer.TestUi(t) cache := &packer.FileCache{CacheDir: os.TempDir()} - tpl, err := template.Parse(strings.NewReader(playbookFilesDockerConfig)) + tpl, err := template.Parse(strings.NewReader(templateString)) if err != nil { t.Fatalf("Unable to parse config: %s", err) } @@ -359,20 +370,64 @@ func TestProvisionerProvisionDocker_PlaybookFiles(t *testing.T) { } } +func assertPlaybooksExecuted(comm *communicatorMock, playbooks []string) { + cmdIndex := 0 + for _, playbook := range playbooks { + playbook = filepath.ToSlash(playbook) + for ; cmdIndex < len(comm.startCommand); cmdIndex++ { + cmd := comm.startCommand[cmdIndex] + if strings.Contains(cmd, "ansible-playbook") && strings.Contains(cmd, playbook) { + break + } + } + if cmdIndex == len(comm.startCommand) { + panic(fmt.Sprintf("Playbook %s was not executed", playbook)) + } + } +} + +func assertPlaybooksUploaded(comm *communicatorMock, playbooks []string) { + fmt.Println(comm.uploadDestination) + uploadIndex := 0 + for _, playbook := range playbooks { + playbook = filepath.ToSlash(playbook) + for ; uploadIndex < len(comm.uploadDestination); uploadIndex++ { + dest := comm.uploadDestination[uploadIndex] + if strings.HasSuffix(dest, playbook) { + break + } + } + if uploadIndex == len(comm.uploadDestination) { + panic(fmt.Sprintf("Playbook %s was not uploaded", playbook)) + } + } +} + +func assertPlaybooksNotUploaded(comm *communicatorMock, playbooks []string) { + for _, playbook := range playbooks { + playbook = filepath.ToSlash(playbook) + for _, destination := range comm.uploadDestination { + if strings.HasSuffix(destination, playbook) { + panic(fmt.Sprintf("Playbook %s was uploaded", playbook)) + } + } + } +} + func testConfig() map[string]interface{} { m := make(map[string]interface{}) return m } -func createTempFile() string { - file, err := ioutil.TempFile("", "") +func createTempFile(dir string) string { + file, err := ioutil.TempFile(dir, "") if err != nil { panic(fmt.Sprintf("err: %s", err)) } return file.Name() } -func createTempFiles(numFiles int) []string { +func createTempFiles(dir string, numFiles int) []string { files := make([]string, 0, numFiles) defer func() { // Cleanup the files if not all were created. @@ -384,7 +439,7 @@ func createTempFiles(numFiles int) []string { }() for i := 0; i < numFiles; i++ { - files = append(files, createTempFile()) + files = append(files, createTempFile(dir)) } return files } @@ -395,7 +450,7 @@ func removeFiles(files ...string) { } } -const playbookFilesDockerConfig = ` +const playbookFilesDockerTemplate = ` { "builders": [ { @@ -421,3 +476,31 @@ const playbookFilesDockerConfig = ` ] } ` + +const playbookFilesWithPlaybookDirDockerTemplate = ` +{ + "builders": [ + { + "type": "docker", + "image": "williamyeh/ansible:centos7", + "discard": true + } + ], + "provisioners": [ + { + "type": "ansible-local", + "playbook_files": [ + "test-fixtures/hello.yml", + "test-fixtures/world.yml" + ], + "playbook_dir": "test-fixtures" + }, + { + "type": "file", + "source": "/tmp/hello_world", + "destination": "hello_world", + "direction": "download" + } + ] +} +` From daca0c2efe8e77986eb06e5ed2ba734bc28ddec5 Mon Sep 17 00:00:00 2001 From: localghost Date: Mon, 10 Jul 2017 22:05:42 +0200 Subject: [PATCH 0013/1216] Remove accidental dependency to moby's ioutils package. --- provisioner/ansible-local/provisioner_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/provisioner/ansible-local/provisioner_test.go b/provisioner/ansible-local/provisioner_test.go index 461e53539..1b4bbe5e7 100644 --- a/provisioner/ansible-local/provisioner_test.go +++ b/provisioner/ansible-local/provisioner_test.go @@ -12,7 +12,6 @@ import ( "github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/provisioner/file" "github.com/hashicorp/packer/template" - "github.com/moby/moby/pkg/ioutils" "os/exec" ) @@ -144,7 +143,7 @@ func TestProvisionerProvision_PlaybookFilesWithPlaybookDir(t *testing.T) { var p Provisioner config := testConfig() - playbook_dir, err := ioutils.TempDir("", "") + playbook_dir, err := ioutil.TempDir("", "") if err != nil { t.Fatalf("Failed to create playbook_dir: %s", err) } From 28a986691db9466f2e89dd8ba46c656b01f36044 Mon Sep 17 00:00:00 2001 From: Bill Wang Date: Tue, 1 Aug 2017 21:24:43 +1000 Subject: [PATCH 0014/1216] Add policies to use spot instance to create the AMI --- website/source/docs/builders/amazon.html.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/website/source/docs/builders/amazon.html.md b/website/source/docs/builders/amazon.html.md index 8873dfcbe..6e3d2cb9f 100644 --- a/website/source/docs/builders/amazon.html.md +++ b/website/source/docs/builders/amazon.html.md @@ -136,6 +136,9 @@ Packer to work: }] } ``` +### Notes to pay for a spot instance to create the AMI + +You need to add two more actions: `ec2:RequestSpotInstances` and `ec2:CancelSpotInstanceRequests` ## Troubleshooting From fdaf4ed8d33bcdfb19fb1269a4c9d1c63e3cc3a5 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 8 Sep 2017 11:31:19 -0700 Subject: [PATCH 0015/1216] Gracefully clean up on SIGTERM --- command/build.go | 3 ++- command/push.go | 3 ++- main.go | 2 ++ packer/plugin/server.go | 6 ++++-- packer/ui.go | 2 +- stdin.go | 3 ++- 6 files changed, 13 insertions(+), 6 deletions(-) diff --git a/command/build.go b/command/build.go index de9da51ee..25fc96e71 100644 --- a/command/build.go +++ b/command/build.go @@ -9,6 +9,7 @@ import ( "strconv" "strings" "sync" + "syscall" "github.com/hashicorp/packer/helper/enumflag" "github.com/hashicorp/packer/packer" @@ -144,7 +145,7 @@ func (c BuildCommand) Run(args []string) int { // Handle interrupts for this build sigCh := make(chan os.Signal, 1) - signal.Notify(sigCh, os.Interrupt) + signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM) defer signal.Stop(sigCh) go func(b packer.Build) { <-sigCh diff --git a/command/push.go b/command/push.go index e1d6c7db2..723e9d92c 100644 --- a/command/push.go +++ b/command/push.go @@ -8,6 +8,7 @@ import ( "path/filepath" "regexp" "strings" + "syscall" "github.com/hashicorp/atlas-go/archive" "github.com/hashicorp/atlas-go/v1" @@ -267,7 +268,7 @@ func (c *PushCommand) Run(args []string) int { // Make a ctrl-C channel sigCh := make(chan os.Signal, 1) - signal.Notify(sigCh, os.Interrupt) + signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM) defer signal.Stop(sigCh) err = nil diff --git a/main.go b/main.go index 689780cd4..b5c884eba 100644 --- a/main.go +++ b/main.go @@ -13,6 +13,7 @@ import ( "path/filepath" "runtime" "sync" + "syscall" "time" "github.com/hashicorp/go-uuid" @@ -86,6 +87,7 @@ func realMain() int { wrapConfig.Writer = io.MultiWriter(logTempFile, logWriter) wrapConfig.Stdout = outW wrapConfig.DetectDuration = 500 * time.Millisecond + wrapConfig.ForwardSignals = []os.Signal{syscall.SIGTERM} exitStatus, err := panicwrap.Wrap(&wrapConfig) if err != nil { fmt.Fprintf(os.Stderr, "Couldn't start Packer: %s", err) diff --git a/packer/plugin/server.go b/packer/plugin/server.go index 667907a5f..470daf950 100644 --- a/packer/plugin/server.go +++ b/packer/plugin/server.go @@ -10,7 +10,6 @@ package plugin import ( "errors" "fmt" - packrpc "github.com/hashicorp/packer/packer/rpc" "io/ioutil" "log" "math/rand" @@ -20,7 +19,10 @@ import ( "runtime" "strconv" "sync/atomic" + "syscall" "time" + + packrpc "github.com/hashicorp/packer/packer/rpc" ) // This is a count of the number of interrupts the process has received. @@ -87,7 +89,7 @@ func Server() (*packrpc.Server, error) { // Eat the interrupts ch := make(chan os.Signal, 1) - signal.Notify(ch, os.Interrupt) + signal.Notify(ch, os.Interrupt, syscall.SIGTERM) go func() { var count int32 = 0 for { diff --git a/packer/ui.go b/packer/ui.go index c107c23b8..c16a2eae0 100644 --- a/packer/ui.go +++ b/packer/ui.go @@ -180,7 +180,7 @@ func (rw *BasicUi) Ask(query string) (string, error) { rw.scanner = bufio.NewScanner(rw.Reader) } sigCh := make(chan os.Signal, 1) - signal.Notify(sigCh, os.Interrupt) + signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM) defer signal.Stop(sigCh) log.Printf("ui: ask: %s", query) diff --git a/stdin.go b/stdin.go index 2df4153f5..758cc6864 100644 --- a/stdin.go +++ b/stdin.go @@ -5,6 +5,7 @@ import ( "log" "os" "os/signal" + "syscall" ) // setupStdin switches out stdin for a pipe. We do this so that we can @@ -26,7 +27,7 @@ func setupStdin() { // Register a signal handler for interrupt in order to close the // writer end of our pipe so that readers get EOF downstream. ch := make(chan os.Signal, 1) - signal.Notify(ch, os.Interrupt) + signal.Notify(ch, os.Interrupt, syscall.SIGTERM) go func() { defer signal.Stop(ch) From 644ac5b367b10d0dd1b82dd84bd3176a804ed739 Mon Sep 17 00:00:00 2001 From: bugbuilder Date: Wed, 20 Sep 2017 22:50:37 -0300 Subject: [PATCH 0016/1216] enable vsphere-template to work with local builders --- .../vsphere-template/post-processor.go | 17 ++++++--- .../vsphere-template/step_mark_as_template.go | 34 ++++++++++++++--- post-processor/vsphere/artifact.go | 38 +++++++++++++++++++ post-processor/vsphere/post-processor.go | 9 ++++- 4 files changed, 85 insertions(+), 13 deletions(-) create mode 100644 post-processor/vsphere/artifact.go diff --git a/post-processor/vsphere-template/post-processor.go b/post-processor/vsphere-template/post-processor.go index 86c9f54b4..e68e2d21c 100644 --- a/post-processor/vsphere-template/post-processor.go +++ b/post-processor/vsphere-template/post-processor.go @@ -7,16 +7,19 @@ import ( "strings" "time" + "github.com/hashicorp/packer/builder/vmware/iso" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" + "github.com/hashicorp/packer/post-processor/vsphere" "github.com/hashicorp/packer/template/interpolate" "github.com/mitchellh/multistep" "github.com/vmware/govmomi" ) var builtins = map[string]string{ - "mitchellh.vmware-esx": "vmware", + vsphere.BuilderId: "vmware", + iso.BuilderIdESX: "vmware", } type Config struct { @@ -90,11 +93,16 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac source := "" for _, path := range artifact.Files() { - if strings.HasSuffix(path, ".vmx") { + if strings.HasSuffix(path, ".vmx") || strings.HasSuffix(path, ".ovf") || strings.HasSuffix(path, ".ova") { source = path break } } + + if source == "" { + return nil, false, fmt.Errorf("VMX, OVF or OVA file not found") + } + // In some occasions the VM state is powered on and if we immediately try to mark as template // (after the ESXi creates it) it will fail. If vSphere is given a few seconds this behavior doesn't reappear. ui.Message("Waiting 10s for VMware vSphere to start") @@ -117,10 +125,7 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac &stepCreateFolder{ Folder: p.config.Folder, }, - &stepMarkAsTemplate{ - VMName: artifact.Id(), - Source: source, - }, + NewStepMarkAsTemplate(artifact.Id(), source), } runner := common.NewRunnerWithPauseFn(steps, p.config.PackerConfig, ui, state) runner.Run(state) diff --git a/post-processor/vsphere-template/step_mark_as_template.go b/post-processor/vsphere-template/step_mark_as_template.go index 0e5465054..dda3984a6 100644 --- a/post-processor/vsphere-template/step_mark_as_template.go +++ b/post-processor/vsphere-template/step_mark_as_template.go @@ -13,8 +13,30 @@ import ( ) type stepMarkAsTemplate struct { - VMName string - Source string + VMName string + Source string + RemoteFolder string +} + +func NewStepMarkAsTemplate(vmname, source string) *stepMarkAsTemplate { + remoteFolder := "Discovered virtual machine" + + if strings.Contains(vmname, "::") { + local := strings.Split(vmname, "::") + + datastore := local[0] + remoteFolder = local[1] + vmname = local[2] + + source = path.Join("/vmfs/volumes/", datastore, vmname, path.Base(source)) + + } + + return &stepMarkAsTemplate{ + VMName: vmname, + Source: source, + RemoteFolder: remoteFolder, + } } func (s *stepMarkAsTemplate) Run(state multistep.StateBag) multistep.StepAction { @@ -25,7 +47,7 @@ func (s *stepMarkAsTemplate) Run(state multistep.StateBag) multistep.StepAction ui.Message("Marking as a template...") - vm, err := findRuntimeVM(cli, dcPath, s.VMName) + vm, err := findRuntimeVM(cli, dcPath, s.VMName, s.RemoteFolder) if err != nil { state.Put("error", err) ui.Error(err.Error()) @@ -75,10 +97,10 @@ func (s *stepMarkAsTemplate) Run(state multistep.StateBag) multistep.StepAction return multistep.ActionContinue } -// We will use the virtual machine created by vmware-iso builder -func findRuntimeVM(cli *govmomi.Client, dcPath, name string) (*object.VirtualMachine, error) { +// We will use the virtual machine created/uploaded by vmware builder (remote or local) +func findRuntimeVM(cli *govmomi.Client, dcPath, name, remoteFolder string) (*object.VirtualMachine, error) { si := object.NewSearchIndex(cli.Client) - fullPath := path.Join(dcPath, "vm", "Discovered virtual machine", name) + fullPath := path.Join(dcPath, "vm", remoteFolder, name) ref, err := si.FindByInventoryPath(context.Background(), fullPath) if err != nil { diff --git a/post-processor/vsphere/artifact.go b/post-processor/vsphere/artifact.go new file mode 100644 index 000000000..d8ef75491 --- /dev/null +++ b/post-processor/vsphere/artifact.go @@ -0,0 +1,38 @@ +package vsphere + +import ( + "fmt" +) + +const BuilderId = "packer.post-processor.vsphere" + +type Artifact struct { + files []string + datastore string + vmfolder string + vmname string +} + +func (*Artifact) BuilderId() string { + return BuilderId +} + +func (a *Artifact) Files() []string { + return a.files +} + +func (a *Artifact) Id() string { + return fmt.Sprintf("%s::%s::%s", a.datastore, a.vmfolder, a.vmname) +} + +func (a *Artifact) String() string { + return fmt.Sprintf("VM: %s Folder: %s Datastore: %s", a.vmname, a.vmfolder, a.datastore) +} + +func (*Artifact) State(name string) interface{} { + return nil +} + +func (a *Artifact) Destroy() error { + return nil +} diff --git a/post-processor/vsphere/post-processor.go b/post-processor/vsphere/post-processor.go index e97147334..ead3b50fd 100644 --- a/post-processor/vsphere/post-processor.go +++ b/post-processor/vsphere/post-processor.go @@ -142,7 +142,14 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac return nil, false, fmt.Errorf("Failed: %s\n", err) } - return artifact, false, nil + artifact = &Artifact{ + datastore: p.config.Datastore, + files: artifact.Files(), + vmfolder: p.config.VMFolder, + vmname: p.config.VMName, + } + + return artifact, true, nil } func (p *PostProcessor) BuildArgs(source, ovftool_uri string) ([]string, error) { From 24a8fddf035ef2360bd5dd4bb846fcb5507d8233 Mon Sep 17 00:00:00 2001 From: bugbuilder Date: Fri, 22 Sep 2017 13:54:11 -0300 Subject: [PATCH 0017/1216] showing artifact info in packer UI --- post-processor/vsphere-template/post-processor.go | 1 + post-processor/vsphere/post-processor.go | 2 ++ 2 files changed, 3 insertions(+) diff --git a/post-processor/vsphere-template/post-processor.go b/post-processor/vsphere-template/post-processor.go index e68e2d21c..5cec3bda6 100644 --- a/post-processor/vsphere-template/post-processor.go +++ b/post-processor/vsphere-template/post-processor.go @@ -87,6 +87,7 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { } func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) { + ui.Say(fmt.Sprintf("Artifact %s with id %s and builderId %s", artifact, artifact.Id(), artifact.BuilderId())) if _, ok := builtins[artifact.BuilderId()]; !ok { return nil, false, fmt.Errorf("Unknown artifact type, can't build box: %s", artifact.BuilderId()) } diff --git a/post-processor/vsphere/post-processor.go b/post-processor/vsphere/post-processor.go index ead3b50fd..0a6e4a6a3 100644 --- a/post-processor/vsphere/post-processor.go +++ b/post-processor/vsphere/post-processor.go @@ -149,6 +149,8 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac vmname: p.config.VMName, } + ui.Say(fmt.Sprintf("New artifact created %s with id %s and builderId %s", artifact, artifact.Id(), artifact.BuilderId())) + return artifact, true, nil } From 99dd19ccfdec298992f57355804181bc1b7353c2 Mon Sep 17 00:00:00 2001 From: bugbuilder Date: Fri, 22 Sep 2017 23:37:27 -0300 Subject: [PATCH 0018/1216] Adding correct reference to VM remote path --- post-processor/vsphere-template/post-processor.go | 1 - post-processor/vsphere-template/step_mark_as_template.go | 2 +- post-processor/vsphere/post-processor.go | 2 -- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/post-processor/vsphere-template/post-processor.go b/post-processor/vsphere-template/post-processor.go index 5cec3bda6..e68e2d21c 100644 --- a/post-processor/vsphere-template/post-processor.go +++ b/post-processor/vsphere-template/post-processor.go @@ -87,7 +87,6 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { } func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) { - ui.Say(fmt.Sprintf("Artifact %s with id %s and builderId %s", artifact, artifact.Id(), artifact.BuilderId())) if _, ok := builtins[artifact.BuilderId()]; !ok { return nil, false, fmt.Errorf("Unknown artifact type, can't build box: %s", artifact.BuilderId()) } diff --git a/post-processor/vsphere-template/step_mark_as_template.go b/post-processor/vsphere-template/step_mark_as_template.go index dda3984a6..60780dd0d 100644 --- a/post-processor/vsphere-template/step_mark_as_template.go +++ b/post-processor/vsphere-template/step_mark_as_template.go @@ -28,7 +28,7 @@ func NewStepMarkAsTemplate(vmname, source string) *stepMarkAsTemplate { remoteFolder = local[1] vmname = local[2] - source = path.Join("/vmfs/volumes/", datastore, vmname, path.Base(source)) + source = path.Join("/vmfs/volumes/", datastore, vmname, vmname+path.Ext(source)) } diff --git a/post-processor/vsphere/post-processor.go b/post-processor/vsphere/post-processor.go index 0a6e4a6a3..ead3b50fd 100644 --- a/post-processor/vsphere/post-processor.go +++ b/post-processor/vsphere/post-processor.go @@ -149,8 +149,6 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac vmname: p.config.VMName, } - ui.Say(fmt.Sprintf("New artifact created %s with id %s and builderId %s", artifact, artifact.Id(), artifact.BuilderId())) - return artifact, true, nil } From f1773a57f8d624d39bcb84bcdf09f7ebd3179ede Mon Sep 17 00:00:00 2001 From: bugbuilder Date: Sat, 23 Sep 2017 03:01:35 -0300 Subject: [PATCH 0019/1216] using vmx extension as default --- post-processor/vsphere-template/step_mark_as_template.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/post-processor/vsphere-template/step_mark_as_template.go b/post-processor/vsphere-template/step_mark_as_template.go index 60780dd0d..82b26a7b5 100644 --- a/post-processor/vsphere-template/step_mark_as_template.go +++ b/post-processor/vsphere-template/step_mark_as_template.go @@ -28,7 +28,7 @@ func NewStepMarkAsTemplate(vmname, source string) *stepMarkAsTemplate { remoteFolder = local[1] vmname = local[2] - source = path.Join("/vmfs/volumes/", datastore, vmname, vmname+path.Ext(source)) + source = path.Join("/vmfs/volumes/", datastore, vmname, vmname+".vmx") } From 75a4ca7351899a0018fee74b8ab621b40546628f Mon Sep 17 00:00:00 2001 From: bugbuilder Date: Sat, 23 Sep 2017 15:43:57 -0300 Subject: [PATCH 0020/1216] adding artifact testing and using builder id --- .../vsphere-template/post-processor.go | 2 +- .../vsphere-template/step_mark_as_template.go | 17 +++++++-------- post-processor/vsphere/artifact.go | 9 ++++++++ post-processor/vsphere/artifact_test.go | 21 +++++++++++++++++++ post-processor/vsphere/post-processor.go | 7 +------ 5 files changed, 40 insertions(+), 16 deletions(-) create mode 100644 post-processor/vsphere/artifact_test.go diff --git a/post-processor/vsphere-template/post-processor.go b/post-processor/vsphere-template/post-processor.go index e68e2d21c..6014180c7 100644 --- a/post-processor/vsphere-template/post-processor.go +++ b/post-processor/vsphere-template/post-processor.go @@ -125,7 +125,7 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac &stepCreateFolder{ Folder: p.config.Folder, }, - NewStepMarkAsTemplate(artifact.Id(), source), + NewStepMarkAsTemplate(artifact, source), } runner := common.NewRunnerWithPauseFn(steps, p.config.PackerConfig, ui, state) runner.Run(state) diff --git a/post-processor/vsphere-template/step_mark_as_template.go b/post-processor/vsphere-template/step_mark_as_template.go index 82b26a7b5..78949d2d9 100644 --- a/post-processor/vsphere-template/step_mark_as_template.go +++ b/post-processor/vsphere-template/step_mark_as_template.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/hashicorp/packer/packer" + "github.com/hashicorp/packer/post-processor/vsphere" "github.com/mitchellh/multistep" "github.com/vmware/govmomi" "github.com/vmware/govmomi/object" @@ -18,18 +19,16 @@ type stepMarkAsTemplate struct { RemoteFolder string } -func NewStepMarkAsTemplate(vmname, source string) *stepMarkAsTemplate { +func NewStepMarkAsTemplate(artifact packer.Artifact, source string) *stepMarkAsTemplate { remoteFolder := "Discovered virtual machine" + vmname := artifact.Id() - if strings.Contains(vmname, "::") { - local := strings.Split(vmname, "::") - - datastore := local[0] - remoteFolder = local[1] - vmname = local[2] - + if artifact.BuilderId() == vsphere.BuilderId { + id := strings.Split(artifact.Id(), "::") + datastore := id[0] + remoteFolder = id[1] + vmname = id[2] source = path.Join("/vmfs/volumes/", datastore, vmname, vmname+".vmx") - } return &stepMarkAsTemplate{ diff --git a/post-processor/vsphere/artifact.go b/post-processor/vsphere/artifact.go index d8ef75491..90e475a28 100644 --- a/post-processor/vsphere/artifact.go +++ b/post-processor/vsphere/artifact.go @@ -13,6 +13,15 @@ type Artifact struct { vmname string } +func NewArtifact(datastore, vmfolder, vmname string, files []string) *Artifact { + return &Artifact{ + files: files, + datastore: datastore, + vmfolder: vmfolder, + vmname: vmname, + } +} + func (*Artifact) BuilderId() string { return BuilderId } diff --git a/post-processor/vsphere/artifact_test.go b/post-processor/vsphere/artifact_test.go new file mode 100644 index 000000000..b4f17c759 --- /dev/null +++ b/post-processor/vsphere/artifact_test.go @@ -0,0 +1,21 @@ +package vsphere + +import ( + "github.com/hashicorp/packer/packer" + "testing" +) + +func TestArtifact_ImplementsArtifact(t *testing.T) { + var raw interface{} + raw = &Artifact{} + if _, ok := raw.(packer.Artifact); !ok { + t.Fatalf("Artifact should be a Artifact") + } +} + +func TestArtifact_Id(t *testing.T) { + artifact := NewArtifact("datastore", "vmfolder", "vmname", nil) + if artifact.Id() != "datastore::vmfolder::vmname" { + t.Fatalf("must return datastore, vmfolder and vmname splitted by :: as Id") + } +} diff --git a/post-processor/vsphere/post-processor.go b/post-processor/vsphere/post-processor.go index ead3b50fd..a022cb23f 100644 --- a/post-processor/vsphere/post-processor.go +++ b/post-processor/vsphere/post-processor.go @@ -142,12 +142,7 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac return nil, false, fmt.Errorf("Failed: %s\n", err) } - artifact = &Artifact{ - datastore: p.config.Datastore, - files: artifact.Files(), - vmfolder: p.config.VMFolder, - vmname: p.config.VMName, - } + artifact = NewArtifact(p.config.Datastore, p.config.VMFolder, p.config.VMName, artifact.Files()) return artifact, true, nil } From b3a0e51fe50b0caedc6ff88bfebfb53c6193745c Mon Sep 17 00:00:00 2001 From: bugbuilder Date: Sun, 24 Sep 2017 01:42:28 -0300 Subject: [PATCH 0021/1216] adding documentation --- .../post-processors/vsphere-template.html.md | 37 +++++++++++++++++-- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/website/source/docs/post-processors/vsphere-template.html.md b/website/source/docs/post-processors/vsphere-template.html.md index e72dc19cc..511224cfd 100644 --- a/website/source/docs/post-processors/vsphere-template.html.md +++ b/website/source/docs/post-processors/vsphere-template.html.md @@ -1,7 +1,7 @@ --- description: | - The Packer vSphere Template post-processor takes an artifact from the VMware-iso builder built on ESXi (i.e. remote) - and allows to mark a VM as a template and leaving it in a path of choice. + The Packer vSphere Template post-processor takes an artifact from the VMware-iso builder, built on ESXi (i.e. remote) + or an artifact from the vSphere post-processor and allows to mark a VM as a template and leaving it in a path of choice. layout: docs page_title: 'vSphere Template - Post-Processors' sidebar_current: 'docs-post-processors-vSphere-template' @@ -11,8 +11,9 @@ sidebar_current: 'docs-post-processors-vSphere-template' Type: `vsphere-template` -The Packer vSphere template post-processor takes an artifact from the VMware-iso builder built on ESXi (i.e. remote) and -allows to mark a VM as a template and leaving it in a path of choice. +The Packer vSphere Template post-processor takes an artifact from the VMware-iso builder, built on ESXi (i.e. remote) +or an artifact from the [vSphere](/docs/post-processors/vsphere.html) post-processor and allows to mark a VM as a +template and leaving it in a path of choice. ## Example @@ -51,3 +52,31 @@ Optional: - `folder` (string) - Target path where the template will be created. - `insecure` (boolean) - If it's true skip verification of server certificate. Default is false + +## Using the vSphere Template with local builders + +Once the [vSphere](/docs/post-processors/vsphere.html) takes an artifact from the VMware builder and uploads it +to a vSphere endpoint, you will likely want to mark that VM as template. Packer can do this for you automatically +using a sequence definition (a collection of post-processors that are treated as as single pipeline, see +[Post-Processors](/docs/templates/post-processors.html) for more information): + +``` json +{ + "post-processors": [ + [ + { + "type": "vsphere", + ... + }, + { + "type": "vsphere-template", + ... + } + ] + ] +} +``` + +In the example above, the result of each builder is passed through the defined sequence of post-processors starting +with the `vsphere` post-processor which will upload the artifact to a vSphere endpoint. The resulting artifact is then +passed on to the `vsphere-template` post-processor which handles marking a VM as a template. From 50904064e19053d18fc61c4f3b26bcd3db339f9a Mon Sep 17 00:00:00 2001 From: bugbuilder Date: Sun, 24 Sep 2017 21:56:35 -0300 Subject: [PATCH 0022/1216] doesn't keep the original artifact --- post-processor/vsphere/post-processor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/post-processor/vsphere/post-processor.go b/post-processor/vsphere/post-processor.go index a022cb23f..d8b40d654 100644 --- a/post-processor/vsphere/post-processor.go +++ b/post-processor/vsphere/post-processor.go @@ -144,7 +144,7 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac artifact = NewArtifact(p.config.Datastore, p.config.VMFolder, p.config.VMName, artifact.Files()) - return artifact, true, nil + return artifact, false, nil } func (p *PostProcessor) BuildArgs(source, ovftool_uri string) ([]string, error) { From 35ef7bce5ca489a6a74a92cb3386f34e637dedc3 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Tue, 3 Oct 2017 15:35:03 -0700 Subject: [PATCH 0023/1216] implement dir check for winrm communicator --- communicator/winrm/communicator.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/communicator/winrm/communicator.go b/communicator/winrm/communicator.go index d1f7d2ab6..a725fe51f 100644 --- a/communicator/winrm/communicator.go +++ b/communicator/winrm/communicator.go @@ -127,6 +127,23 @@ func (c *Communicator) Upload(path string, input io.Reader, _ *os.FileInfo) erro if err != nil { return err } + + // Get information about destination path + endpoint := winrm.NewEndpoint(c.endpoint.Host, c.endpoint.Port, c.config.Https, c.config.Insecure, nil, nil, nil, c.config.Timeout) + client, err := winrm.NewClient(endpoint, c.config.Username, c.config.Password) + if err != nil { + return fmt.Errorf("Was unable to create winrm client: %s", err) + } + stdout, _, _, err := client.RunWithString(fmt.Sprintf("powershell -Command \"(Get-Item %s) -is [System.IO.DirectoryInfo]\"", path), "") + if err != nil { + return fmt.Errorf("Couldn't determine whether destination was a folder or file: %s", err) + } + if strings.Contains(stdout, "True") { + // The path exists and is a directory. + // Upload file into the directory instead of overwriting. + path = filepath.Join(path, filepath.Base((*fi).Name())) + } + log.Printf("Uploading file to '%s'", path) return wcp.Write(path, input) } From a5e61348195383154b9f667c45d36d5270bf00cc Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Tue, 3 Oct 2017 15:55:32 -0700 Subject: [PATCH 0024/1216] implement dir check for ssh communicator --- communicator/ssh/communicator.go | 23 +++++++++++++++++++++++ communicator/winrm/communicator.go | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/communicator/ssh/communicator.go b/communicator/ssh/communicator.go index c10e97dcc..827925f2c 100644 --- a/communicator/ssh/communicator.go +++ b/communicator/ssh/communicator.go @@ -533,6 +533,29 @@ func (c *comm) scpUploadSession(path string, input io.Reader, fi *os.FileInfo) e target_dir := filepath.Dir(path) target_file := filepath.Base(path) + // find out if it's a directory + testDirectoryCommand := fmt.Sprintf("if [ -d \"%s\" ]; then echo directory; fi", path) + cmd := &packer.RemoteCmd{Command: testDirectoryCommand} + var buf, buf2 bytes.Buffer + cmd.Stdout = &buf + cmd.Stdout = io.MultiWriter(cmd.Stdout, &buf2) + + err := c.Start(cmd) + + if err != nil { + log.Printf("Unable to check whether remote path is a dir: %s", err) + return err + } + + stdoutToRead := buf2.String() + if strings.Contains(stdoutToRead, "directory") { + log.Printf("upload locale is a directory") + target_dir = path + target_file = filepath.Base((*fi).Name()) + } + + log.Printf("target_file was %s", target_file) + // On windows, filepath.Dir uses backslash seperators (ie. "\tmp"). // This does not work when the target host is unix. Switch to forward slash // which works for unix and windows diff --git a/communicator/winrm/communicator.go b/communicator/winrm/communicator.go index a725fe51f..3970d1af8 100644 --- a/communicator/winrm/communicator.go +++ b/communicator/winrm/communicator.go @@ -122,7 +122,7 @@ func runCommand(shell *winrm.Shell, cmd *winrm.Command, rc *packer.RemoteCmd) { } // Upload implementation of communicator.Communicator interface -func (c *Communicator) Upload(path string, input io.Reader, _ *os.FileInfo) error { +func (c *Communicator) Upload(path string, input io.Reader, fi *os.FileInfo) error { wcp, err := c.newCopyClient() if err != nil { return err From b3cc90125dde7fbec29d6c9616df4167efe5f968 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Tue, 3 Oct 2017 16:36:27 -0700 Subject: [PATCH 0025/1216] ssh communicator works --- communicator/ssh/communicator.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/communicator/ssh/communicator.go b/communicator/ssh/communicator.go index 827925f2c..3ec2d1aba 100644 --- a/communicator/ssh/communicator.go +++ b/communicator/ssh/communicator.go @@ -534,11 +534,8 @@ func (c *comm) scpUploadSession(path string, input io.Reader, fi *os.FileInfo) e target_file := filepath.Base(path) // find out if it's a directory - testDirectoryCommand := fmt.Sprintf("if [ -d \"%s\" ]; then echo directory; fi", path) + testDirectoryCommand := fmt.Sprintf(`test -d "%s"`, path) cmd := &packer.RemoteCmd{Command: testDirectoryCommand} - var buf, buf2 bytes.Buffer - cmd.Stdout = &buf - cmd.Stdout = io.MultiWriter(cmd.Stdout, &buf2) err := c.Start(cmd) @@ -546,10 +543,9 @@ func (c *comm) scpUploadSession(path string, input io.Reader, fi *os.FileInfo) e log.Printf("Unable to check whether remote path is a dir: %s", err) return err } - - stdoutToRead := buf2.String() - if strings.Contains(stdoutToRead, "directory") { - log.Printf("upload locale is a directory") + cmd.Wait() + if cmd.ExitStatus == 0 { + log.Printf("path is a directory; copying file into directory.") target_dir = path target_file = filepath.Base((*fi).Name()) } From 8452ca898cbf5dcf3b8c6c0a7db975dffc18fee8 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Tue, 3 Oct 2017 17:06:33 -0700 Subject: [PATCH 0026/1216] implemented for docker communicator --- builder/docker/communicator.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/builder/docker/communicator.go b/builder/docker/communicator.go index 13684b2f8..0c4a8833d 100644 --- a/builder/docker/communicator.go +++ b/builder/docker/communicator.go @@ -104,6 +104,21 @@ func (c *Communicator) uploadReader(dst string, src io.Reader) error { // uploadFile uses docker cp to copy the file from the host to the container func (c *Communicator) uploadFile(dst string, src io.Reader, fi *os.FileInfo) error { + // find out if it's a directory + testDirectoryCommand := fmt.Sprintf(`test -d "%s"`, dst) + cmd := &packer.RemoteCmd{Command: testDirectoryCommand} + + err := c.Start(cmd) + + if err != nil { + log.Printf("Unable to check whether remote path is a dir: %s", err) + return err + } + cmd.Wait() + if cmd.ExitStatus == 0 { + log.Printf("path is a directory; copying file into directory.") + dst = filepath.Join(dst, filepath.Base((*fi).Name())) + } // command format: docker cp /path/to/infile containerid:/path/to/outfile log.Printf("Copying to %s on container %s.", dst, c.ContainerID) From 93bddb3e65101d553e596d975098949129dfba36 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Wed, 4 Oct 2017 13:35:15 -0700 Subject: [PATCH 0027/1216] implement directory fix for lxc file uploads --- builder/lxc/communicator.go | 18 ++++++++++++++++++ communicator/ssh/communicator.go | 2 -- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/builder/lxc/communicator.go b/builder/lxc/communicator.go index 8d9765979..77ea3c052 100644 --- a/builder/lxc/communicator.go +++ b/builder/lxc/communicator.go @@ -71,6 +71,24 @@ func (c *LxcAttachCommunicator) Upload(dst string, r io.Reader, fi *os.FileInfo) return err } + if fi != nil { + tfDir := filepath.Dir(tf.Name()) + // rename tempfile to match original file name. This makes sure that if file is being + // moved into a directory, the filename is preserved instead of a temp name. + adjustedTempName := filepath.Join(tfDir, (*fi).Name()) + mvCmd, err := c.CmdWrapper(fmt.Sprintf("sudo mv %s %s", tf.Name(), adjustedTempName)) + if err != nil { + return err + } + defer os.Remove(adjustedTempName) + exitStatus := ShellCommand(mvCmd).Run() + // change cpCmd to use new file name as source + cpCmd, err = c.CmdWrapper(fmt.Sprintf("sudo cp %s %s", adjustedTempName, dst)) + if err != nil { + return err + } + } + log.Printf("Running copy command: %s", dst) return ShellCommand(cpCmd).Run() diff --git a/communicator/ssh/communicator.go b/communicator/ssh/communicator.go index 3ec2d1aba..8d5a388d5 100644 --- a/communicator/ssh/communicator.go +++ b/communicator/ssh/communicator.go @@ -550,8 +550,6 @@ func (c *comm) scpUploadSession(path string, input io.Reader, fi *os.FileInfo) e target_file = filepath.Base((*fi).Name()) } - log.Printf("target_file was %s", target_file) - // On windows, filepath.Dir uses backslash seperators (ie. "\tmp"). // This does not work when the target host is unix. Switch to forward slash // which works for unix and windows From e8cabc1e83b6aa99a678f5555a7218cffc20003c Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Wed, 4 Oct 2017 15:23:36 -0700 Subject: [PATCH 0028/1216] implemented for LXD --- builder/lxc/communicator.go | 2 +- builder/lxd/communicator.go | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/builder/lxc/communicator.go b/builder/lxc/communicator.go index 77ea3c052..bb940851a 100644 --- a/builder/lxc/communicator.go +++ b/builder/lxc/communicator.go @@ -81,7 +81,7 @@ func (c *LxcAttachCommunicator) Upload(dst string, r io.Reader, fi *os.FileInfo) return err } defer os.Remove(adjustedTempName) - exitStatus := ShellCommand(mvCmd).Run() + ShellCommand(mvCmd).Run() // change cpCmd to use new file name as source cpCmd, err = c.CmdWrapper(fmt.Sprintf("sudo cp %s %s", adjustedTempName, dst)) if err != nil { diff --git a/builder/lxd/communicator.go b/builder/lxd/communicator.go index 8eaa47a5f..b59e83f82 100644 --- a/builder/lxd/communicator.go +++ b/builder/lxd/communicator.go @@ -54,7 +54,24 @@ func (c *Communicator) Start(cmd *packer.RemoteCmd) error { } func (c *Communicator) Upload(dst string, r io.Reader, fi *os.FileInfo) error { - cpCmd, err := c.CmdWrapper(fmt.Sprintf("lxc file push - %s", filepath.Join(c.ContainerName, dst))) + fileDestination := filepath.Join(c.ContainerName, dst) + // find out if the place we are pushing to is a directory + testDirectoryCommand := fmt.Sprintf(`test -d "%s"`, dst) + cmd := &packer.RemoteCmd{Command: testDirectoryCommand} + err := c.Start(cmd) + + if err != nil { + log.Printf("Unable to check whether remote path is a dir: %s", err) + return err + } + cmd.Wait() + + if cmd.ExitStatus == 0 { + log.Printf("path is a directory; copying file into directory.") + fileDestination = filepath.Join(c.ContainerName, dst, (*fi).Name()) + } + + cpCmd, err := c.CmdWrapper(fmt.Sprintf("lxc file push - %s", fileDestination)) if err != nil { return err } From a79d5eff4ec3ad22d548a007d44d2bf6ad12cafd Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 5 Oct 2017 10:44:18 -0700 Subject: [PATCH 0029/1216] implement sftp path --- communicator/ssh/communicator.go | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/communicator/ssh/communicator.go b/communicator/ssh/communicator.go index 8d5a388d5..3f668bb8e 100644 --- a/communicator/ssh/communicator.go +++ b/communicator/ssh/communicator.go @@ -377,15 +377,31 @@ func (c *comm) connectToAgent() { func (c *comm) sftpUploadSession(path string, input io.Reader, fi *os.FileInfo) error { sftpFunc := func(client *sftp.Client) error { - return sftpUploadFile(path, input, client, fi) + return c.sftpUploadFile(path, input, client, fi) } return c.sftpSession(sftpFunc) } -func sftpUploadFile(path string, input io.Reader, client *sftp.Client, fi *os.FileInfo) error { +func (c *comm) sftpUploadFile(path string, input io.Reader, client *sftp.Client, fi *os.FileInfo) error { log.Printf("[DEBUG] sftp: uploading %s", path) + // find out if destination is a directory (this is to replicate rsync behavior) + testDirectoryCommand := fmt.Sprintf(`test -d "%s"`, path) + cmd := &packer.RemoteCmd{Command: testDirectoryCommand} + + err := c.Start(cmd) + + if err != nil { + log.Printf("Unable to check whether remote path is a dir: %s", err) + return err + } + cmd.Wait() + if cmd.ExitStatus == 0 { + log.Printf("path is a directory; copying file into directory.") + path = filepath.Join(path, filepath.Base((*fi).Name())) + } + f, err := client.Create(path) if err != nil { return err @@ -436,7 +452,7 @@ func (c *comm) sftpUploadDirSession(dst string, src string, excl []string) error return nil } - return sftpVisitFile(finalDst, path, info, client) + return c.sftpVisitFile(finalDst, path, info, client) } return filepath.Walk(src, walkFunc) @@ -445,7 +461,7 @@ func (c *comm) sftpUploadDirSession(dst string, src string, excl []string) error return c.sftpSession(sftpFunc) } -func sftpMkdir(path string, client *sftp.Client, fi os.FileInfo) error { +func (c *comm) sftpMkdir(path string, client *sftp.Client, fi os.FileInfo) error { log.Printf("[DEBUG] sftp: creating dir %s", path) if err := client.Mkdir(path); err != nil { @@ -463,16 +479,16 @@ func sftpMkdir(path string, client *sftp.Client, fi os.FileInfo) error { return nil } -func sftpVisitFile(dst string, src string, fi os.FileInfo, client *sftp.Client) error { +func (c *comm) sftpVisitFile(dst string, src string, fi os.FileInfo, client *sftp.Client) error { if !fi.IsDir() { f, err := os.Open(src) if err != nil { return err } defer f.Close() - return sftpUploadFile(dst, f, client, &fi) + return c.sftpUploadFile(dst, f, client, &fi) } else { - err := sftpMkdir(dst, client, fi) + err := c.sftpMkdir(dst, client, fi) return err } } @@ -533,7 +549,7 @@ func (c *comm) scpUploadSession(path string, input io.Reader, fi *os.FileInfo) e target_dir := filepath.Dir(path) target_file := filepath.Base(path) - // find out if it's a directory + // find out if destination is a directory (this is to replicate rsync behavior) testDirectoryCommand := fmt.Sprintf(`test -d "%s"`, path) cmd := &packer.RemoteCmd{Command: testDirectoryCommand} From 52c0be2d82cf74f29e4d4d5a246001b5e559bcaf Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 5 Oct 2017 16:57:52 -0700 Subject: [PATCH 0030/1216] fix test --- communicator/winrm/communicator_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/communicator/winrm/communicator_test.go b/communicator/winrm/communicator_test.go index d5eb974ac..3f6b50ae1 100644 --- a/communicator/winrm/communicator_test.go +++ b/communicator/winrm/communicator_test.go @@ -48,6 +48,12 @@ func newMockWinRMServer(t *testing.T) *winrmtest.Remote { func(out, err io.Writer) int { return 0 }) + wrm.CommandFunc( + winrmtest.MatchText(`powershell -Command "(Get-Item C:/Temp/packer.cmd) -is [System.IO.DirectoryInfo]"`), + func(out, err io.Writer) int { + out.Write([]byte("False")) + return 0 + }) return wrm } From 001d632bcdf9c1b1cb43ce5af1992d6a10c591aa Mon Sep 17 00:00:00 2001 From: Jeremy Voorhis Date: Fri, 13 Oct 2017 10:39:05 -0700 Subject: [PATCH 0031/1216] Update mitchellh/cli and add posener/complete dep --- vendor/github.com/mitchellh/cli/README.md | 3 + .../github.com/mitchellh/cli/autocomplete.go | 43 ++++ vendor/github.com/mitchellh/cli/cli.go | 243 +++++++++++++++++- vendor/github.com/mitchellh/cli/command.go | 20 ++ .../github.com/mitchellh/cli/command_mock.go | 21 ++ vendor/github.com/mitchellh/cli/ui_mock.go | 59 ++++- .../github.com/posener/complete/LICENSE.txt | 21 ++ vendor/github.com/posener/complete/args.go | 75 ++++++ vendor/github.com/posener/complete/cmd/cmd.go | 128 +++++++++ .../posener/complete/cmd/install/bash.go | 32 +++ .../posener/complete/cmd/install/install.go | 92 +++++++ .../posener/complete/cmd/install/utils.go | 118 +++++++++ .../posener/complete/cmd/install/zsh.go | 39 +++ vendor/github.com/posener/complete/command.go | 118 +++++++++ .../github.com/posener/complete/complete.go | 86 +++++++ vendor/github.com/posener/complete/log.go | 23 ++ .../github.com/posener/complete/match/file.go | 19 ++ .../posener/complete/match/match.go | 6 + .../posener/complete/match/prefix.go | 9 + .../posener/complete/metalinter.json | 21 ++ vendor/github.com/posener/complete/predict.go | 41 +++ .../posener/complete/predict_files.go | 108 ++++++++ .../posener/complete/predict_set.go | 19 ++ vendor/github.com/posener/complete/readme.md | 116 +++++++++ vendor/github.com/posener/complete/test.sh | 12 + vendor/github.com/posener/complete/utils.go | 46 ++++ vendor/vendor.json | 30 ++- 27 files changed, 1532 insertions(+), 16 deletions(-) create mode 100644 vendor/github.com/mitchellh/cli/autocomplete.go create mode 100644 vendor/github.com/posener/complete/LICENSE.txt create mode 100644 vendor/github.com/posener/complete/args.go create mode 100644 vendor/github.com/posener/complete/cmd/cmd.go create mode 100644 vendor/github.com/posener/complete/cmd/install/bash.go create mode 100644 vendor/github.com/posener/complete/cmd/install/install.go create mode 100644 vendor/github.com/posener/complete/cmd/install/utils.go create mode 100644 vendor/github.com/posener/complete/cmd/install/zsh.go create mode 100644 vendor/github.com/posener/complete/command.go create mode 100644 vendor/github.com/posener/complete/complete.go create mode 100644 vendor/github.com/posener/complete/log.go create mode 100644 vendor/github.com/posener/complete/match/file.go create mode 100644 vendor/github.com/posener/complete/match/match.go create mode 100644 vendor/github.com/posener/complete/match/prefix.go create mode 100644 vendor/github.com/posener/complete/metalinter.json create mode 100644 vendor/github.com/posener/complete/predict.go create mode 100644 vendor/github.com/posener/complete/predict_files.go create mode 100644 vendor/github.com/posener/complete/predict_set.go create mode 100644 vendor/github.com/posener/complete/readme.md create mode 100755 vendor/github.com/posener/complete/test.sh create mode 100644 vendor/github.com/posener/complete/utils.go diff --git a/vendor/github.com/mitchellh/cli/README.md b/vendor/github.com/mitchellh/cli/README.md index dd211cf0e..8f02cdd0a 100644 --- a/vendor/github.com/mitchellh/cli/README.md +++ b/vendor/github.com/mitchellh/cli/README.md @@ -18,6 +18,9 @@ cli is the library that powers the CLI for * Optional support for default subcommands so `cli` does something other than error. +* Support for shell autocompletion of subcommands, flags, and arguments + with callbacks in Go. You don't need to write any shell code. + * Automatic help generation for listing subcommands * Automatic help flag recognition of `-h`, `--help`, etc. diff --git a/vendor/github.com/mitchellh/cli/autocomplete.go b/vendor/github.com/mitchellh/cli/autocomplete.go new file mode 100644 index 000000000..3bec6258f --- /dev/null +++ b/vendor/github.com/mitchellh/cli/autocomplete.go @@ -0,0 +1,43 @@ +package cli + +import ( + "github.com/posener/complete/cmd/install" +) + +// autocompleteInstaller is an interface to be implemented to perform the +// autocomplete installation and uninstallation with a CLI. +// +// This interface is not exported because it only exists for unit tests +// to be able to test that the installation is called properly. +type autocompleteInstaller interface { + Install(string) error + Uninstall(string) error +} + +// realAutocompleteInstaller uses the real install package to do the +// install/uninstall. +type realAutocompleteInstaller struct{} + +func (i *realAutocompleteInstaller) Install(cmd string) error { + return install.Install(cmd) +} + +func (i *realAutocompleteInstaller) Uninstall(cmd string) error { + return install.Uninstall(cmd) +} + +// mockAutocompleteInstaller is used for tests to record the install/uninstall. +type mockAutocompleteInstaller struct { + InstallCalled bool + UninstallCalled bool +} + +func (i *mockAutocompleteInstaller) Install(cmd string) error { + i.InstallCalled = true + return nil +} + +func (i *mockAutocompleteInstaller) Uninstall(cmd string) error { + i.UninstallCalled = true + return nil +} diff --git a/vendor/github.com/mitchellh/cli/cli.go b/vendor/github.com/mitchellh/cli/cli.go index 4a69d176d..61206d6aa 100644 --- a/vendor/github.com/mitchellh/cli/cli.go +++ b/vendor/github.com/mitchellh/cli/cli.go @@ -11,6 +11,7 @@ import ( "text/template" "github.com/armon/go-radix" + "github.com/posener/complete" ) // CLI contains the state necessary to run subcommands and parse the @@ -58,14 +59,56 @@ type CLI struct { // For example, if the key is "foo bar", then to access it our CLI // must be accessed with "./cli foo bar". See the docs for CLI for // notes on how this changes some other behavior of the CLI as well. + // + // The factory should be as cheap as possible, ideally only allocating + // a struct. The factory may be called multiple times in the course + // of a command execution and certain events such as help require the + // instantiation of all commands. Expensive initialization should be + // deferred to function calls within the interface implementation. Commands map[string]CommandFactory + // HiddenCommands is a list of commands that are "hidden". Hidden + // commands are not given to the help function callback and do not + // show up in autocomplete. The values in the slice should be equivalent + // to the keys in the command map. + HiddenCommands []string + // Name defines the name of the CLI. Name string // Version of the CLI. Version string + // Autocomplete enables or disables subcommand auto-completion support. + // This is enabled by default when NewCLI is called. Otherwise, this + // must enabled explicitly. + // + // Autocomplete requires the "Name" option to be set on CLI. This name + // should be set exactly to the binary name that is autocompleted. + // + // Autocompletion is supported via the github.com/posener/complete + // library. This library supports both bash and zsh. To add support + // for other shells, please see that library. + // + // AutocompleteInstall and AutocompleteUninstall are the global flag + // names for installing and uninstalling the autocompletion handlers + // for the user's shell. The flag should omit the hyphen(s) in front of + // the value. Both single and double hyphens will automatically be supported + // for the flag name. These default to `autocomplete-install` and + // `autocomplete-uninstall` respectively. + // + // AutocompleteNoDefaultFlags is a boolean which controls if the default auto- + // complete flags like -help and -version are added to the output. + // + // AutocompleteGlobalFlags are a mapping of global flags for + // autocompletion. The help and version flags are automatically added. + Autocomplete bool + AutocompleteInstall string + AutocompleteUninstall string + AutocompleteNoDefaultFlags bool + AutocompleteGlobalFlags complete.Flags + autocompleteInstaller autocompleteInstaller // For tests + // HelpFunc and HelpWriter are used to output help information, if // requested. // @@ -78,23 +121,33 @@ type CLI struct { HelpFunc HelpFunc HelpWriter io.Writer + //--------------------------------------------------------------- + // Internal fields set automatically + once sync.Once + autocomplete *complete.Complete commandTree *radix.Tree commandNested bool - isHelp bool + commandHidden map[string]struct{} subcommand string subcommandArgs []string topFlags []string - isVersion bool + // These are true when special global flags are set. We can/should + // probably use a bitset for this one day. + isHelp bool + isVersion bool + isAutocompleteInstall bool + isAutocompleteUninstall bool } // NewClI returns a new CLI instance with sensible defaults. func NewCLI(app, version string) *CLI { return &CLI{ - Name: app, - Version: version, - HelpFunc: BasicHelpFunc(app), + Name: app, + Version: version, + HelpFunc: BasicHelpFunc(app), + Autocomplete: true, } } @@ -117,6 +170,14 @@ func (c *CLI) IsVersion() bool { func (c *CLI) Run() (int, error) { c.once.Do(c.init) + // If this is a autocompletion request, satisfy it. This must be called + // first before anything else since its possible to be autocompleting + // -help or -version or other flags and we want to show completions + // and not actually write the help or version. + if c.Autocomplete && c.autocomplete.Complete() { + return 0, nil + } + // Just show the version and exit if instructed. if c.IsVersion() && c.Version != "" { c.HelpWriter.Write([]byte(c.Version + "\n")) @@ -125,16 +186,50 @@ func (c *CLI) Run() (int, error) { // Just print the help when only '-h' or '--help' is passed. if c.IsHelp() && c.Subcommand() == "" { - c.HelpWriter.Write([]byte(c.HelpFunc(c.Commands) + "\n")) + c.HelpWriter.Write([]byte(c.HelpFunc(c.helpCommands(c.Subcommand())) + "\n")) return 0, nil } + // If we're attempting to install or uninstall autocomplete then handle + if c.Autocomplete { + // Autocomplete requires the "Name" to be set so that we know what + // command to setup the autocomplete on. + if c.Name == "" { + return 1, fmt.Errorf( + "internal error: CLI.Name must be specified for autocomplete to work") + } + + // If both install and uninstall flags are specified, then error + if c.isAutocompleteInstall && c.isAutocompleteUninstall { + return 1, fmt.Errorf( + "Either the autocomplete install or uninstall flag may " + + "be specified, but not both.") + } + + // If the install flag is specified, perform the install or uninstall + if c.isAutocompleteInstall { + if err := c.autocompleteInstaller.Install(c.Name); err != nil { + return 1, err + } + + return 0, nil + } + + if c.isAutocompleteUninstall { + if err := c.autocompleteInstaller.Uninstall(c.Name); err != nil { + return 1, err + } + + return 0, nil + } + } + // Attempt to get the factory function for creating the command // implementation. If the command is invalid or blank, it is an error. raw, ok := c.commandTree.Get(c.Subcommand()) if !ok { c.HelpWriter.Write([]byte(c.HelpFunc(c.helpCommands(c.subcommandParent())) + "\n")) - return 1, nil + return 127, nil } command, err := raw.(CommandFactory)() @@ -216,6 +311,14 @@ func (c *CLI) init() { c.HelpWriter = os.Stderr } + // Build our hidden commands + if len(c.HiddenCommands) > 0 { + c.commandHidden = make(map[string]struct{}) + for _, h := range c.HiddenCommands { + c.commandHidden[h] = struct{}{} + } + } + // Build our command tree c.commandTree = radix.New() c.commandNested = false @@ -268,10 +371,113 @@ func (c *CLI) init() { } } + // Setup autocomplete if we have it enabled. We have to do this after + // the command tree is setup so we can use the radix tree to easily find + // all subcommands. + if c.Autocomplete { + c.initAutocomplete() + } + // Process the args c.processArgs() } +func (c *CLI) initAutocomplete() { + if c.AutocompleteInstall == "" { + c.AutocompleteInstall = defaultAutocompleteInstall + } + + if c.AutocompleteUninstall == "" { + c.AutocompleteUninstall = defaultAutocompleteUninstall + } + + if c.autocompleteInstaller == nil { + c.autocompleteInstaller = &realAutocompleteInstaller{} + } + + // Build the root command + cmd := c.initAutocompleteSub("") + + // For the root, we add the global flags to the "Flags". This way + // they don't show up on every command. + if !c.AutocompleteNoDefaultFlags { + cmd.Flags = map[string]complete.Predictor{ + "-" + c.AutocompleteInstall: complete.PredictNothing, + "-" + c.AutocompleteUninstall: complete.PredictNothing, + "-help": complete.PredictNothing, + "-version": complete.PredictNothing, + } + } + cmd.GlobalFlags = c.AutocompleteGlobalFlags + + c.autocomplete = complete.New(c.Name, cmd) +} + +// initAutocompleteSub creates the complete.Command for a subcommand with +// the given prefix. This will continue recursively for all subcommands. +// The prefix "" (empty string) can be used for the root command. +func (c *CLI) initAutocompleteSub(prefix string) complete.Command { + var cmd complete.Command + walkFn := func(k string, raw interface{}) bool { + // Keep track of the full key so that we can nest further if necessary + fullKey := k + + if len(prefix) > 0 { + // If we have a prefix, trim the prefix + 1 (for the space) + // Example: turns "sub one" to "one" with prefix "sub" + k = k[len(prefix)+1:] + } + + if idx := strings.Index(k, " "); idx >= 0 { + // If there is a space, we trim up to the space. This turns + // "sub sub2 sub3" into "sub". The prefix trim above will + // trim our current depth properly. + k = k[:idx] + } + + if _, ok := cmd.Sub[k]; ok { + // If we already tracked this subcommand then ignore + return false + } + + // If the command is hidden, don't record it at all + if _, ok := c.commandHidden[fullKey]; ok { + return false + } + + if cmd.Sub == nil { + cmd.Sub = complete.Commands(make(map[string]complete.Command)) + } + subCmd := c.initAutocompleteSub(fullKey) + + // Instantiate the command so that we can check if the command is + // a CommandAutocomplete implementation. If there is an error + // creating the command, we just ignore it since that will be caught + // later. + impl, err := raw.(CommandFactory)() + if err != nil { + impl = nil + } + + // Check if it implements ComandAutocomplete. If so, setup the autocomplete + if c, ok := impl.(CommandAutocomplete); ok { + subCmd.Args = c.AutocompleteArgs() + subCmd.Flags = c.AutocompleteFlags() + } + + cmd.Sub[k] = subCmd + return false + } + + walkPrefix := prefix + if walkPrefix != "" { + walkPrefix += " " + } + + c.commandTree.WalkPrefix(walkPrefix, walkFn) + return cmd +} + func (c *CLI) commandHelp(command Command) { // Get the template to use tpl := strings.TrimSpace(defaultHelpTemplate) @@ -386,6 +592,11 @@ func (c *CLI) helpCommands(prefix string) map[string]CommandFactory { panic("not found: " + k) } + // If this is a hidden command, don't show it + if _, ok := c.commandHidden[k]; ok { + continue + } + result[k] = raw.(CommandFactory) } @@ -404,6 +615,19 @@ func (c *CLI) processArgs() { continue } + // Check for autocomplete flags + if c.Autocomplete { + if arg == "-"+c.AutocompleteInstall || arg == "--"+c.AutocompleteInstall { + c.isAutocompleteInstall = true + continue + } + + if arg == "-"+c.AutocompleteUninstall || arg == "--"+c.AutocompleteUninstall { + c.isAutocompleteUninstall = true + continue + } + } + if c.subcommand == "" { // Check for version flags if not in a subcommand. if arg == "-v" || arg == "-version" || arg == "--version" { @@ -456,6 +680,11 @@ func (c *CLI) processArgs() { } } +// defaultAutocompleteInstall and defaultAutocompleteUninstall are the +// default values for the autocomplete install and uninstall flags. +const defaultAutocompleteInstall = "autocomplete-install" +const defaultAutocompleteUninstall = "autocomplete-uninstall" + const defaultHelpTemplate = ` {{.Help}}{{if gt (len .Subcommands) 0}} diff --git a/vendor/github.com/mitchellh/cli/command.go b/vendor/github.com/mitchellh/cli/command.go index b4924eb00..bed11faf5 100644 --- a/vendor/github.com/mitchellh/cli/command.go +++ b/vendor/github.com/mitchellh/cli/command.go @@ -1,5 +1,9 @@ package cli +import ( + "github.com/posener/complete" +) + const ( // RunResultHelp is a value that can be returned from Run to signal // to the CLI to render the help output. @@ -26,6 +30,22 @@ type Command interface { Synopsis() string } +// CommandAutocomplete is an extension of Command that enables fine-grained +// autocompletion. Subcommand autocompletion will work even if this interface +// is not implemented. By implementing this interface, more advanced +// autocompletion is enabled. +type CommandAutocomplete interface { + // AutocompleteArgs returns the argument predictor for this command. + // If argument completion is not supported, this should return + // complete.PredictNothing. + AutocompleteArgs() complete.Predictor + + // AutocompleteFlags returns a mapping of supported flags and autocomplete + // options for this command. The map key for the Flags map should be the + // complete flag such as "-foo" or "--foo". + AutocompleteFlags() complete.Flags +} + // CommandHelpTemplate is an extension of Command that also has a function // for returning a template for the help rather than the help itself. In // this scenario, both Help and HelpTemplate should be implemented. diff --git a/vendor/github.com/mitchellh/cli/command_mock.go b/vendor/github.com/mitchellh/cli/command_mock.go index 6371e573b..7a584b7e9 100644 --- a/vendor/github.com/mitchellh/cli/command_mock.go +++ b/vendor/github.com/mitchellh/cli/command_mock.go @@ -1,5 +1,9 @@ package cli +import ( + "github.com/posener/complete" +) + // MockCommand is an implementation of Command that can be used for tests. // It is publicly exported from this package in case you want to use it // externally. @@ -29,6 +33,23 @@ func (c *MockCommand) Synopsis() string { return c.SynopsisText } +// MockCommandAutocomplete is an implementation of CommandAutocomplete. +type MockCommandAutocomplete struct { + MockCommand + + // Settable + AutocompleteArgsValue complete.Predictor + AutocompleteFlagsValue complete.Flags +} + +func (c *MockCommandAutocomplete) AutocompleteArgs() complete.Predictor { + return c.AutocompleteArgsValue +} + +func (c *MockCommandAutocomplete) AutocompleteFlags() complete.Flags { + return c.AutocompleteFlagsValue +} + // MockCommandHelpTemplate is an implementation of CommandHelpTemplate. type MockCommandHelpTemplate struct { MockCommand diff --git a/vendor/github.com/mitchellh/cli/ui_mock.go b/vendor/github.com/mitchellh/cli/ui_mock.go index c46772855..0bfe0a191 100644 --- a/vendor/github.com/mitchellh/cli/ui_mock.go +++ b/vendor/github.com/mitchellh/cli/ui_mock.go @@ -7,12 +7,25 @@ import ( "sync" ) -// MockUi is a mock UI that is used for tests and is exported publicly for -// use in external tests if needed as well. +// NewMockUi returns a fully initialized MockUi instance +// which is safe for concurrent use. +func NewMockUi() *MockUi { + m := new(MockUi) + m.once.Do(m.init) + return m +} + +// MockUi is a mock UI that is used for tests and is exported publicly +// for use in external tests if needed as well. Do not instantite this +// directly since the buffers will be initialized on the first write. If +// there is no write then you will get a nil panic. Please use the +// NewMockUi() constructor function instead. You can fix your code with +// +// sed -i -e 's/new(cli.MockUi)/cli.NewMockUi()/g' *_test.go type MockUi struct { InputReader io.Reader - ErrorWriter *bytes.Buffer - OutputWriter *bytes.Buffer + ErrorWriter *syncBuffer + OutputWriter *syncBuffer once sync.Once } @@ -59,6 +72,40 @@ func (u *MockUi) Warn(message string) { } func (u *MockUi) init() { - u.ErrorWriter = new(bytes.Buffer) - u.OutputWriter = new(bytes.Buffer) + u.ErrorWriter = new(syncBuffer) + u.OutputWriter = new(syncBuffer) +} + +type syncBuffer struct { + sync.RWMutex + b bytes.Buffer +} + +func (b *syncBuffer) Write(data []byte) (int, error) { + b.Lock() + defer b.Unlock() + return b.b.Write(data) +} + +func (b *syncBuffer) Read(data []byte) (int, error) { + b.RLock() + defer b.RUnlock() + return b.b.Read(data) +} + +func (b *syncBuffer) Reset() { + b.Lock() + b.b.Reset() + b.Unlock() +} + +func (b *syncBuffer) String() string { + return string(b.Bytes()) +} + +func (b *syncBuffer) Bytes() []byte { + b.RLock() + data := b.b.Bytes() + b.RUnlock() + return data } diff --git a/vendor/github.com/posener/complete/LICENSE.txt b/vendor/github.com/posener/complete/LICENSE.txt new file mode 100644 index 000000000..16249b4a1 --- /dev/null +++ b/vendor/github.com/posener/complete/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2017 Eyal Posener + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/vendor/github.com/posener/complete/args.go b/vendor/github.com/posener/complete/args.go new file mode 100644 index 000000000..73c356d76 --- /dev/null +++ b/vendor/github.com/posener/complete/args.go @@ -0,0 +1,75 @@ +package complete + +import ( + "os" + "path/filepath" +) + +// Args describes command line arguments +type Args struct { + // All lists of all arguments in command line (not including the command itself) + All []string + // Completed lists of all completed arguments in command line, + // If the last one is still being typed - no space after it, + // it won't appear in this list of arguments. + Completed []string + // Last argument in command line, the one being typed, if the last + // character in the command line is a space, this argument will be empty, + // otherwise this would be the last word. + Last string + // LastCompleted is the last argument that was fully typed. + // If the last character in the command line is space, this would be the + // last word, otherwise, it would be the word before that. + LastCompleted string +} + +// Directory gives the directory of the current written +// last argument if it represents a file name being written. +// in case that it is not, we fall back to the current directory. +func (a Args) Directory() string { + if info, err := os.Stat(a.Last); err == nil && info.IsDir() { + return fixPathForm(a.Last, a.Last) + } + dir := filepath.Dir(a.Last) + if info, err := os.Stat(dir); err != nil || !info.IsDir() { + return "./" + } + return fixPathForm(a.Last, dir) +} + +func newArgs(line []string) Args { + completed := removeLast(line[1:]) + return Args{ + All: line[1:], + Completed: completed, + Last: last(line), + LastCompleted: last(completed), + } +} + +func (a Args) from(i int) Args { + if i > len(a.All) { + i = len(a.All) + } + a.All = a.All[i:] + + if i > len(a.Completed) { + i = len(a.Completed) + } + a.Completed = a.Completed[i:] + return a +} + +func removeLast(a []string) []string { + if len(a) > 0 { + return a[:len(a)-1] + } + return a +} + +func last(args []string) (last string) { + if len(args) > 0 { + last = args[len(args)-1] + } + return +} diff --git a/vendor/github.com/posener/complete/cmd/cmd.go b/vendor/github.com/posener/complete/cmd/cmd.go new file mode 100644 index 000000000..7137dee17 --- /dev/null +++ b/vendor/github.com/posener/complete/cmd/cmd.go @@ -0,0 +1,128 @@ +// Package cmd used for command line options for the complete tool +package cmd + +import ( + "errors" + "flag" + "fmt" + "os" + "strings" + + "github.com/posener/complete/cmd/install" +) + +// CLI for command line +type CLI struct { + Name string + InstallName string + UninstallName string + + install bool + uninstall bool + yes bool +} + +const ( + defaultInstallName = "install" + defaultUninstallName = "uninstall" +) + +// Run is used when running complete in command line mode. +// this is used when the complete is not completing words, but to +// install it or uninstall it. +func (f *CLI) Run() bool { + err := f.validate() + if err != nil { + os.Stderr.WriteString(err.Error() + "\n") + os.Exit(1) + } + + switch { + case f.install: + f.prompt() + err = install.Install(f.Name) + case f.uninstall: + f.prompt() + err = install.Uninstall(f.Name) + default: + // non of the action flags matched, + // returning false should make the real program execute + return false + } + + if err != nil { + fmt.Printf("%s failed! %s\n", f.action(), err) + os.Exit(3) + } + fmt.Println("Done!") + return true +} + +// prompt use for approval +// exit if approval was not given +func (f *CLI) prompt() { + defer fmt.Println(f.action() + "ing...") + if f.yes { + return + } + fmt.Printf("%s completion for %s? ", f.action(), f.Name) + var answer string + fmt.Scanln(&answer) + + switch strings.ToLower(answer) { + case "y", "yes": + return + default: + fmt.Println("Cancelling...") + os.Exit(1) + } +} + +// AddFlags adds the CLI flags to the flag set. +// If flags is nil, the default command line flags will be taken. +// Pass non-empty strings as installName and uninstallName to override the default +// flag names. +func (f *CLI) AddFlags(flags *flag.FlagSet) { + if flags == nil { + flags = flag.CommandLine + } + + if f.InstallName == "" { + f.InstallName = defaultInstallName + } + if f.UninstallName == "" { + f.UninstallName = defaultUninstallName + } + + if flags.Lookup(f.InstallName) == nil { + flags.BoolVar(&f.install, f.InstallName, false, + fmt.Sprintf("Install completion for %s command", f.Name)) + } + if flags.Lookup(f.UninstallName) == nil { + flags.BoolVar(&f.uninstall, f.UninstallName, false, + fmt.Sprintf("Uninstall completion for %s command", f.Name)) + } + if flags.Lookup("y") == nil { + flags.BoolVar(&f.yes, "y", false, "Don't prompt user for typing 'yes'") + } +} + +// validate the CLI +func (f *CLI) validate() error { + if f.install && f.uninstall { + return errors.New("Install and uninstall are mutually exclusive") + } + return nil +} + +// action name according to the CLI values. +func (f *CLI) action() string { + switch { + case f.install: + return "Install" + case f.uninstall: + return "Uninstall" + default: + return "unknown" + } +} diff --git a/vendor/github.com/posener/complete/cmd/install/bash.go b/vendor/github.com/posener/complete/cmd/install/bash.go new file mode 100644 index 000000000..a287f9986 --- /dev/null +++ b/vendor/github.com/posener/complete/cmd/install/bash.go @@ -0,0 +1,32 @@ +package install + +import "fmt" + +// (un)install in bash +// basically adds/remove from .bashrc: +// +// complete -C +type bash struct { + rc string +} + +func (b bash) Install(cmd, bin string) error { + completeCmd := b.cmd(cmd, bin) + if lineInFile(b.rc, completeCmd) { + return fmt.Errorf("already installed in %s", b.rc) + } + return appendToFile(b.rc, completeCmd) +} + +func (b bash) Uninstall(cmd, bin string) error { + completeCmd := b.cmd(cmd, bin) + if !lineInFile(b.rc, completeCmd) { + return fmt.Errorf("does not installed in %s", b.rc) + } + + return removeFromFile(b.rc, completeCmd) +} + +func (bash) cmd(cmd, bin string) string { + return fmt.Sprintf("complete -C %s %s", bin, cmd) +} diff --git a/vendor/github.com/posener/complete/cmd/install/install.go b/vendor/github.com/posener/complete/cmd/install/install.go new file mode 100644 index 000000000..644b40576 --- /dev/null +++ b/vendor/github.com/posener/complete/cmd/install/install.go @@ -0,0 +1,92 @@ +package install + +import ( + "errors" + "os" + "os/user" + "path/filepath" + + "github.com/hashicorp/go-multierror" +) + +type installer interface { + Install(cmd, bin string) error + Uninstall(cmd, bin string) error +} + +// Install complete command given: +// cmd: is the command name +func Install(cmd string) error { + is := installers() + if len(is) == 0 { + return errors.New("Did not find any shells to install") + } + bin, err := getBinaryPath() + if err != nil { + return err + } + + for _, i := range is { + errI := i.Install(cmd, bin) + if errI != nil { + err = multierror.Append(err, errI) + } + } + + return err +} + +// Uninstall complete command given: +// cmd: is the command name +func Uninstall(cmd string) error { + is := installers() + if len(is) == 0 { + return errors.New("Did not find any shells to uninstall") + } + bin, err := getBinaryPath() + if err != nil { + return err + } + + for _, i := range is { + errI := i.Uninstall(cmd, bin) + if errI != nil { + multierror.Append(err, errI) + } + } + + return err +} + +func installers() (i []installer) { + for _, rc := range [...]string{".bashrc", ".bash_profile"} { + if f := rcFile(rc); f != "" { + i = append(i, bash{f}) + break + } + } + if f := rcFile(".zshrc"); f != "" { + i = append(i, zsh{f}) + } + return +} + +func getBinaryPath() (string, error) { + bin, err := os.Executable() + if err != nil { + return "", err + } + return filepath.Abs(bin) +} + +func rcFile(name string) string { + u, err := user.Current() + if err != nil { + return "" + } + path := filepath.Join(u.HomeDir, name) + if _, err := os.Stat(path); err != nil { + return "" + } + return path +} diff --git a/vendor/github.com/posener/complete/cmd/install/utils.go b/vendor/github.com/posener/complete/cmd/install/utils.go new file mode 100644 index 000000000..2c8b44cab --- /dev/null +++ b/vendor/github.com/posener/complete/cmd/install/utils.go @@ -0,0 +1,118 @@ +package install + +import ( + "bufio" + "fmt" + "io" + "io/ioutil" + "os" +) + +func lineInFile(name string, lookFor string) bool { + f, err := os.Open(name) + if err != nil { + return false + } + defer f.Close() + r := bufio.NewReader(f) + prefix := []byte{} + for { + line, isPrefix, err := r.ReadLine() + if err == io.EOF { + return false + } + if err != nil { + return false + } + if isPrefix { + prefix = append(prefix, line...) + continue + } + line = append(prefix, line...) + if string(line) == lookFor { + return true + } + prefix = prefix[:0] + } +} + +func appendToFile(name string, content string) error { + f, err := os.OpenFile(name, os.O_RDWR|os.O_APPEND, 0) + if err != nil { + return err + } + defer f.Close() + _, err = f.WriteString(fmt.Sprintf("\n%s\n", content)) + return err +} + +func removeFromFile(name string, content string) error { + backup := name + ".bck" + err := copyFile(name, backup) + if err != nil { + return err + } + temp, err := removeContentToTempFile(name, content) + if err != nil { + return err + } + + err = copyFile(temp, name) + if err != nil { + return err + } + + return os.Remove(backup) +} + +func removeContentToTempFile(name, content string) (string, error) { + rf, err := os.Open(name) + if err != nil { + return "", err + } + defer rf.Close() + wf, err := ioutil.TempFile("/tmp", "complete-") + if err != nil { + return "", err + } + defer wf.Close() + + r := bufio.NewReader(rf) + prefix := []byte{} + for { + line, isPrefix, err := r.ReadLine() + if err == io.EOF { + break + } + if err != nil { + return "", err + } + if isPrefix { + prefix = append(prefix, line...) + continue + } + line = append(prefix, line...) + str := string(line) + if str == content { + continue + } + wf.WriteString(str + "\n") + prefix = prefix[:0] + } + return wf.Name(), nil +} + +func copyFile(src string, dst string) error { + in, err := os.Open(src) + if err != nil { + return err + } + defer in.Close() + out, err := os.Create(dst) + if err != nil { + return err + } + defer out.Close() + _, err = io.Copy(out, in) + return err +} diff --git a/vendor/github.com/posener/complete/cmd/install/zsh.go b/vendor/github.com/posener/complete/cmd/install/zsh.go new file mode 100644 index 000000000..a625f53cf --- /dev/null +++ b/vendor/github.com/posener/complete/cmd/install/zsh.go @@ -0,0 +1,39 @@ +package install + +import "fmt" + +// (un)install in zsh +// basically adds/remove from .zshrc: +// +// autoload -U +X bashcompinit && bashcompinit" +// complete -C +type zsh struct { + rc string +} + +func (z zsh) Install(cmd, bin string) error { + completeCmd := z.cmd(cmd, bin) + if lineInFile(z.rc, completeCmd) { + return fmt.Errorf("already installed in %s", z.rc) + } + + bashCompInit := "autoload -U +X bashcompinit && bashcompinit" + if !lineInFile(z.rc, bashCompInit) { + completeCmd = bashCompInit + "\n" + completeCmd + } + + return appendToFile(z.rc, completeCmd) +} + +func (z zsh) Uninstall(cmd, bin string) error { + completeCmd := z.cmd(cmd, bin) + if !lineInFile(z.rc, completeCmd) { + return fmt.Errorf("does not installed in %s", z.rc) + } + + return removeFromFile(z.rc, completeCmd) +} + +func (zsh) cmd(cmd, bin string) string { + return fmt.Sprintf("complete -o nospace -C %s %s", bin, cmd) +} diff --git a/vendor/github.com/posener/complete/command.go b/vendor/github.com/posener/complete/command.go new file mode 100644 index 000000000..6de48e960 --- /dev/null +++ b/vendor/github.com/posener/complete/command.go @@ -0,0 +1,118 @@ +package complete + +import "github.com/posener/complete/match" + +// Command represents a command line +// It holds the data that enables auto completion of command line +// Command can also be a sub command. +type Command struct { + // Sub is map of sub commands of the current command + // The key refer to the sub command name, and the value is it's + // Command descriptive struct. + Sub Commands + + // Flags is a map of flags that the command accepts. + // The key is the flag name, and the value is it's predictions. + Flags Flags + + // GlobalFlags is a map of flags that the command accepts. + // Global flags that can appear also after a sub command. + GlobalFlags Flags + + // Args are extra arguments that the command accepts, those who are + // given without any flag before. + Args Predictor +} + +// Predict returns all possible predictions for args according to the command struct +func (c *Command) Predict(a Args) (predictions []string) { + predictions, _ = c.predict(a) + return +} + +// Commands is the type of Sub member, it maps a command name to a command struct +type Commands map[string]Command + +// Predict completion of sub command names names according to command line arguments +func (c Commands) Predict(a Args) (prediction []string) { + for sub := range c { + if match.Prefix(sub, a.Last) { + prediction = append(prediction, sub) + } + } + return +} + +// Flags is the type Flags of the Flags member, it maps a flag name to the flag predictions. +type Flags map[string]Predictor + +// Predict completion of flags names according to command line arguments +func (f Flags) Predict(a Args) (prediction []string) { + for flag := range f { + // If the flag starts with a hyphen, we avoid emitting the prediction + // unless the last typed arg contains a hyphen as well. + flagHyphenStart := len(flag) != 0 && flag[0] == '-' + lastHyphenStart := len(a.Last) != 0 && a.Last[0] == '-' + if flagHyphenStart && !lastHyphenStart { + continue + } + + if match.Prefix(flag, a.Last) { + prediction = append(prediction, flag) + } + } + return +} + +// predict options +// only is set to true if no more options are allowed to be returned +// those are in cases of special flag that has specific completion arguments, +// and other flags or sub commands can't come after it. +func (c *Command) predict(a Args) (options []string, only bool) { + + // search sub commands for predictions first + subCommandFound := false + for i, arg := range a.Completed { + if cmd, ok := c.Sub[arg]; ok { + subCommandFound = true + + // recursive call for sub command + options, only = cmd.predict(a.from(i)) + if only { + return + } + + // We matched so stop searching. Continuing to search can accidentally + // match a subcommand with current set of commands, see issue #46. + break + } + } + + // if last completed word is a global flag that we need to complete + if predictor, ok := c.GlobalFlags[a.LastCompleted]; ok && predictor != nil { + Log("Predicting according to global flag %s", a.LastCompleted) + return predictor.Predict(a), true + } + + options = append(options, c.GlobalFlags.Predict(a)...) + + // if a sub command was entered, we won't add the parent command + // completions and we return here. + if subCommandFound { + return + } + + // if last completed word is a command flag that we need to complete + if predictor, ok := c.Flags[a.LastCompleted]; ok && predictor != nil { + Log("Predicting according to flag %s", a.LastCompleted) + return predictor.Predict(a), true + } + + options = append(options, c.Sub.Predict(a)...) + options = append(options, c.Flags.Predict(a)...) + if c.Args != nil { + options = append(options, c.Args.Predict(a)...) + } + + return +} diff --git a/vendor/github.com/posener/complete/complete.go b/vendor/github.com/posener/complete/complete.go new file mode 100644 index 000000000..1df66170b --- /dev/null +++ b/vendor/github.com/posener/complete/complete.go @@ -0,0 +1,86 @@ +// Package complete provides a tool for bash writing bash completion in go. +// +// Writing bash completion scripts is a hard work. This package provides an easy way +// to create bash completion scripts for any command, and also an easy way to install/uninstall +// the completion of the command. +package complete + +import ( + "flag" + "fmt" + "os" + "strings" + + "github.com/posener/complete/cmd" +) + +const ( + envComplete = "COMP_LINE" + envDebug = "COMP_DEBUG" +) + +// Complete structs define completion for a command with CLI options +type Complete struct { + Command Command + cmd.CLI +} + +// New creates a new complete command. +// name is the name of command we want to auto complete. +// IMPORTANT: it must be the same name - if the auto complete +// completes the 'go' command, name must be equal to "go". +// command is the struct of the command completion. +func New(name string, command Command) *Complete { + return &Complete{ + Command: command, + CLI: cmd.CLI{Name: name}, + } +} + +// Run runs the completion and add installation flags beforehand. +// The flags are added to the main flag CommandLine variable. +func (c *Complete) Run() bool { + c.AddFlags(nil) + flag.Parse() + return c.Complete() +} + +// Complete a command from completion line in environment variable, +// and print out the complete options. +// returns success if the completion ran or if the cli matched +// any of the given flags, false otherwise +// For installation: it assumes that flags were added and parsed before +// it was called. +func (c *Complete) Complete() bool { + line, ok := getLine() + if !ok { + // make sure flags parsed, + // in case they were not added in the main program + return c.CLI.Run() + } + Log("Completing line: %s", line) + + a := newArgs(line) + + options := c.Command.Predict(a) + + Log("Completion: %s", options) + output(options) + return true +} + +func getLine() ([]string, bool) { + line := os.Getenv(envComplete) + if line == "" { + return nil, false + } + return strings.Split(line, " "), true +} + +func output(options []string) { + Log("") + // stdout of program defines the complete options + for _, option := range options { + fmt.Println(option) + } +} diff --git a/vendor/github.com/posener/complete/log.go b/vendor/github.com/posener/complete/log.go new file mode 100644 index 000000000..797a80ced --- /dev/null +++ b/vendor/github.com/posener/complete/log.go @@ -0,0 +1,23 @@ +package complete + +import ( + "io" + "io/ioutil" + "log" + "os" +) + +// Log is used for debugging purposes +// since complete is running on tab completion, it is nice to +// have logs to the stderr (when writing your own completer) +// to write logs, set the COMP_DEBUG environment variable and +// use complete.Log in the complete program +var Log = getLogger() + +func getLogger() func(format string, args ...interface{}) { + var logfile io.Writer = ioutil.Discard + if os.Getenv(envDebug) != "" { + logfile = os.Stderr + } + return log.New(logfile, "complete ", log.Flags()).Printf +} diff --git a/vendor/github.com/posener/complete/match/file.go b/vendor/github.com/posener/complete/match/file.go new file mode 100644 index 000000000..051171e8a --- /dev/null +++ b/vendor/github.com/posener/complete/match/file.go @@ -0,0 +1,19 @@ +package match + +import "strings" + +// File returns true if prefix can match the file +func File(file, prefix string) bool { + // special case for current directory completion + if file == "./" && (prefix == "." || prefix == "") { + return true + } + if prefix == "." && strings.HasPrefix(file, ".") { + return true + } + + file = strings.TrimPrefix(file, "./") + prefix = strings.TrimPrefix(prefix, "./") + + return strings.HasPrefix(file, prefix) +} diff --git a/vendor/github.com/posener/complete/match/match.go b/vendor/github.com/posener/complete/match/match.go new file mode 100644 index 000000000..812fcac96 --- /dev/null +++ b/vendor/github.com/posener/complete/match/match.go @@ -0,0 +1,6 @@ +package match + +// Match matches two strings +// it is used for comparing a term to the last typed +// word, the prefix, and see if it is a possible auto complete option. +type Match func(term, prefix string) bool diff --git a/vendor/github.com/posener/complete/match/prefix.go b/vendor/github.com/posener/complete/match/prefix.go new file mode 100644 index 000000000..9a01ba63a --- /dev/null +++ b/vendor/github.com/posener/complete/match/prefix.go @@ -0,0 +1,9 @@ +package match + +import "strings" + +// Prefix is a simple Matcher, if the word is it's prefix, there is a match +// Match returns true if a has the prefix as prefix +func Prefix(long, prefix string) bool { + return strings.HasPrefix(long, prefix) +} diff --git a/vendor/github.com/posener/complete/metalinter.json b/vendor/github.com/posener/complete/metalinter.json new file mode 100644 index 000000000..799c1d03f --- /dev/null +++ b/vendor/github.com/posener/complete/metalinter.json @@ -0,0 +1,21 @@ +{ + "Vendor": true, + "DisableAll": true, + "Enable": [ + "gofmt", + "goimports", + "interfacer", + "goconst", + "misspell", + "unconvert", + "gosimple", + "golint", + "structcheck", + "deadcode", + "vet" + ], + "Exclude": [ + "initTests is unused" + ], + "Deadline": "2m" +} diff --git a/vendor/github.com/posener/complete/predict.go b/vendor/github.com/posener/complete/predict.go new file mode 100644 index 000000000..820706325 --- /dev/null +++ b/vendor/github.com/posener/complete/predict.go @@ -0,0 +1,41 @@ +package complete + +// Predictor implements a predict method, in which given +// command line arguments returns a list of options it predicts. +type Predictor interface { + Predict(Args) []string +} + +// PredictOr unions two predicate functions, so that the result predicate +// returns the union of their predication +func PredictOr(predictors ...Predictor) Predictor { + return PredictFunc(func(a Args) (prediction []string) { + for _, p := range predictors { + if p == nil { + continue + } + prediction = append(prediction, p.Predict(a)...) + } + return + }) +} + +// PredictFunc determines what terms can follow a command or a flag +// It is used for auto completion, given last - the last word in the already +// in the command line, what words can complete it. +type PredictFunc func(Args) []string + +// Predict invokes the predict function and implements the Predictor interface +func (p PredictFunc) Predict(a Args) []string { + if p == nil { + return nil + } + return p(a) +} + +// PredictNothing does not expect anything after. +var PredictNothing Predictor + +// PredictAnything expects something, but nothing particular, such as a number +// or arbitrary name. +var PredictAnything = PredictFunc(func(Args) []string { return nil }) diff --git a/vendor/github.com/posener/complete/predict_files.go b/vendor/github.com/posener/complete/predict_files.go new file mode 100644 index 000000000..c8adf7e80 --- /dev/null +++ b/vendor/github.com/posener/complete/predict_files.go @@ -0,0 +1,108 @@ +package complete + +import ( + "io/ioutil" + "os" + "path/filepath" + "strings" + + "github.com/posener/complete/match" +) + +// PredictDirs will search for directories in the given started to be typed +// path, if no path was started to be typed, it will complete to directories +// in the current working directory. +func PredictDirs(pattern string) Predictor { + return files(pattern, false) +} + +// PredictFiles will search for files matching the given pattern in the started to +// be typed path, if no path was started to be typed, it will complete to files that +// match the pattern in the current working directory. +// To match any file, use "*" as pattern. To match go files use "*.go", and so on. +func PredictFiles(pattern string) Predictor { + return files(pattern, true) +} + +func files(pattern string, allowFiles bool) PredictFunc { + + // search for files according to arguments, + // if only one directory has matched the result, search recursively into + // this directory to give more results. + return func(a Args) (prediction []string) { + prediction = predictFiles(a, pattern, allowFiles) + + // if the number of prediction is not 1, we either have many results or + // have no results, so we return it. + if len(prediction) != 1 { + return + } + + // only try deeper, if the one item is a directory + if stat, err := os.Stat(prediction[0]); err != nil || !stat.IsDir() { + return + } + + a.Last = prediction[0] + return predictFiles(a, pattern, allowFiles) + } +} + +func predictFiles(a Args, pattern string, allowFiles bool) []string { + if strings.HasSuffix(a.Last, "/..") { + return nil + } + + dir := a.Directory() + files := listFiles(dir, pattern, allowFiles) + + // add dir if match + files = append(files, dir) + + return PredictFilesSet(files).Predict(a) +} + +// PredictFilesSet predict according to file rules to a given set of file names +func PredictFilesSet(files []string) PredictFunc { + return func(a Args) (prediction []string) { + // add all matching files to prediction + for _, f := range files { + f = fixPathForm(a.Last, f) + + // test matching of file to the argument + if match.File(f, a.Last) { + prediction = append(prediction, f) + } + } + return + } +} + +func listFiles(dir, pattern string, allowFiles bool) []string { + // set of all file names + m := map[string]bool{} + + // list files + if files, err := filepath.Glob(filepath.Join(dir, pattern)); err == nil { + for _, f := range files { + if stat, err := os.Stat(f); err != nil || stat.IsDir() || allowFiles { + m[f] = true + } + } + } + + // list directories + if dirs, err := ioutil.ReadDir(dir); err == nil { + for _, d := range dirs { + if d.IsDir() { + m[filepath.Join(dir, d.Name())] = true + } + } + } + + list := make([]string, 0, len(m)) + for k := range m { + list = append(list, k) + } + return list +} diff --git a/vendor/github.com/posener/complete/predict_set.go b/vendor/github.com/posener/complete/predict_set.go new file mode 100644 index 000000000..8fc59d714 --- /dev/null +++ b/vendor/github.com/posener/complete/predict_set.go @@ -0,0 +1,19 @@ +package complete + +import "github.com/posener/complete/match" + +// PredictSet expects specific set of terms, given in the options argument. +func PredictSet(options ...string) Predictor { + return predictSet(options) +} + +type predictSet []string + +func (p predictSet) Predict(a Args) (prediction []string) { + for _, m := range p { + if match.Prefix(m, a.Last) { + prediction = append(prediction, m) + } + } + return +} diff --git a/vendor/github.com/posener/complete/readme.md b/vendor/github.com/posener/complete/readme.md new file mode 100644 index 000000000..74077e357 --- /dev/null +++ b/vendor/github.com/posener/complete/readme.md @@ -0,0 +1,116 @@ +# complete + +[![Build Status](https://travis-ci.org/posener/complete.svg?branch=master)](https://travis-ci.org/posener/complete) +[![codecov](https://codecov.io/gh/posener/complete/branch/master/graph/badge.svg)](https://codecov.io/gh/posener/complete) +[![GoDoc](https://godoc.org/github.com/posener/complete?status.svg)](http://godoc.org/github.com/posener/complete) +[![Go Report Card](https://goreportcard.com/badge/github.com/posener/complete)](https://goreportcard.com/report/github.com/posener/complete) + +A tool for bash writing bash completion in go. + +Writing bash completion scripts is a hard work. This package provides an easy way +to create bash completion scripts for any command, and also an easy way to install/uninstall +the completion of the command. + +## go command bash completion + +In [gocomplete](./gocomplete) there is an example for bash completion for the `go` command line. + +This is an example that uses the `complete` package on the `go` command - the `complete` package +can also be used to implement any completions, see [Usage](#usage). + +### Install + +1. Type in your shell: +``` +go get -u github.com/posener/complete/gocomplete +gocomplete -install +``` + +2. Restart your shell + +Uninstall by `gocomplete -uninstall` + +### Features + +- Complete `go` command, including sub commands and all flags. +- Complete packages names or `.go` files when necessary. +- Complete test names after `-run` flag. + +## complete package + +Supported shells: + +- [x] bash +- [x] zsh + +### Usage + +Assuming you have program called `run` and you want to have bash completion +for it, meaning, if you type `run` then space, then press the `Tab` key, +the shell will suggest relevant complete options. + +In that case, we will create a program called `runcomplete`, a go program, +with a `func main()` and so, that will make the completion of the `run` +program. Once the `runcomplete` will be in a binary form, we could +`runcomplete -install` and that will add to our shell all the bash completion +options for `run`. + +So here it is: + +```go +import "github.com/posener/complete" + +func main() { + + // create a Command object, that represents the command we want + // to complete. + run := complete.Command{ + + // Sub defines a list of sub commands of the program, + // this is recursive, since every command is of type command also. + Sub: complete.Commands{ + + // add a build sub command + "build": complete.Command { + + // define flags of the build sub command + Flags: complete.Flags{ + // build sub command has a flag '-cpus', which + // expects number of cpus after it. in that case + // anything could complete this flag. + "-cpus": complete.PredictAnything, + }, + }, + }, + + // define flags of the 'run' main command + Flags: complete.Flags{ + // a flag -o, which expects a file ending with .out after + // it, the tab completion will auto complete for files matching + // the given pattern. + "-o": complete.PredictFiles("*.out"), + }, + + // define global flags of the 'run' main command + // those will show up also when a sub command was entered in the + // command line + GlobalFlags: complete.Flags{ + + // a flag '-h' which does not expects anything after it + "-h": complete.PredictNothing, + }, + } + + // run the command completion, as part of the main() function. + // this triggers the autocompletion when needed. + // name must be exactly as the binary that we want to complete. + complete.New("run", run).Run() +} +``` + +### Self completing program + +In case that the program that we want to complete is written in go we +can make it self completing. + +Here is an [example](./example/self/main.go) diff --git a/vendor/github.com/posener/complete/test.sh b/vendor/github.com/posener/complete/test.sh new file mode 100755 index 000000000..56bfcf15d --- /dev/null +++ b/vendor/github.com/posener/complete/test.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +set -e +echo "" > coverage.txt + +for d in $(go list ./... | grep -v vendor); do + go test -v -race -coverprofile=profile.out -covermode=atomic $d + if [ -f profile.out ]; then + cat profile.out >> coverage.txt + rm profile.out + fi +done \ No newline at end of file diff --git a/vendor/github.com/posener/complete/utils.go b/vendor/github.com/posener/complete/utils.go new file mode 100644 index 000000000..58b8b7927 --- /dev/null +++ b/vendor/github.com/posener/complete/utils.go @@ -0,0 +1,46 @@ +package complete + +import ( + "os" + "path/filepath" + "strings" +) + +// fixPathForm changes a file name to a relative name +func fixPathForm(last string, file string) string { + // get wording directory for relative name + workDir, err := os.Getwd() + if err != nil { + return file + } + + abs, err := filepath.Abs(file) + if err != nil { + return file + } + + // if last is absolute, return path as absolute + if filepath.IsAbs(last) { + return fixDirPath(abs) + } + + rel, err := filepath.Rel(workDir, abs) + if err != nil { + return file + } + + // fix ./ prefix of path + if rel != "." && strings.HasPrefix(last, ".") { + rel = "./" + rel + } + + return fixDirPath(rel) +} + +func fixDirPath(path string) string { + info, err := os.Stat(path) + if err == nil && info.IsDir() && !strings.HasSuffix(path, "/") { + path += "/" + } + return path +} diff --git a/vendor/vendor.json b/vendor/vendor.json index f9b17b648..c9ec17034 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -894,10 +894,10 @@ "revision": "56b76bdf51f7708750eac80fa38b952bb9f32639" }, { - "checksumSHA1": "UP+pXl+ic9y6qrpZA5MqDIAuGfw=", + "checksumSHA1": "UIqCj7qI0hhIMpAhS9YYqs2jD48=", "path": "github.com/mitchellh/cli", - "revision": "ee8578a9c12a5bb9d55303b9665cc448772c81b8", - "revisionTime": "2017-03-28T05:23:52Z" + "revision": "65fcae5817c8600da98ada9d7edf26dd1a84837b", + "revisionTime": "2017-09-08T18:10:43Z" }, { "checksumSHA1": "mVqDwKcibat0IKAdzAhfGIHPwI8=", @@ -1020,6 +1020,30 @@ "path": "github.com/pmezard/go-difflib/difflib", "revision": "792786c7400a136282c1664665ae0a8db921c6c2" }, + { + "checksumSHA1": "rTNABfFJ9wtLQRH8uYNkEZGQOrY=", + "path": "github.com/posener/complete", + "revision": "88e59760adaddb8276c9b15511302890690e2dae", + "revisionTime": "2017-09-08T12:52:45Z" + }, + { + "checksumSHA1": "NB7uVS0/BJDmNu68vPAlbrq4TME=", + "path": "github.com/posener/complete/cmd", + "revision": "88e59760adaddb8276c9b15511302890690e2dae", + "revisionTime": "2017-09-08T12:52:45Z" + }, + { + "checksumSHA1": "Hwojin3GxRyKwPAiz5r7UszqkPc=", + "path": "github.com/posener/complete/cmd/install", + "revision": "88e59760adaddb8276c9b15511302890690e2dae", + "revisionTime": "2017-09-08T12:52:45Z" + }, + { + "checksumSHA1": "DMo94FwJAm9ZCYCiYdJU2+bh4no=", + "path": "github.com/posener/complete/match", + "revision": "88e59760adaddb8276c9b15511302890690e2dae", + "revisionTime": "2017-09-08T12:52:45Z" + }, { "checksumSHA1": "Kq0fF7R65dDcGReuhf47O3LQgrY=", "path": "github.com/profitbricks/profitbricks-sdk-go", From 40625777c158436f99b4ef494f9289c19e5a0742 Mon Sep 17 00:00:00 2001 From: Jeremy Voorhis Date: Fri, 13 Oct 2017 10:39:23 -0700 Subject: [PATCH 0032/1216] Enable autocomplete for top-level commands --- main.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/main.go b/main.go index 0f00b9038..ea5266373 100644 --- a/main.go +++ b/main.go @@ -206,11 +206,13 @@ func wrappedMain() int { } cli := &cli.CLI{ - Args: args, - Commands: Commands, - HelpFunc: excludeHelpFunc(Commands, []string{"plugin"}), - HelpWriter: os.Stdout, - Version: version.Version, + Args: args, + Autocomplete: true, + Commands: Commands, + HelpFunc: excludeHelpFunc(Commands, []string{"plugin"}), + HelpWriter: os.Stdout, + Name: "packer", + Version: version.Version, } exitCode, err := cli.Run() From a4cb8ae41b334a6f0f086685d3be6108e444edcf Mon Sep 17 00:00:00 2001 From: Jeremy Voorhis Date: Fri, 13 Oct 2017 11:41:42 -0700 Subject: [PATCH 0033/1216] Define methods on *BuildCommand (consistency) --- command/build.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/command/build.go b/command/build.go index de9da51ee..b5ae3bd66 100644 --- a/command/build.go +++ b/command/build.go @@ -19,7 +19,7 @@ type BuildCommand struct { Meta } -func (c BuildCommand) Run(args []string) int { +func (c *BuildCommand) Run(args []string) int { var cfgColor, cfgDebug, cfgForce, cfgParallel bool var cfgOnError string flags := c.Meta.FlagSet("build", FlagSetBuildFilter|FlagSetVars) @@ -279,7 +279,7 @@ func (c BuildCommand) Run(args []string) int { return 0 } -func (BuildCommand) Help() string { +func (*BuildCommand) Help() string { helpText := ` Usage: packer build [options] TEMPLATE @@ -303,6 +303,6 @@ Options: return strings.TrimSpace(helpText) } -func (BuildCommand) Synopsis() string { +func (*BuildCommand) Synopsis() string { return "build image(s) from template" } From bfc75eb9d9afc044b595d869ac993931cabb7552 Mon Sep 17 00:00:00 2001 From: Jeremy Voorhis Date: Fri, 13 Oct 2017 11:42:22 -0700 Subject: [PATCH 0034/1216] Implement cli.CommandAutocomplete for most commands --- command/build.go | 21 +++++++++++++++++++++ command/fix.go | 12 ++++++++++++ command/inspect.go | 12 ++++++++++++ command/push.go | 16 ++++++++++++++++ command/validate.go | 16 ++++++++++++++++ 5 files changed, 77 insertions(+) diff --git a/command/build.go b/command/build.go index b5ae3bd66..bf5d72aee 100644 --- a/command/build.go +++ b/command/build.go @@ -13,6 +13,8 @@ import ( "github.com/hashicorp/packer/helper/enumflag" "github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/template" + + "github.com/posener/complete" ) type BuildCommand struct { @@ -306,3 +308,22 @@ Options: func (*BuildCommand) Synopsis() string { return "build image(s) from template" } + +func (*BuildCommand) AutocompleteArgs() complete.Predictor { + return complete.PredictNothing +} + +func (*BuildCommand) AutocompleteFlags() complete.Flags { + return complete.Flags{ + "-color": complete.PredictNothing, + "-debug": complete.PredictNothing, + "-except": complete.PredictNothing, + "-only": complete.PredictNothing, + "-force": complete.PredictNothing, + "-machine-readable": complete.PredictNothing, + "-on-error": complete.PredictNothing, + "-parallel": complete.PredictNothing, + "-var": complete.PredictNothing, + "-var-file": complete.PredictNothing, + } +} diff --git a/command/fix.go b/command/fix.go index 1a94f5786..49142d379 100644 --- a/command/fix.go +++ b/command/fix.go @@ -10,6 +10,8 @@ import ( "github.com/hashicorp/packer/fix" "github.com/hashicorp/packer/template" + + "github.com/posener/complete" ) type FixCommand struct { @@ -140,3 +142,13 @@ Options: func (c *FixCommand) Synopsis() string { return "fixes templates from old versions of packer" } + +func (c *FixCommand) AutocompleteArgs() complete.Predictor { + return complete.PredictNothing +} + +func (c *FixCommand) AutocompleteFlags() complete.Flags { + return complete.Flags{ + "-validate": complete.PredictNothing, + } +} diff --git a/command/inspect.go b/command/inspect.go index 71ddbba1d..f71efcf20 100644 --- a/command/inspect.go +++ b/command/inspect.go @@ -6,6 +6,8 @@ import ( "strings" "github.com/hashicorp/packer/template" + + "github.com/posener/complete" ) type InspectCommand struct { @@ -160,3 +162,13 @@ Options: func (c *InspectCommand) Synopsis() string { return "see components of a template" } + +func (c *InspectCommand) AutocompleteArgs() complete.Predictor { + return complete.PredictNothing +} + +func (c *InspectCommand) AutocompleteFlags() complete.Flags { + return complete.Flags{ + "-machine-readable": complete.PredictNothing, + } +} diff --git a/command/push.go b/command/push.go index e1d6c7db2..586b7729b 100644 --- a/command/push.go +++ b/command/push.go @@ -14,6 +14,8 @@ import ( "github.com/hashicorp/packer/helper/flag-kv" "github.com/hashicorp/packer/helper/flag-slice" "github.com/hashicorp/packer/template" + + "github.com/posener/complete" ) // archiveTemplateEntry is the name the template always takes within the slug. @@ -324,6 +326,20 @@ func (*PushCommand) Synopsis() string { return "push a template and supporting files to a Packer build service" } +func (*PushCommand) AutocompleteArgs() complete.Predictor { + return complete.PredictNothing +} + +func (*PushCommand) AutocompleteFlags() complete.Flags { + return complete.Flags{ + "-name": complete.PredictNothing, + "-token": complete.PredictNothing, + "-sensitive": complete.PredictNothing, + "-var": complete.PredictNothing, + "-var-file": complete.PredictNothing, + } +} + func (c *PushCommand) upload( r *archive.Archive, opts *uploadOpts) (<-chan struct{}, <-chan error, error) { if c.uploadFn != nil { diff --git a/command/validate.go b/command/validate.go index a2fb47ff6..05c29dc2c 100644 --- a/command/validate.go +++ b/command/validate.go @@ -7,6 +7,8 @@ import ( "github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/template" + + "github.com/posener/complete" ) type ValidateCommand struct { @@ -136,3 +138,17 @@ Options: func (*ValidateCommand) Synopsis() string { return "check that a template is valid" } + +func (*ValidateCommand) AutocompleteArgs() complete.Predictor { + return complete.PredictNothing +} + +func (*ValidateCommand) AutocompleteFlags() complete.Flags { + return complete.Flags{ + "-syntax-only": complete.PredictNothing, + "-except": complete.PredictNothing, + "-only": complete.PredictNothing, + "-var": complete.PredictNothing, + "-var-file": complete.PredictNothing, + } +} From 4303c3bae8ce2144b351ca122121074857821ac9 Mon Sep 17 00:00:00 2001 From: Jeremy Voorhis Date: Fri, 13 Oct 2017 12:11:05 -0700 Subject: [PATCH 0035/1216] Drop go 1.7.4 for Travis builds --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5e753eb01..26877142c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,6 @@ sudo: false language: go go: - - 1.7.4 - 1.8.3 - 1.9 From b0cd1ffc161d0da3b7328b68166ab0c6187aefda Mon Sep 17 00:00:00 2001 From: Jeremy Voorhis Date: Fri, 13 Oct 2017 12:43:50 -0700 Subject: [PATCH 0036/1216] Add documentation for autocompletion setup. Based off of Vault docs at https://www.vaultproject.io/docs/commands/index.html --- website/source/docs/commands/index.html.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/website/source/docs/commands/index.html.md b/website/source/docs/commands/index.html.md index 39060745f..843a2db43 100644 --- a/website/source/docs/commands/index.html.md +++ b/website/source/docs/commands/index.html.md @@ -106,3 +106,18 @@ The set of machine-readable message types can be found in the documentation section. This section contains documentation on all the message types exposed by Packer core as well as all the components that ship with Packer by default. + +## Autocompletion + +The `packer` command features opt-in subcommand autocompletion that you can +enable for your shell with `packer -autocomplete-install`. After doing so, +you can invoke a new shell and use the feature. + +For example, assume a tab is typed at the end of each prompt line: + +``` +$ packer p +plugin push +$ packer push - +-name -sensitive -token -var -var-file +``` From 820811675d8ea7b7e94391c90a163f9e2225fd44 Mon Sep 17 00:00:00 2001 From: Olivier Bilodeau Date: Fri, 13 Oct 2017 19:00:48 -0400 Subject: [PATCH 0037/1216] vmware-iso builder: Logging on network errors on connection refused --- builder/vmware/iso/driver_esx5.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/builder/vmware/iso/driver_esx5.go b/builder/vmware/iso/driver_esx5.go index 86ddf015b..8da66f0d2 100644 --- a/builder/vmware/iso/driver_esx5.go +++ b/builder/vmware/iso/driver_esx5.go @@ -302,6 +302,9 @@ func (d *ESX5Driver) CommHost(state multistep.StateBag) (string, error) { if e.Timeout() { log.Printf("Timeout connecting to %s", record["IPAddress"]) continue + } else if strings.Contains(e.Error(), "connection refused") { + log.Printf("Connection refused when connecting to: %s", record["IPAddress"]) + continue } } } else { From 5949bc91c444cc9e84a15184ef7ecdef08b84cc9 Mon Sep 17 00:00:00 2001 From: DanHam Date: Wed, 25 Oct 2017 13:50:58 +0100 Subject: [PATCH 0038/1216] Extend upload and subsequent 'dot sourcing' of env vars to std PS command * Wrap funcs to flatten and upload env vars with new func prepareEnvVars. While the wrapped funcs could be combined, keeping them separate simplifies testing. * Configure/refactor std and elevated PS to use new funcs to prepare, upload and dot source env vars. * Dot sourcing the env vars in this way avoids the need to embed them directly in the command string. This avoids the need to escape the env vars to ensure the command string is correctly parsed. * Characters within the env vars that are special to PS (such as $'s and backticks) will still need to be escaped to allow them to be correctly interpreted by PS. * The std and elevated PS commands now inject env vars into the remote env via the same mechanism. This ensures consistent behaviour across the two command types. Fixes #5471 --- provisioner/powershell/provisioner.go | 52 +++++++++++++++++++-------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/provisioner/powershell/provisioner.go b/provisioner/powershell/provisioner.go index 4297acf08..3d2f16f85 100644 --- a/provisioner/powershell/provisioner.go +++ b/provisioner/powershell/provisioner.go @@ -113,7 +113,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { } if p.config.EnvVarFormat == "" { - p.config.EnvVarFormat = `$env:%s=\"%s\"; ` + p.config.EnvVarFormat = `$env:%s="%s"; ` } if p.config.ElevatedEnvVarFormat == "" { @@ -121,7 +121,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { } if p.config.ExecuteCommand == "" { - p.config.ExecuteCommand = `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};{{.Vars}}&'{{.Path}}';exit $LastExitCode }"` + p.config.ExecuteCommand = `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};. {{.Vars}}; &'{{.Path}}';exit $LastExitCode }"` } if p.config.ElevatedExecuteCommand == "" { @@ -331,6 +331,19 @@ func (p *Provisioner) retryable(f func() error) error { } } +// Enviroment variables required within the remote environment are uploaded within a PS script and +// then enabled by 'dot sourcing' the script immediately prior to execution of the main command +func (p *Provisioner) prepareEnvVars(elevated bool) (envVarPath string, err error) { + // Collate all required env vars into a plain string with required formatting applied + flattenedEnvVars := p.createFlattenedEnvVars(elevated) + // Create a powershell script on the target build fs containing the flattened env vars + envVarPath, err = p.uploadEnvVars(flattenedEnvVars) + if err != nil { + return "", err + } + return +} + func (p *Provisioner) createFlattenedEnvVars(elevated bool) (flattened string) { flattened = "" envVars := make(map[string]string) @@ -367,6 +380,19 @@ func (p *Provisioner) createFlattenedEnvVars(elevated bool) (flattened string) { return } +func (p *Provisioner) uploadEnvVars(flattenedEnvVars string) (envVarPath string, err error) { + // Upload all env vars to a powershell script on the target build file system + envVarReader := strings.NewReader(flattenedEnvVars) + uuid := uuid.TimeOrderedUUID() + envVarPath = fmt.Sprintf(`${env:SYSTEMROOT}\Temp\packer-env-vars-%s.ps1`, uuid) + log.Printf("Uploading env vars to %s", envVarPath) + err = p.communicator.Upload(envVarPath, envVarReader, nil) + if err != nil { + return "", fmt.Errorf("Error uploading ps script containing env vars: %s", err) + } + return +} + func (p *Provisioner) createCommandText() (command string, err error) { // Return the interpolated command if p.config.ElevatedUser == "" { @@ -377,12 +403,15 @@ func (p *Provisioner) createCommandText() (command string, err error) { } func (p *Provisioner) createCommandTextNonPrivileged() (command string, err error) { - // Create environment variables to set before executing the command - flattenedEnvVars := p.createFlattenedEnvVars(false) + // Prepare everything needed to enable the required env vars within the remote environment + envVarPath, err := p.prepareEnvVars(false) + if err != nil { + return "", err + } p.config.ctx.Data = &ExecuteCommandTemplate{ - Vars: flattenedEnvVars, Path: p.config.RemotePath, + Vars: envVarPath, } command, err = interpolate.Render(p.config.ExecuteCommand, &p.config.ctx) @@ -395,17 +424,10 @@ func (p *Provisioner) createCommandTextNonPrivileged() (command string, err erro } func (p *Provisioner) createCommandTextPrivileged() (command string, err error) { - // Can't double escape the env vars, lets create shiny new ones - flattenedEnvVars := p.createFlattenedEnvVars(true) - // Need to create a mini ps1 script containing all of the environment variables we want; - // we'll be dot-sourcing this later - envVarReader := strings.NewReader(flattenedEnvVars) - uuid := uuid.TimeOrderedUUID() - envVarPath := fmt.Sprintf(`${env:SYSTEMROOT}\Temp\packer-env-vars-%s.ps1`, uuid) - log.Printf("Uploading env vars to %s", envVarPath) - err = p.communicator.Upload(envVarPath, envVarReader, nil) + // Prepare everything needed to enable the required env vars within the remote environment + envVarPath, err := p.prepareEnvVars(false) if err != nil { - return "", fmt.Errorf("Error preparing elevated powershell script: %s", err) + return "", err } p.config.ctx.Data = &ExecuteCommandTemplate{ From 4b89fc1c009fb7eb46fc530d805d1dcf3511ff33 Mon Sep 17 00:00:00 2001 From: DanHam Date: Wed, 25 Oct 2017 22:47:08 +0100 Subject: [PATCH 0039/1216] Fix tests post changes. Add test for upload func. --- provisioner/powershell/provisioner_test.go | 85 +++++++++++++++------- 1 file changed, 58 insertions(+), 27 deletions(-) diff --git a/provisioner/powershell/provisioner_test.go b/provisioner/powershell/provisioner_test.go index e7e64d52e..749564d4d 100644 --- a/provisioner/powershell/provisioner_test.go +++ b/provisioner/powershell/provisioner_test.go @@ -79,8 +79,8 @@ func TestProvisionerPrepare_Defaults(t *testing.T) { t.Error("expected elevated_password to be empty") } - if p.config.ExecuteCommand != `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};{{.Vars}}&'{{.Path}}';exit $LastExitCode }"` { - t.Fatalf(`Default command should be 'powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};{{.Vars}}&'{{.Path}}';exit $LastExitCode }"', but got '%s'`, p.config.ExecuteCommand) + if p.config.ExecuteCommand != `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};. {{.Vars}}; &'{{.Path}}';exit $LastExitCode }"` { + t.Fatalf(`Default command should be 'powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};. {{.Vars}}; &'{{.Path}}';exit $LastExitCode }"', but got '%s'`, p.config.ExecuteCommand) } if p.config.ElevatedExecuteCommand != `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};. {{.Vars}}; &'{{.Path}}'; exit $LastExitCode }"` { @@ -403,7 +403,7 @@ func TestProvisionerProvision_Inline(t *testing.T) { ui := testUi() p := new(Provisioner) - // Defaults provided by Packer + // Defaults provided by Packer - env vars should not appear in cmd p.config.PackerBuildName = "vmware" p.config.PackerBuilderType = "iso" comm := new(packer.MockCommunicator) @@ -413,11 +413,14 @@ func TestProvisionerProvision_Inline(t *testing.T) { t.Fatal("should not have error") } - expectedCommand := `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};$env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; &'c:/Windows/Temp/inlineScript.ps1';exit $LastExitCode }"` - if comm.StartCmd.Command != expectedCommand { - t.Fatalf("Expect command to be: %s, got %s", expectedCommand, comm.StartCmd.Command) + cmd := comm.StartCmd.Command + re := regexp.MustCompile(`powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){\$ProgressPreference='SilentlyContinue'};\. \${env:SYSTEMROOT}\\Temp\\packer-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/inlineScript.ps1';exit \$LastExitCode }"`) + matched := re.MatchString(cmd) + if !matched { + t.Fatalf("Got unexpected command: %s", cmd) } + // User supplied env vars should not change things envVars := make([]string, 2) envVars[0] = "FOO=BAR" envVars[1] = "BAR=BAZ" @@ -430,9 +433,11 @@ func TestProvisionerProvision_Inline(t *testing.T) { t.Fatal("should not have error") } - expectedCommand = `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};$env:BAR=\"BAZ\"; $env:FOO=\"BAR\"; $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; &'c:/Windows/Temp/inlineScript.ps1';exit $LastExitCode }"` - if comm.StartCmd.Command != expectedCommand { - t.Fatalf("Expect command to be: %s, got %s", expectedCommand, comm.StartCmd.Command) + cmd = comm.StartCmd.Command + re = regexp.MustCompile(`powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){\$ProgressPreference='SilentlyContinue'};\. \${env:SYSTEMROOT}\\Temp\\packer-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/inlineScript.ps1';exit \$LastExitCode }"`) + matched = re.MatchString(cmd) + if !matched { + t.Fatalf("Got unexpected command: %s", cmd) } } @@ -455,11 +460,12 @@ func TestProvisionerProvision_Scripts(t *testing.T) { t.Fatal("should not have error") } - expectedCommand := `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};$env:PACKER_BUILDER_TYPE=\"footype\"; $env:PACKER_BUILD_NAME=\"foobuild\"; &'c:/Windows/Temp/script.ps1';exit $LastExitCode }"` - if comm.StartCmd.Command != expectedCommand { - t.Fatalf("Expect command to be: %s, got %s", expectedCommand, comm.StartCmd.Command) + cmd := comm.StartCmd.Command + re := regexp.MustCompile(`powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){\$ProgressPreference='SilentlyContinue'};\. \${env:SYSTEMROOT}\\Temp\\packer-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/script.ps1';exit \$LastExitCode }"`) + matched := re.MatchString(cmd) + if !matched { + t.Fatalf("Got unexpected command: %s", cmd) } - } func TestProvisionerProvision_ScriptsWithEnvVars(t *testing.T) { @@ -488,9 +494,11 @@ func TestProvisionerProvision_ScriptsWithEnvVars(t *testing.T) { t.Fatal("should not have error") } - expectedCommand := `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};$env:BAR=\"BAZ\"; $env:FOO=\"BAR\"; $env:PACKER_BUILDER_TYPE=\"footype\"; $env:PACKER_BUILD_NAME=\"foobuild\"; &'c:/Windows/Temp/script.ps1';exit $LastExitCode }"` - if comm.StartCmd.Command != expectedCommand { - t.Fatalf("Expect command to be: %s, got %s", expectedCommand, comm.StartCmd.Command) + cmd := comm.StartCmd.Command + re := regexp.MustCompile(`powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){\$ProgressPreference='SilentlyContinue'};\. \${env:SYSTEMROOT}\\Temp\\packer-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/script.ps1';exit \$LastExitCode }"`) + matched := re.MatchString(cmd) + if !matched { + t.Fatalf("Got unexpected command: %s", cmd) } } @@ -547,11 +555,11 @@ func TestProvisioner_createFlattenedEnvVars_windows(t *testing.T) { {"FOO==bar"}, // User env var with value starting with equals } expected := []string{ - `$env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; `, - `$env:FOO=\"bar\"; $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; `, - `$env:BAZ=\"qux\"; $env:FOO=\"bar\"; $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; `, - `$env:FOO=\"bar=baz\"; $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; `, - `$env:FOO=\"=bar\"; $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; `, + `$env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; `, + `$env:FOO="bar"; $env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; `, + `$env:BAZ="qux"; $env:FOO="bar"; $env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; `, + `$env:FOO="bar=baz"; $env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; `, + `$env:FOO="=bar"; $env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; `, } p := new(Provisioner) @@ -571,7 +579,6 @@ func TestProvisioner_createFlattenedEnvVars_windows(t *testing.T) { } func TestProvision_createCommandText(t *testing.T) { - config := testConfig() config["remote_path"] = "c:/Windows/Temp/script.ps1" p := new(Provisioner) @@ -586,22 +593,46 @@ func TestProvision_createCommandText(t *testing.T) { // Non-elevated cmd, _ := p.createCommandText() - expectedCommand := `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};$env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; &'c:/Windows/Temp/script.ps1';exit $LastExitCode }"` - - if cmd != expectedCommand { - t.Fatalf("Expected Non-elevated command: %s, got %s", expectedCommand, cmd) + re := regexp.MustCompile(`powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){\$ProgressPreference='SilentlyContinue'};\. \${env:SYSTEMROOT}\\Temp\\packer-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/script.ps1';exit \$LastExitCode }"`) + matched := re.MatchString(cmd) + if !matched { + t.Fatalf("Got unexpected command: %s", cmd) } // Elevated p.config.ElevatedUser = "vagrant" p.config.ElevatedPassword = "vagrant" cmd, _ = p.createCommandText() - matched, _ := regexp.MatchString("powershell -executionpolicy bypass -file \"%TEMP%(.{1})packer-elevated-shell.*", cmd) + re = regexp.MustCompile(`powershell -executionpolicy bypass -file "%TEMP%\\packer-elevated-shell-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1"`) + matched = re.MatchString(cmd) if !matched { t.Fatalf("Got unexpected elevated command: %s", cmd) } } +func TestProvision_uploadEnvVars(t *testing.T) { + p := new(Provisioner) + comm := new(packer.MockCommunicator) + p.communicator = comm + + flattenedEnvVars := `$env:PACKER_BUILDER_TYPE="footype"; $env:PACKER_BUILD_NAME="foobuild";` + + envVarPath, err := p.uploadEnvVars(flattenedEnvVars) + if err != nil { + t.Fatalf("Did not expect error: %s", err.Error()) + } + + if comm.UploadCalled != true { + t.Fatalf("Failed to upload env var file") + } + + re := regexp.MustCompile(`\${env:SYSTEMROOT}\\Temp\\packer-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1`) + matched := re.MatchString(envVarPath) + if !matched { + t.Fatalf("Got unexpected path for env var file: %s", envVarPath) + } +} + func TestProvision_generateElevatedShellRunner(t *testing.T) { // Non-elevated From 3cdc79de6a9fc5c6bf276b3e9f3627eeb0c0c5f5 Mon Sep 17 00:00:00 2001 From: DanHam Date: Wed, 25 Oct 2017 23:06:01 +0100 Subject: [PATCH 0040/1216] Update docs to reflect new upload and dot source of env var for std ps cmd --- .../docs/provisioners/powershell.html.md | 50 ++++++++++--------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/website/source/docs/provisioners/powershell.html.md b/website/source/docs/provisioners/powershell.html.md index f085a6e82..a096bf2f5 100644 --- a/website/source/docs/provisioners/powershell.html.md +++ b/website/source/docs/provisioners/powershell.html.md @@ -1,8 +1,8 @@ --- description: | - The shell Packer provisioner provisions machines built by Packer using shell - scripts. Shell provisioning is the easiest way to get software installed and - configured on a machine. + The shell Packer provisioner provisions machines built by Packer using + shell scripts. Shell provisioning is the easiest way to get software + installed and configured on a machine. layout: docs page_title: 'PowerShell - Provisioners' sidebar_current: 'docs-provisioners-powershell' @@ -29,20 +29,21 @@ The example below is fully functional. ## Configuration Reference The reference of available configuration options is listed below. The only -required element is either "inline" or "script". Every other option is optional. +required element is either "inline" or "script". Every other option is +optional. Exactly *one* of the following is required: - `inline` (array of strings) - This is an array of commands to execute. The - commands are concatenated by newlines and turned into a single file, so they - are all executed within the same context. This allows you to change + commands are concatenated by newlines and turned into a single file, so + they are all executed within the same context. This allows you to change directories in one command and use something in the directory in the next and so on. Inline scripts are the easiest way to pull off simple tasks within the machine. - `script` (string) - The path to a script to upload and execute in - the machine. This path can be absolute or relative. If it is relative, it is - relative to the working directory when Packer is executed. + the machine. This path can be absolute or relative. If it is relative, it + is relative to the working directory when Packer is executed. - `scripts` (array of strings) - An array of scripts to execute. The scripts will be uploaded and executed in the order specified. Each script is @@ -51,12 +52,12 @@ Exactly *one* of the following is required: Optional parameters: -- `binary` (boolean) - If true, specifies that the script(s) are binary files, - and Packer should therefore not convert Windows line endings to Unix line - endings (if there are any). By default this is false. +- `binary` (boolean) - If true, specifies that the script(s) are binary + files, and Packer should therefore not convert Windows line endings to Unix + line endings (if there are any). By default this is false. -- `elevated_execute_command` (string) - The command to use to execute the elevated - script. By default this is as follows: +- `elevated_execute_command` (string) - The command to use to execute the + elevated script. By default this is as follows: ``` powershell powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};. {{.Vars}}; &'{{.Path}}'; exit $LastExitCode }" @@ -65,32 +66,34 @@ Optional parameters: The value of this is treated as [configuration template](/docs/templates/engine.html). There are two available variables: `Path`, which is the path to the script to run, and - `Vars`, which is the location of a temp file containing the list of `environment_vars`, if configured. + `Vars`, which is the location of a temp file containing the list of + `environment_vars`, if configured. - `environment_vars` (array of strings) - An array of key/value pairs to inject prior to the execute\_command. The format should be `key=value`. - Packer injects some environmental variables by default into the environment, - as well, which are covered in the section below. + Packer injects some environmental variables by default into the + environment, as well, which are covered in the section below. - `execute_command` (string) - The command to use to execute the script. By default this is as follows: ``` powershell - powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};{{.Vars}}&'{{.Path}}';exit $LastExitCode }" + powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};. {{.Vars}}; &'{{.Path}}'; exit $LastExitCode }" ``` The value of this is treated as [configuration template](/docs/templates/engine.html). There are two available variables: `Path`, which is the path to the script to run, and - `Vars`, which is the list of `environment_vars`, if configured. + `Vars`, which is the location of a temp file containing the list of + `environment_vars`, if configured. - `elevated_user` and `elevated_password` (string) - If specified, the PowerShell script will be run with elevated privileges using the given Windows user. - `remote_path` (string) - The path where the script will be uploaded to in - the machine. This defaults to "c:/Windows/Temp/script.ps1". This value must be a - writable location and any parent directories must already exist. + the machine. This defaults to "c:/Windows/Temp/script.ps1". This value must + be a writable location and any parent directories must already exist. - `start_retry_timeout` (string) - The amount of time to attempt to *start* the remote process. By default this is "5m" or 5 minutes. This setting @@ -111,9 +114,10 @@ commonly useful environmental variables: This is most useful when Packer is making multiple builds and you want to distinguish them slightly from a common provisioning script. -- `PACKER_BUILDER_TYPE` is the type of the builder that was used to create the - machine that the script is running on. This is useful if you want to run - only certain parts of the script on systems built with certain builders. +- `PACKER_BUILDER_TYPE` is the type of the builder that was used to create + the machine that the script is running on. This is useful if you want to + run only certain parts of the script on systems built with certain + builders. - `PACKER_HTTP_ADDR` If using a builder that provides an http server for file transfer (such as hyperv, parallels, qemu, virtualbox, and vmware), this From d8900519400dba65ac50a164c3ba4fbaec45c633 Mon Sep 17 00:00:00 2001 From: Erlend Graff Date: Sun, 5 Nov 2017 14:51:18 +0100 Subject: [PATCH 0041/1216] hyper-v: implement driver mock --- builder/hyperv/common/driver_mock.go | 534 +++++++++++++++++++++++++++ 1 file changed, 534 insertions(+) create mode 100644 builder/hyperv/common/driver_mock.go diff --git a/builder/hyperv/common/driver_mock.go b/builder/hyperv/common/driver_mock.go new file mode 100644 index 000000000..433dfb814 --- /dev/null +++ b/builder/hyperv/common/driver_mock.go @@ -0,0 +1,534 @@ +package common + +type DriverMock struct { + IsRunning_Called bool + IsRunning_VmName string + IsRunning_Return bool + IsRunning_Err error + + IsOff_Called bool + IsOff_VmName string + IsOff_Return bool + IsOff_Err error + + Uptime_Called bool + Uptime_VmName string + Uptime_Return uint64 + Uptime_Err error + + Start_Called bool + Start_VmName string + Start_Err error + + Stop_Called bool + Stop_VmName string + Stop_Err error + + Verify_Called bool + Verify_Err error + + Mac_Called bool + Mac_VmName string + Mac_Return string + Mac_Err error + + IpAddress_Called bool + IpAddress_Mac string + IpAddress_Return string + IpAddress_Err error + + GetHostName_Called bool + GetHostName_Ip string + GetHostName_Return string + GetHostName_Err error + + GetVirtualMachineGeneration_Called bool + GetVirtualMachineGeneration_VmName string + GetVirtualMachineGeneration_Return uint + GetVirtualMachineGeneration_Err error + + GetHostAdapterIpAddressForSwitch_Called bool + GetHostAdapterIpAddressForSwitch_SwitchName string + GetHostAdapterIpAddressForSwitch_Return string + GetHostAdapterIpAddressForSwitch_Err error + + TypeScanCodes_Called bool + TypeScanCodes_VmName string + TypeScanCodes_ScanCodes string + TypeScanCodes_Err error + + GetVirtualMachineNetworkAdapterAddress_Called bool + GetVirtualMachineNetworkAdapterAddress_VmName string + GetVirtualMachineNetworkAdapterAddress_Return string + GetVirtualMachineNetworkAdapterAddress_Err error + + SetNetworkAdapterVlanId_Called bool + SetNetworkAdapterVlanId_SwitchName string + SetNetworkAdapterVlanId_VlanId string + SetNetworkAdapterVlanId_Err error + + SetVirtualMachineVlanId_Called bool + SetVirtualMachineVlanId_VmName string + SetVirtualMachineVlanId_VlanId string + SetVirtualMachineVlanId_Err error + + UntagVirtualMachineNetworkAdapterVlan_Called bool + UntagVirtualMachineNetworkAdapterVlan_VmName string + UntagVirtualMachineNetworkAdapterVlan_SwitchName string + UntagVirtualMachineNetworkAdapterVlan_Err error + + CreateExternalVirtualSwitch_Called bool + CreateExternalVirtualSwitch_VmName string + CreateExternalVirtualSwitch_SwitchName string + CreateExternalVirtualSwitch_Err error + + GetVirtualMachineSwitchName_Called bool + GetVirtualMachineSwitchName_VmName string + GetVirtualMachineSwitchName_Return string + GetVirtualMachineSwitchName_Err error + + ConnectVirtualMachineNetworkAdapterToSwitch_Called bool + ConnectVirtualMachineNetworkAdapterToSwitch_VmName string + ConnectVirtualMachineNetworkAdapterToSwitch_SwitchName string + ConnectVirtualMachineNetworkAdapterToSwitch_Err error + + DeleteVirtualSwitch_Called bool + DeleteVirtualSwitch_SwitchName string + DeleteVirtualSwitch_Err error + + CreateVirtualSwitch_Called bool + CreateVirtualSwitch_SwitchName string + CreateVirtualSwitch_SwitchType string + CreateVirtualSwitch_Return bool + CreateVirtualSwitch_Err error + + AddVirtualMachineHardDrive_Called bool + AddVirtualMachineHardDrive_VmName string + AddVirtualMachineHardDrive_VhdFile string + AddVirtualMachineHardDrive_VhdName string + AddVirtualMachineHardDrive_VhdSizeBytes int64 + AddVirtualMachineHardDrive_ControllerType string + AddVirtualMachineHardDrive_Err error + + CreateVirtualMachine_Called bool + CreateVirtualMachine_VmName string + CreateVirtualMachine_Path string + CreateVirtualMachine_HarddrivePath string + CreateVirtualMachine_VhdPath string + CreateVirtualMachine_Ram int64 + CreateVirtualMachine_DiskSize int64 + CreateVirtualMachine_SwitchName string + CreateVirtualMachine_Generation uint + CreateVirtualMachine_Err error + + CloneVirtualMachine_Called bool + CloneVirtualMachine_CloneFromVmxcPath string + CloneVirtualMachine_CloneFromVmName string + CloneVirtualMachine_CloneFromSnapshotName string + CloneVirtualMachine_CloneAllSnapshots bool + CloneVirtualMachine_VmName string + CloneVirtualMachine_Path string + CloneVirtualMachine_HarddrivePath string + CloneVirtualMachine_Ram int64 + CloneVirtualMachine_SwitchName string + CloneVirtualMachine_Err error + + DeleteVirtualMachine_Called bool + DeleteVirtualMachine_VmName string + DeleteVirtualMachine_Err error + + SetVirtualMachineCpuCount_Called bool + SetVirtualMachineCpuCount_VmName string + SetVirtualMachineCpuCount_Cpu uint + SetVirtualMachineCpuCount_Err error + + SetVirtualMachineMacSpoofing_Called bool + SetVirtualMachineMacSpoofing_VmName string + SetVirtualMachineMacSpoofing_Enable bool + SetVirtualMachineMacSpoofing_Err error + + SetVirtualMachineDynamicMemory_Called bool + SetVirtualMachineDynamicMemory_VmName string + SetVirtualMachineDynamicMemory_Enable bool + SetVirtualMachineDynamicMemory_Err error + + SetVirtualMachineSecureBoot_Called bool + SetVirtualMachineSecureBoot_VmName string + SetVirtualMachineSecureBoot_Enable bool + SetVirtualMachineSecureBoot_Err error + + SetVirtualMachineVirtualizationExtensions_Called bool + SetVirtualMachineVirtualizationExtensions_VmName string + SetVirtualMachineVirtualizationExtensions_Enable bool + SetVirtualMachineVirtualizationExtensions_Err error + + EnableVirtualMachineIntegrationService_Called bool + EnableVirtualMachineIntegrationService_VmName string + EnableVirtualMachineIntegrationService_IntegrationServiceName string + EnableVirtualMachineIntegrationService_Err error + + ExportVirtualMachine_Called bool + ExportVirtualMachine_VmName string + ExportVirtualMachine_Path string + ExportVirtualMachine_Err error + + CompactDisks_Called bool + CompactDisks_ExpPath string + CompactDisks_VhdDir string + CompactDisks_Err error + + CopyExportedVirtualMachine_Called bool + CopyExportedVirtualMachine_ExpPath string + CopyExportedVirtualMachine_OutputPath string + CopyExportedVirtualMachine_VhdDir string + CopyExportedVirtualMachine_VmDir string + CopyExportedVirtualMachine_Err error + + RestartVirtualMachine_Called bool + RestartVirtualMachine_VmName string + RestartVirtualMachine_Err error + + CreateDvdDrive_Called bool + CreateDvdDrive_VmName string + CreateDvdDrive_IsoPath string + CreateDvdDrive_Generation uint + CreateDvdDrive_ControllerNumber uint + CreateDvdDrive_ControllerLocation uint + CreateDvdDrive_Err error + + MountDvdDrive_Called bool + MountDvdDrive_VmName string + MountDvdDrive_Path string + MountDvdDrive_ControllerNumber uint + MountDvdDrive_ControllerLocation uint + MountDvdDrive_Err error + + SetBootDvdDrive_Called bool + SetBootDvdDrive_VmName string + SetBootDvdDrive_ControllerNumber uint + SetBootDvdDrive_ControllerLocation uint + SetBootDvdDrive_Generation uint + SetBootDvdDrive_Err error + + UnmountDvdDrive_Called bool + UnmountDvdDrive_VmName string + UnmountDvdDrive_ControllerNumber uint + UnmountDvdDrive_ControllerLocation uint + UnmountDvdDrive_Err error + + DeleteDvdDrive_Called bool + DeleteDvdDrive_VmName string + DeleteDvdDrive_ControllerNumber uint + DeleteDvdDrive_ControllerLocation uint + DeleteDvdDrive_Err error + + MountFloppyDrive_Called bool + MountFloppyDrive_VmName string + MountFloppyDrive_Path string + MountFloppyDrive_Err error + + UnmountFloppyDrive_Called bool + UnmountFloppyDrive_VmName string + UnmountFloppyDrive_Err error +} + +func (d *DriverMock) IsRunning(vmName string) (bool, error) { + d.IsRunning_Called = true + d.IsRunning_VmName = vmName + return d.IsRunning_Return, d.IsRunning_Err +} + +func (d *DriverMock) IsOff(vmName string) (bool, error) { + d.IsOff_Called = true + d.IsOff_VmName = vmName + return d.IsOff_Return, d.IsOff_Err +} + +func (d *DriverMock) Uptime(vmName string) (uint64, error) { + d.Uptime_Called = true + d.Uptime_VmName = vmName + return d.Uptime_Return, d.Uptime_Err +} + +func (d *DriverMock) Start(vmName string) error { + d.Start_Called = true + d.Start_VmName = vmName + return d.Start_Err +} + +func (d *DriverMock) Stop(vmName string) error { + d.Stop_Called = true + d.Stop_VmName = vmName + return d.Stop_Err +} + +func (d *DriverMock) Verify() error { + d.Verify_Called = true + return d.Verify_Err +} + +func (d *DriverMock) Mac(vmName string) (string, error) { + d.Mac_Called = true + d.Mac_VmName = vmName + return d.Mac_Return, d.Mac_Err +} + +func (d *DriverMock) IpAddress(mac string) (string, error) { + d.IpAddress_Called = true + d.IpAddress_Mac = mac + return d.IpAddress_Return, d.IpAddress_Err +} + +func (d *DriverMock) GetHostName(ip string) (string, error) { + d.GetHostName_Called = true + d.GetHostName_Ip = ip + return d.GetHostName_Return, d.GetHostName_Err +} + +func (d *DriverMock) GetVirtualMachineGeneration(vmName string) (uint, error) { + d.GetVirtualMachineGeneration_Called = true + d.GetVirtualMachineGeneration_VmName = vmName + return d.GetVirtualMachineGeneration_Return, d.GetVirtualMachineGeneration_Err +} + +func (d *DriverMock) GetHostAdapterIpAddressForSwitch(switchName string) (string, error) { + d.GetHostAdapterIpAddressForSwitch_Called = true + d.GetHostAdapterIpAddressForSwitch_SwitchName = switchName + return d.GetHostAdapterIpAddressForSwitch_Return, d.GetHostAdapterIpAddressForSwitch_Err +} + +func (d *DriverMock) TypeScanCodes(vmName string, scanCodes string) error { + d.TypeScanCodes_Called = true + d.TypeScanCodes_VmName = vmName + d.TypeScanCodes_ScanCodes = scanCodes + return d.TypeScanCodes_Err +} + +func (d *DriverMock) GetVirtualMachineNetworkAdapterAddress(vmName string) (string, error) { + d.GetVirtualMachineNetworkAdapterAddress_Called = true + d.GetVirtualMachineNetworkAdapterAddress_VmName = vmName + return d.GetVirtualMachineNetworkAdapterAddress_Return, d.GetVirtualMachineNetworkAdapterAddress_Err +} + +func (d *DriverMock) SetNetworkAdapterVlanId(switchName string, vlanId string) error { + d.SetNetworkAdapterVlanId_Called = true + d.SetNetworkAdapterVlanId_SwitchName = switchName + d.SetNetworkAdapterVlanId_VlanId = vlanId + return d.SetNetworkAdapterVlanId_Err +} + +func (d *DriverMock) SetVirtualMachineVlanId(vmName string, vlanId string) error { + d.SetVirtualMachineVlanId_Called = true + d.SetVirtualMachineVlanId_VmName = vmName + d.SetVirtualMachineVlanId_VlanId = vlanId + return d.SetVirtualMachineVlanId_Err +} + +func (d *DriverMock) UntagVirtualMachineNetworkAdapterVlan(vmName string, switchName string) error { + d.UntagVirtualMachineNetworkAdapterVlan_Called = true + d.UntagVirtualMachineNetworkAdapterVlan_VmName = vmName + d.UntagVirtualMachineNetworkAdapterVlan_SwitchName = switchName + return d.UntagVirtualMachineNetworkAdapterVlan_Err +} + +func (d *DriverMock) CreateExternalVirtualSwitch(vmName string, switchName string) error { + d.CreateExternalVirtualSwitch_Called = true + d.CreateExternalVirtualSwitch_VmName = vmName + d.CreateExternalVirtualSwitch_SwitchName = switchName + return d.CreateExternalVirtualSwitch_Err +} + +func (d *DriverMock) GetVirtualMachineSwitchName(vmName string) (string, error) { + d.GetVirtualMachineSwitchName_Called = true + d.GetVirtualMachineSwitchName_VmName = vmName + return d.GetVirtualMachineSwitchName_Return, d.GetVirtualMachineSwitchName_Err +} + +func (d *DriverMock) ConnectVirtualMachineNetworkAdapterToSwitch(vmName string, switchName string) error { + d.ConnectVirtualMachineNetworkAdapterToSwitch_Called = true + d.ConnectVirtualMachineNetworkAdapterToSwitch_VmName = vmName + d.ConnectVirtualMachineNetworkAdapterToSwitch_SwitchName = switchName + return d.ConnectVirtualMachineNetworkAdapterToSwitch_Err +} + +func (d *DriverMock) DeleteVirtualSwitch(switchName string) error { + d.DeleteVirtualSwitch_Called = true + d.DeleteVirtualSwitch_SwitchName = switchName + return d.DeleteVirtualSwitch_Err +} + +func (d *DriverMock) CreateVirtualSwitch(switchName string, switchType string) (bool, error) { + d.CreateVirtualSwitch_Called = true + d.CreateVirtualSwitch_SwitchName = switchName + d.CreateVirtualSwitch_SwitchType = switchType + return d.CreateVirtualSwitch_Return, d.CreateVirtualSwitch_Err +} + +func (d *DriverMock) AddVirtualMachineHardDrive(vmName string, vhdFile string, vhdName string, vhdSizeBytes int64, controllerType string) error { + d.AddVirtualMachineHardDrive_Called = true + d.AddVirtualMachineHardDrive_VmName = vmName + d.AddVirtualMachineHardDrive_VhdFile = vhdFile + d.AddVirtualMachineHardDrive_VhdName = vhdName + d.AddVirtualMachineHardDrive_VhdSizeBytes = vhdSizeBytes + d.AddVirtualMachineHardDrive_ControllerType = controllerType + return d.AddVirtualMachineHardDrive_Err +} + +func (d *DriverMock) CreateVirtualMachine(vmName string, path string, harddrivePath string, vhdPath string, ram int64, diskSize int64, switchName string, generation uint) error { + d.CreateVirtualMachine_Called = true + d.CreateVirtualMachine_VmName = vmName + d.CreateVirtualMachine_Path = path + d.CreateVirtualMachine_HarddrivePath = harddrivePath + d.CreateVirtualMachine_VhdPath = vhdPath + d.CreateVirtualMachine_Ram = ram + d.CreateVirtualMachine_DiskSize = diskSize + d.CreateVirtualMachine_SwitchName = switchName + d.CreateVirtualMachine_Generation = generation + return d.CreateVirtualMachine_Err +} + +func (d *DriverMock) CloneVirtualMachine(cloneFromVmxcPath string, cloneFromVmName string, cloneFromSnapshotName string, cloneAllSnapshots bool, vmName string, path string, harddrivePath string, ram int64, switchName string) error { + d.CloneVirtualMachine_Called = true + d.CloneVirtualMachine_CloneFromVmxcPath = cloneFromVmxcPath + d.CloneVirtualMachine_CloneFromVmName = cloneFromVmName + d.CloneVirtualMachine_CloneFromSnapshotName = cloneFromSnapshotName + d.CloneVirtualMachine_CloneAllSnapshots = cloneAllSnapshots + d.CloneVirtualMachine_VmName = vmName + d.CloneVirtualMachine_Path = path + d.CloneVirtualMachine_HarddrivePath = harddrivePath + d.CloneVirtualMachine_Ram = ram + d.CloneVirtualMachine_SwitchName = switchName + return d.CloneVirtualMachine_Err +} + +func (d *DriverMock) DeleteVirtualMachine(vmName string) error { + d.DeleteVirtualMachine_Called = true + d.DeleteVirtualMachine_VmName = vmName + return d.DeleteVirtualMachine_Err +} + +func (d *DriverMock) SetVirtualMachineCpuCount(vmName string, cpu uint) error { + d.SetVirtualMachineCpuCount_Called = true + d.SetVirtualMachineCpuCount_VmName = vmName + d.SetVirtualMachineCpuCount_Cpu = cpu + return d.SetVirtualMachineCpuCount_Err +} + +func (d *DriverMock) SetVirtualMachineMacSpoofing(vmName string, enable bool) error { + d.SetVirtualMachineMacSpoofing_Called = true + d.SetVirtualMachineMacSpoofing_VmName = vmName + d.SetVirtualMachineMacSpoofing_Enable = enable + return d.SetVirtualMachineMacSpoofing_Err +} + +func (d *DriverMock) SetVirtualMachineDynamicMemory(vmName string, enable bool) error { + d.SetVirtualMachineDynamicMemory_Called = true + d.SetVirtualMachineDynamicMemory_VmName = vmName + d.SetVirtualMachineDynamicMemory_Enable = enable + return d.SetVirtualMachineDynamicMemory_Err +} + +func (d *DriverMock) SetVirtualMachineSecureBoot(vmName string, enable bool) error { + d.SetVirtualMachineSecureBoot_Called = true + d.SetVirtualMachineSecureBoot_VmName = vmName + d.SetVirtualMachineSecureBoot_Enable = enable + return d.SetVirtualMachineSecureBoot_Err +} + +func (d *DriverMock) SetVirtualMachineVirtualizationExtensions(vmName string, enable bool) error { + d.SetVirtualMachineVirtualizationExtensions_Called = true + d.SetVirtualMachineVirtualizationExtensions_VmName = vmName + d.SetVirtualMachineVirtualizationExtensions_Enable = enable + return d.SetVirtualMachineVirtualizationExtensions_Err +} + +func (d *DriverMock) EnableVirtualMachineIntegrationService(vmName string, integrationServiceName string) error { + d.EnableVirtualMachineIntegrationService_Called = true + d.EnableVirtualMachineIntegrationService_VmName = vmName + d.EnableVirtualMachineIntegrationService_IntegrationServiceName = integrationServiceName + return d.EnableVirtualMachineIntegrationService_Err +} + +func (d *DriverMock) ExportVirtualMachine(vmName string, path string) error { + d.ExportVirtualMachine_Called = true + d.ExportVirtualMachine_VmName = vmName + d.ExportVirtualMachine_Path = path + return d.ExportVirtualMachine_Err +} + +func (d *DriverMock) CompactDisks(expPath string, vhdDir string) error { + d.CompactDisks_Called = true + d.CompactDisks_ExpPath = expPath + d.CompactDisks_VhdDir = vhdDir + return d.CompactDisks_Err +} + +func (d *DriverMock) CopyExportedVirtualMachine(expPath string, outputPath string, vhdDir string, vmDir string) error { + d.CopyExportedVirtualMachine_Called = true + d.CopyExportedVirtualMachine_ExpPath = expPath + d.CopyExportedVirtualMachine_OutputPath = outputPath + d.CopyExportedVirtualMachine_VhdDir = vhdDir + d.CopyExportedVirtualMachine_VmDir = vmDir + return d.CopyExportedVirtualMachine_Err +} + +func (d *DriverMock) RestartVirtualMachine(vmName string) error { + d.RestartVirtualMachine_Called = true + d.RestartVirtualMachine_VmName = vmName + return d.RestartVirtualMachine_Err +} + +func (d *DriverMock) CreateDvdDrive(vmName string, isoPath string, generation uint) (uint, uint, error) { + d.CreateDvdDrive_Called = true + d.CreateDvdDrive_VmName = vmName + d.CreateDvdDrive_IsoPath = isoPath + d.CreateDvdDrive_Generation = generation + return d.CreateDvdDrive_ControllerNumber, d.CreateDvdDrive_ControllerLocation, d.CreateDvdDrive_Err +} + +func (d *DriverMock) MountDvdDrive(vmName string, path string, controllerNumber uint, controllerLocation uint) error { + d.MountDvdDrive_Called = true + d.MountDvdDrive_VmName = vmName + d.MountDvdDrive_Path = path + d.MountDvdDrive_ControllerNumber = controllerNumber + d.MountDvdDrive_ControllerLocation = controllerLocation + return d.MountDvdDrive_Err +} + +func (d *DriverMock) SetBootDvdDrive(vmName string, controllerNumber uint, controllerLocation uint, generation uint) error { + d.SetBootDvdDrive_Called = true + d.SetBootDvdDrive_VmName = vmName + d.SetBootDvdDrive_ControllerNumber = controllerNumber + d.SetBootDvdDrive_ControllerLocation = controllerLocation + d.SetBootDvdDrive_Generation = generation + return d.SetBootDvdDrive_Err +} + +func (d *DriverMock) UnmountDvdDrive(vmName string, controllerNumber uint, controllerLocation uint) error { + d.UnmountDvdDrive_Called = true + d.UnmountDvdDrive_VmName = vmName + d.UnmountDvdDrive_ControllerNumber = controllerNumber + d.UnmountDvdDrive_ControllerLocation = controllerLocation + return d.UnmountDvdDrive_Err +} + +func (d *DriverMock) DeleteDvdDrive(vmName string, controllerNumber uint, controllerLocation uint) error { + d.DeleteDvdDrive_Called = true + d.DeleteDvdDrive_VmName = vmName + d.DeleteDvdDrive_ControllerNumber = controllerNumber + d.DeleteDvdDrive_ControllerLocation = controllerLocation + return d.DeleteDvdDrive_Err +} + +func (d *DriverMock) MountFloppyDrive(vmName string, path string) error { + d.MountFloppyDrive_Called = true + d.MountFloppyDrive_VmName = vmName + d.MountFloppyDrive_Path = path + return d.MountFloppyDrive_Err +} + +func (d *DriverMock) UnmountFloppyDrive(vmName string) error { + d.UnmountFloppyDrive_Called = true + d.UnmountFloppyDrive_VmName = vmName + return d.UnmountFloppyDrive_Err +} From eeeee3ec351a20b6a48cce10e8d1e950ae274af9 Mon Sep 17 00:00:00 2001 From: Erlend Graff Date: Sun, 5 Nov 2017 14:55:56 +0100 Subject: [PATCH 0042/1216] hyper-v/vmcx: add missing InterpolateContext --- builder/hyperv/vmcx/builder.go | 1 + builder/hyperv/vmcx/builder_test.go | 54 ++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/builder/hyperv/vmcx/builder.go b/builder/hyperv/vmcx/builder.go index a9583327a..6ba93caac 100644 --- a/builder/hyperv/vmcx/builder.go +++ b/builder/hyperv/vmcx/builder.go @@ -98,6 +98,7 @@ type Config struct { func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, + InterpolateContext: &b.config.ctx, InterpolateFilter: &interpolate.RenderFilter{ Exclude: []string{ "boot_command", diff --git a/builder/hyperv/vmcx/builder_test.go b/builder/hyperv/vmcx/builder_test.go index 209094bf3..8c0be09a4 100644 --- a/builder/hyperv/vmcx/builder_test.go +++ b/builder/hyperv/vmcx/builder_test.go @@ -1,11 +1,13 @@ package vmcx import ( + "fmt" "reflect" "testing" - "fmt" + hypervcommon "github.com/hashicorp/packer/builder/hyperv/common" "github.com/hashicorp/packer/packer" + "github.com/mitchellh/multistep" "io/ioutil" "os" ) @@ -486,3 +488,53 @@ func TestBuilderPrepare_CommConfig(t *testing.T) { } } } + +func TestUserVariablesInBootCommand(t *testing.T) { + var b Builder + config := testConfig() + + //Create vmxc folder + td, err := ioutil.TempDir("", "packer") + if err != nil { + t.Fatalf("err: %s", err) + } + defer os.RemoveAll(td) + config["clone_from_vmxc_path"] = td + + config[packer.UserVariablesConfigKey] = map[string]string{"test-variable": "test"} + config["boot_command"] = []string{"blah {{user `test-variable`}} blah"} + + warns, err := b.Prepare(config) + if len(warns) > 0 { + t.Fatalf("bad: %#v", warns) + } + if err != nil { + t.Fatalf("should not have error: %s", err) + } + + ui := packer.TestUi(t) + cache := &packer.FileCache{CacheDir: os.TempDir()} + hook := &packer.MockHook{} + driver := &hypervcommon.DriverMock{} + + // Set up the state. + state := new(multistep.BasicStateBag) + state.Put("cache", cache) + state.Put("config", &b.config) + state.Put("driver", driver) + state.Put("hook", hook) + state.Put("http_port", uint(0)) + state.Put("ui", ui) + state.Put("vmName", "packer-foo") + + step := &hypervcommon.StepTypeBootCommand{ + BootCommand: b.config.BootCommand, + SwitchName: b.config.SwitchName, + Ctx: b.config.ctx, + } + + ret := step.Run(state) + if ret != multistep.ActionContinue { + t.Fatalf("should not have error: %s", ret) + } +} From ae6987c74b7282436da63f596788b368cef12cd4 Mon Sep 17 00:00:00 2001 From: Erlend Graff Date: Sun, 5 Nov 2017 14:58:08 +0100 Subject: [PATCH 0043/1216] hyper-v: add test for hashicorp/packer#5184 --- builder/hyperv/iso/builder_test.go | 45 ++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/builder/hyperv/iso/builder_test.go b/builder/hyperv/iso/builder_test.go index 73ae5591b..546ffae6d 100644 --- a/builder/hyperv/iso/builder_test.go +++ b/builder/hyperv/iso/builder_test.go @@ -6,7 +6,10 @@ import ( "strconv" "testing" + hypervcommon "github.com/hashicorp/packer/builder/hyperv/common" "github.com/hashicorp/packer/packer" + "github.com/mitchellh/multistep" + "os" ) func testConfig() map[string]interface{} { @@ -472,3 +475,45 @@ func TestBuilderPrepare_CommConfig(t *testing.T) { } } + +func TestUserVariablesInBootCommand(t *testing.T) { + var b Builder + config := testConfig() + + config[packer.UserVariablesConfigKey] = map[string]string{"test-variable": "test"} + config["boot_command"] = []string{"blah {{user `test-variable`}} blah"} + + warns, err := b.Prepare(config) + if len(warns) > 0 { + t.Fatalf("bad: %#v", warns) + } + if err != nil { + t.Fatalf("should not have error: %s", err) + } + + ui := packer.TestUi(t) + cache := &packer.FileCache{CacheDir: os.TempDir()} + hook := &packer.MockHook{} + driver := &hypervcommon.DriverMock{} + + // Set up the state. + state := new(multistep.BasicStateBag) + state.Put("cache", cache) + state.Put("config", &b.config) + state.Put("driver", driver) + state.Put("hook", hook) + state.Put("http_port", uint(0)) + state.Put("ui", ui) + state.Put("vmName", "packer-foo") + + step := &hypervcommon.StepTypeBootCommand{ + BootCommand: b.config.BootCommand, + SwitchName: b.config.SwitchName, + Ctx: b.config.ctx, + } + + ret := step.Run(state) + if ret != multistep.ActionContinue { + t.Fatalf("should not have error: %s", ret) + } +} From 27fc72c137d0728bba8cab4eca54142824bc06c8 Mon Sep 17 00:00:00 2001 From: Erlend Graff Date: Sun, 5 Nov 2017 15:16:47 +0100 Subject: [PATCH 0044/1216] fix formatting errors --- builder/hyperv/common/driver_mock.go | 22 +++++++++++----------- builder/hyperv/iso/builder_test.go | 2 +- builder/hyperv/vmcx/builder.go | 2 +- builder/hyperv/vmcx/builder_test.go | 2 +- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/builder/hyperv/common/driver_mock.go b/builder/hyperv/common/driver_mock.go index 433dfb814..dcdd857a5 100644 --- a/builder/hyperv/common/driver_mock.go +++ b/builder/hyperv/common/driver_mock.go @@ -121,17 +121,17 @@ type DriverMock struct { CreateVirtualMachine_Generation uint CreateVirtualMachine_Err error - CloneVirtualMachine_Called bool - CloneVirtualMachine_CloneFromVmxcPath string - CloneVirtualMachine_CloneFromVmName string - CloneVirtualMachine_CloneFromSnapshotName string - CloneVirtualMachine_CloneAllSnapshots bool - CloneVirtualMachine_VmName string - CloneVirtualMachine_Path string - CloneVirtualMachine_HarddrivePath string - CloneVirtualMachine_Ram int64 - CloneVirtualMachine_SwitchName string - CloneVirtualMachine_Err error + CloneVirtualMachine_Called bool + CloneVirtualMachine_CloneFromVmxcPath string + CloneVirtualMachine_CloneFromVmName string + CloneVirtualMachine_CloneFromSnapshotName string + CloneVirtualMachine_CloneAllSnapshots bool + CloneVirtualMachine_VmName string + CloneVirtualMachine_Path string + CloneVirtualMachine_HarddrivePath string + CloneVirtualMachine_Ram int64 + CloneVirtualMachine_SwitchName string + CloneVirtualMachine_Err error DeleteVirtualMachine_Called bool DeleteVirtualMachine_VmName string diff --git a/builder/hyperv/iso/builder_test.go b/builder/hyperv/iso/builder_test.go index 546ffae6d..ba167413b 100644 --- a/builder/hyperv/iso/builder_test.go +++ b/builder/hyperv/iso/builder_test.go @@ -514,6 +514,6 @@ func TestUserVariablesInBootCommand(t *testing.T) { ret := step.Run(state) if ret != multistep.ActionContinue { - t.Fatalf("should not have error: %s", ret) + t.Fatalf("should not have error: %s", ret) } } diff --git a/builder/hyperv/vmcx/builder.go b/builder/hyperv/vmcx/builder.go index 6ba93caac..2a9459ace 100644 --- a/builder/hyperv/vmcx/builder.go +++ b/builder/hyperv/vmcx/builder.go @@ -97,7 +97,7 @@ type Config struct { // Prepare processes the build configuration parameters. func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ - Interpolate: true, + Interpolate: true, InterpolateContext: &b.config.ctx, InterpolateFilter: &interpolate.RenderFilter{ Exclude: []string{ diff --git a/builder/hyperv/vmcx/builder_test.go b/builder/hyperv/vmcx/builder_test.go index 8c0be09a4..89cac5878 100644 --- a/builder/hyperv/vmcx/builder_test.go +++ b/builder/hyperv/vmcx/builder_test.go @@ -535,6 +535,6 @@ func TestUserVariablesInBootCommand(t *testing.T) { ret := step.Run(state) if ret != multistep.ActionContinue { - t.Fatalf("should not have error: %s", ret) + t.Fatalf("should not have error: %s", ret) } } From f4600a208ffc75ddbe76274257991fc8d09ab5d1 Mon Sep 17 00:00:00 2001 From: Arjen Schwarz Date: Mon, 6 Nov 2017 16:16:58 +1100 Subject: [PATCH 0045/1216] Azure: Keep temporary resource group. Fixes #5045 This changeset will detect if the defined temporary resource group already exists. If it does, it will not destroy it, but clean up every resource required for building that is created by Packer individually, both on success and failure. Unit tests have been fixed, but more tests should be added for the new functionalities. --- builder/azure/arm/azure_client.go | 15 + .../azure/arm/step_create_resource_group.go | 51 +- .../arm/step_create_resource_group_test.go | 46 +- builder/azure/arm/step_delete_os_disk.go | 33 +- builder/azure/arm/step_delete_os_disk_test.go | 9 +- .../azure/arm/step_delete_resource_group.go | 73 +- .../arm/step_delete_resource_group_test.go | 6 +- builder/azure/arm/step_deploy_template.go | 149 +++- builder/azure/common/constants/stateBag.go | 1 + .../Azure/azure-sdk-for-go/arm/disk/client.go | 53 ++ .../Azure/azure-sdk-for-go/arm/disk/disks.go | 728 +++++++++++++++++ .../Azure/azure-sdk-for-go/arm/disk/models.go | 278 +++++++ .../azure-sdk-for-go/arm/disk/snapshots.go | 733 ++++++++++++++++++ .../azure-sdk-for-go/arm/disk/version.go | 29 + vendor/vendor.json | 6 + 15 files changed, 2168 insertions(+), 42 deletions(-) create mode 100755 vendor/github.com/Azure/azure-sdk-for-go/arm/disk/client.go create mode 100755 vendor/github.com/Azure/azure-sdk-for-go/arm/disk/disks.go create mode 100755 vendor/github.com/Azure/azure-sdk-for-go/arm/disk/models.go create mode 100755 vendor/github.com/Azure/azure-sdk-for-go/arm/disk/snapshots.go create mode 100755 vendor/github.com/Azure/azure-sdk-for-go/arm/disk/version.go diff --git a/builder/azure/arm/azure_client.go b/builder/azure/arm/azure_client.go index 3fd58bc34..08dd95eae 100644 --- a/builder/azure/arm/azure_client.go +++ b/builder/azure/arm/azure_client.go @@ -10,6 +10,7 @@ import ( "strconv" "github.com/Azure/azure-sdk-for-go/arm/compute" + "github.com/Azure/azure-sdk-for-go/arm/disk" "github.com/Azure/azure-sdk-for-go/arm/network" "github.com/Azure/azure-sdk-for-go/arm/resources/resources" armStorage "github.com/Azure/azure-sdk-for-go/arm/storage" @@ -32,6 +33,7 @@ var ( type AzureClient struct { storage.BlobStorageClient resources.DeploymentsClient + resources.DeploymentOperationsClient resources.GroupsClient network.PublicIPAddressesClient network.InterfacesClient @@ -41,6 +43,7 @@ type AzureClient struct { compute.VirtualMachinesClient common.VaultClient armStorage.AccountsClient + disk.DisksClient InspectorMaxLength int Template *CaptureTemplate @@ -135,6 +138,18 @@ func NewAzureClient(subscriptionID, resourceGroupName, storageAccountName string azureClient.DeploymentsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) azureClient.DeploymentsClient.UserAgent += packerUserAgent + azureClient.DeploymentOperationsClient = resources.NewDeploymentOperationsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) + azureClient.DeploymentOperationsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) + azureClient.DeploymentOperationsClient.RequestInspector = withInspection(maxlen) + azureClient.DeploymentOperationsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) + azureClient.DeploymentOperationsClient.UserAgent += packerUserAgent + + azureClient.DisksClient = disk.NewDisksClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) + azureClient.DisksClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) + azureClient.DisksClient.RequestInspector = withInspection(maxlen) + azureClient.DisksClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) + azureClient.DisksClient.UserAgent += packerUserAgent + azureClient.GroupsClient = resources.NewGroupsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) azureClient.GroupsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) azureClient.GroupsClient.RequestInspector = withInspection(maxlen) diff --git a/builder/azure/arm/step_create_resource_group.go b/builder/azure/arm/step_create_resource_group.go index 079ad5279..745748669 100644 --- a/builder/azure/arm/step_create_resource_group.go +++ b/builder/azure/arm/step_create_resource_group.go @@ -14,6 +14,7 @@ type StepCreateResourceGroup struct { create func(resourceGroupName string, location string, tags *map[string]*string) error say func(message string) error func(e error) + exists func(resourceGroupName string) (bool, error) } func NewStepCreateResourceGroup(client *AzureClient, ui packer.Ui) *StepCreateResourceGroup { @@ -24,6 +25,7 @@ func NewStepCreateResourceGroup(client *AzureClient, ui packer.Ui) *StepCreateRe } step.create = step.createResourceGroup + step.exists = step.doesResourceGroupExist return step } @@ -39,6 +41,15 @@ func (s *StepCreateResourceGroup) createResourceGroup(resourceGroupName string, return err } +func (s *StepCreateResourceGroup) doesResourceGroupExist(resourceGroupName string) (bool, error) { + exists, err := s.client.GroupsClient.CheckExistence(resourceGroupName) + if err != nil { + s.say(s.client.LastError.Error()) + } + + return exists.Response.StatusCode != 404, err +} + func (s *StepCreateResourceGroup) Run(state multistep.StateBag) multistep.StepAction { s.say("Creating resource group ...") @@ -53,8 +64,19 @@ func (s *StepCreateResourceGroup) Run(state multistep.StateBag) multistep.StepAc s.say(fmt.Sprintf(" ->> %s : %s", k, *v)) } - err := s.create(resourceGroupName, location, tags) - if err == nil { + exists, err := s.exists(resourceGroupName) + if err != nil { + s.say(s.client.LastError.Error()) + } + state.Put(constants.ArmIsExistingResourceGroup, exists) + // If the resource group exists, we may not have permissions to update it so we don't. + if !exists { + err = s.create(resourceGroupName, location, tags) + if err == nil { + state.Put(constants.ArmIsResourceGroupCreated, true) + } + } else { + // Mark the resource group as created to deal with later checks state.Put(constants.ArmIsResourceGroupCreated, true) } @@ -68,17 +90,22 @@ func (s *StepCreateResourceGroup) Cleanup(state multistep.StateBag) { } ui := state.Get("ui").(packer.Ui) - ui.Say("\nCleanup requested, deleting resource group ...") + if state.Get(constants.ArmIsExistingResourceGroup).(bool) { + ui.Say("\nThe resource group was not created by Packer, not deleting ...") + return + } else { + ui.Say("\nCleanup requested, deleting resource group ...") - var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) - _, errChan := s.client.GroupsClient.Delete(resourceGroupName, nil) - err := <-errChan + var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) + _, errChan := s.client.GroupsClient.Delete(resourceGroupName, nil) + err := <-errChan - if err != nil { - ui.Error(fmt.Sprintf("Error deleting resource group. Please delete it manually.\n\n"+ - "Name: %s\n"+ - "Error: %s", resourceGroupName, err)) + if err != nil { + ui.Error(fmt.Sprintf("Error deleting resource group. Please delete it manually.\n\n"+ + "Name: %s\n"+ + "Error: %s", resourceGroupName, err)) + } + + ui.Say("Resource group has been deleted.") } - - ui.Say("Resource group has been deleted.") } diff --git a/builder/azure/arm/step_create_resource_group_test.go b/builder/azure/arm/step_create_resource_group_test.go index 4e6b7592f..47a4a01e6 100644 --- a/builder/azure/arm/step_create_resource_group_test.go +++ b/builder/azure/arm/step_create_resource_group_test.go @@ -13,6 +13,7 @@ func TestStepCreateResourceGroupShouldFailIfCreateFails(t *testing.T) { create: func(string, string, *map[string]*string) error { return fmt.Errorf("!! Unit Test FAIL !!") }, say: func(message string) {}, error: func(e error) {}, + exists: func(string) (bool, error) { return false, nil }, } stateBag := createTestStateBagStepCreateResourceGroup() @@ -32,6 +33,7 @@ func TestStepCreateResourceGroupShouldPassIfCreatePasses(t *testing.T) { create: func(string, string, *map[string]*string) error { return nil }, say: func(message string) {}, error: func(e error) {}, + exists: func(string) (bool, error) { return false, nil }, } stateBag := createTestStateBagStepCreateResourceGroup() @@ -58,8 +60,9 @@ func TestStepCreateResourceGroupShouldTakeStepArgumentsFromStateBag(t *testing.T actualTags = tags return nil }, - say: func(message string) {}, - error: func(e error) {}, + say: func(message string) {}, + error: func(e error) {}, + exists: func(string) (bool, error) { return false, nil }, } stateBag := createTestStateBagStepCreateResourceGroup() @@ -91,6 +94,44 @@ func TestStepCreateResourceGroupShouldTakeStepArgumentsFromStateBag(t *testing.T } } +func TestStepCreateResourceGroupMarkAsNonExistingIfDoesntExist(t *testing.T) { + var testSubject = &StepCreateResourceGroup{ + create: func(string, string, *map[string]*string) error { return fmt.Errorf("!! Unit Test FAIL !!") }, + say: func(message string) {}, + error: func(e error) {}, + exists: func(string) (bool, error) { return false, nil }, + } + + stateBag := createTestStateBagStepCreateResourceGroup() + + if _, ok := stateBag.GetOk(constants.ArmIsExistingResourceGroup); ok == true { + t.Fatalf("Expected 'constants.ArmIsExistingResourceGroup' not to be set, but it was") + } + testSubject.Run(stateBag) + if stateBag.Get(constants.ArmIsExistingResourceGroup).(bool) == true { + t.Fatalf("Expected 'constants.ArmIsExistingResourceGroup' to be set to false, but it wasn't.") + } +} + +func TestStepCreateResourceGroupMarkAsExistingIfExists(t *testing.T) { + var testSubject = &StepCreateResourceGroup{ + create: func(string, string, *map[string]*string) error { return fmt.Errorf("!! Unit Test FAIL !!") }, + say: func(message string) {}, + error: func(e error) {}, + exists: func(string) (bool, error) { return true, nil }, + } + + stateBag := createTestStateBagStepCreateResourceGroup() + + if _, ok := stateBag.GetOk(constants.ArmIsExistingResourceGroup); ok == true { + t.Fatalf("Expected 'constants.ArmIsExistingResourceGroup' not to be set, but it was") + } + testSubject.Run(stateBag) + if stateBag.Get(constants.ArmIsExistingResourceGroup).(bool) == false { + t.Fatalf("Expected 'constants.ArmIsExistingResourceGroup' to be set to true, but it wasn't.") + } +} + func createTestStateBagStepCreateResourceGroup() multistep.StateBag { stateBag := new(multistep.BasicStateBag) @@ -103,6 +144,5 @@ func createTestStateBagStepCreateResourceGroup() multistep.StateBag { } stateBag.Put(constants.ArmTags, &tags) - return stateBag } diff --git a/builder/azure/arm/step_delete_os_disk.go b/builder/azure/arm/step_delete_os_disk.go index 1dd28ec85..6c56ac96d 100644 --- a/builder/azure/arm/step_delete_os_disk.go +++ b/builder/azure/arm/step_delete_os_disk.go @@ -12,10 +12,11 @@ import ( ) type StepDeleteOSDisk struct { - client *AzureClient - delete func(string, string) error - say func(message string) - error func(e error) + client *AzureClient + delete func(string, string) error + deleteManaged func(string, string) error + say func(message string) + error func(e error) } func NewStepDeleteOSDisk(client *AzureClient, ui packer.Ui) *StepDeleteOSDisk { @@ -26,6 +27,7 @@ func NewStepDeleteOSDisk(client *AzureClient, ui packer.Ui) *StepDeleteOSDisk { } step.delete = step.deleteBlob + step.deleteManaged = step.deleteManagedDisk return step } @@ -39,19 +41,40 @@ func (s *StepDeleteOSDisk) deleteBlob(storageContainerName string, blobName stri return err } +func (s *StepDeleteOSDisk) deleteManagedDisk(resourceGroupName string, imageName string) error { + s.say("In the deleting part") + xs := strings.Split(imageName, "/") + diskName := xs[len(xs)-1] + _, errChan := s.client.DisksClient.Delete(resourceGroupName, diskName, nil) + err := <-errChan + return err +} + func (s *StepDeleteOSDisk) Run(state multistep.StateBag) multistep.StepAction { s.say("Deleting the temporary OS disk ...") var osDisk = state.Get(constants.ArmOSDiskVhd).(string) var isManagedDisk = state.Get(constants.ArmIsManagedImage).(bool) + var isExistingResourceGroup = state.Get(constants.ArmIsExistingResourceGroup).(bool) + var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) - if isManagedDisk { + if isManagedDisk && !isExistingResourceGroup { s.say(fmt.Sprintf(" -> OS Disk : skipping, managed disk was used...")) return multistep.ActionContinue } s.say(fmt.Sprintf(" -> OS Disk : '%s'", osDisk)) + var err error + if isManagedDisk { + err = s.deleteManaged(resourceGroupName, osDisk) + if err != nil { + s.say("Failed to delete the managed OS Disk!") + return multistep.ActionHalt + } + s.say("After deleting") + return multistep.ActionContinue + } u, err := url.Parse(osDisk) if err != nil { s.say("Failed to parse the OS Disk's VHD URI!") diff --git a/builder/azure/arm/step_delete_os_disk_test.go b/builder/azure/arm/step_delete_os_disk_test.go index 2724ee611..e19f0e0ba 100644 --- a/builder/azure/arm/step_delete_os_disk_test.go +++ b/builder/azure/arm/step_delete_os_disk_test.go @@ -10,9 +10,10 @@ import ( func TestStepDeleteOSDiskShouldFailIfGetFails(t *testing.T) { var testSubject = &StepDeleteOSDisk{ - delete: func(string, string) error { return fmt.Errorf("!! Unit Test FAIL !!") }, - say: func(message string) {}, - error: func(e error) {}, + delete: func(string, string) error { return fmt.Errorf("!! Unit Test FAIL !!") }, + deleteManaged: func(string, string) error { return nil }, + say: func(message string) {}, + error: func(e error) {}, } stateBag := DeleteTestStateBagStepDeleteOSDisk("http://storage.blob.core.windows.net/images/pkrvm_os.vhd") @@ -106,6 +107,8 @@ func DeleteTestStateBagStepDeleteOSDisk(osDiskVhd string) multistep.StateBag { stateBag := new(multistep.BasicStateBag) stateBag.Put(constants.ArmOSDiskVhd, osDiskVhd) stateBag.Put(constants.ArmIsManagedImage, false) + stateBag.Put(constants.ArmIsExistingResourceGroup, false) + stateBag.Put(constants.ArmResourceGroupName, "testgroup") return stateBag } diff --git a/builder/azure/arm/step_delete_resource_group.go b/builder/azure/arm/step_delete_resource_group.go index 2606b9fc8..28b7c4142 100644 --- a/builder/azure/arm/step_delete_resource_group.go +++ b/builder/azure/arm/step_delete_resource_group.go @@ -3,6 +3,7 @@ package arm import ( "fmt" + "github.com/Azure/go-autorest/autorest" "github.com/hashicorp/packer/builder/azure/common" "github.com/hashicorp/packer/builder/azure/common/constants" "github.com/hashicorp/packer/packer" @@ -11,7 +12,7 @@ import ( type StepDeleteResourceGroup struct { client *AzureClient - delete func(resourceGroupName string, cancelCh <-chan struct{}) error + delete func(state multistep.StateBag, resourceGroupName string, cancelCh <-chan struct{}) error say func(message string) error func(e error) } @@ -27,14 +28,69 @@ func NewStepDeleteResourceGroup(client *AzureClient, ui packer.Ui) *StepDeleteRe return step } -func (s *StepDeleteResourceGroup) deleteResourceGroup(resourceGroupName string, cancelCh <-chan struct{}) error { - _, errChan := s.client.GroupsClient.Delete(resourceGroupName, cancelCh) +func (s *StepDeleteResourceGroup) deleteResourceGroup(state multistep.StateBag, resourceGroupName string, cancelCh <-chan struct{}) error { + var err error + if state.Get(constants.ArmIsExistingResourceGroup).(bool) { + s.say("\nThe resource group was not created by Packer, only deleting individual resources ...") + var deploymentName = state.Get(constants.ArmDeploymentName).(string) + if deploymentName != "" { + maxResources := int32(50) + deploymentOperations, err := s.client.DeploymentOperationsClient.List(resourceGroupName, deploymentName, &maxResources) + if err != nil { + s.say(fmt.Sprintf("Error deleting resources. Please delete them manually.\n\n"+ + "Name: %s\n"+ + "Error: %s", resourceGroupName, err)) + s.error(err) + } + for _, deploymentOperation := range *deploymentOperations.Value { + // Sometimes an empty operation is added to the list by Azure + if deploymentOperation.Properties.TargetResource == nil { + continue + } + s.say(fmt.Sprintf(" -> %s : '%s'", + *deploymentOperation.Properties.TargetResource.ResourceType, + *deploymentOperation.Properties.TargetResource.ResourceName)) + var networkDeleteFunction func(string, string, <-chan struct{}) (<-chan autorest.Response, <-chan error) + switch *deploymentOperation.Properties.TargetResource.ResourceType { + case "Microsoft.Compute/virtualMachines": + _, errChan := s.client.VirtualMachinesClient.Delete(resourceGroupName, *deploymentOperation.Properties.TargetResource.ResourceName, nil) + err := <-errChan + if err != nil { + s.say(fmt.Sprintf("Error deleting resource. Please delete manually.\n\n"+ + "Name: %s\n"+ + "Error: %s", *deploymentOperation.Properties.TargetResource.ResourceName, err.Error())) + s.error(err) + } + case "Microsoft.Network/networkInterfaces": + networkDeleteFunction = s.client.InterfacesClient.Delete + case "Microsoft.Network/virtualNetworks": + networkDeleteFunction = s.client.VirtualNetworksClient.Delete + case "Microsoft.Network/publicIPAddresses": + networkDeleteFunction = s.client.PublicIPAddressesClient.Delete + } + if networkDeleteFunction != nil { + _, errChan := networkDeleteFunction(resourceGroupName, *deploymentOperation.Properties.TargetResource.ResourceName, nil) + err := <-errChan + if err != nil { + s.say(fmt.Sprintf("Error deleting resource. Please delete manually.\n\n"+ + "Name: %s\n"+ + "Error: %s", *deploymentOperation.Properties.TargetResource.ResourceName, err.Error())) + s.error(err) + } + } + } + } + return err + } else { + _, errChan := s.client.GroupsClient.Delete(resourceGroupName, cancelCh) + s.say(state.Get(constants.ArmIsExistingResourceGroup).(string)) + err = <-errChan - err := <-errChan - if err != nil { - s.say(s.client.LastError.Error()) + if err != nil { + s.say(s.client.LastError.Error()) + } + return err } - return err } func (s *StepDeleteResourceGroup) Run(state multistep.StateBag) multistep.StepAction { @@ -45,8 +101,7 @@ func (s *StepDeleteResourceGroup) Run(state multistep.StateBag) multistep.StepAc result := common.StartInterruptibleTask( func() bool { return common.IsStateCancelled(state) }, - func(cancelCh <-chan struct{}) error { return s.delete(resourceGroupName, cancelCh) }) - + func(cancelCh <-chan struct{}) error { return s.delete(state, resourceGroupName, cancelCh) }) stepAction := processInterruptibleResult(result, s.error, state) state.Put(constants.ArmIsResourceGroupCreated, false) diff --git a/builder/azure/arm/step_delete_resource_group_test.go b/builder/azure/arm/step_delete_resource_group_test.go index 25ab14e4d..c9d6e22da 100644 --- a/builder/azure/arm/step_delete_resource_group_test.go +++ b/builder/azure/arm/step_delete_resource_group_test.go @@ -10,7 +10,7 @@ import ( func TestStepDeleteResourceGroupShouldFailIfDeleteFails(t *testing.T) { var testSubject = &StepDeleteResourceGroup{ - delete: func(string, <-chan struct{}) error { return fmt.Errorf("!! Unit Test FAIL !!") }, + delete: func(multistep.StateBag, string, <-chan struct{}) error { return fmt.Errorf("!! Unit Test FAIL !!") }, say: func(message string) {}, error: func(e error) {}, } @@ -29,7 +29,7 @@ func TestStepDeleteResourceGroupShouldFailIfDeleteFails(t *testing.T) { func TestStepDeleteResourceGroupShouldPassIfDeletePasses(t *testing.T) { var testSubject = &StepDeleteResourceGroup{ - delete: func(string, <-chan struct{}) error { return nil }, + delete: func(multistep.StateBag, string, <-chan struct{}) error { return nil }, say: func(message string) {}, error: func(e error) {}, } @@ -48,7 +48,7 @@ func TestStepDeleteResourceGroupShouldPassIfDeletePasses(t *testing.T) { func TestStepDeleteResourceGroupShouldDeleteStateBagArmResourceGroupCreated(t *testing.T) { var testSubject = &StepDeleteResourceGroup{ - delete: func(resourceGroupName string, cancelCh <-chan struct{}) error { + delete: func(s multistep.StateBag, resourceGroupName string, cancelCh <-chan struct{}) error { return nil }, say: func(message string) {}, diff --git a/builder/azure/arm/step_deploy_template.go b/builder/azure/arm/step_deploy_template.go index 8d73402b7..53b639d51 100644 --- a/builder/azure/arm/step_deploy_template.go +++ b/builder/azure/arm/step_deploy_template.go @@ -2,7 +2,10 @@ package arm import ( "fmt" + "net/url" + "strings" + "github.com/Azure/go-autorest/autorest" "github.com/hashicorp/packer/builder/azure/common" "github.com/hashicorp/packer/builder/azure/common/constants" "github.com/hashicorp/packer/packer" @@ -10,12 +13,15 @@ import ( ) type StepDeployTemplate struct { - client *AzureClient - deploy func(resourceGroupName string, deploymentName string, cancelCh <-chan struct{}) error - say func(message string) - error func(e error) - config *Config - factory templateFactoryFunc + client *AzureClient + deploy func(resourceGroupName string, deploymentName string, cancelCh <-chan struct{}) error + delete func(resourceType string, resourceName string, resourceGroupName string) error + disk func(resourceGroupName string, computeName string) (string, string, error) + deleteDisk func(imageType string, imageName string, resourceGroupName string) error + say func(message string) + error func(e error) + config *Config + factory templateFactoryFunc } func NewStepDeployTemplate(client *AzureClient, ui packer.Ui, config *Config, factory templateFactoryFunc) *StepDeployTemplate { @@ -28,6 +34,9 @@ func NewStepDeployTemplate(client *AzureClient, ui packer.Ui, config *Config, fa } step.deploy = step.deployTemplate + step.delete = step.deleteOperationResource + step.disk = step.getImageDetails + step.deleteDisk = step.deleteImage return step } @@ -65,5 +74,131 @@ func (s *StepDeployTemplate) Run(state multistep.StateBag) multistep.StepAction return processInterruptibleResult(result, s.error, state) } -func (*StepDeployTemplate) Cleanup(multistep.StateBag) { +func (s *StepDeployTemplate) getImageDetails(resourceGroupName string, computeName string) (string, string, error) { + //We can't depend on constants.ArmOSDiskVhd being set + var imageName string + var imageType string + vm, err := s.client.VirtualMachinesClient.Get(resourceGroupName, computeName, "") + if err != nil { + return imageName, imageType, err + } else { + if vm.StorageProfile.OsDisk.Vhd != nil { + imageType = "image" + imageName = *vm.StorageProfile.OsDisk.Vhd.URI + } else { + imageType = "Microsoft.Compute/disks" + imageName = *vm.StorageProfile.OsDisk.ManagedDisk.ID + } + } + return imageType, imageName, nil +} + +func (s *StepDeployTemplate) deleteOperationResource(resourceType string, resourceName string, resourceGroupName string) error { + var networkDeleteFunction func(string, string, <-chan struct{}) (<-chan autorest.Response, <-chan error) + switch resourceType { + case "Microsoft.Compute/virtualMachines": + _, errChan := s.client.VirtualMachinesClient.Delete(resourceGroupName, + resourceName, nil) + err := <-errChan + if err != nil { + return err + + } + case "Microsoft.Network/networkInterfaces": + networkDeleteFunction = s.client.InterfacesClient.Delete + case "Microsoft.Network/virtualNetworks": + networkDeleteFunction = s.client.VirtualNetworksClient.Delete + case "Microsoft.Network/publicIPAddresses": + networkDeleteFunction = s.client.PublicIPAddressesClient.Delete + } + if networkDeleteFunction != nil { + _, errChan := networkDeleteFunction(resourceGroupName, resourceName, nil) + err := <-errChan + if err != nil { + return err + } + } + return nil +} + +func (s *StepDeployTemplate) deleteImage(imageType string, imageName string, resourceGroupName string) error { + // Managed disk + if imageType == "Microsoft.Compute/disks" { + xs := strings.Split(imageName, "/") + diskName := xs[len(xs)-1] + _, errChan := s.client.DisksClient.Delete(resourceGroupName, diskName, nil) + err := <-errChan + return err + } + // VHD image + u, err := url.Parse(imageName) + if err != nil { + return err + } + xs := strings.Split(u.Path, "/") + var storageAccountName = xs[1] + var blobName = strings.Join(xs[2:], "/") + + blob := s.client.BlobStorageClient.GetContainerReference(storageAccountName).GetBlobReference(blobName) + err = blob.Delete(nil) + return err + +} + +func (s *StepDeployTemplate) Cleanup(state multistep.StateBag) { + //Only clean up if this was an existing resource group and the resource group + //is marked as created + var existingResourceGroup = state.Get(constants.ArmIsExistingResourceGroup).(bool) + var resourceGroupCreated = state.Get(constants.ArmIsResourceGroupCreated).(bool) + if !existingResourceGroup || !resourceGroupCreated { + return + } + ui := state.Get("ui").(packer.Ui) + ui.Say("\nThe resource group was not created by Packer, deleting individual resources ...") + + var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) + var computeName = state.Get(constants.ArmComputeName).(string) + var deploymentName = state.Get(constants.ArmDeploymentName).(string) + imageType, imageName, err := s.disk(resourceGroupName, computeName) + if err != nil { + ui.Error("Could not retrieve OS Image details") + } + + ui.Say(" -> Deployment: " + deploymentName) + if deploymentName != "" { + maxResources := int32(50) + deploymentOperations, err := s.client.DeploymentOperationsClient.List(resourceGroupName, deploymentName, &maxResources) + if err != nil { + ui.Error(fmt.Sprintf("Error deleting resources. Please delete them manually.\n\n"+ + "Name: %s\n"+ + "Error: %s", resourceGroupName, err)) + } + for _, deploymentOperation := range *deploymentOperations.Value { + // Sometimes an empty operation is added to the list by Azure + if deploymentOperation.Properties.TargetResource == nil { + continue + } + ui.Say(fmt.Sprintf(" -> %s : '%s'", + *deploymentOperation.Properties.TargetResource.ResourceType, + *deploymentOperation.Properties.TargetResource.ResourceName)) + err = s.delete(*deploymentOperation.Properties.TargetResource.ResourceType, + *deploymentOperation.Properties.TargetResource.ResourceName, + resourceGroupName) + if err != nil { + ui.Error(fmt.Sprintf("Error deleting resource. Please delete manually.\n\n"+ + "Name: %s\n"+ + "Error: %s", *deploymentOperation.Properties.TargetResource.ResourceName, err)) + } + } + + // The disk is not defined as an operation in the template so has to be + // deleted separately + ui.Say(fmt.Sprintf(" -> %s : '%s'", imageType, imageName)) + err = s.deleteDisk(imageType, imageName, resourceGroupName) + if err != nil { + ui.Error(fmt.Sprintf("Error deleting resource. Please delete manually.\n\n"+ + "Name: %s\n"+ + "Error: %s", imageName, err)) + } + } } diff --git a/builder/azure/common/constants/stateBag.go b/builder/azure/common/constants/stateBag.go index a6eef5bd4..1df94a8f0 100644 --- a/builder/azure/common/constants/stateBag.go +++ b/builder/azure/common/constants/stateBag.go @@ -26,6 +26,7 @@ const ( ArmStorageAccountName string = "arm.StorageAccountName" ArmTags string = "arm.Tags" ArmVirtualMachineCaptureParameters string = "arm.VirtualMachineCaptureParameters" + ArmIsExistingResourceGroup string = "arm.IsExistingResourceGroup" ArmIsManagedImage string = "arm.IsManagedImage" ArmManagedImageResourceGroupName string = "arm.ManagedImageResourceGroupName" diff --git a/vendor/github.com/Azure/azure-sdk-for-go/arm/disk/client.go b/vendor/github.com/Azure/azure-sdk-for-go/arm/disk/client.go new file mode 100755 index 000000000..8bab7acc1 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/arm/disk/client.go @@ -0,0 +1,53 @@ +// Package disk implements the Azure ARM Disk service API version +// 2016-04-30-preview. +// +// The Disk Resource Provider Client. +package disk + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 +// Changes may cause incorrect behavior and will be lost if the code is +// regenerated. + +import ( + "github.com/Azure/go-autorest/autorest" +) + +const ( + // DefaultBaseURI is the default URI used for the service Disk + DefaultBaseURI = "https://management.azure.com" +) + +// ManagementClient is the base client for Disk. +type ManagementClient struct { + autorest.Client + BaseURI string + SubscriptionID string +} + +// New creates an instance of the ManagementClient client. +func New(subscriptionID string) ManagementClient { + return NewWithBaseURI(DefaultBaseURI, subscriptionID) +} + +// NewWithBaseURI creates an instance of the ManagementClient client. +func NewWithBaseURI(baseURI string, subscriptionID string) ManagementClient { + return ManagementClient{ + Client: autorest.NewClientWithUserAgent(UserAgent()), + BaseURI: baseURI, + SubscriptionID: subscriptionID, + } +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/arm/disk/disks.go b/vendor/github.com/Azure/azure-sdk-for-go/arm/disk/disks.go new file mode 100755 index 000000000..4f7fce74f --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/arm/disk/disks.go @@ -0,0 +1,728 @@ +package disk + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 +// Changes may cause incorrect behavior and will be lost if the code is +// regenerated. + +import ( + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" + "github.com/Azure/go-autorest/autorest/validation" + "net/http" +) + +// DisksClient is the the Disk Resource Provider Client. +type DisksClient struct { + ManagementClient +} + +// NewDisksClient creates an instance of the DisksClient client. +func NewDisksClient(subscriptionID string) DisksClient { + return NewDisksClientWithBaseURI(DefaultBaseURI, subscriptionID) +} + +// NewDisksClientWithBaseURI creates an instance of the DisksClient client. +func NewDisksClientWithBaseURI(baseURI string, subscriptionID string) DisksClient { + return DisksClient{NewWithBaseURI(baseURI, subscriptionID)} +} + +// CreateOrUpdate creates or updates a disk. This method may poll for +// completion. Polling can be canceled by passing the cancel channel argument. +// The channel will be used to cancel polling and any outstanding HTTP +// requests. +// +// resourceGroupName is the name of the resource group. diskName is the name of +// the disk within the given subscription and resource group. diskParameter is +// disk object supplied in the body of the Put disk operation. +func (client DisksClient) CreateOrUpdate(resourceGroupName string, diskName string, diskParameter Model, cancel <-chan struct{}) (<-chan Model, <-chan error) { + resultChan := make(chan Model, 1) + errChan := make(chan error, 1) + if err := validation.Validate([]validation.Validation{ + {TargetValue: diskParameter, + Constraints: []validation.Constraint{{Target: "diskParameter.Properties", Name: validation.Null, Rule: false, + Chain: []validation.Constraint{{Target: "diskParameter.Properties.CreationData", Name: validation.Null, Rule: true, + Chain: []validation.Constraint{{Target: "diskParameter.Properties.CreationData.ImageReference", Name: validation.Null, Rule: false, + Chain: []validation.Constraint{{Target: "diskParameter.Properties.CreationData.ImageReference.ID", Name: validation.Null, Rule: true, Chain: nil}}}, + }}, + {Target: "diskParameter.Properties.EncryptionSettings", Name: validation.Null, Rule: false, + Chain: []validation.Constraint{{Target: "diskParameter.Properties.EncryptionSettings.DiskEncryptionKey", Name: validation.Null, Rule: false, + Chain: []validation.Constraint{{Target: "diskParameter.Properties.EncryptionSettings.DiskEncryptionKey.SourceVault", Name: validation.Null, Rule: true, Chain: nil}, + {Target: "diskParameter.Properties.EncryptionSettings.DiskEncryptionKey.SecretURL", Name: validation.Null, Rule: true, Chain: nil}, + }}, + {Target: "diskParameter.Properties.EncryptionSettings.KeyEncryptionKey", Name: validation.Null, Rule: false, + Chain: []validation.Constraint{{Target: "diskParameter.Properties.EncryptionSettings.KeyEncryptionKey.SourceVault", Name: validation.Null, Rule: true, Chain: nil}, + {Target: "diskParameter.Properties.EncryptionSettings.KeyEncryptionKey.KeyURL", Name: validation.Null, Rule: true, Chain: nil}, + }}, + }}, + }}}}}); err != nil { + errChan <- validation.NewErrorWithValidationError(err, "disk.DisksClient", "CreateOrUpdate") + close(errChan) + close(resultChan) + return resultChan, errChan + } + + go func() { + var err error + var result Model + defer func() { + resultChan <- result + errChan <- err + close(resultChan) + close(errChan) + }() + req, err := client.CreateOrUpdatePreparer(resourceGroupName, diskName, diskParameter, cancel) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.DisksClient", "CreateOrUpdate", nil, "Failure preparing request") + return + } + + resp, err := client.CreateOrUpdateSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "disk.DisksClient", "CreateOrUpdate", resp, "Failure sending request") + return + } + + result, err = client.CreateOrUpdateResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.DisksClient", "CreateOrUpdate", resp, "Failure responding to request") + } + }() + return resultChan, errChan +} + +// CreateOrUpdatePreparer prepares the CreateOrUpdate request. +func (client DisksClient) CreateOrUpdatePreparer(resourceGroupName string, diskName string, diskParameter Model, cancel <-chan struct{}) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "diskName": autorest.Encode("path", diskName), + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2016-04-30-preview" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsJSON(), + autorest.AsPut(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/disks/{diskName}", pathParameters), + autorest.WithJSON(diskParameter), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{Cancel: cancel}) +} + +// CreateOrUpdateSender sends the CreateOrUpdate request. The method will close the +// http.Response Body if it receives an error. +func (client DisksClient) CreateOrUpdateSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, + req, + azure.DoPollForAsynchronous(client.PollingDelay)) +} + +// CreateOrUpdateResponder handles the response to the CreateOrUpdate request. The method always +// closes the http.Response Body. +func (client DisksClient) CreateOrUpdateResponder(resp *http.Response) (result Model, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// Delete deletes a disk. This method may poll for completion. Polling can be +// canceled by passing the cancel channel argument. The channel will be used to +// cancel polling and any outstanding HTTP requests. +// +// resourceGroupName is the name of the resource group. diskName is the name of +// the disk within the given subscription and resource group. +func (client DisksClient) Delete(resourceGroupName string, diskName string, cancel <-chan struct{}) (<-chan OperationStatusResponse, <-chan error) { + resultChan := make(chan OperationStatusResponse, 1) + errChan := make(chan error, 1) + go func() { + var err error + var result OperationStatusResponse + defer func() { + resultChan <- result + errChan <- err + close(resultChan) + close(errChan) + }() + req, err := client.DeletePreparer(resourceGroupName, diskName, cancel) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.DisksClient", "Delete", nil, "Failure preparing request") + return + } + + resp, err := client.DeleteSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "disk.DisksClient", "Delete", resp, "Failure sending request") + return + } + + result, err = client.DeleteResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.DisksClient", "Delete", resp, "Failure responding to request") + } + }() + return resultChan, errChan +} + +// DeletePreparer prepares the Delete request. +func (client DisksClient) DeletePreparer(resourceGroupName string, diskName string, cancel <-chan struct{}) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "diskName": autorest.Encode("path", diskName), + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2016-04-30-preview" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsDelete(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/disks/{diskName}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{Cancel: cancel}) +} + +// DeleteSender sends the Delete request. The method will close the +// http.Response Body if it receives an error. +func (client DisksClient) DeleteSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, + req, + azure.DoPollForAsynchronous(client.PollingDelay)) +} + +// DeleteResponder handles the response to the Delete request. The method always +// closes the http.Response Body. +func (client DisksClient) DeleteResponder(resp *http.Response) (result OperationStatusResponse, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted, http.StatusNoContent), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// Get gets information about a disk. +// +// resourceGroupName is the name of the resource group. diskName is the name of +// the disk within the given subscription and resource group. +func (client DisksClient) Get(resourceGroupName string, diskName string) (result Model, err error) { + req, err := client.GetPreparer(resourceGroupName, diskName) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.DisksClient", "Get", nil, "Failure preparing request") + return + } + + resp, err := client.GetSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "disk.DisksClient", "Get", resp, "Failure sending request") + return + } + + result, err = client.GetResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.DisksClient", "Get", resp, "Failure responding to request") + } + + return +} + +// GetPreparer prepares the Get request. +func (client DisksClient) GetPreparer(resourceGroupName string, diskName string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "diskName": autorest.Encode("path", diskName), + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2016-04-30-preview" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/disks/{diskName}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// GetSender sends the Get request. The method will close the +// http.Response Body if it receives an error. +func (client DisksClient) GetSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// GetResponder handles the response to the Get request. The method always +// closes the http.Response Body. +func (client DisksClient) GetResponder(resp *http.Response) (result Model, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// GrantAccess grants access to a disk. This method may poll for completion. +// Polling can be canceled by passing the cancel channel argument. The channel +// will be used to cancel polling and any outstanding HTTP requests. +// +// resourceGroupName is the name of the resource group. diskName is the name of +// the disk within the given subscription and resource group. grantAccessData +// is access data object supplied in the body of the get disk access operation. +func (client DisksClient) GrantAccess(resourceGroupName string, diskName string, grantAccessData GrantAccessData, cancel <-chan struct{}) (<-chan AccessURI, <-chan error) { + resultChan := make(chan AccessURI, 1) + errChan := make(chan error, 1) + if err := validation.Validate([]validation.Validation{ + {TargetValue: grantAccessData, + Constraints: []validation.Constraint{{Target: "grantAccessData.DurationInSeconds", Name: validation.Null, Rule: true, Chain: nil}}}}); err != nil { + errChan <- validation.NewErrorWithValidationError(err, "disk.DisksClient", "GrantAccess") + close(errChan) + close(resultChan) + return resultChan, errChan + } + + go func() { + var err error + var result AccessURI + defer func() { + resultChan <- result + errChan <- err + close(resultChan) + close(errChan) + }() + req, err := client.GrantAccessPreparer(resourceGroupName, diskName, grantAccessData, cancel) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.DisksClient", "GrantAccess", nil, "Failure preparing request") + return + } + + resp, err := client.GrantAccessSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "disk.DisksClient", "GrantAccess", resp, "Failure sending request") + return + } + + result, err = client.GrantAccessResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.DisksClient", "GrantAccess", resp, "Failure responding to request") + } + }() + return resultChan, errChan +} + +// GrantAccessPreparer prepares the GrantAccess request. +func (client DisksClient) GrantAccessPreparer(resourceGroupName string, diskName string, grantAccessData GrantAccessData, cancel <-chan struct{}) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "diskName": autorest.Encode("path", diskName), + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2016-04-30-preview" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsJSON(), + autorest.AsPost(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/disks/{diskName}/beginGetAccess", pathParameters), + autorest.WithJSON(grantAccessData), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{Cancel: cancel}) +} + +// GrantAccessSender sends the GrantAccess request. The method will close the +// http.Response Body if it receives an error. +func (client DisksClient) GrantAccessSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, + req, + azure.DoPollForAsynchronous(client.PollingDelay)) +} + +// GrantAccessResponder handles the response to the GrantAccess request. The method always +// closes the http.Response Body. +func (client DisksClient) GrantAccessResponder(resp *http.Response) (result AccessURI, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// List lists all the disks under a subscription. +func (client DisksClient) List() (result ListType, err error) { + req, err := client.ListPreparer() + if err != nil { + err = autorest.NewErrorWithError(err, "disk.DisksClient", "List", nil, "Failure preparing request") + return + } + + resp, err := client.ListSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "disk.DisksClient", "List", resp, "Failure sending request") + return + } + + result, err = client.ListResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.DisksClient", "List", resp, "Failure responding to request") + } + + return +} + +// ListPreparer prepares the List request. +func (client DisksClient) ListPreparer() (*http.Request, error) { + pathParameters := map[string]interface{}{ + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2016-04-30-preview" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers/Microsoft.Compute/disks", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// ListSender sends the List request. The method will close the +// http.Response Body if it receives an error. +func (client DisksClient) ListSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// ListResponder handles the response to the List request. The method always +// closes the http.Response Body. +func (client DisksClient) ListResponder(resp *http.Response) (result ListType, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// ListNextResults retrieves the next set of results, if any. +func (client DisksClient) ListNextResults(lastResults ListType) (result ListType, err error) { + req, err := lastResults.ListTypePreparer() + if err != nil { + return result, autorest.NewErrorWithError(err, "disk.DisksClient", "List", nil, "Failure preparing next results request") + } + if req == nil { + return + } + + resp, err := client.ListSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + return result, autorest.NewErrorWithError(err, "disk.DisksClient", "List", resp, "Failure sending next results request") + } + + result, err = client.ListResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.DisksClient", "List", resp, "Failure responding to next results request") + } + + return +} + +// ListByResourceGroup lists all the disks under a resource group. +// +// resourceGroupName is the name of the resource group. +func (client DisksClient) ListByResourceGroup(resourceGroupName string) (result ListType, err error) { + req, err := client.ListByResourceGroupPreparer(resourceGroupName) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.DisksClient", "ListByResourceGroup", nil, "Failure preparing request") + return + } + + resp, err := client.ListByResourceGroupSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "disk.DisksClient", "ListByResourceGroup", resp, "Failure sending request") + return + } + + result, err = client.ListByResourceGroupResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.DisksClient", "ListByResourceGroup", resp, "Failure responding to request") + } + + return +} + +// ListByResourceGroupPreparer prepares the ListByResourceGroup request. +func (client DisksClient) ListByResourceGroupPreparer(resourceGroupName string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2016-04-30-preview" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/disks", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// ListByResourceGroupSender sends the ListByResourceGroup request. The method will close the +// http.Response Body if it receives an error. +func (client DisksClient) ListByResourceGroupSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// ListByResourceGroupResponder handles the response to the ListByResourceGroup request. The method always +// closes the http.Response Body. +func (client DisksClient) ListByResourceGroupResponder(resp *http.Response) (result ListType, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// ListByResourceGroupNextResults retrieves the next set of results, if any. +func (client DisksClient) ListByResourceGroupNextResults(lastResults ListType) (result ListType, err error) { + req, err := lastResults.ListTypePreparer() + if err != nil { + return result, autorest.NewErrorWithError(err, "disk.DisksClient", "ListByResourceGroup", nil, "Failure preparing next results request") + } + if req == nil { + return + } + + resp, err := client.ListByResourceGroupSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + return result, autorest.NewErrorWithError(err, "disk.DisksClient", "ListByResourceGroup", resp, "Failure sending next results request") + } + + result, err = client.ListByResourceGroupResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.DisksClient", "ListByResourceGroup", resp, "Failure responding to next results request") + } + + return +} + +// RevokeAccess revokes access to a disk. This method may poll for completion. +// Polling can be canceled by passing the cancel channel argument. The channel +// will be used to cancel polling and any outstanding HTTP requests. +// +// resourceGroupName is the name of the resource group. diskName is the name of +// the disk within the given subscription and resource group. +func (client DisksClient) RevokeAccess(resourceGroupName string, diskName string, cancel <-chan struct{}) (<-chan OperationStatusResponse, <-chan error) { + resultChan := make(chan OperationStatusResponse, 1) + errChan := make(chan error, 1) + go func() { + var err error + var result OperationStatusResponse + defer func() { + resultChan <- result + errChan <- err + close(resultChan) + close(errChan) + }() + req, err := client.RevokeAccessPreparer(resourceGroupName, diskName, cancel) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.DisksClient", "RevokeAccess", nil, "Failure preparing request") + return + } + + resp, err := client.RevokeAccessSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "disk.DisksClient", "RevokeAccess", resp, "Failure sending request") + return + } + + result, err = client.RevokeAccessResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.DisksClient", "RevokeAccess", resp, "Failure responding to request") + } + }() + return resultChan, errChan +} + +// RevokeAccessPreparer prepares the RevokeAccess request. +func (client DisksClient) RevokeAccessPreparer(resourceGroupName string, diskName string, cancel <-chan struct{}) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "diskName": autorest.Encode("path", diskName), + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2016-04-30-preview" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsPost(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/disks/{diskName}/endGetAccess", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{Cancel: cancel}) +} + +// RevokeAccessSender sends the RevokeAccess request. The method will close the +// http.Response Body if it receives an error. +func (client DisksClient) RevokeAccessSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, + req, + azure.DoPollForAsynchronous(client.PollingDelay)) +} + +// RevokeAccessResponder handles the response to the RevokeAccess request. The method always +// closes the http.Response Body. +func (client DisksClient) RevokeAccessResponder(resp *http.Response) (result OperationStatusResponse, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// Update updates (patches) a disk. This method may poll for completion. +// Polling can be canceled by passing the cancel channel argument. The channel +// will be used to cancel polling and any outstanding HTTP requests. +// +// resourceGroupName is the name of the resource group. diskName is the name of +// the disk within the given subscription and resource group. diskParameter is +// disk object supplied in the body of the Patch disk operation. +func (client DisksClient) Update(resourceGroupName string, diskName string, diskParameter UpdateType, cancel <-chan struct{}) (<-chan Model, <-chan error) { + resultChan := make(chan Model, 1) + errChan := make(chan error, 1) + go func() { + var err error + var result Model + defer func() { + resultChan <- result + errChan <- err + close(resultChan) + close(errChan) + }() + req, err := client.UpdatePreparer(resourceGroupName, diskName, diskParameter, cancel) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.DisksClient", "Update", nil, "Failure preparing request") + return + } + + resp, err := client.UpdateSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "disk.DisksClient", "Update", resp, "Failure sending request") + return + } + + result, err = client.UpdateResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.DisksClient", "Update", resp, "Failure responding to request") + } + }() + return resultChan, errChan +} + +// UpdatePreparer prepares the Update request. +func (client DisksClient) UpdatePreparer(resourceGroupName string, diskName string, diskParameter UpdateType, cancel <-chan struct{}) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "diskName": autorest.Encode("path", diskName), + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2016-04-30-preview" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsJSON(), + autorest.AsPatch(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/disks/{diskName}", pathParameters), + autorest.WithJSON(diskParameter), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{Cancel: cancel}) +} + +// UpdateSender sends the Update request. The method will close the +// http.Response Body if it receives an error. +func (client DisksClient) UpdateSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, + req, + azure.DoPollForAsynchronous(client.PollingDelay)) +} + +// UpdateResponder handles the response to the Update request. The method always +// closes the http.Response Body. +func (client DisksClient) UpdateResponder(resp *http.Response) (result Model, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/arm/disk/models.go b/vendor/github.com/Azure/azure-sdk-for-go/arm/disk/models.go new file mode 100755 index 000000000..e8118696a --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/arm/disk/models.go @@ -0,0 +1,278 @@ +package disk + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 +// Changes may cause incorrect behavior and will be lost if the code is +// regenerated. + +import ( + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/date" + "github.com/Azure/go-autorest/autorest/to" + "net/http" +) + +// AccessLevel enumerates the values for access level. +type AccessLevel string + +const ( + // None specifies the none state for access level. + None AccessLevel = "None" + // Read specifies the read state for access level. + Read AccessLevel = "Read" +) + +// CreateOption enumerates the values for create option. +type CreateOption string + +const ( + // Attach specifies the attach state for create option. + Attach CreateOption = "Attach" + // Copy specifies the copy state for create option. + Copy CreateOption = "Copy" + // Empty specifies the empty state for create option. + Empty CreateOption = "Empty" + // FromImage specifies the from image state for create option. + FromImage CreateOption = "FromImage" + // Import specifies the import state for create option. + Import CreateOption = "Import" + // Restore specifies the restore state for create option. + Restore CreateOption = "Restore" +) + +// OperatingSystemTypes enumerates the values for operating system types. +type OperatingSystemTypes string + +const ( + // Linux specifies the linux state for operating system types. + Linux OperatingSystemTypes = "Linux" + // Windows specifies the windows state for operating system types. + Windows OperatingSystemTypes = "Windows" +) + +// StorageAccountTypes enumerates the values for storage account types. +type StorageAccountTypes string + +const ( + // PremiumLRS specifies the premium lrs state for storage account types. + PremiumLRS StorageAccountTypes = "Premium_LRS" + // StandardLRS specifies the standard lrs state for storage account types. + StandardLRS StorageAccountTypes = "Standard_LRS" +) + +// AccessURI is a disk access SAS uri. +type AccessURI struct { + autorest.Response `json:"-"` + *AccessURIOutput `json:"properties,omitempty"` +} + +// AccessURIOutput is azure properties, including output. +type AccessURIOutput struct { + *AccessURIRaw `json:"output,omitempty"` +} + +// AccessURIRaw is this object gets 'bubbled up' through flattening. +type AccessURIRaw struct { + AccessSAS *string `json:"accessSAS,omitempty"` +} + +// APIError is api error. +type APIError struct { + Details *[]APIErrorBase `json:"details,omitempty"` + Innererror *InnerError `json:"innererror,omitempty"` + Code *string `json:"code,omitempty"` + Target *string `json:"target,omitempty"` + Message *string `json:"message,omitempty"` +} + +// APIErrorBase is api error base. +type APIErrorBase struct { + Code *string `json:"code,omitempty"` + Target *string `json:"target,omitempty"` + Message *string `json:"message,omitempty"` +} + +// CreationData is data used when creating a disk. +type CreationData struct { + CreateOption CreateOption `json:"createOption,omitempty"` + StorageAccountID *string `json:"storageAccountId,omitempty"` + ImageReference *ImageDiskReference `json:"imageReference,omitempty"` + SourceURI *string `json:"sourceUri,omitempty"` + SourceResourceID *string `json:"sourceResourceId,omitempty"` +} + +// EncryptionSettings is encryption settings for disk or snapshot +type EncryptionSettings struct { + Enabled *bool `json:"enabled,omitempty"` + DiskEncryptionKey *KeyVaultAndSecretReference `json:"diskEncryptionKey,omitempty"` + KeyEncryptionKey *KeyVaultAndKeyReference `json:"keyEncryptionKey,omitempty"` +} + +// GrantAccessData is data used for requesting a SAS. +type GrantAccessData struct { + Access AccessLevel `json:"access,omitempty"` + DurationInSeconds *int32 `json:"durationInSeconds,omitempty"` +} + +// ImageDiskReference is the source image used for creating the disk. +type ImageDiskReference struct { + ID *string `json:"id,omitempty"` + Lun *int32 `json:"lun,omitempty"` +} + +// InnerError is inner error details. +type InnerError struct { + Exceptiontype *string `json:"exceptiontype,omitempty"` + Errordetail *string `json:"errordetail,omitempty"` +} + +// KeyVaultAndKeyReference is key Vault Key Url and vault id of KeK, KeK is +// optional and when provided is used to unwrap the encryptionKey +type KeyVaultAndKeyReference struct { + SourceVault *SourceVault `json:"sourceVault,omitempty"` + KeyURL *string `json:"keyUrl,omitempty"` +} + +// KeyVaultAndSecretReference is key Vault Secret Url and vault id of the +// encryption key +type KeyVaultAndSecretReference struct { + SourceVault *SourceVault `json:"sourceVault,omitempty"` + SecretURL *string `json:"secretUrl,omitempty"` +} + +// ListType is the List Disks operation response. +type ListType struct { + autorest.Response `json:"-"` + Value *[]Model `json:"value,omitempty"` + NextLink *string `json:"nextLink,omitempty"` +} + +// ListTypePreparer prepares a request to retrieve the next set of results. It returns +// nil if no more results exist. +func (client ListType) ListTypePreparer() (*http.Request, error) { + if client.NextLink == nil || len(to.String(client.NextLink)) <= 0 { + return nil, nil + } + return autorest.Prepare(&http.Request{}, + autorest.AsJSON(), + autorest.AsGet(), + autorest.WithBaseURL(to.String(client.NextLink))) +} + +// Model is disk resource. +type Model struct { + autorest.Response `json:"-"` + ID *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Type *string `json:"type,omitempty"` + Location *string `json:"location,omitempty"` + Tags *map[string]*string `json:"tags,omitempty"` + *Properties `json:"properties,omitempty"` +} + +// OperationStatusResponse is operation status response +type OperationStatusResponse struct { + autorest.Response `json:"-"` + Name *string `json:"name,omitempty"` + Status *string `json:"status,omitempty"` + StartTime *date.Time `json:"startTime,omitempty"` + EndTime *date.Time `json:"endTime,omitempty"` + Error *APIError `json:"error,omitempty"` +} + +// Properties is disk resource properties. +type Properties struct { + AccountType StorageAccountTypes `json:"accountType,omitempty"` + TimeCreated *date.Time `json:"timeCreated,omitempty"` + OsType OperatingSystemTypes `json:"osType,omitempty"` + CreationData *CreationData `json:"creationData,omitempty"` + DiskSizeGB *int32 `json:"diskSizeGB,omitempty"` + EncryptionSettings *EncryptionSettings `json:"encryptionSettings,omitempty"` + OwnerID *string `json:"ownerId,omitempty"` + ProvisioningState *string `json:"provisioningState,omitempty"` +} + +// Resource is the Resource model definition. +type Resource struct { + ID *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Type *string `json:"type,omitempty"` + Location *string `json:"location,omitempty"` + Tags *map[string]*string `json:"tags,omitempty"` +} + +// ResourceUpdate is the Resource model definition. +type ResourceUpdate struct { + Tags *map[string]*string `json:"tags,omitempty"` +} + +// Snapshot is snapshot resource. +type Snapshot struct { + autorest.Response `json:"-"` + ID *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Type *string `json:"type,omitempty"` + Location *string `json:"location,omitempty"` + Tags *map[string]*string `json:"tags,omitempty"` + *Properties `json:"properties,omitempty"` +} + +// SnapshotList is the List Snapshots operation response. +type SnapshotList struct { + autorest.Response `json:"-"` + Value *[]Snapshot `json:"value,omitempty"` + NextLink *string `json:"nextLink,omitempty"` +} + +// SnapshotListPreparer prepares a request to retrieve the next set of results. It returns +// nil if no more results exist. +func (client SnapshotList) SnapshotListPreparer() (*http.Request, error) { + if client.NextLink == nil || len(to.String(client.NextLink)) <= 0 { + return nil, nil + } + return autorest.Prepare(&http.Request{}, + autorest.AsJSON(), + autorest.AsGet(), + autorest.WithBaseURL(to.String(client.NextLink))) +} + +// SnapshotUpdate is snapshot update resource. +type SnapshotUpdate struct { + Tags *map[string]*string `json:"tags,omitempty"` + *UpdateProperties `json:"properties,omitempty"` +} + +// SourceVault is the vault id is an Azure Resource Manager Resoure id in the +// form +// /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.KeyVault/vaults/{vaultName} +type SourceVault struct { + ID *string `json:"id,omitempty"` +} + +// UpdateProperties is disk resource update properties. +type UpdateProperties struct { + AccountType StorageAccountTypes `json:"accountType,omitempty"` + OsType OperatingSystemTypes `json:"osType,omitempty"` + CreationData *CreationData `json:"creationData,omitempty"` + DiskSizeGB *int32 `json:"diskSizeGB,omitempty"` + EncryptionSettings *EncryptionSettings `json:"encryptionSettings,omitempty"` +} + +// UpdateType is disk update resource. +type UpdateType struct { + Tags *map[string]*string `json:"tags,omitempty"` + *UpdateProperties `json:"properties,omitempty"` +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/arm/disk/snapshots.go b/vendor/github.com/Azure/azure-sdk-for-go/arm/disk/snapshots.go new file mode 100755 index 000000000..f4e5579d0 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/arm/disk/snapshots.go @@ -0,0 +1,733 @@ +package disk + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 +// Changes may cause incorrect behavior and will be lost if the code is +// regenerated. + +import ( + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" + "github.com/Azure/go-autorest/autorest/validation" + "net/http" +) + +// SnapshotsClient is the the Disk Resource Provider Client. +type SnapshotsClient struct { + ManagementClient +} + +// NewSnapshotsClient creates an instance of the SnapshotsClient client. +func NewSnapshotsClient(subscriptionID string) SnapshotsClient { + return NewSnapshotsClientWithBaseURI(DefaultBaseURI, subscriptionID) +} + +// NewSnapshotsClientWithBaseURI creates an instance of the SnapshotsClient +// client. +func NewSnapshotsClientWithBaseURI(baseURI string, subscriptionID string) SnapshotsClient { + return SnapshotsClient{NewWithBaseURI(baseURI, subscriptionID)} +} + +// CreateOrUpdate creates or updates a snapshot. This method may poll for +// completion. Polling can be canceled by passing the cancel channel argument. +// The channel will be used to cancel polling and any outstanding HTTP +// requests. +// +// resourceGroupName is the name of the resource group. snapshotName is the +// name of the snapshot within the given subscription and resource group. +// snapshot is snapshot object supplied in the body of the Put disk operation. +func (client SnapshotsClient) CreateOrUpdate(resourceGroupName string, snapshotName string, snapshot Snapshot, cancel <-chan struct{}) (<-chan Snapshot, <-chan error) { + resultChan := make(chan Snapshot, 1) + errChan := make(chan error, 1) + if err := validation.Validate([]validation.Validation{ + {TargetValue: snapshot, + Constraints: []validation.Constraint{{Target: "snapshot.Properties", Name: validation.Null, Rule: false, + Chain: []validation.Constraint{{Target: "snapshot.Properties.CreationData", Name: validation.Null, Rule: true, + Chain: []validation.Constraint{{Target: "snapshot.Properties.CreationData.ImageReference", Name: validation.Null, Rule: false, + Chain: []validation.Constraint{{Target: "snapshot.Properties.CreationData.ImageReference.ID", Name: validation.Null, Rule: true, Chain: nil}}}, + }}, + {Target: "snapshot.Properties.EncryptionSettings", Name: validation.Null, Rule: false, + Chain: []validation.Constraint{{Target: "snapshot.Properties.EncryptionSettings.DiskEncryptionKey", Name: validation.Null, Rule: false, + Chain: []validation.Constraint{{Target: "snapshot.Properties.EncryptionSettings.DiskEncryptionKey.SourceVault", Name: validation.Null, Rule: true, Chain: nil}, + {Target: "snapshot.Properties.EncryptionSettings.DiskEncryptionKey.SecretURL", Name: validation.Null, Rule: true, Chain: nil}, + }}, + {Target: "snapshot.Properties.EncryptionSettings.KeyEncryptionKey", Name: validation.Null, Rule: false, + Chain: []validation.Constraint{{Target: "snapshot.Properties.EncryptionSettings.KeyEncryptionKey.SourceVault", Name: validation.Null, Rule: true, Chain: nil}, + {Target: "snapshot.Properties.EncryptionSettings.KeyEncryptionKey.KeyURL", Name: validation.Null, Rule: true, Chain: nil}, + }}, + }}, + }}}}}); err != nil { + errChan <- validation.NewErrorWithValidationError(err, "disk.SnapshotsClient", "CreateOrUpdate") + close(errChan) + close(resultChan) + return resultChan, errChan + } + + go func() { + var err error + var result Snapshot + defer func() { + resultChan <- result + errChan <- err + close(resultChan) + close(errChan) + }() + req, err := client.CreateOrUpdatePreparer(resourceGroupName, snapshotName, snapshot, cancel) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "CreateOrUpdate", nil, "Failure preparing request") + return + } + + resp, err := client.CreateOrUpdateSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "CreateOrUpdate", resp, "Failure sending request") + return + } + + result, err = client.CreateOrUpdateResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "CreateOrUpdate", resp, "Failure responding to request") + } + }() + return resultChan, errChan +} + +// CreateOrUpdatePreparer prepares the CreateOrUpdate request. +func (client SnapshotsClient) CreateOrUpdatePreparer(resourceGroupName string, snapshotName string, snapshot Snapshot, cancel <-chan struct{}) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "snapshotName": autorest.Encode("path", snapshotName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2016-04-30-preview" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsJSON(), + autorest.AsPut(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/snapshots/{snapshotName}", pathParameters), + autorest.WithJSON(snapshot), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{Cancel: cancel}) +} + +// CreateOrUpdateSender sends the CreateOrUpdate request. The method will close the +// http.Response Body if it receives an error. +func (client SnapshotsClient) CreateOrUpdateSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, + req, + azure.DoPollForAsynchronous(client.PollingDelay)) +} + +// CreateOrUpdateResponder handles the response to the CreateOrUpdate request. The method always +// closes the http.Response Body. +func (client SnapshotsClient) CreateOrUpdateResponder(resp *http.Response) (result Snapshot, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// Delete deletes a snapshot. This method may poll for completion. Polling can +// be canceled by passing the cancel channel argument. The channel will be used +// to cancel polling and any outstanding HTTP requests. +// +// resourceGroupName is the name of the resource group. snapshotName is the +// name of the snapshot within the given subscription and resource group. +func (client SnapshotsClient) Delete(resourceGroupName string, snapshotName string, cancel <-chan struct{}) (<-chan OperationStatusResponse, <-chan error) { + resultChan := make(chan OperationStatusResponse, 1) + errChan := make(chan error, 1) + go func() { + var err error + var result OperationStatusResponse + defer func() { + resultChan <- result + errChan <- err + close(resultChan) + close(errChan) + }() + req, err := client.DeletePreparer(resourceGroupName, snapshotName, cancel) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "Delete", nil, "Failure preparing request") + return + } + + resp, err := client.DeleteSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "Delete", resp, "Failure sending request") + return + } + + result, err = client.DeleteResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "Delete", resp, "Failure responding to request") + } + }() + return resultChan, errChan +} + +// DeletePreparer prepares the Delete request. +func (client SnapshotsClient) DeletePreparer(resourceGroupName string, snapshotName string, cancel <-chan struct{}) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "snapshotName": autorest.Encode("path", snapshotName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2016-04-30-preview" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsDelete(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/snapshots/{snapshotName}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{Cancel: cancel}) +} + +// DeleteSender sends the Delete request. The method will close the +// http.Response Body if it receives an error. +func (client SnapshotsClient) DeleteSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, + req, + azure.DoPollForAsynchronous(client.PollingDelay)) +} + +// DeleteResponder handles the response to the Delete request. The method always +// closes the http.Response Body. +func (client SnapshotsClient) DeleteResponder(resp *http.Response) (result OperationStatusResponse, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted, http.StatusNoContent), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// Get gets information about a snapshot. +// +// resourceGroupName is the name of the resource group. snapshotName is the +// name of the snapshot within the given subscription and resource group. +func (client SnapshotsClient) Get(resourceGroupName string, snapshotName string) (result Snapshot, err error) { + req, err := client.GetPreparer(resourceGroupName, snapshotName) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "Get", nil, "Failure preparing request") + return + } + + resp, err := client.GetSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "Get", resp, "Failure sending request") + return + } + + result, err = client.GetResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "Get", resp, "Failure responding to request") + } + + return +} + +// GetPreparer prepares the Get request. +func (client SnapshotsClient) GetPreparer(resourceGroupName string, snapshotName string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "snapshotName": autorest.Encode("path", snapshotName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2016-04-30-preview" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/snapshots/{snapshotName}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// GetSender sends the Get request. The method will close the +// http.Response Body if it receives an error. +func (client SnapshotsClient) GetSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// GetResponder handles the response to the Get request. The method always +// closes the http.Response Body. +func (client SnapshotsClient) GetResponder(resp *http.Response) (result Snapshot, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// GrantAccess grants access to a snapshot. This method may poll for +// completion. Polling can be canceled by passing the cancel channel argument. +// The channel will be used to cancel polling and any outstanding HTTP +// requests. +// +// resourceGroupName is the name of the resource group. snapshotName is the +// name of the snapshot within the given subscription and resource group. +// grantAccessData is access data object supplied in the body of the get +// snapshot access operation. +func (client SnapshotsClient) GrantAccess(resourceGroupName string, snapshotName string, grantAccessData GrantAccessData, cancel <-chan struct{}) (<-chan AccessURI, <-chan error) { + resultChan := make(chan AccessURI, 1) + errChan := make(chan error, 1) + if err := validation.Validate([]validation.Validation{ + {TargetValue: grantAccessData, + Constraints: []validation.Constraint{{Target: "grantAccessData.DurationInSeconds", Name: validation.Null, Rule: true, Chain: nil}}}}); err != nil { + errChan <- validation.NewErrorWithValidationError(err, "disk.SnapshotsClient", "GrantAccess") + close(errChan) + close(resultChan) + return resultChan, errChan + } + + go func() { + var err error + var result AccessURI + defer func() { + resultChan <- result + errChan <- err + close(resultChan) + close(errChan) + }() + req, err := client.GrantAccessPreparer(resourceGroupName, snapshotName, grantAccessData, cancel) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "GrantAccess", nil, "Failure preparing request") + return + } + + resp, err := client.GrantAccessSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "GrantAccess", resp, "Failure sending request") + return + } + + result, err = client.GrantAccessResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "GrantAccess", resp, "Failure responding to request") + } + }() + return resultChan, errChan +} + +// GrantAccessPreparer prepares the GrantAccess request. +func (client SnapshotsClient) GrantAccessPreparer(resourceGroupName string, snapshotName string, grantAccessData GrantAccessData, cancel <-chan struct{}) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "snapshotName": autorest.Encode("path", snapshotName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2016-04-30-preview" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsJSON(), + autorest.AsPost(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/snapshots/{snapshotName}/beginGetAccess", pathParameters), + autorest.WithJSON(grantAccessData), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{Cancel: cancel}) +} + +// GrantAccessSender sends the GrantAccess request. The method will close the +// http.Response Body if it receives an error. +func (client SnapshotsClient) GrantAccessSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, + req, + azure.DoPollForAsynchronous(client.PollingDelay)) +} + +// GrantAccessResponder handles the response to the GrantAccess request. The method always +// closes the http.Response Body. +func (client SnapshotsClient) GrantAccessResponder(resp *http.Response) (result AccessURI, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// List lists snapshots under a subscription. +func (client SnapshotsClient) List() (result SnapshotList, err error) { + req, err := client.ListPreparer() + if err != nil { + err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "List", nil, "Failure preparing request") + return + } + + resp, err := client.ListSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "List", resp, "Failure sending request") + return + } + + result, err = client.ListResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "List", resp, "Failure responding to request") + } + + return +} + +// ListPreparer prepares the List request. +func (client SnapshotsClient) ListPreparer() (*http.Request, error) { + pathParameters := map[string]interface{}{ + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2016-04-30-preview" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers/Microsoft.Compute/snapshots", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// ListSender sends the List request. The method will close the +// http.Response Body if it receives an error. +func (client SnapshotsClient) ListSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// ListResponder handles the response to the List request. The method always +// closes the http.Response Body. +func (client SnapshotsClient) ListResponder(resp *http.Response) (result SnapshotList, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// ListNextResults retrieves the next set of results, if any. +func (client SnapshotsClient) ListNextResults(lastResults SnapshotList) (result SnapshotList, err error) { + req, err := lastResults.SnapshotListPreparer() + if err != nil { + return result, autorest.NewErrorWithError(err, "disk.SnapshotsClient", "List", nil, "Failure preparing next results request") + } + if req == nil { + return + } + + resp, err := client.ListSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + return result, autorest.NewErrorWithError(err, "disk.SnapshotsClient", "List", resp, "Failure sending next results request") + } + + result, err = client.ListResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "List", resp, "Failure responding to next results request") + } + + return +} + +// ListByResourceGroup lists snapshots under a resource group. +// +// resourceGroupName is the name of the resource group. +func (client SnapshotsClient) ListByResourceGroup(resourceGroupName string) (result SnapshotList, err error) { + req, err := client.ListByResourceGroupPreparer(resourceGroupName) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "ListByResourceGroup", nil, "Failure preparing request") + return + } + + resp, err := client.ListByResourceGroupSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "ListByResourceGroup", resp, "Failure sending request") + return + } + + result, err = client.ListByResourceGroupResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "ListByResourceGroup", resp, "Failure responding to request") + } + + return +} + +// ListByResourceGroupPreparer prepares the ListByResourceGroup request. +func (client SnapshotsClient) ListByResourceGroupPreparer(resourceGroupName string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2016-04-30-preview" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/snapshots", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// ListByResourceGroupSender sends the ListByResourceGroup request. The method will close the +// http.Response Body if it receives an error. +func (client SnapshotsClient) ListByResourceGroupSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// ListByResourceGroupResponder handles the response to the ListByResourceGroup request. The method always +// closes the http.Response Body. +func (client SnapshotsClient) ListByResourceGroupResponder(resp *http.Response) (result SnapshotList, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// ListByResourceGroupNextResults retrieves the next set of results, if any. +func (client SnapshotsClient) ListByResourceGroupNextResults(lastResults SnapshotList) (result SnapshotList, err error) { + req, err := lastResults.SnapshotListPreparer() + if err != nil { + return result, autorest.NewErrorWithError(err, "disk.SnapshotsClient", "ListByResourceGroup", nil, "Failure preparing next results request") + } + if req == nil { + return + } + + resp, err := client.ListByResourceGroupSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + return result, autorest.NewErrorWithError(err, "disk.SnapshotsClient", "ListByResourceGroup", resp, "Failure sending next results request") + } + + result, err = client.ListByResourceGroupResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "ListByResourceGroup", resp, "Failure responding to next results request") + } + + return +} + +// RevokeAccess revokes access to a snapshot. This method may poll for +// completion. Polling can be canceled by passing the cancel channel argument. +// The channel will be used to cancel polling and any outstanding HTTP +// requests. +// +// resourceGroupName is the name of the resource group. snapshotName is the +// name of the snapshot within the given subscription and resource group. +func (client SnapshotsClient) RevokeAccess(resourceGroupName string, snapshotName string, cancel <-chan struct{}) (<-chan OperationStatusResponse, <-chan error) { + resultChan := make(chan OperationStatusResponse, 1) + errChan := make(chan error, 1) + go func() { + var err error + var result OperationStatusResponse + defer func() { + resultChan <- result + errChan <- err + close(resultChan) + close(errChan) + }() + req, err := client.RevokeAccessPreparer(resourceGroupName, snapshotName, cancel) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "RevokeAccess", nil, "Failure preparing request") + return + } + + resp, err := client.RevokeAccessSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "RevokeAccess", resp, "Failure sending request") + return + } + + result, err = client.RevokeAccessResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "RevokeAccess", resp, "Failure responding to request") + } + }() + return resultChan, errChan +} + +// RevokeAccessPreparer prepares the RevokeAccess request. +func (client SnapshotsClient) RevokeAccessPreparer(resourceGroupName string, snapshotName string, cancel <-chan struct{}) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "snapshotName": autorest.Encode("path", snapshotName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2016-04-30-preview" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsPost(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/snapshots/{snapshotName}/endGetAccess", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{Cancel: cancel}) +} + +// RevokeAccessSender sends the RevokeAccess request. The method will close the +// http.Response Body if it receives an error. +func (client SnapshotsClient) RevokeAccessSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, + req, + azure.DoPollForAsynchronous(client.PollingDelay)) +} + +// RevokeAccessResponder handles the response to the RevokeAccess request. The method always +// closes the http.Response Body. +func (client SnapshotsClient) RevokeAccessResponder(resp *http.Response) (result OperationStatusResponse, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// Update updates (patches) a snapshot. This method may poll for completion. +// Polling can be canceled by passing the cancel channel argument. The channel +// will be used to cancel polling and any outstanding HTTP requests. +// +// resourceGroupName is the name of the resource group. snapshotName is the +// name of the snapshot within the given subscription and resource group. +// snapshot is snapshot object supplied in the body of the Patch snapshot +// operation. +func (client SnapshotsClient) Update(resourceGroupName string, snapshotName string, snapshot SnapshotUpdate, cancel <-chan struct{}) (<-chan Snapshot, <-chan error) { + resultChan := make(chan Snapshot, 1) + errChan := make(chan error, 1) + go func() { + var err error + var result Snapshot + defer func() { + resultChan <- result + errChan <- err + close(resultChan) + close(errChan) + }() + req, err := client.UpdatePreparer(resourceGroupName, snapshotName, snapshot, cancel) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "Update", nil, "Failure preparing request") + return + } + + resp, err := client.UpdateSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "Update", resp, "Failure sending request") + return + } + + result, err = client.UpdateResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "Update", resp, "Failure responding to request") + } + }() + return resultChan, errChan +} + +// UpdatePreparer prepares the Update request. +func (client SnapshotsClient) UpdatePreparer(resourceGroupName string, snapshotName string, snapshot SnapshotUpdate, cancel <-chan struct{}) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "snapshotName": autorest.Encode("path", snapshotName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2016-04-30-preview" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsJSON(), + autorest.AsPatch(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/snapshots/{snapshotName}", pathParameters), + autorest.WithJSON(snapshot), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{Cancel: cancel}) +} + +// UpdateSender sends the Update request. The method will close the +// http.Response Body if it receives an error. +func (client SnapshotsClient) UpdateSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, + req, + azure.DoPollForAsynchronous(client.PollingDelay)) +} + +// UpdateResponder handles the response to the Update request. The method always +// closes the http.Response Body. +func (client SnapshotsClient) UpdateResponder(resp *http.Response) (result Snapshot, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/arm/disk/version.go b/vendor/github.com/Azure/azure-sdk-for-go/arm/disk/version.go new file mode 100755 index 000000000..a78b351a0 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/arm/disk/version.go @@ -0,0 +1,29 @@ +package disk + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 +// Changes may cause incorrect behavior and will be lost if the code is +// regenerated. + +// UserAgent returns the UserAgent string to use when sending http.Requests. +func UserAgent() string { + return "Azure-SDK-For-Go/v10.0.2-beta arm-disk/2016-04-30-preview" +} + +// Version returns the semantic version (see http://semver.org) of the client. +func Version() string { + return "v10.0.2-beta" +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 0a06ac4b1..503290446 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -17,6 +17,12 @@ "version": "v10.0.3-beta", "versionExact": "v10.0.3-beta" }, + { + "checksumSHA1": "Kr6/gY3LrNiOxMfDBCfBvBvenYw=", + "path": "github.com/Azure/azure-sdk-for-go/arm/disk", + "revision": "26132835cbefa2669a306b777f34b929b56aa0a2", + "revisionTime": "2017-05-18T20:34:07Z" + }, { "checksumSHA1": "HMu0WrEHVs+wqyjsTzXxRsEJimI=", "comment": "v3.1.0-beta", From 0377140c399fa081354d86ade59dc005723a650c Mon Sep 17 00:00:00 2001 From: Arjen Schwarz Date: Thu, 9 Nov 2017 22:07:43 +1100 Subject: [PATCH 0046/1216] Azure docs: Add documentation concerning build_resource_group_name Part of #5045 --- website/source/docs/builders/azure.html.md | 39 ++++++++++++---------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/website/source/docs/builders/azure.html.md b/website/source/docs/builders/azure.html.md index bcb2b5d77..8f9d23442 100644 --- a/website/source/docs/builders/azure.html.md +++ b/website/source/docs/builders/azure.html.md @@ -47,7 +47,7 @@ builder. - `location` (string) Azure datacenter in which your VM will build. CLI example `azure location list` - + #### VHD or Managed Image The Azure builder can create either a VHD, or a managed image. If you @@ -55,10 +55,10 @@ are creating a VHD, you **must** start with a VHD. Likewise, if you want to create a managed image you **must** start with a managed image. When creating a VHD the following two options are required. -- `capture_container_name` (string) Destination container name. Essentially the "directory" where your VHD will be +- `capture_container_name` (string) Destination container name. Essentially the "directory" where your VHD will be organized in Azure. The captured VHD's URL will be `https://.blob.core.windows.net/system/Microsoft.Compute/Images//.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.vhd`. - -- `capture_name_prefix` (string) VHD prefix. The final artifacts will be named `PREFIX-osDisk.UUID` and + +- `capture_name_prefix` (string) VHD prefix. The final artifacts will be named `PREFIX-osDisk.UUID` and `PREFIX-vmTemplate.UUID`. - `resource_group_name` (string) Resource group under which the final artifact will be stored. @@ -68,13 +68,13 @@ image. When creating a VHD the following two options are required. When creating a managed image the following two options are required. - `managed_image_name` (string) Specify the managed image name where the result of the Packer build will be saved. The - image name must not exist ahead of time, and will not be overwritten. If this value is set, the value - `managed_image_resource_group_name` must also be set. See [documentation](https://docs.microsoft.com/en-us/azure/storage/storage-managed-disks-overview#images) + image name must not exist ahead of time, and will not be overwritten. If this value is set, the value + `managed_image_resource_group_name` must also be set. See [documentation](https://docs.microsoft.com/en-us/azure/storage/storage-managed-disks-overview#images) to learn more about managed images. - -- `managed_image_resource_group_name` (string) Specify the managed image resource group name where the result of the Packer build will be - saved. The resource group must already exist. If this value is set, the value `managed_image_name` must also be - set. See [documentation](https://docs.microsoft.com/en-us/azure/storage/storage-managed-disks-overview#images) to + +- `managed_image_resource_group_name` (string) Specify the managed image resource group name where the result of the Packer build will be + saved. The resource group must already exist. If this value is set, the value `managed_image_name` must also be + set. See [documentation](https://docs.microsoft.com/en-us/azure/storage/storage-managed-disks-overview#images) to learn more about managed images. ### Optional: @@ -83,12 +83,14 @@ When creating a managed image the following two options are required. characters, and tag values cannot exceed 256 characters. Tags are applied to every resource deployed by a Packer build, i.e. Resource Group, VM, NIC, VNET, Public IP, KeyVault, etc. +- `build_resource_group_name` (string) - Specify an existing resource group to run the build in. Cannot be used together with `temp_resource_group_name` and requires less permissions due to not creating or destroying a resource group. + - `cloud_environment_name` (string) One of `Public`, `China`, `Germany`, or `USGovernment`. Defaults to `Public`. Long forms such as `USGovernmentCloud` and `AzureUSGovernmentCloud` are also supported. - + - `custom_data_file` (string) Specify a file containing custom data to inject into the cloud-init process. The contents - of the file are read, base64 encoded, and injected into the ARM template. The custom data will be passed to + of the file are read, base64 encoded, and injected into the ARM template. The custom data will be passed to cloud-init for processing at the time of provisioning. See [documentation](http://cloudinit.readthedocs.io/en/latest/topics/examples.html) to learn more about custom data, and how it can be used to influence the provisioning process. @@ -110,11 +112,11 @@ When creating a managed image the following two options are required. - `image_url` (string) Specify a custom VHD to use. If this value is set, do not set image\_publisher, image\_offer, image\_sku, or image\_version. - + - `managed_image_storage_account_type` (string) Specify the storage account type for a managed image. Valid values are Standard_LRS and Premium\_LRS. The default is Standard\_LRS. - + - `object_id` (string) Specify an OAuth Object ID to protect WinRM certificates created at runtime. This variable is required when creating images based on Windows; this variable is not used by non-Windows builds. See `Windows` @@ -128,12 +130,12 @@ When creating a managed image the following two options are required. `Linux` this configures an SSH authorized key. For `Windows` this configures a WinRM certificate. -- `temp_compute_name` (string) temporary name assigned to the VM. If this value is not set, a random value will be - assigned. Knowing the resource group and VM name allows one to execute commands to update the VM during a Packer +- `temp_compute_name` (string) temporary name assigned to the VM. If this value is not set, a random value will be + assigned. Knowing the resource group and VM name allows one to execute commands to update the VM during a Packer build, e.g. attach a resource disk to the VM. -- `temp_resource_group_name` (string) temporary name assigned to the resource group. If this value is not set, a random - value will be assigned. +- `temp_resource_group_name` (string) name assigned to the temporary resource group created during the build. If this value is not set, a random + value will be assigned. Cannot be used together with `build_resource_group_name`. - `tenant_id` (string) The account identifier with which your `client_id` and `subscription_id` are associated. If not specified, `tenant_id` will be looked up using `subscription_id`. @@ -270,6 +272,7 @@ The Azure builder attempts to pick default values that provide for a just works - The default user name is packer not root as in other builders. Most distros on Azure do not allow root to SSH to a VM hence the need for a non-root default user. Set the ssh\_username option to override the default value. - The default VM size is Standard\_A1. Set the vm\_size option to override the default value. - The default image version is latest. Set the image\_version option to override the default value. +- By default a temporary resource group will be created and destroyed as part of the build. If you do not have permissions to do so, use `build_resource_group_name` to specify an existing resource group to run the build in. ## Implementation From 0e706320ad3ba30cd6d04598bbfe948eb8395588 Mon Sep 17 00:00:00 2001 From: Arjen Schwarz Date: Thu, 9 Nov 2017 22:20:09 +1100 Subject: [PATCH 0047/1216] Issue #5045 - Add build_resource_group_name * Created a new parameter for using existing resource groups * Implemented logic to ensure temp_ and build_ can't both be used * Implemented logic to ensure they can only be used in correct context * Implemented tests for this logic * Updated where required to ensure the process works --- builder/azure/arm/builder.go | 11 ++- builder/azure/arm/config.go | 14 ++- .../azure/arm/step_create_resource_group.go | 39 +++++--- .../arm/step_create_resource_group_test.go | 93 ++++++++++++++++--- builder/azure/arm/step_delete_os_disk.go | 6 +- builder/azure/arm/step_delete_os_disk_test.go | 72 ++++++++++++++ builder/azure/common/constants/stateBag.go | 1 + 7 files changed, 204 insertions(+), 32 deletions(-) diff --git a/builder/azure/arm/builder.go b/builder/azure/arm/builder.go index e6993cfb5..3874cdb05 100644 --- a/builder/azure/arm/builder.go +++ b/builder/azure/arm/builder.go @@ -287,7 +287,16 @@ func (b *Builder) configureStateBag(stateBag multistep.StateBag) { stateBag.Put(constants.ArmLocation, b.config.Location) stateBag.Put(constants.ArmNicName, DefaultNicName) stateBag.Put(constants.ArmPublicIPAddressName, DefaultPublicIPAddressName) - stateBag.Put(constants.ArmResourceGroupName, b.config.tmpResourceGroupName) + if b.config.TempResourceGroupName != "" && b.config.BuildResourceGroupName != "" { + stateBag.Put(constants.ArmDoubleResourceGroupNameSet, true) + } + if b.config.tmpResourceGroupName != "" { + stateBag.Put(constants.ArmResourceGroupName, b.config.tmpResourceGroupName) + stateBag.Put(constants.ArmIsExistingResourceGroup, false) + } else { + stateBag.Put(constants.ArmResourceGroupName, b.config.BuildResourceGroupName) + stateBag.Put(constants.ArmIsExistingResourceGroup, true) + } stateBag.Put(constants.ArmStorageAccountName, b.config.StorageAccount) stateBag.Put(constants.ArmIsManagedImage, b.config.isManagedImage()) diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index 3fc4f13b9..fd6bd9edb 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -83,6 +83,7 @@ type Config struct { StorageAccount string `mapstructure:"storage_account"` TempComputeName string `mapstructure:"temp_compute_name"` TempResourceGroupName string `mapstructure:"temp_resource_group_name"` + BuildResourceGroupName string `mapstructure:"build_resource_group_name"` storageAccountBlobEndpoint string CloudEnvironmentName string `mapstructure:"cloud_environment_name"` cloudEnvironment *azure.Environment @@ -129,7 +130,13 @@ type keyVaultCertificate struct { } func (c *Config) toVMID() string { - return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/virtualMachines/%s", c.SubscriptionID, c.tmpResourceGroupName, c.tmpComputeName) + var resourceGroupName string + if c.tmpResourceGroupName != "" { + resourceGroupName = c.tmpResourceGroupName + } else { + resourceGroupName = c.BuildResourceGroupName + } + return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/virtualMachines/%s", c.SubscriptionID, resourceGroupName, c.tmpComputeName) } func (c *Config) isManagedImage() bool { @@ -328,9 +335,10 @@ func setRuntimeValues(c *Config) { c.tmpComputeName = c.TempComputeName } c.tmpDeploymentName = tempName.DeploymentName - if c.TempResourceGroupName == "" { + // Only set tmpResourceGroupName if no name has been specified + if c.TempResourceGroupName == "" && c.BuildResourceGroupName == "" { c.tmpResourceGroupName = tempName.ResourceGroupName - } else { + } else if c.TempResourceGroupName != "" && c.BuildResourceGroupName == "" { c.tmpResourceGroupName = c.TempResourceGroupName } c.tmpOSDiskName = tempName.OSDiskName diff --git a/builder/azure/arm/step_create_resource_group.go b/builder/azure/arm/step_create_resource_group.go index 745748669..934cb3225 100644 --- a/builder/azure/arm/step_create_resource_group.go +++ b/builder/azure/arm/step_create_resource_group.go @@ -1,6 +1,7 @@ package arm import ( + "errors" "fmt" "github.com/Azure/azure-sdk-for-go/arm/resources/resources" @@ -51,32 +52,48 @@ func (s *StepCreateResourceGroup) doesResourceGroupExist(resourceGroupName strin } func (s *StepCreateResourceGroup) Run(state multistep.StateBag) multistep.StepAction { - s.say("Creating resource group ...") + var doubleResource, ok = state.GetOk(constants.ArmDoubleResourceGroupNameSet) + if ok && doubleResource.(bool) { + err := errors.New("You have filled in both temp_resource_group_name and build_resource_group_name. Please choose one.") + return processStepResult(err, s.error, state) + } var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) var location = state.Get(constants.ArmLocation).(string) var tags = state.Get(constants.ArmTags).(*map[string]*string) - s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName)) - s.say(fmt.Sprintf(" -> Location : '%s'", location)) - s.say(fmt.Sprintf(" -> Tags :")) - for k, v := range *tags { - s.say(fmt.Sprintf(" ->> %s : %s", k, *v)) - } - exists, err := s.exists(resourceGroupName) if err != nil { - s.say(s.client.LastError.Error()) + return processStepResult(err, s.error, state) } - state.Put(constants.ArmIsExistingResourceGroup, exists) + configThinksExists := state.Get(constants.ArmIsExistingResourceGroup).(bool) + if exists != configThinksExists { + if configThinksExists { + err = errors.New("The resource group you want to use does not exist yet. Please use temp_resource_group_name to create a temporary resource group.") + } else { + err = errors.New("A resource group with that name already exists. Please use build_resource_group_name to use an existing resource group.") + } + return processStepResult(err, s.error, state) + } + // If the resource group exists, we may not have permissions to update it so we don't. if !exists { + s.say("Creating resource group ...") + + s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName)) + s.say(fmt.Sprintf(" -> Location : '%s'", location)) + s.say(fmt.Sprintf(" -> Tags :")) + for k, v := range *tags { + s.say(fmt.Sprintf(" ->> %s : %s", k, *v)) + } err = s.create(resourceGroupName, location, tags) if err == nil { state.Put(constants.ArmIsResourceGroupCreated, true) } } else { - // Mark the resource group as created to deal with later checks + s.say("Using existing resource group ...") + s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName)) + s.say(fmt.Sprintf(" -> Location : '%s'", location)) state.Put(constants.ArmIsResourceGroupCreated, true) } diff --git a/builder/azure/arm/step_create_resource_group_test.go b/builder/azure/arm/step_create_resource_group_test.go index 47a4a01e6..afd47cfef 100644 --- a/builder/azure/arm/step_create_resource_group_test.go +++ b/builder/azure/arm/step_create_resource_group_test.go @@ -1,6 +1,7 @@ package arm import ( + "errors" "fmt" "testing" @@ -8,6 +9,33 @@ import ( "github.com/mitchellh/multistep" ) +func TestStepCreateResourceGroupShouldFailIfBothGroupNames(t *testing.T) { + stateBag := new(multistep.BasicStateBag) + + stateBag.Put(constants.ArmDoubleResourceGroupNameSet, true) + + value := "Unit Test: Tags" + tags := map[string]*string{ + "tag01": &value, + } + + stateBag.Put(constants.ArmTags, &tags) + var testSubject = &StepCreateResourceGroup{ + create: func(string, string, *map[string]*string) error { return nil }, + say: func(message string) {}, + error: func(e error) {}, + exists: func(string) (bool, error) { return false, nil }, + } + var result = testSubject.Run(stateBag) + if result != multistep.ActionHalt { + t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result) + } + + if _, ok := stateBag.GetOk(constants.Error); ok == false { + t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error) + } +} + func TestStepCreateResourceGroupShouldFailIfCreateFails(t *testing.T) { var testSubject = &StepCreateResourceGroup{ create: func(string, string, *map[string]*string) error { return fmt.Errorf("!! Unit Test FAIL !!") }, @@ -28,6 +56,26 @@ func TestStepCreateResourceGroupShouldFailIfCreateFails(t *testing.T) { } } +func TestStepCreateResourceGroupShouldFailIfExistsFails(t *testing.T) { + var testSubject = &StepCreateResourceGroup{ + create: func(string, string, *map[string]*string) error { return nil }, + say: func(message string) {}, + error: func(e error) {}, + exists: func(string) (bool, error) { return false, errors.New("FAIL") }, + } + + stateBag := createTestStateBagStepCreateResourceGroup() + + var result = testSubject.Run(stateBag) + if result != multistep.ActionHalt { + t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result) + } + + if _, ok := stateBag.GetOk(constants.Error); ok == false { + t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error) + } +} + func TestStepCreateResourceGroupShouldPassIfCreatePasses(t *testing.T) { var testSubject = &StepCreateResourceGroup{ create: func(string, string, *map[string]*string) error { return nil }, @@ -94,7 +142,7 @@ func TestStepCreateResourceGroupShouldTakeStepArgumentsFromStateBag(t *testing.T } } -func TestStepCreateResourceGroupMarkAsNonExistingIfDoesntExist(t *testing.T) { +func TestStepCreateResourceGroupMarkShouldFailIfTryingExistingButDoesntExist(t *testing.T) { var testSubject = &StepCreateResourceGroup{ create: func(string, string, *map[string]*string) error { return fmt.Errorf("!! Unit Test FAIL !!") }, say: func(message string) {}, @@ -102,18 +150,19 @@ func TestStepCreateResourceGroupMarkAsNonExistingIfDoesntExist(t *testing.T) { exists: func(string) (bool, error) { return false, nil }, } - stateBag := createTestStateBagStepCreateResourceGroup() + stateBag := createTestExistingStateBagStepCreateResourceGroup() - if _, ok := stateBag.GetOk(constants.ArmIsExistingResourceGroup); ok == true { - t.Fatalf("Expected 'constants.ArmIsExistingResourceGroup' not to be set, but it was") + var result = testSubject.Run(stateBag) + if result != multistep.ActionHalt { + t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result) } - testSubject.Run(stateBag) - if stateBag.Get(constants.ArmIsExistingResourceGroup).(bool) == true { - t.Fatalf("Expected 'constants.ArmIsExistingResourceGroup' to be set to false, but it wasn't.") + + if _, ok := stateBag.GetOk(constants.Error); ok == false { + t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error) } } -func TestStepCreateResourceGroupMarkAsExistingIfExists(t *testing.T) { +func TestStepCreateResourceGroupMarkShouldFailIfTryingTempButExist(t *testing.T) { var testSubject = &StepCreateResourceGroup{ create: func(string, string, *map[string]*string) error { return fmt.Errorf("!! Unit Test FAIL !!") }, say: func(message string) {}, @@ -123,12 +172,13 @@ func TestStepCreateResourceGroupMarkAsExistingIfExists(t *testing.T) { stateBag := createTestStateBagStepCreateResourceGroup() - if _, ok := stateBag.GetOk(constants.ArmIsExistingResourceGroup); ok == true { - t.Fatalf("Expected 'constants.ArmIsExistingResourceGroup' not to be set, but it was") + var result = testSubject.Run(stateBag) + if result != multistep.ActionHalt { + t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result) } - testSubject.Run(stateBag) - if stateBag.Get(constants.ArmIsExistingResourceGroup).(bool) == false { - t.Fatalf("Expected 'constants.ArmIsExistingResourceGroup' to be set to true, but it wasn't.") + + if _, ok := stateBag.GetOk(constants.Error); ok == false { + t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error) } } @@ -137,6 +187,23 @@ func createTestStateBagStepCreateResourceGroup() multistep.StateBag { stateBag.Put(constants.ArmLocation, "Unit Test: Location") stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName") + stateBag.Put(constants.ArmIsExistingResourceGroup, false) + + value := "Unit Test: Tags" + tags := map[string]*string{ + "tag01": &value, + } + + stateBag.Put(constants.ArmTags, &tags) + return stateBag +} + +func createTestExistingStateBagStepCreateResourceGroup() multistep.StateBag { + stateBag := new(multistep.BasicStateBag) + + stateBag.Put(constants.ArmLocation, "Unit Test: Location") + stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName") + stateBag.Put(constants.ArmIsExistingResourceGroup, true) value := "Unit Test: Tags" tags := map[string]*string{ diff --git a/builder/azure/arm/step_delete_os_disk.go b/builder/azure/arm/step_delete_os_disk.go index 6c56ac96d..af5e2fa9b 100644 --- a/builder/azure/arm/step_delete_os_disk.go +++ b/builder/azure/arm/step_delete_os_disk.go @@ -42,7 +42,6 @@ func (s *StepDeleteOSDisk) deleteBlob(storageContainerName string, blobName stri } func (s *StepDeleteOSDisk) deleteManagedDisk(resourceGroupName string, imageName string) error { - s.say("In the deleting part") xs := strings.Split(imageName, "/") diskName := xs[len(xs)-1] _, errChan := s.client.DisksClient.Delete(resourceGroupName, diskName, nil) @@ -70,15 +69,14 @@ func (s *StepDeleteOSDisk) Run(state multistep.StateBag) multistep.StepAction { err = s.deleteManaged(resourceGroupName, osDisk) if err != nil { s.say("Failed to delete the managed OS Disk!") - return multistep.ActionHalt + return processStepResult(err, s.error, state) } - s.say("After deleting") return multistep.ActionContinue } u, err := url.Parse(osDisk) if err != nil { s.say("Failed to parse the OS Disk's VHD URI!") - return multistep.ActionHalt + return processStepResult(err, s.error, state) } xs := strings.Split(u.Path, "/") diff --git a/builder/azure/arm/step_delete_os_disk_test.go b/builder/azure/arm/step_delete_os_disk_test.go index e19f0e0ba..7fdd7468e 100644 --- a/builder/azure/arm/step_delete_os_disk_test.go +++ b/builder/azure/arm/step_delete_os_disk_test.go @@ -1,6 +1,7 @@ package arm import ( + "errors" "fmt" "testing" @@ -103,6 +104,77 @@ func TestStepDeleteOSDiskShouldHandleComplexStorageContainerNames(t *testing.T) } } +func TestStepDeleteOSDiskShouldPassIfManagedDiskInTempResourceGroup(t *testing.T) { + var testSubject = &StepDeleteOSDisk{ + delete: func(string, string) error { return nil }, + say: func(message string) {}, + error: func(e error) {}, + } + + stateBag := new(multistep.BasicStateBag) + stateBag.Put(constants.ArmOSDiskVhd, "subscriptions/123-456-789/resourceGroups/existingresourcegroup/providers/Microsoft.Compute/disks/osdisk") + stateBag.Put(constants.ArmIsManagedImage, true) + stateBag.Put(constants.ArmIsExistingResourceGroup, false) + stateBag.Put(constants.ArmResourceGroupName, "testgroup") + + var result = testSubject.Run(stateBag) + if result != multistep.ActionContinue { + t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) + } + + if _, ok := stateBag.GetOk(constants.Error); ok == true { + t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error) + } +} + +func TestStepDeleteOSDiskShouldFailIfManagedDiskInExistingResourceGroupFailsToDelete(t *testing.T) { + var testSubject = &StepDeleteOSDisk{ + delete: func(string, string) error { return nil }, + say: func(message string) {}, + error: func(e error) {}, + deleteManaged: func(string, string) error { return errors.New("UNIT TEST FAIL!") }, + } + + stateBag := new(multistep.BasicStateBag) + stateBag.Put(constants.ArmOSDiskVhd, "subscriptions/123-456-789/resourceGroups/existingresourcegroup/providers/Microsoft.Compute/disks/osdisk") + stateBag.Put(constants.ArmIsManagedImage, true) + stateBag.Put(constants.ArmIsExistingResourceGroup, true) + stateBag.Put(constants.ArmResourceGroupName, "testgroup") + + var result = testSubject.Run(stateBag) + if result != multistep.ActionHalt { + t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result) + } + + if _, ok := stateBag.GetOk(constants.Error); ok == false { + t.Fatalf("Expected the step to not stateBag['%s'], but it was.", constants.Error) + } +} + +func TestStepDeleteOSDiskShouldFailIfManagedDiskInExistingResourceGroupIsDeleted(t *testing.T) { + var testSubject = &StepDeleteOSDisk{ + delete: func(string, string) error { return nil }, + say: func(message string) {}, + error: func(e error) {}, + deleteManaged: func(string, string) error { return nil }, + } + + stateBag := new(multistep.BasicStateBag) + stateBag.Put(constants.ArmOSDiskVhd, "subscriptions/123-456-789/resourceGroups/existingresourcegroup/providers/Microsoft.Compute/disks/osdisk") + stateBag.Put(constants.ArmIsManagedImage, true) + stateBag.Put(constants.ArmIsExistingResourceGroup, true) + stateBag.Put(constants.ArmResourceGroupName, "testgroup") + + var result = testSubject.Run(stateBag) + if result != multistep.ActionContinue { + t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) + } + + if _, ok := stateBag.GetOk(constants.Error); ok == true { + t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error) + } +} + func DeleteTestStateBagStepDeleteOSDisk(osDiskVhd string) multistep.StateBag { stateBag := new(multistep.BasicStateBag) stateBag.Put(constants.ArmOSDiskVhd, osDiskVhd) diff --git a/builder/azure/common/constants/stateBag.go b/builder/azure/common/constants/stateBag.go index 1df94a8f0..6d5a93068 100644 --- a/builder/azure/common/constants/stateBag.go +++ b/builder/azure/common/constants/stateBag.go @@ -23,6 +23,7 @@ const ( ArmPublicIPAddressName string = "arm.PublicIPAddressName" ArmResourceGroupName string = "arm.ResourceGroupName" ArmIsResourceGroupCreated string = "arm.IsResourceGroupCreated" + ArmDoubleResourceGroupNameSet string = "arm.DoubleResourceGroupNameSet" ArmStorageAccountName string = "arm.StorageAccountName" ArmTags string = "arm.Tags" ArmVirtualMachineCaptureParameters string = "arm.VirtualMachineCaptureParameters" From f43f3155d4a0d11089977460def7e9f65d063879 Mon Sep 17 00:00:00 2001 From: Arjen Schwarz Date: Fri, 10 Nov 2017 11:04:31 +1100 Subject: [PATCH 0048/1216] Remove breaking debug statement --- builder/azure/arm/step_delete_resource_group.go | 1 - 1 file changed, 1 deletion(-) diff --git a/builder/azure/arm/step_delete_resource_group.go b/builder/azure/arm/step_delete_resource_group.go index 28b7c4142..1c299ad7c 100644 --- a/builder/azure/arm/step_delete_resource_group.go +++ b/builder/azure/arm/step_delete_resource_group.go @@ -83,7 +83,6 @@ func (s *StepDeleteResourceGroup) deleteResourceGroup(state multistep.StateBag, return err } else { _, errChan := s.client.GroupsClient.Delete(resourceGroupName, cancelCh) - s.say(state.Get(constants.ArmIsExistingResourceGroup).(string)) err = <-errChan if err != nil { From b754b715191920cc40e2a2e69e20b2f491cf903e Mon Sep 17 00:00:00 2001 From: bugbuilder Date: Fri, 10 Nov 2017 22:57:39 -0300 Subject: [PATCH 0049/1216] return vsphere artifact to can build template --- .../vsphere-template/step_mark_as_template.go | 13 +++++++------ post-processor/vsphere/post-processor.go | 2 ++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/post-processor/vsphere-template/step_mark_as_template.go b/post-processor/vsphere-template/step_mark_as_template.go index 8f95ba4d6..2ffe46956 100644 --- a/post-processor/vsphere-template/step_mark_as_template.go +++ b/post-processor/vsphere-template/step_mark_as_template.go @@ -27,6 +27,7 @@ func NewStepMarkAsTemplate(artifact packer.Artifact) *stepMarkAsTemplate { if artifact.BuilderId() == vsphere.BuilderId { id := strings.Split(artifact.Id(), "::") remoteFolder = id[1] + vmname = id[2] } return &stepMarkAsTemplate{ @@ -50,12 +51,6 @@ func (s *stepMarkAsTemplate) Run(state multistep.StateBag) multistep.StepAction return multistep.ActionHalt } - if err := unregisterPreviousVM(cli, folder, s.VMName); err != nil { - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - dsPath, err := datastorePath(vm) if err != nil { state.Put("error", err) @@ -76,6 +71,12 @@ func (s *stepMarkAsTemplate) Run(state multistep.StateBag) multistep.StepAction return multistep.ActionHalt } + if err := unregisterPreviousVM(cli, folder, s.VMName); err != nil { + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + task, err := folder.RegisterVM(context.Background(), dsPath.String(), s.VMName, true, nil, host) if err != nil { state.Put("error", err) diff --git a/post-processor/vsphere/post-processor.go b/post-processor/vsphere/post-processor.go index c028cd087..b9702fe82 100644 --- a/post-processor/vsphere/post-processor.go +++ b/post-processor/vsphere/post-processor.go @@ -140,6 +140,8 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac ui.Message(p.filterLog(out.String())) + artifact = NewArtifact(p.config.Datastore, p.config.VMFolder, p.config.VMName, artifact.Files()) + return artifact, false, nil } From f0299ba713bd62a1517397f496815ddd8fdfa8b3 Mon Sep 17 00:00:00 2001 From: Daniel Hess Date: Sun, 12 Nov 2017 00:45:22 -0800 Subject: [PATCH 0050/1216] Adding GCE container optimized os image project --- builder/googlecompute/driver_gce.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/googlecompute/driver_gce.go b/builder/googlecompute/driver_gce.go index 69af625e9..44dcb85c6 100644 --- a/builder/googlecompute/driver_gce.go +++ b/builder/googlecompute/driver_gce.go @@ -170,7 +170,7 @@ func (d *driverGCE) DeleteDisk(zone, name string) (<-chan error, error) { } func (d *driverGCE) GetImage(name string, fromFamily bool) (*Image, error) { - projects := []string{d.projectId, "centos-cloud", "coreos-cloud", "debian-cloud", "google-containers", "opensuse-cloud", "rhel-cloud", "suse-cloud", "ubuntu-os-cloud", "windows-cloud", "gce-nvme"} + projects := []string{d.projectId, "centos-cloud", "coreos-cloud", "cos-cloud", "debian-cloud", "google-containers", "opensuse-cloud", "rhel-cloud", "suse-cloud", "ubuntu-os-cloud", "windows-cloud", "gce-nvme"} var errs error for _, project := range projects { image, err := d.GetImageFromProject(project, name, fromFamily) From ec6d6098de256f1db5b7db6977d1f350e49cc3fb Mon Sep 17 00:00:00 2001 From: Rickard von Essen Date: Sun, 12 Nov 2017 10:48:04 +0100 Subject: [PATCH 0051/1216] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dfdab519f..3787a54aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ * core: Rewrite vagrantfile code to make cross-platform development easier. [5539] * builder/triton: Update triton-go sdk. [GH-5531] * builder/google: Add clean_image_name template engine. [GH-5463] +* builder/google: Allow Selecting Container Optimized Images. [GH-5576] ### BUG FIXES: From 853b04420c83fed3daa1de54404c94a80a63c3f6 Mon Sep 17 00:00:00 2001 From: Pavel Boldin Date: Mon, 6 Nov 2017 00:32:39 +0100 Subject: [PATCH 0052/1216] iso_config: allow for subdirs in hash sum files Allow hash sum files and ISOs to be in different directories as Ubuntu does. Signed-off-by: Pavel Boldin --- common/iso_config.go | 18 +++++++++++--- common/iso_config_test.go | 51 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/common/iso_config.go b/common/iso_config.go index 1ddb78d17..5216dc458 100644 --- a/common/iso_config.go +++ b/common/iso_config.go @@ -140,9 +140,20 @@ func (c *ISOConfig) parseCheckSumFile(rd *bufio.Reader) error { if err != nil { return err } + + checksumurl, err := url.Parse(c.ISOChecksumURL) + if err != nil { + return err + } + + relpath, err := filepath.Rel(filepath.Dir(checksumurl.Path), u.Path) + if err != nil { + return err + } + filename := filepath.Base(u.Path) - errNotFound := fmt.Errorf("No checksum for %q found at: %s", filename, c.ISOChecksumURL) + errNotFound := fmt.Errorf("No checksum for %q or %q found at: %s", filename, relpath, c.ISOChecksumURL) for { line, err := rd.ReadString('\n') if err != nil && line == "" { @@ -154,7 +165,8 @@ func (c *ISOConfig) parseCheckSumFile(rd *bufio.Reader) error { } if strings.ToLower(parts[0]) == c.ISOChecksumType { // BSD-style checksum - if parts[1] == fmt.Sprintf("(%s)", filename) { + if parts[1] == fmt.Sprintf("(%s)", filename) || parts[1] == fmt.Sprintf("(%s)", relpath) || + parts[1] == fmt.Sprintf("(./%s)", relpath) { c.ISOChecksum = parts[3] return nil } @@ -164,7 +176,7 @@ func (c *ISOConfig) parseCheckSumFile(rd *bufio.Reader) error { // Binary mode parts[1] = parts[1][1:] } - if parts[1] == filename { + if parts[1] == filename || parts[1] == relpath || parts[1] == "./"+relpath { c.ISOChecksum = parts[0] return nil } diff --git a/common/iso_config_test.go b/common/iso_config_test.go index 7a5a0b026..f31c687b2 100644 --- a/common/iso_config_test.go +++ b/common/iso_config_test.go @@ -24,11 +24,21 @@ MD5 (other.iso) = bAr MD5 (the-OS.iso) = baZ ` +var cs_bsd_style_subdir = ` +MD5 (other.iso) = bAr +MD5 (./subdir/the-OS.iso) = baZ +` + var cs_gnu_style = ` bAr0 *the-OS.iso baZ0 other.iso ` +var cs_gnu_style_subdir = ` +bAr0 *./subdir/the-OS.iso +baZ0 other.iso +` + var cs_bsd_style_no_newline = ` MD5 (other.iso) = bAr MD5 (the-OS.iso) = baZ` @@ -134,6 +144,27 @@ func TestISOConfigPrepare_ISOChecksumURL(t *testing.T) { t.Fatalf("should've found \"baz\" got: %s", i.ISOChecksum) } + // Test good - ISOChecksumURL BSD style with relative path + i = testISOConfig() + i.ISOChecksum = "" + + cs_dir, _ := ioutil.TempDir("", "packer-testdir-") + cs_file, _ = ioutil.TempFile(cs_dir, "packer-test-") + ioutil.WriteFile(cs_file.Name(), []byte(cs_bsd_style_subdir), 0666) + i.ISOChecksumURL = fmt.Sprintf("%s%s", filePrefix, cs_file.Name()) + i.RawSingleISOUrl = fmt.Sprintf("%s%s", cs_dir, "/subdir/the-OS.iso") + warns, err = i.Prepare(nil) + if len(warns) > 0 { + t.Fatalf("bad: %#v", warns) + } + if err != nil { + t.Fatalf("should not have error: %s", err) + } + + if i.ISOChecksum != "baz" { + t.Fatalf("should've found \"baz\" got: %s", i.ISOChecksum) + } + // Test good - ISOChecksumURL GNU style no newline i = testISOConfig() i.ISOChecksum = "" @@ -171,6 +202,26 @@ func TestISOConfigPrepare_ISOChecksumURL(t *testing.T) { if i.ISOChecksum != "bar0" { t.Fatalf("should've found \"bar0\" got: %s", i.ISOChecksum) } + + // Test good - ISOChecksumURL GNU style with relative path + i = testISOConfig() + i.ISOChecksum = "" + + cs_file, _ = ioutil.TempFile(cs_dir, "packer-test-") + ioutil.WriteFile(cs_file.Name(), []byte(cs_gnu_style_subdir), 0666) + i.ISOChecksumURL = fmt.Sprintf("%s%s", filePrefix, cs_file.Name()) + i.RawSingleISOUrl = fmt.Sprintf("%s%s", cs_dir, "/subdir/the-OS.iso") + warns, err = i.Prepare(nil) + if len(warns) > 0 { + t.Fatalf("bad: %#v", warns) + } + if err != nil { + t.Fatalf("should not have error: %s", err) + } + + if i.ISOChecksum != "bar0" { + t.Fatalf("should've found \"bar0\" got: %s", i.ISOChecksum) + } } func TestISOConfigPrepare_ISOChecksumType(t *testing.T) { From 487ceac7845c53caa72e7403a2e5fec759f68e7c Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Mon, 13 Nov 2017 11:45:31 -0800 Subject: [PATCH 0053/1216] fix Vet error. --- post-processor/vsphere-template/step_mark_as_template.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/post-processor/vsphere-template/step_mark_as_template.go b/post-processor/vsphere-template/step_mark_as_template.go index 7dc9211fa..dffc871ca 100644 --- a/post-processor/vsphere-template/step_mark_as_template.go +++ b/post-processor/vsphere-template/step_mark_as_template.go @@ -100,7 +100,10 @@ func datastorePath(vm *object.VirtualMachine) (*object.DatastorePath, error) { datastore := re.FindStringSubmatch(disk)[1] vmxPath := path.Join("/", path.Dir(strings.Split(disk, " ")[1]), vm.Name()+".vmx") - return &object.DatastorePath{datastore, vmxPath}, nil + return &object.DatastorePath{ + Datastore: datastore, + Path: vmxPath, + }, nil } // We will use the virtual machine created by vmware-iso builder From e45a006d619eeb99e3997be2117077b0c82f2ae8 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 10 Nov 2017 16:30:08 -0800 Subject: [PATCH 0054/1216] clearly state that url is wrong at validation stage of build --- common/config.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/common/config.go b/common/config.go index cc7eced6a..02b214902 100644 --- a/common/config.go +++ b/common/config.go @@ -131,5 +131,12 @@ func DownloadableURL(original string) (string, error) { return "", fmt.Errorf("Unsupported URL scheme: %s", url.Scheme) } + // if cleaned filepath does not exist, error out now. + if url.Scheme == "file" { + if _, err := os.Stat(url.Path); err != nil { + return "", fmt.Errorf("The filepath doesn't exist either as a "+ + "relative or an absolute path: %s", err) + } + } return url.String(), nil } From 3a9dfb5b1897bc3fd795ca1e837bd7dd419221eb Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 10 Nov 2017 16:45:06 -0800 Subject: [PATCH 0055/1216] better --- common/config.go | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/common/config.go b/common/config.go index 02b214902..572f2afa4 100644 --- a/common/config.go +++ b/common/config.go @@ -96,6 +96,8 @@ func DownloadableURL(original string) (string, error) { } url.Path = filepath.Clean(url.Path) + } else { + return "", err } if runtime.GOOS == "windows" { @@ -109,14 +111,6 @@ func DownloadableURL(original string) (string, error) { // Make sure it is lowercased url.Scheme = strings.ToLower(url.Scheme) - // This is to work around issue #5927. This can safely be removed once - // we distribute with a version of Go that fixes that bug. - // - // See: https://code.google.com/p/go/issues/detail?id=5927 - if url.Path != "" && url.Path[0] != '/' { - url.Path = "/" + url.Path - } - // Verify that the scheme is something we support in our common downloader. supported := []string{"file", "http", "https"} found := false @@ -131,12 +125,5 @@ func DownloadableURL(original string) (string, error) { return "", fmt.Errorf("Unsupported URL scheme: %s", url.Scheme) } - // if cleaned filepath does not exist, error out now. - if url.Scheme == "file" { - if _, err := os.Stat(url.Path); err != nil { - return "", fmt.Errorf("The filepath doesn't exist either as a "+ - "relative or an absolute path: %s", err) - } - } return url.String(), nil } From 0efcb1bba21ec8fe09a081f72076e33d186c5da0 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Mon, 13 Nov 2017 10:53:17 -0800 Subject: [PATCH 0056/1216] dont error in the downloadableURL function; save validation for preflight steps --- common/config.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/common/config.go b/common/config.go index 572f2afa4..490553388 100644 --- a/common/config.go +++ b/common/config.go @@ -96,8 +96,6 @@ func DownloadableURL(original string) (string, error) { } url.Path = filepath.Clean(url.Path) - } else { - return "", err } if runtime.GOOS == "windows" { From 0d18de29428094689e3909ce21fd62b4e51ec06d Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Mon, 13 Nov 2017 11:44:59 -0800 Subject: [PATCH 0057/1216] do validation in vmx config stage --- builder/virtualbox/ovf/config.go | 8 ++++++++ builder/virtualbox/ovf/config_test.go | 6 +++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/builder/virtualbox/ovf/config.go b/builder/virtualbox/ovf/config.go index 77b7ffa0e..ff8080dd7 100644 --- a/builder/virtualbox/ovf/config.go +++ b/builder/virtualbox/ovf/config.go @@ -2,6 +2,7 @@ package ovf import ( "fmt" + "os" "strings" vboxcommon "github.com/hashicorp/packer/builder/virtualbox/common" @@ -101,6 +102,13 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { if err != nil { errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is invalid: %s", err)) } + // file must exist now. + if (len(c.SourcePath) > 7) && (c.SourcePath[:7] == "file://") { + if _, err := os.Stat(c.SourcePath[7:]); err != nil { + errs = packer.MultiErrorAppend(errs, + fmt.Errorf("source file needs to exist at time of config validation: %s", err)) + } + } } validMode := false diff --git a/builder/virtualbox/ovf/config_test.go b/builder/virtualbox/ovf/config_test.go index 980c4d4ef..bdab9b763 100644 --- a/builder/virtualbox/ovf/config_test.go +++ b/builder/virtualbox/ovf/config_test.go @@ -65,14 +65,14 @@ func TestNewConfig_sourcePath(t *testing.T) { t.Fatalf("should error with empty `source_path`") } - // Okay, because it gets caught during download + // Want this to fail on validation c = testConfig(t) c["source_path"] = "/i/dont/exist" _, warns, err = NewConfig(c) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } - if err != nil { + if err == nil { t.Fatalf("bad: %s", err) } @@ -84,7 +84,7 @@ func TestNewConfig_sourcePath(t *testing.T) { t.Fatalf("bad: %#v", warns) } if err == nil { - t.Fatal("should error") + t.Fatal("Nonexistent source file should be caught in validation") } // Good From 764be03876808d20f4b45a3bcf6c9063160b2d02 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Mon, 13 Nov 2017 12:42:20 -0800 Subject: [PATCH 0058/1216] didn't mean for this error message to get changed --- builder/virtualbox/ovf/config_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/virtualbox/ovf/config_test.go b/builder/virtualbox/ovf/config_test.go index bdab9b763..0724968d3 100644 --- a/builder/virtualbox/ovf/config_test.go +++ b/builder/virtualbox/ovf/config_test.go @@ -84,7 +84,7 @@ func TestNewConfig_sourcePath(t *testing.T) { t.Fatalf("bad: %#v", warns) } if err == nil { - t.Fatal("Nonexistent source file should be caught in validation") + t.Fatal("should error") } // Good From 771349e58c9a350463678e76a63a46bb2979bc70 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Mon, 13 Nov 2017 12:52:47 -0800 Subject: [PATCH 0059/1216] fix error message --- builder/virtualbox/ovf/config_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/virtualbox/ovf/config_test.go b/builder/virtualbox/ovf/config_test.go index 0724968d3..5e67b242d 100644 --- a/builder/virtualbox/ovf/config_test.go +++ b/builder/virtualbox/ovf/config_test.go @@ -73,7 +73,7 @@ func TestNewConfig_sourcePath(t *testing.T) { t.Fatalf("bad: %#v", warns) } if err == nil { - t.Fatalf("bad: %s", err) + t.Fatalf("Nonexistant file should throw a validation error!") } // Bad From 6756df9510fcd49793ef576e5fd61760b6069981 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Mon, 13 Nov 2017 12:57:53 -0800 Subject: [PATCH 0060/1216] use url library instead of parsing string naiively --- builder/virtualbox/ovf/config.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/builder/virtualbox/ovf/config.go b/builder/virtualbox/ovf/config.go index ff8080dd7..3e4b9b879 100644 --- a/builder/virtualbox/ovf/config.go +++ b/builder/virtualbox/ovf/config.go @@ -2,6 +2,7 @@ package ovf import ( "fmt" + "net/url" "os" "strings" @@ -103,8 +104,9 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is invalid: %s", err)) } // file must exist now. - if (len(c.SourcePath) > 7) && (c.SourcePath[:7] == "file://") { - if _, err := os.Stat(c.SourcePath[7:]); err != nil { + fileURL, _ := url.Parse(c.SourcePath) + if fileURL.Scheme == "file" { + if _, err := os.Stat(fileURL.Path); err != nil { errs = packer.MultiErrorAppend(errs, fmt.Errorf("source file needs to exist at time of config validation: %s", err)) } From 9eacd3d61860e68f466ddb3dd6a36040fe50ca95 Mon Sep 17 00:00:00 2001 From: Brett Richardson Date: Tue, 14 Nov 2017 13:54:13 +0000 Subject: [PATCH 0061/1216] Update chef provisioner documentation The URL has changed, see chef documentation https://docs.chef.io/install_omnibus.html --- website/source/docs/provisioners/chef-client.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/provisioners/chef-client.html.md b/website/source/docs/provisioners/chef-client.html.md index 54cd5e778..dabf37ca5 100644 --- a/website/source/docs/provisioners/chef-client.html.md +++ b/website/source/docs/provisioners/chef-client.html.md @@ -212,7 +212,7 @@ readability) to install Chef. This command can be customized if you want to install Chef in another way. ``` text -curl -L https://www.chef.io/chef/install.sh | \ +curl -L https:///omnitruck.chef.io/chef/install.sh | \ {{if .Sudo}}sudo{{end}} bash ``` From 4a5bb756a72727b836932cf57216185a9ab71e80 Mon Sep 17 00:00:00 2001 From: Ladar Levison Date: Tue, 14 Nov 2017 16:40:06 -0600 Subject: [PATCH 0062/1216] Docker Example Typo I think the intention was to show you can tag, and push the same image to multiple repos, but the example given is to the same repo, twice. This change updates the example so it uses hashicorp/packer1, and hashicorp/packer2. --- website/source/docs/builders/docker.html.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/source/docs/builders/docker.html.md b/website/source/docs/builders/docker.html.md index 13c43da60..8b8e684cd 100644 --- a/website/source/docs/builders/docker.html.md +++ b/website/source/docs/builders/docker.html.md @@ -303,7 +303,7 @@ nearly-identical sequence definitions, as demonstrated by the example below: [ { "type": "docker-tag", - "repository": "hashicorp/packer", + "repository": "hashicorp/packer1", "tag": "0.7" }, "docker-push" @@ -311,7 +311,7 @@ nearly-identical sequence definitions, as demonstrated by the example below: [ { "type": "docker-tag", - "repository": "hashicorp/packer", + "repository": "hashicorp/packer2", "tag": "0.7" }, "docker-push" From 46f41d1f1210ad40b88a43325b02caa6130f5e8e Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 2 Nov 2017 19:46:03 -0700 Subject: [PATCH 0063/1216] WIP: add options to telemetry --- packer/build.go | 4 ++-- packer/provisioner.go | 2 +- packer/telemetry.go | 12 ++++++++++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/packer/build.go b/packer/build.go index 996c9a331..9e406ac63 100644 --- a/packer/build.go +++ b/packer/build.go @@ -221,7 +221,7 @@ func (b *coreBuild) Run(originalUi Ui, cache Cache) ([]Artifact, error) { } log.Printf("Running builder: %s", b.builderType) - ts := CheckpointReporter.AddSpan(b.builderType, "builder") + ts := CheckpointReporter.AddSpan(b.builderType, "builder", b.builderConfig) builderArtifact, err := b.builder.Run(builderUi, hook, cache) ts.End(err) if err != nil { @@ -248,7 +248,7 @@ PostProcessorRunSeqLoop: } builderUi.Say(fmt.Sprintf("Running post-processor: %s", corePP.processorType)) - ts := CheckpointReporter.AddSpan(corePP.processorType, "post-processor") + ts := CheckpointReporter.AddSpan(corePP.processorType, "post-processor", corePP.config) artifact, keep, err := corePP.processor.PostProcess(ppUi, priorArtifact) ts.End(err) if err != nil { diff --git a/packer/provisioner.go b/packer/provisioner.go index 639e8b1a8..a9782bb92 100644 --- a/packer/provisioner.go +++ b/packer/provisioner.go @@ -63,7 +63,7 @@ func (h *ProvisionHook) Run(name string, ui Ui, comm Communicator, data interfac h.runningProvisioner = p h.lock.Unlock() - ts := CheckpointReporter.AddSpan(h.ProvisionerTypes[i], "provisioner") + ts := CheckpointReporter.AddSpan(h.ProvisionerTypes[i], "provisioner", p) err := p.Provision(ui, comm) ts.End(err) if err != nil { diff --git a/packer/telemetry.go b/packer/telemetry.go index 7224de582..60005e606 100644 --- a/packer/telemetry.go +++ b/packer/telemetry.go @@ -11,7 +11,7 @@ import ( packerVersion "github.com/hashicorp/packer/version" ) -const TelemetryVersion string = "beta/packer/4" +const TelemetryVersion string = "beta/packer/5" const TelemetryPanicVersion string = "beta/packer_panic/4" var CheckpointReporter *CheckpointTelemetry @@ -85,11 +85,18 @@ func (c *CheckpointTelemetry) ReportPanic(m string) error { return checkpoint.Report(ctx, panicParams) } -func (c *CheckpointTelemetry) AddSpan(name, pluginType string) *TelemetrySpan { +func (c *CheckpointTelemetry) AddSpan(name, pluginType string, options interface{}) *TelemetrySpan { if c == nil { return nil } log.Printf("[INFO] (telemetry) Starting %s %s", pluginType, name) + + //strOpts := []string{} + if m, ok := options.(map[string]interface{}); ok { + for k, _ := range m { + log.Println("AddSpan options:", k) + } + } ts := &TelemetrySpan{ Name: name, Type: pluginType, @@ -130,6 +137,7 @@ type TelemetrySpan struct { StartTime time.Time `json:"start_time"` EndTime time.Time `json:"end_time"` Error string `json:"error"` + Options []string `json:"options"` } func (s *TelemetrySpan) End(err error) { From 3e3768ec3e84928500d68614d2347f6a8bec7df9 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 2 Nov 2017 23:31:32 -0700 Subject: [PATCH 0064/1216] fix tests --- packer/build.go | 10 +--------- packer/provisioner.go | 17 +++++++++++------ packer/provisioner_test.go | 17 +++++++++++------ 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/packer/build.go b/packer/build.go index 9e406ac63..c99164ce8 100644 --- a/packer/build.go +++ b/packer/build.go @@ -194,20 +194,12 @@ func (b *coreBuild) Run(originalUi Ui, cache Cache) ([]Artifact, error) { // Add a hook for the provisioners if we have provisioners if len(b.provisioners) > 0 { - provisioners := make([]Provisioner, len(b.provisioners)) - provisionerTypes := make([]string, len(b.provisioners)) - for i, p := range b.provisioners { - provisioners[i] = p.provisioner - provisionerTypes[i] = p.pType - } - if _, ok := hooks[HookProvision]; !ok { hooks[HookProvision] = make([]Hook, 0, 1) } hooks[HookProvision] = append(hooks[HookProvision], &ProvisionHook{ - Provisioners: provisioners, - ProvisionerTypes: provisionerTypes, + Provisioners: b.provisioners, }) } diff --git a/packer/provisioner.go b/packer/provisioner.go index a9782bb92..dc850daa3 100644 --- a/packer/provisioner.go +++ b/packer/provisioner.go @@ -30,8 +30,7 @@ type Provisioner interface { type ProvisionHook struct { // The provisioners to run as part of the hook. These should already // be prepared (by calling Prepare) at some earlier stage. - Provisioners []Provisioner - ProvisionerTypes []string + Provisioners []coreBuildProvisioner lock sync.Mutex runningProvisioner Provisioner @@ -58,13 +57,19 @@ func (h *ProvisionHook) Run(name string, ui Ui, comm Communicator, data interfac h.runningProvisioner = nil }() - for i, p := range h.Provisioners { + for _, p := range h.Provisioners { h.lock.Lock() - h.runningProvisioner = p + h.runningProvisioner = p.provisioner h.lock.Unlock() - ts := CheckpointReporter.AddSpan(h.ProvisionerTypes[i], "provisioner", p) - err := p.Provision(ui, comm) + var pConfig interface{} + if len(p.config) > 0 { + pConfig = p.config[0] + } + ts := CheckpointReporter.AddSpan(p.pType, "provisioner", pConfig) + + err := p.provisioner.Provision(ui, comm) + ts.End(err) if err != nil { return err diff --git a/packer/provisioner_test.go b/packer/provisioner_test.go index f303bc7d2..8e79d2f81 100644 --- a/packer/provisioner_test.go +++ b/packer/provisioner_test.go @@ -23,8 +23,10 @@ func TestProvisionHook(t *testing.T) { var data interface{} = nil hook := &ProvisionHook{ - Provisioners: []Provisioner{pA, pB}, - ProvisionerTypes: []string{"", ""}, + Provisioners: []coreBuildProvisioner{ + {"", pA, nil}, + {"", pB, nil}, + }, } hook.Run("foo", ui, comm, data) @@ -47,8 +49,10 @@ func TestProvisionHook_nilComm(t *testing.T) { var data interface{} = nil hook := &ProvisionHook{ - Provisioners: []Provisioner{pA, pB}, - ProvisionerTypes: []string{"", ""}, + Provisioners: []coreBuildProvisioner{ + {"", pA, nil}, + {"", pB, nil}, + }, } err := hook.Run("foo", ui, comm, data) @@ -74,8 +78,9 @@ func TestProvisionHook_cancel(t *testing.T) { } hook := &ProvisionHook{ - Provisioners: []Provisioner{p}, - ProvisionerTypes: []string{""}, + Provisioners: []coreBuildProvisioner{ + {"", p, nil}, + }, } finished := make(chan struct{}) From a5197840dfa4fd3925b508614bc4b46bf01e9bef Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 2 Nov 2017 23:45:01 -0700 Subject: [PATCH 0065/1216] add config key reporting --- packer/telemetry.go | 39 +++++++++++++++++++++++++++++---------- packer/telemetry_test.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 10 deletions(-) create mode 100644 packer/telemetry_test.go diff --git a/packer/telemetry.go b/packer/telemetry.go index 60005e606..f413dd34a 100644 --- a/packer/telemetry.go +++ b/packer/telemetry.go @@ -91,16 +91,11 @@ func (c *CheckpointTelemetry) AddSpan(name, pluginType string, options interface } log.Printf("[INFO] (telemetry) Starting %s %s", pluginType, name) - //strOpts := []string{} - if m, ok := options.(map[string]interface{}); ok { - for k, _ := range m { - log.Println("AddSpan options:", k) - } - } ts := &TelemetrySpan{ Name: name, - Type: pluginType, + Options: flattenConfigKeys(options), StartTime: time.Now().UTC(), + Type: pluginType, } c.spans = append(c.spans, ts) return ts @@ -123,6 +118,8 @@ func (c *CheckpointTelemetry) Finalize(command string, errCode int, err error) e extra.Error = err.Error() } params.Payload = extra + // b, _ := json.MarshalIndent(params, "", " ") + // log.Println(string(b)) ctx, cancel := context.WithTimeout(context.Background(), 1500*time.Millisecond) defer cancel() @@ -132,12 +129,12 @@ func (c *CheckpointTelemetry) Finalize(command string, errCode int, err error) e } type TelemetrySpan struct { - Name string `json:"name"` - Type string `json:"type"` - StartTime time.Time `json:"start_time"` EndTime time.Time `json:"end_time"` Error string `json:"error"` + Name string `json:"name"` Options []string `json:"options"` + StartTime time.Time `json:"start_time"` + Type string `json:"type"` } func (s *TelemetrySpan) End(err error) { @@ -151,3 +148,25 @@ func (s *TelemetrySpan) End(err error) { log.Printf("[INFO] (telemetry) found error: %s", err.Error()) } } + +func flattenConfigKeys(options interface{}) []string { + var flatten func(string, interface{}) []string + + flatten = func(prefix string, options interface{}) (strOpts []string) { + if m, ok := options.(map[string]interface{}); ok { + for k, v := range m { + if prefix != "" { + k = prefix + "/" + k + } + if n, ok := v.(map[string]interface{}); ok { + strOpts = append(strOpts, flatten(k, n)...) + } else { + strOpts = append(strOpts, k) + } + } + } + return + } + + return flatten("", options) +} diff --git a/packer/telemetry_test.go b/packer/telemetry_test.go new file mode 100644 index 000000000..c4192f61f --- /dev/null +++ b/packer/telemetry_test.go @@ -0,0 +1,32 @@ +package packer + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestFlattenConfigKeys_nil(t *testing.T) { + f := flattenConfigKeys(nil) + assert.Zero(t, f, "Expected empty list.") +} + +func TestFlattenConfigKeys_nested(t *testing.T) { + inp := make(map[string]interface{}) + inp["A"] = "" + inp["B"] = []string{} + + c := make(map[string]interface{}) + c["X"] = "" + d := make(map[string]interface{}) + d["a"] = "" + + c["Y"] = d + inp["C"] = c + + assert.Equal(t, + []string{"A", "B", "C/X", "C/Y/a"}, + flattenConfigKeys(inp), + "Input didn't flatten correctly.", + ) +} From 49c20e3b1cbd4fb0a8f09965926c5da72f465b97 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 2 Nov 2017 23:54:03 -0700 Subject: [PATCH 0066/1216] bump telemetry timeout to 2s --- packer/telemetry.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packer/telemetry.go b/packer/telemetry.go index f413dd34a..13931d419 100644 --- a/packer/telemetry.go +++ b/packer/telemetry.go @@ -121,7 +121,7 @@ func (c *CheckpointTelemetry) Finalize(command string, errCode int, err error) e // b, _ := json.MarshalIndent(params, "", " ") // log.Println(string(b)) - ctx, cancel := context.WithTimeout(context.Background(), 1500*time.Millisecond) + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() log.Printf("[INFO] (telemetry) Finalizing.") From b07a0cd6f06692046684c7a1b9166d074b15dd8b Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Sat, 4 Nov 2017 13:06:36 -0700 Subject: [PATCH 0067/1216] fix tests always sort telemetry options --- builder/docker/communicator_test.go | 31 ++-- packer/build.go | 15 +- packer/provisioner.go | 19 +- packer/provisioner_test.go | 16 +- packer/telemetry.go | 5 +- .../testify/assert/assertion_forward.go | 167 +++++++++++------- .../stretchr/testify/assert/assertions.go | 101 +++-------- .../testify/assert/http_assertions.go | 2 +- vendor/vendor.json | 5 +- 9 files changed, 183 insertions(+), 178 deletions(-) diff --git a/builder/docker/communicator_test.go b/builder/docker/communicator_test.go index bdfaef996..14bcdf496 100644 --- a/builder/docker/communicator_test.go +++ b/builder/docker/communicator_test.go @@ -67,11 +67,10 @@ func TestUploadDownload(t *testing.T) { hooks := map[string][]packer.Hook{} hooks[packer.HookProvision] = []packer.Hook{ &packer.ProvisionHook{ - Provisioners: []packer.Provisioner{ - upload, - download, + Provisioners: []*packer.HookedProvisioner{ + {upload, nil, ""}, + {download, nil, ""}, }, - ProvisionerTypes: []string{"", ""}, }, } hook := &packer.DispatchHook{Mapping: hooks} @@ -157,12 +156,11 @@ func TestLargeDownload(t *testing.T) { hooks := map[string][]packer.Hook{} hooks[packer.HookProvision] = []packer.Hook{ &packer.ProvisionHook{ - Provisioners: []packer.Provisioner{ - shell, - downloadCupcake, - downloadBigcake, + Provisioners: []*packer.HookedProvisioner{ + {shell, nil, ""}, + {downloadCupcake, nil, ""}, + {downloadBigcake, nil, ""}, }, - ProvisionerTypes: []string{"", "", ""}, }, } hook := &packer.DispatchHook{Mapping: hooks} @@ -203,8 +201,8 @@ func TestLargeDownload(t *testing.T) { // 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) + // t.Fatalf("Input and output files do not match\n"+ + // "Input:\n%s\nOutput:\n%s\n", inputFile, outputFile) // } } @@ -267,13 +265,12 @@ func TestFixUploadOwner(t *testing.T) { hooks := map[string][]packer.Hook{} hooks[packer.HookProvision] = []packer.Hook{ &packer.ProvisionHook{ - Provisioners: []packer.Provisioner{ - fileProvisioner, - dirProvisioner, - shellProvisioner, - verifyProvisioner, + Provisioners: []*packer.HookedProvisioner{ + {fileProvisioner, nil, ""}, + {dirProvisioner, nil, ""}, + {shellProvisioner, nil, ""}, + {verifyProvisioner, nil, ""}, }, - ProvisionerTypes: []string{"", "", "", ""}, }, } hook := &packer.DispatchHook{Mapping: hooks} diff --git a/packer/build.go b/packer/build.go index c99164ce8..1187e49d7 100644 --- a/packer/build.go +++ b/packer/build.go @@ -194,12 +194,25 @@ func (b *coreBuild) Run(originalUi Ui, cache Cache) ([]Artifact, error) { // Add a hook for the provisioners if we have provisioners if len(b.provisioners) > 0 { + hookedProvisioners := make([]*HookedProvisioner, len(b.provisioners)) + for i, p := range b.provisioners { + var pConfig interface{} + if len(p.config) > 0 { + pConfig = p.config[0] + } + hookedProvisioners[i] = &HookedProvisioner{ + p.provisioner, + pConfig, + p.pType, + } + } + if _, ok := hooks[HookProvision]; !ok { hooks[HookProvision] = make([]Hook, 0, 1) } hooks[HookProvision] = append(hooks[HookProvision], &ProvisionHook{ - Provisioners: b.provisioners, + Provisioners: hookedProvisioners, }) } diff --git a/packer/provisioner.go b/packer/provisioner.go index dc850daa3..a754f7544 100644 --- a/packer/provisioner.go +++ b/packer/provisioner.go @@ -26,11 +26,18 @@ type Provisioner interface { Cancel() } +// A HookedProvisioner represents a provisioner and information describing it +type HookedProvisioner struct { + Provisioner Provisioner + Config interface{} + TypeName string +} + // A Hook implementation that runs the given provisioners. type ProvisionHook struct { // The provisioners to run as part of the hook. These should already // be prepared (by calling Prepare) at some earlier stage. - Provisioners []coreBuildProvisioner + Provisioners []*HookedProvisioner lock sync.Mutex runningProvisioner Provisioner @@ -59,16 +66,12 @@ func (h *ProvisionHook) Run(name string, ui Ui, comm Communicator, data interfac for _, p := range h.Provisioners { h.lock.Lock() - h.runningProvisioner = p.provisioner + h.runningProvisioner = p.Provisioner h.lock.Unlock() - var pConfig interface{} - if len(p.config) > 0 { - pConfig = p.config[0] - } - ts := CheckpointReporter.AddSpan(p.pType, "provisioner", pConfig) + ts := CheckpointReporter.AddSpan(p.TypeName, "provisioner", p.Config) - err := p.provisioner.Provision(ui, comm) + err := p.Provisioner.Provision(ui, comm) ts.End(err) if err != nil { diff --git a/packer/provisioner_test.go b/packer/provisioner_test.go index 8e79d2f81..97304d2a5 100644 --- a/packer/provisioner_test.go +++ b/packer/provisioner_test.go @@ -23,9 +23,9 @@ func TestProvisionHook(t *testing.T) { var data interface{} = nil hook := &ProvisionHook{ - Provisioners: []coreBuildProvisioner{ - {"", pA, nil}, - {"", pB, nil}, + Provisioners: []*HookedProvisioner{ + {pA, nil, ""}, + {pB, nil, ""}, }, } @@ -49,9 +49,9 @@ func TestProvisionHook_nilComm(t *testing.T) { var data interface{} = nil hook := &ProvisionHook{ - Provisioners: []coreBuildProvisioner{ - {"", pA, nil}, - {"", pB, nil}, + Provisioners: []*HookedProvisioner{ + {pA, nil, ""}, + {pB, nil, ""}, }, } @@ -78,8 +78,8 @@ func TestProvisionHook_cancel(t *testing.T) { } hook := &ProvisionHook{ - Provisioners: []coreBuildProvisioner{ - {"", p, nil}, + Provisioners: []*HookedProvisioner{ + {p, nil, ""}, }, } diff --git a/packer/telemetry.go b/packer/telemetry.go index 13931d419..a58b1c701 100644 --- a/packer/telemetry.go +++ b/packer/telemetry.go @@ -5,6 +5,7 @@ import ( "log" "os" "path/filepath" + "sort" "time" checkpoint "github.com/hashicorp/go-checkpoint" @@ -168,5 +169,7 @@ func flattenConfigKeys(options interface{}) []string { return } - return flatten("", options) + flattened := flatten("", options) + sort.Strings(flattened) + return flattened } diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/vendor/github.com/stretchr/testify/assert/assertion_forward.go index 29b71d176..e6a796046 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_forward.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_forward.go @@ -1,345 +1,386 @@ /* * CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen * THIS FILE MUST NOT BE EDITED BY HAND - */ +*/ package assert import ( + http "net/http" url "net/url" time "time" ) + // Condition uses a Comparison to assert a complex condition. func (a *Assertions) Condition(comp Comparison, msgAndArgs ...interface{}) bool { return Condition(a.t, comp, msgAndArgs...) } + // Contains asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. -// +// // a.Contains("Hello World", "World", "But 'Hello World' does contain 'World'") // a.Contains(["Hello", "World"], "World", "But ["Hello", "World"] does contain 'World'") // a.Contains({"Hello": "World"}, "Hello", "But {'Hello': 'World'} does contain 'Hello'") -// +// // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { return Contains(a.t, s, contains, msgAndArgs...) } + // Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. -// +// // a.Empty(obj) -// +// // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { return Empty(a.t, object, msgAndArgs...) } + // Equal asserts that two objects are equal. -// +// // a.Equal(123, 123, "123 and 123 should be equal") -// +// // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { return Equal(a.t, expected, actual, msgAndArgs...) } + // EqualError asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. -// +// // actualObj, err := SomeFunction() -// a.EqualError(err, expectedErrorString, "An error was expected") -// +// if assert.Error(t, err, "An error was expected") { +// assert.Equal(t, err, expectedError) +// } +// // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) bool { return EqualError(a.t, theError, errString, msgAndArgs...) } + // EqualValues asserts that two objects are equal or convertable to the same types // and equal. -// +// // a.EqualValues(uint32(123), int32(123), "123 and 123 should be equal") -// +// // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { return EqualValues(a.t, expected, actual, msgAndArgs...) } + // Error asserts that a function returned an error (i.e. not `nil`). -// +// // actualObj, err := SomeFunction() // if a.Error(err, "An error was expected") { // assert.Equal(t, err, expectedError) // } -// +// // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool { return Error(a.t, err, msgAndArgs...) } + // Exactly asserts that two objects are equal is value and type. -// +// // a.Exactly(int32(123), int64(123), "123 and 123 should NOT be equal") -// +// // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { return Exactly(a.t, expected, actual, msgAndArgs...) } + // Fail reports a failure through func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) bool { return Fail(a.t, failureMessage, msgAndArgs...) } + // FailNow fails test func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) bool { return FailNow(a.t, failureMessage, msgAndArgs...) } + // False asserts that the specified value is false. -// +// // a.False(myBool, "myBool should be false") -// +// // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool { return False(a.t, value, msgAndArgs...) } + // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. -// +// // a.HTTPBodyContains(myHandler, "www.google.com", nil, "I'm Feeling Lucky") -// +// // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) bool { return HTTPBodyContains(a.t, handler, method, url, values, str) } + // HTTPBodyNotContains asserts that a specified handler returns a // body that does not contain a string. -// +// // a.HTTPBodyNotContains(myHandler, "www.google.com", nil, "I'm Feeling Lucky") -// +// // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) bool { return HTTPBodyNotContains(a.t, handler, method, url, values, str) } + // HTTPError asserts that a specified handler returns an error status code. -// +// // a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} -// +// // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values) bool { return HTTPError(a.t, handler, method, url, values) } + // HTTPRedirect asserts that a specified handler returns a redirect status code. -// +// // a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} -// +// // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values) bool { return HTTPRedirect(a.t, handler, method, url, values) } + // HTTPSuccess asserts that a specified handler returns a success status code. -// +// // a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) -// +// // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values) bool { return HTTPSuccess(a.t, handler, method, url, values) } + // Implements asserts that an object is implemented by the specified interface. -// +// // a.Implements((*MyInterface)(nil), new(MyObject), "MyObject") func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { return Implements(a.t, interfaceObject, object, msgAndArgs...) } + // InDelta asserts that the two numerals are within delta of each other. -// +// // a.InDelta(math.Pi, (22 / 7.0), 0.01) -// +// // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { return InDelta(a.t, expected, actual, delta, msgAndArgs...) } + // InDeltaSlice is the same as InDelta, except it compares two slices. func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { return InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...) } + // InEpsilon asserts that expected and actual have a relative error less than epsilon -// +// // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { return InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...) } -// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. -func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { - return InEpsilonSlice(a.t, expected, actual, epsilon, msgAndArgs...) + +// InEpsilonSlice is the same as InEpsilon, except it compares two slices. +func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + return InEpsilonSlice(a.t, expected, actual, delta, msgAndArgs...) } + // IsType asserts that the specified objects are of the same type. func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { return IsType(a.t, expectedType, object, msgAndArgs...) } + // JSONEq asserts that two JSON strings are equivalent. -// +// // a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) -// +// // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) bool { return JSONEq(a.t, expected, actual, msgAndArgs...) } + // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. -// +// // a.Len(mySlice, 3, "The size of slice is not 3") -// +// // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) bool { return Len(a.t, object, length, msgAndArgs...) } + // Nil asserts that the specified object is nil. -// +// // a.Nil(err, "err should be nothing") -// +// // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool { return Nil(a.t, object, msgAndArgs...) } + // NoError asserts that a function returned no error (i.e. `nil`). -// +// // actualObj, err := SomeFunction() // if a.NoError(err) { // assert.Equal(t, actualObj, expectedObj) // } -// +// // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool { return NoError(a.t, err, msgAndArgs...) } + // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. -// +// // a.NotContains("Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'") // a.NotContains(["Hello", "World"], "Earth", "But ['Hello', 'World'] does NOT contain 'Earth'") // a.NotContains({"Hello": "World"}, "Earth", "But {'Hello': 'World'} does NOT contain 'Earth'") -// +// // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { return NotContains(a.t, s, contains, msgAndArgs...) } + // NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. -// +// // if a.NotEmpty(obj) { // assert.Equal(t, "two", obj[1]) // } -// +// // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) bool { return NotEmpty(a.t, object, msgAndArgs...) } + // NotEqual asserts that the specified values are NOT equal. -// +// // a.NotEqual(obj1, obj2, "two objects shouldn't be equal") -// +// // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { return NotEqual(a.t, expected, actual, msgAndArgs...) } + // NotNil asserts that the specified object is not nil. -// +// // a.NotNil(err, "err should be something") -// +// // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool { return NotNil(a.t, object, msgAndArgs...) } + // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. -// +// // a.NotPanics(func(){ // RemainCalm() // }, "Calling RemainCalm() should NOT panic") -// +// // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool { return NotPanics(a.t, f, msgAndArgs...) } + // NotRegexp asserts that a specified regexp does not match a string. -// +// // a.NotRegexp(regexp.MustCompile("starts"), "it's starting") // a.NotRegexp("^start", "it's not starting") -// +// // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { return NotRegexp(a.t, rx, str, msgAndArgs...) } + // NotZero asserts that i is not the zero value for its type and returns the truth. func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) bool { return NotZero(a.t, i, msgAndArgs...) } + // Panics asserts that the code inside the specified PanicTestFunc panics. -// +// // a.Panics(func(){ // GoCrazy() // }, "Calling GoCrazy() should panic") -// +// // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool { return Panics(a.t, f, msgAndArgs...) } + // Regexp asserts that a specified regexp matches a string. -// +// // a.Regexp(regexp.MustCompile("start"), "it's starting") // a.Regexp("start...$", "it's not starting") -// +// // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { return Regexp(a.t, rx, str, msgAndArgs...) } + // True asserts that the specified value is true. -// +// // a.True(myBool, "myBool should be true") -// +// // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool { return True(a.t, value, msgAndArgs...) } + // WithinDuration asserts that the two times are within duration delta of each other. -// +// // a.WithinDuration(time.Now(), time.Now(), 10*time.Second, "The difference should not be more than 10s") -// +// // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { return WithinDuration(a.t, expected, actual, delta, msgAndArgs...) } + // Zero asserts that i is the zero value for its type and returns the truth. func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) bool { return Zero(a.t, i, msgAndArgs...) diff --git a/vendor/github.com/stretchr/testify/assert/assertions.go b/vendor/github.com/stretchr/testify/assert/assertions.go index 835084ffc..348d5f1bc 100644 --- a/vendor/github.com/stretchr/testify/assert/assertions.go +++ b/vendor/github.com/stretchr/testify/assert/assertions.go @@ -18,10 +18,6 @@ import ( "github.com/pmezard/go-difflib/difflib" ) -func init() { - spew.Config.SortKeys = true -} - // TestingT is an interface wrapper around *testing.T type TestingT interface { Errorf(format string, args ...interface{}) @@ -69,7 +65,7 @@ func ObjectsAreEqualValues(expected, actual interface{}) bool { /* CallerInfo is necessary because the assert functions use the testing object internally, causing it to print the file:line of the assert method, rather than where -the problem actually occurred in calling code.*/ +the problem actually occured in calling code.*/ // CallerInfo returns an array of strings containing the file and line number // of each stack frame leading from the current test to the assert call that @@ -86,9 +82,7 @@ func CallerInfo() []string { for i := 0; ; i++ { pc, file, line, ok = runtime.Caller(i) if !ok { - // The breaks below failed to terminate the loop, and we ran off the - // end of the call stack. - break + return nil } // This is a huge edge case, but it will panic if this is the case, see #180 @@ -96,21 +90,6 @@ func CallerInfo() []string { break } - f := runtime.FuncForPC(pc) - if f == nil { - break - } - name = f.Name() - - // testing.tRunner is the standard library function that calls - // tests. Subtests are called directly by tRunner, without going through - // the Test/Benchmark/Example function that contains the t.Run calls, so - // with subtests we should break when we hit tRunner, without adding it - // to the list of callers. - if name == "testing.tRunner" { - break - } - parts := strings.Split(file, "/") dir := parts[len(parts)-2] file = parts[len(parts)-1] @@ -118,6 +97,11 @@ func CallerInfo() []string { callers = append(callers, fmt.Sprintf("%s:%d", file, line)) } + f := runtime.FuncForPC(pc) + if f == nil { + break + } + name = f.Name() // Drop the package segments := strings.Split(name, ".") name = segments[len(segments)-1] @@ -278,48 +262,14 @@ func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) if !ObjectsAreEqual(expected, actual) { diff := diff(expected, actual) - expected, actual = formatUnequalValues(expected, actual) - return Fail(t, fmt.Sprintf("Not equal: \n"+ - "expected: %s\n"+ - "received: %s%s", expected, actual, diff), msgAndArgs...) + return Fail(t, fmt.Sprintf("Not equal: %#v (expected)\n"+ + " != %#v (actual)%s", expected, actual, diff), msgAndArgs...) } return true } -// formatUnequalValues takes two values of arbitrary types and returns string -// representations appropriate to be presented to the user. -// -// If the values are not of like type, the returned strings will be prefixed -// with the type name, and the value will be enclosed in parenthesis similar -// to a type conversion in the Go grammar. -func formatUnequalValues(expected, actual interface{}) (e string, a string) { - aType := reflect.TypeOf(expected) - bType := reflect.TypeOf(actual) - - if aType != bType && isNumericType(aType) && isNumericType(bType) { - return fmt.Sprintf("%v(%#v)", aType, expected), - fmt.Sprintf("%v(%#v)", bType, actual) - } - - return fmt.Sprintf("%#v", expected), - fmt.Sprintf("%#v", actual) -} - -func isNumericType(t reflect.Type) bool { - switch t.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return true - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return true - case reflect.Float32, reflect.Float64: - return true - } - - return false -} - // EqualValues asserts that two objects are equal or convertable to the same types // and equal. // @@ -329,11 +279,8 @@ func isNumericType(t reflect.Type) bool { func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { if !ObjectsAreEqualValues(expected, actual) { - diff := diff(expected, actual) - expected, actual = formatUnequalValues(expected, actual) - return Fail(t, fmt.Sprintf("Not equal: \n"+ - "expected: %s\n"+ - "received: %s%s", expected, actual, diff), msgAndArgs...) + return Fail(t, fmt.Sprintf("Not equal: %#v (expected)\n"+ + " != %#v (actual)", expected, actual), msgAndArgs...) } return true @@ -886,7 +833,7 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m // Returns whether the assertion was successful (true) or not (false). func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { if err != nil { - return Fail(t, fmt.Sprintf("Received unexpected error:\n%+v", err), msgAndArgs...) + return Fail(t, fmt.Sprintf("Received unexpected error %q", err), msgAndArgs...) } return true @@ -902,8 +849,9 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { // Returns whether the assertion was successful (true) or not (false). func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { + message := messageFromMsgAndArgs(msgAndArgs...) if err == nil { - return Fail(t, "An error is expected but got nil.", msgAndArgs...) + return Fail(t, "An error is expected but got nil. %s", message) } return true @@ -913,22 +861,20 @@ func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { // and that it is equal to the provided error. // // actualObj, err := SomeFunction() -// assert.EqualError(t, err, expectedErrorString, "An error was expected") +// if assert.Error(t, err, "An error was expected") { +// assert.Equal(t, err, expectedError) +// } // // Returns whether the assertion was successful (true) or not (false). func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool { - if !Error(t, theError, msgAndArgs...) { + + message := messageFromMsgAndArgs(msgAndArgs...) + if !NotNil(t, theError, "An error is expected but got nil. %s", message) { return false } - expected := errString - actual := theError.Error() - // don't need to use deep equals here, we know they are both strings - if expected != actual { - return Fail(t, fmt.Sprintf("Error message not equal:\n"+ - "expected: %q\n"+ - "received: %q", expected, actual), msgAndArgs...) - } - return true + s := "An error with value \"%s\" is expected but got \"%s\". %s" + return Equal(t, errString, theError.Error(), + s, errString, theError.Error(), message) } // matchRegexp return true if a specified regexp matches a string. @@ -1043,6 +989,7 @@ func diff(expected interface{}, actual interface{}) string { return "" } + spew.Config.SortKeys = true e := spew.Sdump(expected) a := spew.Sdump(actual) diff --git a/vendor/github.com/stretchr/testify/assert/http_assertions.go b/vendor/github.com/stretchr/testify/assert/http_assertions.go index fa7ab89b1..e1b9442b5 100644 --- a/vendor/github.com/stretchr/testify/assert/http_assertions.go +++ b/vendor/github.com/stretchr/testify/assert/http_assertions.go @@ -99,7 +99,7 @@ func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url strin contains := strings.Contains(body, fmt.Sprint(str)) if contains { - Fail(t, fmt.Sprintf("Expected response body for \"%s\" to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)) + Fail(t, "Expected response body for %s to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body) } return !contains diff --git a/vendor/vendor.json b/vendor/vendor.json index a8db37219..3778555c7 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1057,10 +1057,11 @@ "revisionTime": "2017-03-21T23:07:31Z" }, { - "checksumSHA1": "Q2V7Zs3diLmLfmfbiuLpSxETSuY=", + "checksumSHA1": "iydUphwYqZRq3WhstEdGsbvBAKs=", "comment": "v1.1.4-4-g976c720", "path": "github.com/stretchr/testify/assert", - "revision": "976c720a22c8eb4eb6a0b4348ad85ad12491a506" + "revision": "d77da356e56a7428ad25149ca77381849a6a5232", + "revisionTime": "2016-06-15T09:26:46Z" }, { "checksumSHA1": "GQ9bu6PuydK3Yor1JgtVKUfEJm8=", From 97ef340423c2e006004d6a06a3e4aa3f549c191c Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Tue, 14 Nov 2017 16:43:57 -0800 Subject: [PATCH 0068/1216] fix type --- website/source/docs/provisioners/chef-client.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/provisioners/chef-client.html.md b/website/source/docs/provisioners/chef-client.html.md index dabf37ca5..05898263e 100644 --- a/website/source/docs/provisioners/chef-client.html.md +++ b/website/source/docs/provisioners/chef-client.html.md @@ -212,7 +212,7 @@ readability) to install Chef. This command can be customized if you want to install Chef in another way. ``` text -curl -L https:///omnitruck.chef.io/chef/install.sh | \ +curl -L https://omnitruck.chef.io/chef/install.sh | \ {{if .Sudo}}sudo{{end}} bash ``` From 18b9487a386ca2e8e5183cb68a1dc72d03031cf3 Mon Sep 17 00:00:00 2001 From: Malet Date: Wed, 15 Nov 2017 12:02:08 +0000 Subject: [PATCH 0069/1216] Fix typo --- website/source/docs/builders/googlecompute.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/builders/googlecompute.html.md b/website/source/docs/builders/googlecompute.html.md index 4ccfe4cb1..1c4717768 100644 --- a/website/source/docs/builders/googlecompute.html.md +++ b/website/source/docs/builders/googlecompute.html.md @@ -70,7 +70,7 @@ straightforwarded, it is documented here. 4. Create new service account that at least has `Compute Engine Instance Admin (v1)` and `Service Account User` roles. -5. Chose `JSON` as Key type and click "Create". +5. Choose `JSON` as Key type and click "Create". A JSON file will be downloaded automatically. This is your *account file*. ### Precedence of Authentication Methods From b8bd66d10da64b6e885f8576ef279520891e2e99 Mon Sep 17 00:00:00 2001 From: Ammar Ansari Date: Wed, 15 Nov 2017 14:47:46 -0500 Subject: [PATCH 0070/1216] Align virtual disk size for qemu builder When booting from a disk image, the Qemu builder resizes the disk to 40000 which is not a multiple of 1kB. This causes problems while booting from the image. Updating the default disk size to 40960 fixes this issue --- builder/qemu/builder.go | 2 +- builder/qemu/builder_test.go | 2 +- website/source/docs/builders/qemu.html.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/builder/qemu/builder.go b/builder/qemu/builder.go index 2b6b206d9..43ec6b4c3 100644 --- a/builder/qemu/builder.go +++ b/builder/qemu/builder.go @@ -144,7 +144,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { warnings := make([]string, 0) if b.config.DiskSize == 0 { - b.config.DiskSize = 40000 + b.config.DiskSize = 40960 } if b.config.DiskCache == "" { diff --git a/builder/qemu/builder_test.go b/builder/qemu/builder_test.go index 24c5dc47e..7ef29034e 100644 --- a/builder/qemu/builder_test.go +++ b/builder/qemu/builder_test.go @@ -208,7 +208,7 @@ func TestBuilderPrepare_DiskSize(t *testing.T) { t.Fatalf("bad err: %s", err) } - if b.config.DiskSize != 40000 { + if b.config.DiskSize != 40960 { t.Fatalf("bad size: %d", b.config.DiskSize) } diff --git a/website/source/docs/builders/qemu.html.md b/website/source/docs/builders/qemu.html.md index e1bff6043..f3de7cf9c 100644 --- a/website/source/docs/builders/qemu.html.md +++ b/website/source/docs/builders/qemu.html.md @@ -157,7 +157,7 @@ Linux server and have not enabled X11 forwarding (`ssh -X`). interface under these circumstances will cause the build to fail. - `disk_size` (number) - The size, in megabytes, of the hard disk to create - for the VM. By default, this is 40000 (about 40 GB). + for the VM. By default, this is 40960 (40 GB). - `floppy_files` (array of strings) - A list of files to place onto a floppy disk that is attached when the VM is booted. This is most useful for From 581bb9a329b3c189546ec68d4654794dbd061938 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Wed, 15 Nov 2017 12:41:14 -0800 Subject: [PATCH 0071/1216] update changelog --- CHANGELOG.md | 64 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3787a54aa..618431a34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,35 +2,55 @@ ### IMRPOVEMENTS: -* post-processor/docker-push: Add `aws_profile` option to control the aws profile for ECR. [GH-5470] -* builder/docker: Add `aws_profile` option to control the aws profile for ECR. [GH-5470] -* post-processor/vsphere: Properly capture `ovftool` output. [GH-5499] -* builder/hyper-v: Also disable automatic checkpoints for gen 2 VMs. [GH-5517] -* builder/hyper-v: Add `disk_additional_size` option to allow for up to 64 additional disks. [GH-5491] -* builder/amazon: correctly deregister AMIs when `force_deregister` is set. [GH-5525] +* builder/amazon: correctly deregister AMIs when `force_deregister` is set. + [GH-5525] * builder/digitalocean: Add `ipv6` option to enable on droplet. [GH-5534] -* builder/triton: Add `source_machine_image_filter` option to select an image ID based on a variety of parameters. [GH-5538] -* communicator/ssh: Add socks 5 proxy support. [GH-5439] -* builder/lxc: Add new `publish_properties` field to set image properties. [GH-5475] -* builder/virtualbox-ovf: Retry while removing VM to solve for transient errors. [GH-5512] -* builder/google: Interpolate network and subnetwork values, rather than relying on an API call that packer may not have permission for. [GH-5343] -* builder/lxc: Add three new configuration option categories to LXC builder: create_options, start_options, and attach_options. [GH-5530] -* core: Rewrite vagrantfile code to make cross-platform development easier. [5539] -* builder/triton: Update triton-go sdk. [GH-5531] +* builder/docker: Add `aws_profile` option to control the aws profile for ECR. + [GH-5470] * builder/google: Add clean_image_name template engine. [GH-5463] * builder/google: Allow Selecting Container Optimized Images. [GH-5576] +* builder/google: Interpolate network and subnetwork values, rather than + relying on an API call that packer may not have permission for. [GH-5343] +* builder/hyper-v: Add `disk_additional_size` option to allow for up to 64 + additional disks. [GH-5491] +* builder/hyper-v: Also disable automatic checkpoints for gen 2 VMs. [GH-5517] +* builder/lxc: Add new `publish_properties` field to set image properties. + [GH-5475] +* builder/lxc: Add three new configuration option categories to LXC builder: + create_options, start_options, and attach_options. [GH-5530] +* builder/triton: Add `source_machine_image_filter` option to select an image + ID based on a variety of parameters. [GH-5538] +* builder/triton: Update triton-go sdk. [GH-5531] +* builder/virtualbox-ovf: Error during prepare if source path doesn't exist. + [GH-5573] +* builder/virtualbox-ovf: Retry while removing VM to solve for transient + errors. [GH-5512] +* communicator/ssh: Add socks 5 proxy support. [GH-5439] +* core: Rewrite vagrantfile code to make cross-platform development easier. + [GH-5539] +* post-processor/docker-push: Add `aws_profile` option to control the aws + profile for ECR. [GH-5470] +* post-processor/vsphere: Properly capture `ovftool` output. [GH-5499] ### BUG FIXES: -* builder/docker: Remove `login_email`, which no longer exists in the docker client. [GH-5511] -* builder/triton: Fix a bug where partially created images can be reported as complete. [GH-5566] -* builder/amazon: region is set from profile, if profile is set, rather than being overridden by metadata. [GH-5562] -* provisioner/windows-restart: Wait for restart no longer endlessly loops if user specifies a custom restart check command. [GH-5563] -* post-processor/vsphere: Use the vm disk path information to re-create the vmx datastore path. [GH-5567] * builder/amazon: Add a delay option to security group waiter. [GH-5536] -* builder/amazon: Fix regressions relating to spot instances and EBS volumes. [GH-5495] -* builder/hyperv: Fix admin check that was causing powershell failures. [GH-5510] -* builder/oracle: Defaulting of OCI builder region will first check the packer template and the OCI config file. [GH-5407] +* builder/amazon: Fix regressions relating to spot instances and EBS volumes. + [GH-5495] +* builder/amazon: region is set from profile, if profile is set, rather than + being overridden by metadata. [GH-5562] +* builder/docker: Remove `login_email`, which no longer exists in the docker + client. [GH-5511] +* builder/hyperv: Fix admin check that was causing powershell failures. + [GH-5510] +* builder/oracle: Defaulting of OCI builder region will first check the packer + template and the OCI config file. [GH-5407] +* builder/triton: Fix a bug where partially created images can be reported as + complete. [GH-5566] +* post-processor/vsphere: Use the vm disk path information to re-create the vmx + datastore path. [GH-5567] +* provisioner/windows-restart: Wait for restart no longer endlessly loops if + user specifies a custom restart check command. [GH-5563] ## 1.1.1 (October 13, 2017) From bc64c6cb658d227aa97e87be02b295f7c4e40a42 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Wed, 15 Nov 2017 12:45:01 -0800 Subject: [PATCH 0072/1216] update changelog --- CHANGELOG.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 618431a34..940d5fcf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,13 +2,13 @@ ### IMRPOVEMENTS: -* builder/amazon: correctly deregister AMIs when `force_deregister` is set. +* builder/amazon: Correctly deregister AMIs when `force_deregister` is set. [GH-5525] * builder/digitalocean: Add `ipv6` option to enable on droplet. [GH-5534] * builder/docker: Add `aws_profile` option to control the aws profile for ECR. [GH-5470] -* builder/google: Add clean_image_name template engine. [GH-5463] -* builder/google: Allow Selecting Container Optimized Images. [GH-5576] +* builder/google: Add `clean_image_name` template engine. [GH-5463] +* builder/google: Allow selecting container optimized images. [GH-5576] * builder/google: Interpolate network and subnetwork values, rather than relying on an API call that packer may not have permission for. [GH-5343] * builder/hyper-v: Add `disk_additional_size` option to allow for up to 64 @@ -17,15 +17,15 @@ * builder/lxc: Add new `publish_properties` field to set image properties. [GH-5475] * builder/lxc: Add three new configuration option categories to LXC builder: - create_options, start_options, and attach_options. [GH-5530] + `create_options`, `start_options`, and `attach_options`. [GH-5530] * builder/triton: Add `source_machine_image_filter` option to select an image ID based on a variety of parameters. [GH-5538] -* builder/triton: Update triton-go sdk. [GH-5531] * builder/virtualbox-ovf: Error during prepare if source path doesn't exist. [GH-5573] * builder/virtualbox-ovf: Retry while removing VM to solve for transient errors. [GH-5512] * communicator/ssh: Add socks 5 proxy support. [GH-5439] +* core/iso_config: Support relative paths in checksum file. [GH-5578] * core: Rewrite vagrantfile code to make cross-platform development easier. [GH-5539] * post-processor/docker-push: Add `aws_profile` option to control the aws @@ -37,8 +37,8 @@ * builder/amazon: Add a delay option to security group waiter. [GH-5536] * builder/amazon: Fix regressions relating to spot instances and EBS volumes. [GH-5495] -* builder/amazon: region is set from profile, if profile is set, rather than - being overridden by metadata. [GH-5562] +* builder/amazon: Set region from profile, if profile is set, rather than being + overridden by metadata. [GH-5562] * builder/docker: Remove `login_email`, which no longer exists in the docker client. [GH-5511] * builder/hyperv: Fix admin check that was causing powershell failures. From 40f18a3e1aebc7384a026270a9828156b6deaca5 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Wed, 15 Nov 2017 13:48:00 -0800 Subject: [PATCH 0073/1216] prepare for 1.1.2 --- CHANGELOG.md | 2 +- version/version.go | 2 +- website/config.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 940d5fcf6..085510744 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## (UNRELEASED) +## 1.1.2 (November 15, 2017) ### IMRPOVEMENTS: diff --git a/version/version.go b/version/version.go index f3d38291a..c4dfaf8f0 100644 --- a/version/version.go +++ b/version/version.go @@ -14,7 +14,7 @@ const Version = "1.1.2" // A pre-release marker for the version. If this is "" (empty string) // then it means that it is a final release. Otherwise, this is a pre-release // such as "dev" (in development), "beta", "rc1", etc. -const VersionPrerelease = "dev" +const VersionPrerelease = "" func FormattedVersion() string { var versionString bytes.Buffer diff --git a/website/config.rb b/website/config.rb index 93d234cbe..b3a77be6a 100644 --- a/website/config.rb +++ b/website/config.rb @@ -2,7 +2,7 @@ set :base_url, "https://www.packer.io/" activate :hashicorp do |h| h.name = "packer" - h.version = "1.1.1" + h.version = "1.1.2" h.github_slug = "hashicorp/packer" h.website_root = "website" end From 097296792a9cc67451e5065868d11a0efe6af8ec Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Wed, 15 Nov 2017 14:08:58 -0800 Subject: [PATCH 0075/1216] prepare for next version --- version/version.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version/version.go b/version/version.go index c4dfaf8f0..7ab8bb073 100644 --- a/version/version.go +++ b/version/version.go @@ -9,12 +9,12 @@ import ( var GitCommit string // The main version number that is being run at the moment. -const Version = "1.1.2" +const Version = "1.1.3" // A pre-release marker for the version. If this is "" (empty string) // then it means that it is a final release. Otherwise, this is a pre-release // such as "dev" (in development), "beta", "rc1", etc. -const VersionPrerelease = "" +const VersionPrerelease = "dev" func FormattedVersion() string { var versionString bytes.Buffer From 9ee13852836998eff1f3ba9cdf69783041f1628f Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Wed, 15 Nov 2017 14:17:00 -0800 Subject: [PATCH 0076/1216] update changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 085510744..fa0ea5c15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## (UNRELEASED) + +### IMPROVEMENTS: + +### BUG FIXES: + +* builder/qemu: Set default disk size to 40960 MB to prevent boot failures. [GH-5588] + ## 1.1.2 (November 15, 2017) ### IMRPOVEMENTS: From 284ffa25623a1577b7a2c541f8562f5e63c00269 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Wed, 15 Nov 2017 14:19:28 -0800 Subject: [PATCH 0077/1216] spell fix --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa0ea5c15..889ed4d44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ ## 1.1.2 (November 15, 2017) -### IMRPOVEMENTS: +### IMPROVEMENTS: * builder/amazon: Correctly deregister AMIs when `force_deregister` is set. [GH-5525] @@ -267,7 +267,7 @@ * core: Remove logging that shouldn't be there when running commands. [GH-5042] * provisioner/shell: Fix bug where scripts were being run under `sh`. [GH-5043] -### IMRPOVEMENTS: +### IMPROVEMENTS: * provisioner/windows-restart: make it clear that timeouts come from the provisioner, not winrm. [GH-5040] From 20390ff1ecd222cb7ff9ddd8ecafb0447c416987 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Wed, 15 Nov 2017 17:01:53 -0800 Subject: [PATCH 0078/1216] fix vetting for test fail print statements --- builder/hyperv/iso/builder_test.go | 2 +- builder/hyperv/vmcx/builder_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/builder/hyperv/iso/builder_test.go b/builder/hyperv/iso/builder_test.go index ba167413b..383ab5b30 100644 --- a/builder/hyperv/iso/builder_test.go +++ b/builder/hyperv/iso/builder_test.go @@ -514,6 +514,6 @@ func TestUserVariablesInBootCommand(t *testing.T) { ret := step.Run(state) if ret != multistep.ActionContinue { - t.Fatalf("should not have error: %s", ret) + t.Fatalf("should not have error: %#v", ret) } } diff --git a/builder/hyperv/vmcx/builder_test.go b/builder/hyperv/vmcx/builder_test.go index 89cac5878..fb7f35c52 100644 --- a/builder/hyperv/vmcx/builder_test.go +++ b/builder/hyperv/vmcx/builder_test.go @@ -535,6 +535,6 @@ func TestUserVariablesInBootCommand(t *testing.T) { ret := step.Run(state) if ret != multistep.ActionContinue { - t.Fatalf("should not have error: %s", ret) + t.Fatalf("should not have error: %#v", ret) } } From 74a4cc04fed4bccd8f52c6e56f9e3b298b099f8a Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 16 Nov 2017 10:22:12 -0800 Subject: [PATCH 0079/1216] fix regression :( --- common/config.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/common/config.go b/common/config.go index 490553388..cc7eced6a 100644 --- a/common/config.go +++ b/common/config.go @@ -109,6 +109,14 @@ func DownloadableURL(original string) (string, error) { // Make sure it is lowercased url.Scheme = strings.ToLower(url.Scheme) + // This is to work around issue #5927. This can safely be removed once + // we distribute with a version of Go that fixes that bug. + // + // See: https://code.google.com/p/go/issues/detail?id=5927 + if url.Path != "" && url.Path[0] != '/' { + url.Path = "/" + url.Path + } + // Verify that the scheme is something we support in our common downloader. supported := []string{"file", "http", "https"} found := false From 4fb8a27879421803d82e2a5d7d437560a3208d74 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 16 Nov 2017 11:03:10 -0800 Subject: [PATCH 0080/1216] remove the actual offending code --- common/config.go | 8 -------- common/download.go | 4 ---- 2 files changed, 12 deletions(-) diff --git a/common/config.go b/common/config.go index cc7eced6a..490553388 100644 --- a/common/config.go +++ b/common/config.go @@ -109,14 +109,6 @@ func DownloadableURL(original string) (string, error) { // Make sure it is lowercased url.Scheme = strings.ToLower(url.Scheme) - // This is to work around issue #5927. This can safely be removed once - // we distribute with a version of Go that fixes that bug. - // - // See: https://code.google.com/p/go/issues/detail?id=5927 - if url.Path != "" && url.Path[0] != '/' { - url.Path = "/" + url.Path - } - // Verify that the scheme is something we support in our common downloader. supported := []string{"file", "http", "https"} found := false diff --git a/common/download.go b/common/download.go index 86dea34d1..8061f3223 100644 --- a/common/download.go +++ b/common/download.go @@ -130,10 +130,6 @@ func (d *DownloadClient) Get() (string, error) { // locally and we don't make a copy. Normally we would copy or download. log.Printf("[DEBUG] Using local file: %s", finalPath) - // Remove forward slash on absolute Windows file URLs before processing - if runtime.GOOS == "windows" && len(finalPath) > 0 && finalPath[0] == '/' { - finalPath = finalPath[1:] - } // Keep track of the source so we can make sure not to delete this later sourcePath = finalPath if _, err = os.Stat(finalPath); err != nil { From 3c20176dbb066f55a76ceda6d62e505bbc718117 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 16 Nov 2017 11:10:21 -0800 Subject: [PATCH 0081/1216] runtime imported but not used --- common/download.go | 1 - 1 file changed, 1 deletion(-) diff --git a/common/download.go b/common/download.go index 8061f3223..8eaf236fc 100644 --- a/common/download.go +++ b/common/download.go @@ -15,7 +15,6 @@ import ( "net/http" "net/url" "os" - "runtime" ) // DownloadConfig is the configuration given to instantiate a new From 787f08f39bba1b04d9c71801ac2ebe7584ae60c8 Mon Sep 17 00:00:00 2001 From: Christopher Boumenot Date: Thu, 16 Nov 2017 16:31:05 -0800 Subject: [PATCH 0082/1216] azure: add user message to indicate what is being deleted --- builder/azure/arm/step_delete_resource_group.go | 1 + 1 file changed, 1 insertion(+) diff --git a/builder/azure/arm/step_delete_resource_group.go b/builder/azure/arm/step_delete_resource_group.go index 1c299ad7c..ca6631f7a 100644 --- a/builder/azure/arm/step_delete_resource_group.go +++ b/builder/azure/arm/step_delete_resource_group.go @@ -82,6 +82,7 @@ func (s *StepDeleteResourceGroup) deleteResourceGroup(state multistep.StateBag, } return err } else { + s.say("\nThe resource group was created by Packer, deleting ...") _, errChan := s.client.GroupsClient.Delete(resourceGroupName, cancelCh) err = <-errChan From 2684153cb2852bcc9c208d2a23c98ff443ea8079 Mon Sep 17 00:00:00 2001 From: Christopher Boumenot Date: Thu, 16 Nov 2017 16:34:13 -0800 Subject: [PATCH 0083/1216] azure: reject bad configuration faster --- builder/azure/arm/config.go | 4 ++++ builder/azure/arm/config_test.go | 25 ++++++++++++++++++++++ website/source/docs/builders/azure.html.md | 5 +++-- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index fd6bd9edb..7f69d5d10 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -519,6 +519,10 @@ func assertRequiredParametersSet(c *Config, errs *packer.MultiError) { } } + if c.TempResourceGroupName != "" && c.BuildResourceGroupName != "" { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("The settings temp_resource_group_name and build_resource_group_name cannot both be defined. Please define one or neither.")) + } + ///////////////////////////////////////////// // Compute toInt := func(b bool) int { diff --git a/builder/azure/arm/config_test.go b/builder/azure/arm/config_test.go index b7523f655..20954632e 100644 --- a/builder/azure/arm/config_test.go +++ b/builder/azure/arm/config_test.go @@ -899,6 +899,31 @@ func TestConfigShouldAcceptManagedImageStorageAccountTypes(t *testing.T) { } } +func TestConfigShouldRejectTempAndBuildResourceGroupName(t *testing.T) { + config := map[string]interface{}{ + "capture_name_prefix": "ignore", + "capture_container_name": "ignore", + "image_offer": "ignore", + "image_publisher": "ignore", + "image_sku": "ignore", + "location": "ignore", + "storage_account": "ignore", + "resource_group_name": "ignore", + "subscription_id": "ignore", + "communicator": "none", + + // custom may define one or the other, but not both + "temp_resource_group_name": "rgn00", + "build_resource_group_name": "rgn00", + } + + _, _, err := newConfig(config, getPackerConfiguration()) + if err == nil { + t.Fatal("expected config to reject the use of both temp_resource_group_name and build_resource_group_name") + } +} + + func getArmBuilderConfiguration() map[string]string { m := make(map[string]string) for _, v := range requiredConfigValues { diff --git a/website/source/docs/builders/azure.html.md b/website/source/docs/builders/azure.html.md index 8f9d23442..a4068702a 100644 --- a/website/source/docs/builders/azure.html.md +++ b/website/source/docs/builders/azure.html.md @@ -134,8 +134,9 @@ When creating a managed image the following two options are required. assigned. Knowing the resource group and VM name allows one to execute commands to update the VM during a Packer build, e.g. attach a resource disk to the VM. -- `temp_resource_group_name` (string) name assigned to the temporary resource group created during the build. If this value is not set, a random - value will be assigned. Cannot be used together with `build_resource_group_name`. +- `temp_resource_group_name` (string) name assigned to the temporary resource group created during the build. If this + value is not set, a random value will be assigned. This resource group is deleted at the end of the build. Cannot be + used together with `build_resource_group_name`. - `tenant_id` (string) The account identifier with which your `client_id` and `subscription_id` are associated. If not specified, `tenant_id` will be looked up using `subscription_id`. From ee767e55d1ef2751fe5d0ef1ec55776ffcfccd43 Mon Sep 17 00:00:00 2001 From: Christopher Boumenot Date: Thu, 16 Nov 2017 16:36:42 -0800 Subject: [PATCH 0084/1216] azure: change literal to constant --- builder/azure/arm/step_delete_resource_group.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/builder/azure/arm/step_delete_resource_group.go b/builder/azure/arm/step_delete_resource_group.go index ca6631f7a..d47ed7f4c 100644 --- a/builder/azure/arm/step_delete_resource_group.go +++ b/builder/azure/arm/step_delete_resource_group.go @@ -10,6 +10,10 @@ import ( "github.com/mitchellh/multistep" ) +const ( + maxResourcesToDelete = 50 +) + type StepDeleteResourceGroup struct { client *AzureClient delete func(state multistep.StateBag, resourceGroupName string, cancelCh <-chan struct{}) error @@ -34,7 +38,7 @@ func (s *StepDeleteResourceGroup) deleteResourceGroup(state multistep.StateBag, s.say("\nThe resource group was not created by Packer, only deleting individual resources ...") var deploymentName = state.Get(constants.ArmDeploymentName).(string) if deploymentName != "" { - maxResources := int32(50) + maxResources := int32(maxResourcesToDelete) deploymentOperations, err := s.client.DeploymentOperationsClient.List(resourceGroupName, deploymentName, &maxResources) if err != nil { s.say(fmt.Sprintf("Error deleting resources. Please delete them manually.\n\n"+ From 8985bd45ba93d995fe353c0ffcc10dc7e4dd8051 Mon Sep 17 00:00:00 2001 From: Christopher Boumenot Date: Thu, 16 Nov 2017 17:32:35 -0800 Subject: [PATCH 0085/1216] azure: go fmt --- builder/azure/arm/config_test.go | 3 +-- builder/azure/arm/step_delete_resource_group.go | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/builder/azure/arm/config_test.go b/builder/azure/arm/config_test.go index 20954632e..8cbfe8f6e 100644 --- a/builder/azure/arm/config_test.go +++ b/builder/azure/arm/config_test.go @@ -913,7 +913,7 @@ func TestConfigShouldRejectTempAndBuildResourceGroupName(t *testing.T) { "communicator": "none", // custom may define one or the other, but not both - "temp_resource_group_name": "rgn00", + "temp_resource_group_name": "rgn00", "build_resource_group_name": "rgn00", } @@ -923,7 +923,6 @@ func TestConfigShouldRejectTempAndBuildResourceGroupName(t *testing.T) { } } - func getArmBuilderConfiguration() map[string]string { m := make(map[string]string) for _, v := range requiredConfigValues { diff --git a/builder/azure/arm/step_delete_resource_group.go b/builder/azure/arm/step_delete_resource_group.go index d47ed7f4c..147a33349 100644 --- a/builder/azure/arm/step_delete_resource_group.go +++ b/builder/azure/arm/step_delete_resource_group.go @@ -86,7 +86,7 @@ func (s *StepDeleteResourceGroup) deleteResourceGroup(state multistep.StateBag, } return err } else { - s.say("\nThe resource group was created by Packer, deleting ...") + s.say("\nThe resource group was created by Packer, deleting ...") _, errChan := s.client.GroupsClient.Delete(resourceGroupName, cancelCh) err = <-errChan From e9a63d865357d313c7535ccb3a00bbbec9dc1440 Mon Sep 17 00:00:00 2001 From: Carlos Nunez Date: Sat, 18 Nov 2017 15:27:54 -0600 Subject: [PATCH 0086/1216] Add instructions for the Python client. Azure uses the Python client in their Docker image. I've added additional documentation on how to "install" it as well as translations the node.js commands for it. --- .../source/docs/builders/azure-setup.html.md | 105 +++++++++++++++++- 1 file changed, 103 insertions(+), 2 deletions(-) diff --git a/website/source/docs/builders/azure-setup.html.md b/website/source/docs/builders/azure-setup.html.md index 90c1a6e73..86522642d 100644 --- a/website/source/docs/builders/azure-setup.html.md +++ b/website/source/docs/builders/azure-setup.html.md @@ -63,6 +63,14 @@ If you already have node.js installed you can use `npm` to install `azure-cli`: $ npm install -g azure-cli --no-progress ``` +You can also use the Python-based Azure CLI in Docker. It also comes with `jq` pre-installed: + +```shell +$ docker run -it azuresdk/azure-cli-python +``` + +As there are differences between the node.js client and the Python client, we've included commands for the Python client underneath each node.js command. + ## Guided Setup The Packer project includes a [setup script](https://github.com/hashicorp/packer/blob/master/contrib/azure-setup.sh) that can help you setup your account. It uses an interactive bash script to log you into Azure, name your resources, and export your Packer configuration. @@ -80,6 +88,32 @@ $ azure config mode arm $ azure login -u USERNAME ``` +If you're using the Python client: + +```shell +$ az login +# To sign in, use a web browser to open the page https://aka.ms/devicelogin and enter the code CODE_PROVIDED to authenticate +``` + +Once you've completed logging in, you should get a JSON array like the one below: + +```shell +[ + { + "cloudName": "AzureCloud", + "id": "$uuid", + "isDefault": false, + "name": "Pay-As-You-Go", + "state": "Enabled", + "tenantId": "$tenant_uuid", + "user": { + "name": "my_email@anywhere.com", + "type": "user" + } + } +] + +``` Get your account information ``` shell @@ -88,6 +122,11 @@ $ azure account set ACCOUNTNAME $ azure account show --json | jq -r ".[] | .id" ``` +Python: +```shell +$ az account set "$(az account list | jq -r '.[].name')" +``` + -> Throughout this document when you see a command pipe to `jq` you may instead omit `--json` and everything after it, but the output will be more verbose. For example you can simply run `azure account list` instead. This will print out one line that look like this: @@ -107,6 +146,12 @@ $ azure location list $ azure group create -n GROUPNAME -l LOCATION ``` +Python: + +```shell +$ az group create -n GROUPNAME -l LOCATION +``` + Your storage account (below) will need to use the same `GROUPNAME` and `LOCATION`. ### Create a Storage Account @@ -121,9 +166,15 @@ $ azure storage account create \ --kind storage STORAGENAME ``` --> `LRS` is meant as a literal "LRS" and not as a variable. +Python: -Make sure that `GROUPNAME` and `LOCATION` are the same as above. +```shell +$ az storage account create -n STORAGENAME -g GROUPNAME -l LOCATION --sku Standard_LRS +``` + +-> `LRS` and `Standard_LRS` are meant as literal "LRS" or "Standard_LRS" and not as variables. + +Make sure that `GROUPNAME` and `LOCATION` are the same as above. Also, ensure that `GROUPNAME` is less than 24 characters long and contains only lowercase letters and numbers. ### Create an Application @@ -137,6 +188,12 @@ $ azure ad app create \ -p PASSWORD ``` +Python: + +```shell +az ad app create --display-name APPNAME --identifier-uris APPURL --homepage APPURL --password PASSWORD +``` + Password is your `client_secret` and can be anything you like. I recommend using `openssl rand -base64 24`. ### Create a Service Principal @@ -153,6 +210,13 @@ $ azure ad app list --json \ $ azure ad sp create --applicationId APPID ``` +Python: + +```shell +$ id=$(az ad app list | jq -r '.[] | select(.displayName == "Packer") | .appId') +$ az ad sp create --appid "$id" +``` + ### Grant Permissions to Your Application Finally, we will associate the proper permissions with our application's service principal. We're going to assign the `Owner` role to our Packer application and change the scope to manage our whole subscription. (The `Owner` role can be scoped to a specific resource group to further reduce the scope of the account.) This allows Packer to create temporary resource groups for each build. @@ -164,6 +228,13 @@ $ azure role assignment create \ -c /subscriptions/SUBSCRIPTIONID ``` +Python: + +```shell +# NOTE: Trying to assign the role to the service principal by name directly yields a HTTP 400 error. See: https://github.com/Azure/azure-cli/issues/4911 +$ az role assignment create --assignee "$(az ad sp list | jq -r '.[] | select(.displayName == "APPNAME") | .objectId')" --role Owner +``` + There are a lot of pre-defined roles and you can define your own with more granular permissions, though this is out of scope. You can see a list of pre-configured roles via: ``` shell @@ -182,6 +253,12 @@ $ azure account show --json \ | jq ".[] | .id" ``` +Python: + +```shell +$ az account show | jq -r '.id' +``` + Get `client_id` ``` shell @@ -189,6 +266,12 @@ $ azure ad app list --json \ | jq '.[] | select(.displayName | contains("APPNAME")) | .appId' ``` +Python + +```shell +$ az ad app list | jq '.[] | select(.displayName | contains("APPNAME")) | .appId' +``` + Get `client_secret` This cannot be retrieved. If you forgot this, you will have to delete and re-create your service principal and the associated permissions. @@ -199,14 +282,32 @@ Get `object_id` (OSTYpe=Windows only) azure ad sp show -n CLIENT_ID ``` +Python: + +```shell +$ az ad sp show -n CLIENT_ID +``` + Get `resource_group_name` ``` shell $ azure group list ``` +Python: + +```shell +$ az group list | jq '.[] | select(.name=="GROUPNAME") | .id' +``` + Get `storage_account` ``` shell $ azure storage account list ``` + +Python: + +```shell +$ az storage account list | jq '.[] | select(.name=="GROUPNAME") | .id' +``` From a8ff095059c1188e013be9af6a4e64c496e073bd Mon Sep 17 00:00:00 2001 From: Carlos Nunez Date: Sat, 18 Nov 2017 15:44:10 -0600 Subject: [PATCH 0087/1216] Generate a JSON object for Packer This changes adds code that will generate a JSON object containing the things you'll need to make Packer work with Azure. --- .../source/docs/builders/azure-setup.html.md | 46 +++++++------------ 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/website/source/docs/builders/azure-setup.html.md b/website/source/docs/builders/azure-setup.html.md index 86522642d..618e38543 100644 --- a/website/source/docs/builders/azure-setup.html.md +++ b/website/source/docs/builders/azure-setup.html.md @@ -246,6 +246,23 @@ $ azure role list --json \ Now (finally) everything has been setup in Azure. Let's get our configuration keys together: +Python: + +```shell +$ cat < { +> "subscription_id": $(az account show | jq '.id'), +> "client_id": $(az ad app list | jq '.[] | select(.displayName == "Packer") | .appId'), +> "client_secret": "$password", +> "location": "$location", +> "tenant_id": $(az account show | jq '.tenantId') +> "object_id": $(az ad app list | jq '.[] | select(.displayName == "Packer") | .objectId') +> } +> EOF +``` + +node.js: + Get `subscription_id`: ``` shell @@ -253,12 +270,6 @@ $ azure account show --json \ | jq ".[] | .id" ``` -Python: - -```shell -$ az account show | jq -r '.id' -``` - Get `client_id` ``` shell @@ -266,11 +277,6 @@ $ azure ad app list --json \ | jq '.[] | select(.displayName | contains("APPNAME")) | .appId' ``` -Python - -```shell -$ az ad app list | jq '.[] | select(.displayName | contains("APPNAME")) | .appId' -``` Get `client_secret` @@ -282,32 +288,14 @@ Get `object_id` (OSTYpe=Windows only) azure ad sp show -n CLIENT_ID ``` -Python: - -```shell -$ az ad sp show -n CLIENT_ID -``` - Get `resource_group_name` ``` shell $ azure group list ``` -Python: - -```shell -$ az group list | jq '.[] | select(.name=="GROUPNAME") | .id' -``` - Get `storage_account` ``` shell $ azure storage account list ``` - -Python: - -```shell -$ az storage account list | jq '.[] | select(.name=="GROUPNAME") | .id' -``` From a3c94850624b57c75fd99718426416bc9e7a14fd Mon Sep 17 00:00:00 2001 From: Christopher Boumenot Date: Fri, 17 Nov 2017 09:49:23 -0800 Subject: [PATCH 0088/1216] azure: sanity check resource group names --- builder/azure/arm/config.go | 58 ++++++++++++++ builder/azure/arm/config_test.go | 133 +++++++++++++++++++++++++++++++ 2 files changed, 191 insertions(+) diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index 7f69d5d10..b2301db6c 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -38,9 +38,23 @@ const ( DefaultVMSize = "Standard_A1" ) +const ( + // https://docs.microsoft.com/en-us/azure/architecture/best-practices/naming-conventions#naming-rules-and-restrictions + // Regular expressions in Go are not expressive enough, such that the regular expression returned by Azure + // can be used (no backtracking). + // + // -> ^[^_\W][\w-._]{0,79}(? Date: Tue, 21 Nov 2017 21:09:01 +0100 Subject: [PATCH 0089/1216] ansible-local: Add ability to clean staging directory. Signed-off-by: Krzysztof Wilczynski --- provisioner/ansible-local/provisioner.go | 32 +++++++++++++++++-- provisioner/ansible-local/provisioner_test.go | 23 +++++++++++++ .../docs/provisioners/ansible-local.html.md | 4 +++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/provisioner/ansible-local/provisioner.go b/provisioner/ansible-local/provisioner.go index 2f6a873f9..ec1eec041 100644 --- a/provisioner/ansible-local/provisioner.go +++ b/provisioner/ansible-local/provisioner.go @@ -48,6 +48,9 @@ type Config struct { // permissions in this directory. StagingDir string `mapstructure:"staging_directory"` + // If true, staging directory is removed after executing ansible. + CleanStagingDir bool `mapstructure:"clean_staging_directory"` + // The optional inventory file InventoryFile string `mapstructure:"inventory_file"` @@ -260,6 +263,13 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { if err := p.executeAnsible(ui, comm); err != nil { return fmt.Errorf("Error executing Ansible: %s", err) } + + if p.config.CleanStagingDir { + ui.Message("Removing staging directory...") + if err := p.removeDir(ui, comm, p.config.StagingDir); err != nil { + return fmt.Errorf("Error removing staging directory: %s", err) + } + } return nil } @@ -367,15 +377,33 @@ func (p *Provisioner) uploadFile(ui packer.Ui, comm packer.Communicator, dst, sr } func (p *Provisioner) createDir(ui packer.Ui, comm packer.Communicator, dir string) error { - ui.Message(fmt.Sprintf("Creating directory: %s", dir)) cmd := &packer.RemoteCmd{ Command: fmt.Sprintf("mkdir -p '%s'", dir), } + + ui.Message(fmt.Sprintf("Creating directory: %s", dir)) if err := cmd.StartWithUi(comm, ui); err != nil { return err } + if cmd.ExitStatus != 0 { - return fmt.Errorf("Non-zero exit status.") + return fmt.Errorf("Non-zero exit status. See output above for more information.") + } + return nil +} + +func (p *Provisioner) removeDir(ui packer.Ui, comm packer.Communicator, dir string) error { + cmd := &packer.RemoteCmd{ + Command: fmt.Sprintf("rm -rf '%s'", dir), + } + + ui.Message(fmt.Sprintf("Removing directory: %s", dir)) + if err := cmd.StartWithUi(comm, ui); err != nil { + return err + } + + if cmd.ExitStatus != 0 { + return fmt.Errorf("Non-zero exit status. See output above for more information.") } return nil } diff --git a/provisioner/ansible-local/provisioner_test.go b/provisioner/ansible-local/provisioner_test.go index 2195b7107..d96ed113f 100644 --- a/provisioner/ansible-local/provisioner_test.go +++ b/provisioner/ansible-local/provisioner_test.go @@ -188,3 +188,26 @@ func TestProvisionerPrepare_Dirs(t *testing.T) { t.Fatalf("err: %s", err) } } + +func TestProvisionerPrepare_CleanStagingDir(t *testing.T) { + var p Provisioner + config := testConfig() + + playbook_file, err := ioutil.TempFile("", "playbook") + if err != nil { + t.Fatalf("err: %s", err) + } + defer os.Remove(playbook_file.Name()) + + config["playbook_file"] = playbook_file.Name() + config["clean_staging_directory"] = true + + err = p.Prepare(config) + if err != nil { + t.Fatalf("err: %s", err) + } + + if !p.config.CleanStagingDir { + t.Fatalf("expected clean_staging_directory to be set") + } +} diff --git a/website/source/docs/provisioners/ansible-local.html.md b/website/source/docs/provisioners/ansible-local.html.md index 92d46fb3d..5277c8a89 100644 --- a/website/source/docs/provisioners/ansible-local.html.md +++ b/website/source/docs/provisioners/ansible-local.html.md @@ -140,6 +140,10 @@ chi-appservers are not correct, use a shell provisioner prior to this to configure it properly. +- `clean_staging_directory` (boolean) - If set to `true`, the content of + the `staging_directory` will be removed after executing ansible. By + default, this is set to `false`. + ## Default Extra Variables In addition to being able to specify extra arguments using the From 7d80e37c144b83312dc5021c4cb742d5ef39074d Mon Sep 17 00:00:00 2001 From: Krzysztof Wilczynski Date: Tue, 21 Nov 2017 21:59:27 +0100 Subject: [PATCH 0090/1216] Add new `packer_version` function. Signed-off-by: Krzysztof Wilczynski --- template/interpolate/funcs.go | 26 +++++++++++++------- template/interpolate/funcs_test.go | 21 ++++++++++++++++ website/source/docs/templates/engine.html.md | 1 + 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/template/interpolate/funcs.go b/template/interpolate/funcs.go index ee5e21ccc..924c13d73 100644 --- a/template/interpolate/funcs.go +++ b/template/interpolate/funcs.go @@ -11,6 +11,7 @@ import ( "time" "github.com/hashicorp/packer/common/uuid" + "github.com/hashicorp/packer/version" ) // InitTime is the UTC time when this package was initialized. It is @@ -24,15 +25,16 @@ func init() { // Funcs are the interpolation funcs that are available within interpolations. var FuncGens = map[string]FuncGenerator{ - "build_name": funcGenBuildName, - "build_type": funcGenBuildType, - "env": funcGenEnv, - "isotime": funcGenIsotime, - "pwd": funcGenPwd, - "template_dir": funcGenTemplateDir, - "timestamp": funcGenTimestamp, - "uuid": funcGenUuid, - "user": funcGenUser, + "build_name": funcGenBuildName, + "build_type": funcGenBuildType, + "env": funcGenEnv, + "isotime": funcGenIsotime, + "pwd": funcGenPwd, + "template_dir": funcGenTemplateDir, + "timestamp": funcGenTimestamp, + "uuid": funcGenUuid, + "user": funcGenUser, + "packer_version": funcGenPackerVersion, "upper": funcGenPrimitive(strings.ToUpper), "lower": funcGenPrimitive(strings.ToLower), @@ -152,3 +154,9 @@ func funcGenUuid(ctx *Context) interface{} { return uuid.TimeOrderedUUID() } } + +func funcGenPackerVersion(ctx *Context) interface{} { + return func() string { + return version.FormattedVersion() + } +} diff --git a/template/interpolate/funcs_test.go b/template/interpolate/funcs_test.go index ab8ee36a4..eca121025 100644 --- a/template/interpolate/funcs_test.go +++ b/template/interpolate/funcs_test.go @@ -4,8 +4,11 @@ import ( "os" "path/filepath" "strconv" + "strings" "testing" "time" + + "github.com/hashicorp/packer/version" ) func TestFuncBuildName(t *testing.T) { @@ -257,3 +260,21 @@ func TestFuncUser(t *testing.T) { } } } + +func TestFuncPackerVersion(t *testing.T) { + template := `{{packer_version}}` + + ctx := &Context{} + i := &I{Value: template} + + result, err := i.Render(ctx) + if err != nil { + t.Fatalf("Input: %s\n\nerr: %s", template, err) + } + + // Only match the X.Y.Z portion of the whole version string. + if !strings.HasPrefix(result, version.Version) { + t.Fatalf("Expected input to include: %s\n\nGot: %s", + version.Version, result) + } +} diff --git a/website/source/docs/templates/engine.html.md b/website/source/docs/templates/engine.html.md index 3b7c60b24..065e6001b 100644 --- a/website/source/docs/templates/engine.html.md +++ b/website/source/docs/templates/engine.html.md @@ -43,6 +43,7 @@ Here is a full list of the available functions for reference. - `uuid` - Returns a random UUID. - `upper` - Uppercases the string. - `user` - Specifies a user variable. +- `packer_version` - Returns Packer version. #### Specific to Amazon builders: From 707ec675b2d64cf16cade5e16c89b5d32fcf61f1 Mon Sep 17 00:00:00 2001 From: John Davies-Colley Date: Wed, 22 Nov 2017 15:49:38 +1300 Subject: [PATCH 0091/1216] =?UTF-8?q?ssh=20interface=20for=20amazon=20buil?= =?UTF-8?q?ders=20=F0=9F=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- builder/amazon/common/run_config.go | 6 +++ builder/amazon/common/ssh.go | 31 ++++++++++--- builder/amazon/common/ssh_test.go | 60 +++++++++++++++----------- builder/amazon/ebs/builder.go | 2 +- builder/amazon/ebssurrogate/builder.go | 2 +- builder/amazon/ebsvolume/builder.go | 2 +- builder/amazon/instance/builder.go | 2 +- 7 files changed, 71 insertions(+), 34 deletions(-) diff --git a/builder/amazon/common/run_config.go b/builder/amazon/common/run_config.go index 73970e59e..8b605392b 100644 --- a/builder/amazon/common/run_config.go +++ b/builder/amazon/common/run_config.go @@ -54,6 +54,7 @@ type RunConfig struct { Comm communicator.Config `mapstructure:",squash"` SSHKeyPairName string `mapstructure:"ssh_keypair_name"` SSHPrivateIp bool `mapstructure:"ssh_private_ip"` + SSHInterface string `mapstructure:"ssh_interface` } func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { @@ -75,6 +76,11 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { c.RunTags = make(map[string]string) } + // Legacy configurable + if c.SSHPrivateIp { + c.SSHInterface = "private_ip" + } + // Validation errs := c.Comm.Prepare(ctx) if c.SSHKeyPairName != "" { diff --git a/builder/amazon/common/ssh.go b/builder/amazon/common/ssh.go index c451bbf96..34fb64ce1 100644 --- a/builder/amazon/common/ssh.go +++ b/builder/amazon/common/ssh.go @@ -25,21 +25,40 @@ var ( // SSHHost returns a function that can be given to the SSH communicator // for determining the SSH address based on the instance DNS name. -func SSHHost(e ec2Describer, private bool) func(multistep.StateBag) (string, error) { +func SSHHost(e ec2Describer, sshInterface string) func(multistep.StateBag) (string, error) { return func(state multistep.StateBag) (string, error) { const tries = 2 // <= with current structure to check result of describing `tries` times for j := 0; j <= tries; j++ { var host string i := state.Get("instance").(*ec2.Instance) - if i.VpcId != nil && *i.VpcId != "" { - if i.PublicIpAddress != nil && *i.PublicIpAddress != "" && !private { + if sshInterface != "" { + switch sshInterface { + case "public_ip": + if i.PublicIpAddress != nil { + host = *i.PublicIpAddress + } + case "private_ip": + if i.PrivateIpAddress != nil { + host = *i.PrivateIpAddress + } + case "public_dns": + if i.PublicDnsName != nil { + host = *i.PublicDnsName + } + case "private_dns": + if i.PrivateDnsName != nil { + host = *i.PrivateDnsName + } + default: + return "", fmt.Errorf("unknown interface type: %s", sshInterface) + } + } else if i.VpcId != nil && *i.VpcId != "" { + if i.PublicIpAddress != nil && *i.PublicIpAddress != "" { host = *i.PublicIpAddress } else if i.PrivateIpAddress != nil && *i.PrivateIpAddress != "" { host = *i.PrivateIpAddress } - } else if private && i.PrivateIpAddress != nil && *i.PrivateIpAddress != "" { - host = *i.PrivateIpAddress } else if i.PublicDnsName != nil && *i.PublicDnsName != "" { host = *i.PublicDnsName } @@ -63,7 +82,7 @@ func SSHHost(e ec2Describer, private bool) func(multistep.StateBag) (string, err time.Sleep(sshHostSleepDuration) } - return "", errors.New("couldn't determine IP address for instance") + return "", errors.New("couldn't determine address for instance") } } diff --git a/builder/amazon/common/ssh_test.go b/builder/amazon/common/ssh_test.go index 9f1d4a53e..e39088e67 100644 --- a/builder/amazon/common/ssh_test.go +++ b/builder/amazon/common/ssh_test.go @@ -9,9 +9,10 @@ import ( ) const ( - privateIP = "10.0.0.1" - publicIP = "192.168.1.1" - publicDNS = "public.dns.test" + privateIP = "10.0.0.1" + publicIP = "192.168.1.1" + privateDNS = "private.dns.test" + publicDNS = "public.dns.test" ) func TestSSHHost(t *testing.T) { @@ -20,44 +21,54 @@ func TestSSHHost(t *testing.T) { sshHostSleepDuration = 0 var cases = []struct { - allowTries int - vpcId string - private bool + allowTries int + vpcId string + sshInterface string ok bool wantHost string }{ - {1, "", false, true, publicDNS}, - {1, "", true, true, privateIP}, - {1, "vpc-id", false, true, publicIP}, - {1, "vpc-id", true, true, privateIP}, - {2, "", false, true, publicDNS}, - {2, "", true, true, privateIP}, - {2, "vpc-id", false, true, publicIP}, - {2, "vpc-id", true, true, privateIP}, - {3, "", false, false, ""}, - {3, "", true, false, ""}, - {3, "vpc-id", false, false, ""}, - {3, "vpc-id", true, false, ""}, + {1, "", "", true, publicDNS}, + {1, "", "private_ip", true, privateIP}, + {1, "vpc-id", "", true, publicIP}, + {1, "vpc-id", "private_ip", true, privateIP}, + {1, "vpc-id", "private_dns", true, privateDNS}, + {1, "vpc-id", "public_dns", true, publicDNS}, + {1, "vpc-id", "public_ip", true, publicIP}, + {2, "", "", true, publicDNS}, + {2, "", "private_ip", true, privateIP}, + {2, "vpc-id", "", true, publicIP}, + {2, "vpc-id", "private_ip", true, privateIP}, + {2, "vpc-id", "private_dns", true, privateDNS}, + {2, "vpc-id", "public_dns", true, publicDNS}, + {2, "vpc-id", "public_ip", true, publicIP}, + {3, "", "", false, ""}, + {3, "", "private_ip", false, ""}, + {3, "vpc-id", "", false, ""}, + {3, "vpc-id", "private_ip", false, ""}, + {3, "vpc-id", "private_dns", false, ""}, + {3, "vpc-id", "public_dns", false, ""}, + {3, "vpc-id", "public_ip", false, ""}, } for _, c := range cases { - testSSHHost(t, c.allowTries, c.vpcId, c.private, c.ok, c.wantHost) + testSSHHost(t, c.allowTries, c.vpcId, c.sshInterface, c.ok, c.wantHost) } } -func testSSHHost(t *testing.T, allowTries int, vpcId string, private, ok bool, wantHost string) { - t.Logf("allowTries=%d vpcId=%s private=%t ok=%t wantHost=%q", allowTries, vpcId, private, ok, wantHost) +func testSSHHost(t *testing.T, allowTries int, vpcId string, sshInterface string, ok bool, wantHost string) { + t.Logf("allowTries=%d vpcId=%s sshInterface=%s ok=%t wantHost=%q", allowTries, vpcId, sshInterface, ok, wantHost) e := &fakeEC2Describer{ allowTries: allowTries, vpcId: vpcId, privateIP: privateIP, publicIP: publicIP, + privateDNS: privateDNS, publicDNS: publicDNS, } - f := SSHHost(e, private) + f := SSHHost(e, sshInterface) st := &multistep.BasicStateBag{} st.Put("instance", &ec2.Instance{ InstanceId: aws.String("instance-id"), @@ -85,8 +96,8 @@ type fakeEC2Describer struct { allowTries int tries int - vpcId string - privateIP, publicIP, publicDNS string + vpcId string + privateIP, publicIP, privateDNS, publicDNS string } func (d *fakeEC2Describer) DescribeInstances(in *ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error) { @@ -104,6 +115,7 @@ func (d *fakeEC2Describer) DescribeInstances(in *ec2.DescribeInstancesInput) (*e instance.PublicIpAddress = aws.String(d.publicIP) instance.PrivateIpAddress = aws.String(d.privateIP) instance.PublicDnsName = aws.String(d.publicDNS) + instance.PrivateDnsName = aws.String(d.privateDNS) } out := &ec2.DescribeInstancesOutput{ diff --git a/builder/amazon/ebs/builder.go b/builder/amazon/ebs/builder.go index 1cc346472..56757b826 100644 --- a/builder/amazon/ebs/builder.go +++ b/builder/amazon/ebs/builder.go @@ -193,7 +193,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Config: &b.config.RunConfig.Comm, Host: awscommon.SSHHost( ec2conn, - b.config.SSHPrivateIp), + b.config.SSHInterface), SSHConfig: awscommon.SSHConfig( b.config.RunConfig.Comm.SSHAgentAuth, b.config.RunConfig.Comm.SSHUsername, diff --git a/builder/amazon/ebssurrogate/builder.go b/builder/amazon/ebssurrogate/builder.go index 16e8367da..fec997816 100644 --- a/builder/amazon/ebssurrogate/builder.go +++ b/builder/amazon/ebssurrogate/builder.go @@ -204,7 +204,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Config: &b.config.RunConfig.Comm, Host: awscommon.SSHHost( ec2conn, - b.config.SSHPrivateIp), + b.config.SSHInterface), SSHConfig: awscommon.SSHConfig( b.config.RunConfig.Comm.SSHAgentAuth, b.config.RunConfig.Comm.SSHUsername, diff --git a/builder/amazon/ebsvolume/builder.go b/builder/amazon/ebsvolume/builder.go index ea3f74b61..6bd8927ee 100644 --- a/builder/amazon/ebsvolume/builder.go +++ b/builder/amazon/ebsvolume/builder.go @@ -181,7 +181,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Config: &b.config.RunConfig.Comm, Host: awscommon.SSHHost( ec2conn, - b.config.SSHPrivateIp), + b.config.SSHInterface), SSHConfig: awscommon.SSHConfig( b.config.RunConfig.Comm.SSHAgentAuth, b.config.RunConfig.Comm.SSHUsername, diff --git a/builder/amazon/instance/builder.go b/builder/amazon/instance/builder.go index 7cee44c09..220ee7b10 100644 --- a/builder/amazon/instance/builder.go +++ b/builder/amazon/instance/builder.go @@ -268,7 +268,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Config: &b.config.RunConfig.Comm, Host: awscommon.SSHHost( ec2conn, - b.config.SSHPrivateIp), + b.config.SSHInterface), SSHConfig: awscommon.SSHConfig( b.config.RunConfig.Comm.SSHAgentAuth, b.config.RunConfig.Comm.SSHUsername, From 7b03613649b4753a0aa281bcb169a1e296bc856b Mon Sep 17 00:00:00 2001 From: Henry Muru Paenga Date: Wed, 22 Nov 2017 15:52:22 +1300 Subject: [PATCH 0092/1216] Update docs with ssh_interface --- website/source/docs/builders/amazon-ebs.html.md | 13 ++++++++++--- .../docs/builders/amazon-ebssurrogate.html.md | 11 +++++++++-- .../source/docs/builders/amazon-ebsvolume.html.md | 9 ++++++++- .../source/docs/builders/amazon-instance.html.md | 11 +++++++++-- 4 files changed, 36 insertions(+), 8 deletions(-) diff --git a/website/source/docs/builders/amazon-ebs.html.md b/website/source/docs/builders/amazon-ebs.html.md index c11525dd0..4385a754a 100644 --- a/website/source/docs/builders/amazon-ebs.html.md +++ b/website/source/docs/builders/amazon-ebs.html.md @@ -237,7 +237,7 @@ builder. - `temporary_security_group_source_cidr` (string) - An IPv4 CIDR block to be authorized access to the instance, when packer is creating a temporary security group. - The default is `0.0.0.0/0` (ie, allow any IPv4 source). This is only used + The default is `0.0.0.0/0` (ie, allow any IPv4 source). This is only used when `security_group_id` or `security_group_ids` is not specified. - `shutdown_behavior` (string) - Automatically terminate instances on shutdown @@ -328,8 +328,15 @@ builder. in AWS with the source instance, set the `ssh_keypair_name` field to the name of the key pair. -- `ssh_private_ip` (boolean) - If true, then SSH will always use the private - IP if available. Also works for WinRM. +- `ssh_private_ip` (boolean) - If `true`, then SSH will always use the private + IP if available. Also works for WinRM. Overrides `ssh_interface`. + +- `ssh_interface` (string) - One of `PublicIpAddress`, `PrivateIpAddress`, + `PublicDnsName` or `PrivateDnsName`. If set, either the public IP address, + private IP address, public DNS name or private DNS name will used as the host for SSH. + The default behaviour if inside a VPC is to use the public IP address if available, + otherwise the private IP address will be used. If not in a VPC the public DNS name + will be used. - `subnet_id` (string) - If using VPC, the ID of the subnet, such as `subnet-12345def`, where Packer will launch the EC2 instance. This field is diff --git a/website/source/docs/builders/amazon-ebssurrogate.html.md b/website/source/docs/builders/amazon-ebssurrogate.html.md index c044fc350..30b19ad6b 100644 --- a/website/source/docs/builders/amazon-ebssurrogate.html.md +++ b/website/source/docs/builders/amazon-ebssurrogate.html.md @@ -321,8 +321,15 @@ builder. in AWS with the source instance, set the `ssh_keypair_name` field to the name of the key pair. -- `ssh_private_ip` (boolean) - If true, then SSH will always use the private - IP if available. +- `ssh_private_ip` (boolean) - If `true`, then SSH will always use the private + IP if available. Also works for WinRM. Overrides `ssh_interface`. + +- `ssh_interface` (string) - One of `PublicIpAddress`, `PrivateIpAddress`, + `PublicDnsName` or `PrivateDnsName`. If set, either the public IP address, + private IP address, public DNS name or private DNS name will used as the host for SSH. + The default behaviour if inside a VPC is to use the public IP address if available, + otherwise the private IP address will be used. If not in a VPC the public DNS name + will be used. - `subnet_id` (string) - If using VPC, the ID of the subnet, such as `subnet-12345def`, where Packer will launch the EC2 instance. This field is diff --git a/website/source/docs/builders/amazon-ebsvolume.html.md b/website/source/docs/builders/amazon-ebsvolume.html.md index 125064626..cc6cf39de 100644 --- a/website/source/docs/builders/amazon-ebsvolume.html.md +++ b/website/source/docs/builders/amazon-ebsvolume.html.md @@ -226,7 +226,14 @@ builder. must be specified with this. - `ssh_private_ip` (boolean) - If `true`, then SSH will always use the private - IP if available. Also works for WinRM. + IP if available. Also works for WinRM. Overrides `ssh_interface`. + +- `ssh_interface` (string) - One of `PublicIpAddress`, `PrivateIpAddress`, + `PublicDnsName` or `PrivateDnsName`. If set, either the public IP address, + private IP address, public DNS name or private DNS name will used as the host for SSH. + The default behaviour if inside a VPC is to use the public IP address if available, + otherwise the private IP address will be used. If not in a VPC the public DNS name + will be used. - `subnet_id` (string) - If using VPC, the ID of the subnet, such as `subnet-12345def`, where Packer will launch the EC2 instance. This field is diff --git a/website/source/docs/builders/amazon-instance.html.md b/website/source/docs/builders/amazon-instance.html.md index 95a1bc007..93975fb88 100644 --- a/website/source/docs/builders/amazon-instance.html.md +++ b/website/source/docs/builders/amazon-instance.html.md @@ -329,8 +329,15 @@ builder. in AWS with the source instance, set the `ssh_keypair_name` field to the name of the key pair. -- `ssh_private_ip` (boolean) - If true, then SSH will always use the private - IP if available. Also works for WinRM. +- `ssh_private_ip` (boolean) - If `true`, then SSH will always use the private + IP if available. Also works for WinRM. Overrides `ssh_interface`. + +- `ssh_interface` (string) - One of `PublicIpAddress`, `PrivateIpAddress`, + `PublicDnsName` or `PrivateDnsName`. If set, either the public IP address, + private IP address, public DNS name or private DNS name will used as the host for SSH. + The default behaviour if inside a VPC is to use the public IP address if available, + otherwise the private IP address will be used. If not in a VPC the public DNS name + will be used. - `subnet_id` (string) - If using VPC, the ID of the subnet, such as `subnet-12345def`, where Packer will launch the EC2 instance. This field is From 75320440ec2e904faa885c1769bdd3343d04d436 Mon Sep 17 00:00:00 2001 From: John Davies-Colley Date: Wed, 22 Nov 2017 15:59:15 +1300 Subject: [PATCH 0093/1216] =?UTF-8?q?adding=20missing=20quote=20?= =?UTF-8?q?=F0=9F=99=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- builder/amazon/common/run_config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/amazon/common/run_config.go b/builder/amazon/common/run_config.go index 8b605392b..48f8337f8 100644 --- a/builder/amazon/common/run_config.go +++ b/builder/amazon/common/run_config.go @@ -54,7 +54,7 @@ type RunConfig struct { Comm communicator.Config `mapstructure:",squash"` SSHKeyPairName string `mapstructure:"ssh_keypair_name"` SSHPrivateIp bool `mapstructure:"ssh_private_ip"` - SSHInterface string `mapstructure:"ssh_interface` + SSHInterface string `mapstructure:"ssh_interface"` } func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { From d4f37ab5f69166eea0494c31fa731475a738b2b3 Mon Sep 17 00:00:00 2001 From: John Davies-Colley Date: Wed, 22 Nov 2017 17:15:46 +1300 Subject: [PATCH 0094/1216] =?UTF-8?q?changing=20config=20varibles=20name?= =?UTF-8?q?=20in=20docs=20to=20match=20names=20in=20code=20=E2=9A=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- website/source/docs/builders/amazon-ebs.html.md | 4 ++-- website/source/docs/builders/amazon-ebssurrogate.html.md | 4 ++-- website/source/docs/builders/amazon-ebsvolume.html.md | 4 ++-- website/source/docs/builders/amazon-instance.html.md | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/website/source/docs/builders/amazon-ebs.html.md b/website/source/docs/builders/amazon-ebs.html.md index 4385a754a..4d8c56a1e 100644 --- a/website/source/docs/builders/amazon-ebs.html.md +++ b/website/source/docs/builders/amazon-ebs.html.md @@ -331,8 +331,8 @@ builder. - `ssh_private_ip` (boolean) - If `true`, then SSH will always use the private IP if available. Also works for WinRM. Overrides `ssh_interface`. -- `ssh_interface` (string) - One of `PublicIpAddress`, `PrivateIpAddress`, - `PublicDnsName` or `PrivateDnsName`. If set, either the public IP address, +- `ssh_interface` (string) - One of `public_ip`, `private_ip`, + `public_dns` or `private_dns`. If set, either the public IP address, private IP address, public DNS name or private DNS name will used as the host for SSH. The default behaviour if inside a VPC is to use the public IP address if available, otherwise the private IP address will be used. If not in a VPC the public DNS name diff --git a/website/source/docs/builders/amazon-ebssurrogate.html.md b/website/source/docs/builders/amazon-ebssurrogate.html.md index 30b19ad6b..acd976538 100644 --- a/website/source/docs/builders/amazon-ebssurrogate.html.md +++ b/website/source/docs/builders/amazon-ebssurrogate.html.md @@ -324,8 +324,8 @@ builder. - `ssh_private_ip` (boolean) - If `true`, then SSH will always use the private IP if available. Also works for WinRM. Overrides `ssh_interface`. -- `ssh_interface` (string) - One of `PublicIpAddress`, `PrivateIpAddress`, - `PublicDnsName` or `PrivateDnsName`. If set, either the public IP address, +- `ssh_interface` (string) - One of `public_ip`, `private_ip`, + `public_dns` or `private_dns`. If set, either the public IP address, private IP address, public DNS name or private DNS name will used as the host for SSH. The default behaviour if inside a VPC is to use the public IP address if available, otherwise the private IP address will be used. If not in a VPC the public DNS name diff --git a/website/source/docs/builders/amazon-ebsvolume.html.md b/website/source/docs/builders/amazon-ebsvolume.html.md index cc6cf39de..b022c9e81 100644 --- a/website/source/docs/builders/amazon-ebsvolume.html.md +++ b/website/source/docs/builders/amazon-ebsvolume.html.md @@ -228,8 +228,8 @@ builder. - `ssh_private_ip` (boolean) - If `true`, then SSH will always use the private IP if available. Also works for WinRM. Overrides `ssh_interface`. -- `ssh_interface` (string) - One of `PublicIpAddress`, `PrivateIpAddress`, - `PublicDnsName` or `PrivateDnsName`. If set, either the public IP address, +- `ssh_interface` (string) - One of `public_ip`, `private_ip`, + `public_dns` or `private_dns`. If set, either the public IP address, private IP address, public DNS name or private DNS name will used as the host for SSH. The default behaviour if inside a VPC is to use the public IP address if available, otherwise the private IP address will be used. If not in a VPC the public DNS name diff --git a/website/source/docs/builders/amazon-instance.html.md b/website/source/docs/builders/amazon-instance.html.md index 93975fb88..8636b6a49 100644 --- a/website/source/docs/builders/amazon-instance.html.md +++ b/website/source/docs/builders/amazon-instance.html.md @@ -332,8 +332,8 @@ builder. - `ssh_private_ip` (boolean) - If `true`, then SSH will always use the private IP if available. Also works for WinRM. Overrides `ssh_interface`. -- `ssh_interface` (string) - One of `PublicIpAddress`, `PrivateIpAddress`, - `PublicDnsName` or `PrivateDnsName`. If set, either the public IP address, +- `ssh_interface` (string) - One of `public_ip`, `private_ip`, + `public_dns` or `private_dns`. If set, either the public IP address, private IP address, public DNS name or private DNS name will used as the host for SSH. The default behaviour if inside a VPC is to use the public IP address if available, otherwise the private IP address will be used. If not in a VPC the public DNS name From 0dae5bc8bdaf5d510276e341889445d1e8e0d697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilon=20Sj=C3=B6gren?= Date: Wed, 22 Nov 2017 09:13:47 +0100 Subject: [PATCH 0095/1216] Should state that file provisioner requires source at execution time --- website/source/docs/provisioners/file.html.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/website/source/docs/provisioners/file.html.md b/website/source/docs/provisioners/file.html.md index da78d4b61..26e2fc239 100644 --- a/website/source/docs/provisioners/file.html.md +++ b/website/source/docs/provisioners/file.html.md @@ -47,6 +47,9 @@ The available configuration options are listed below. All elements are required. - `direction` (string) - The direction of the file transfer. This defaults to "upload". If it is set to "download" then the file "source" in the machine will be downloaded locally to "destination" + +The source file must exist before running Packer, this means that creating the +file with a provisioner does not work. ## Directory Uploads @@ -90,6 +93,9 @@ drwxr-xr-x 3 mwhooker staff 102 Jan 27 17:10 a lrwxr-xr-x 1 mwhooker staff 1 Jan 27 17:10 b -> a -rw-r--r-- 1 mwhooker staff 0 Jan 27 17:10 file1 lrwxr-xr-x 1 mwhooker staff 5 Jan 27 17:10 file1link -> file1 +$ ls -l toupload +total 0 +-rw-r--r-- 1 mwhooker staff 0 Jan 27 17:10 files.tar ``` ``` json @@ -97,7 +103,7 @@ lrwxr-xr-x 1 mwhooker staff 5 Jan 27 17:10 file1link -> file1 "provisioners": [ { "type": "shell-local", - "command": "mkdir -p toupload; tar cf toupload/files.tar files" + "command": "tar cf toupload/files.tar files" }, { "destination": "/tmp/", From b7e3f37b44bf4738c4b749c5c48316f9e16915fd Mon Sep 17 00:00:00 2001 From: Vijaya Bhaskar Reddy Kondreddi Date: Thu, 12 Oct 2017 16:05:31 +0530 Subject: [PATCH 0096/1216] Add support for differential disk --- builder/hyperv/common/driver.go | 2 +- builder/hyperv/common/driver_ps_4.go | 4 ++-- builder/hyperv/common/step_create_vm.go | 3 ++- builder/hyperv/iso/builder.go | 4 ++++ builder/hyperv/vmcx/builder.go | 3 +++ common/powershell/hyperv/hyperv.go | 23 ++++++++++++++++------- 6 files changed, 28 insertions(+), 11 deletions(-) diff --git a/builder/hyperv/common/driver.go b/builder/hyperv/common/driver.go index 3e44b0ece..2a2f0b810 100644 --- a/builder/hyperv/common/driver.go +++ b/builder/hyperv/common/driver.go @@ -64,7 +64,7 @@ type Driver interface { DeleteVirtualSwitch(string) error - CreateVirtualMachine(string, string, string, string, int64, int64, string, uint) error + CreateVirtualMachine(string, string, string, string, int64, int64, string, uint, bool) error AddVirtualMachineHardDrive(string, string, string, int64, string) error diff --git a/builder/hyperv/common/driver_ps_4.go b/builder/hyperv/common/driver_ps_4.go index 82b4c3f42..853aa7021 100644 --- a/builder/hyperv/common/driver_ps_4.go +++ b/builder/hyperv/common/driver_ps_4.go @@ -174,8 +174,8 @@ func (d *HypervPS4Driver) AddVirtualMachineHardDrive(vmName string, vhdFile stri return hyperv.AddVirtualMachineHardDiskDrive(vmName, vhdFile, vhdName, vhdSizeBytes, controllerType) } -func (d *HypervPS4Driver) CreateVirtualMachine(vmName string, path string, harddrivePath string, vhdPath string, ram int64, diskSize int64, switchName string, generation uint) error { - return hyperv.CreateVirtualMachine(vmName, path, harddrivePath, vhdPath, ram, diskSize, switchName, generation) +func (d *HypervPS4Driver) CreateVirtualMachine(vmName string, path string, harddrivePath string, vhdPath string, ram int64, diskSize int64, switchName string, generation uint, diffDisks bool) error { + return hyperv.CreateVirtualMachine(vmName, path, harddrivePath, vhdPath, ram, diskSize, switchName, generation, diffDisks) } func (d *HypervPS4Driver) CloneVirtualMachine(cloneFromVmxcPath string, cloneFromVmName string, cloneFromSnapshotName string, cloneAllSnapshots bool, vmName string, path string, harddrivePath string, ram int64, switchName string) error { diff --git a/builder/hyperv/common/step_create_vm.go b/builder/hyperv/common/step_create_vm.go index a1d7f0d05..02171d9c6 100644 --- a/builder/hyperv/common/step_create_vm.go +++ b/builder/hyperv/common/step_create_vm.go @@ -27,6 +27,7 @@ type StepCreateVM struct { EnableSecureBoot bool EnableVirtualizationExtensions bool AdditionalDiskSize []uint + DifferencingDisk bool } func (s *StepCreateVM) Run(state multistep.StateBag) multistep.StepAction { @@ -54,7 +55,7 @@ func (s *StepCreateVM) Run(state multistep.StateBag) multistep.StepAction { ramSize := int64(s.RamSize * 1024 * 1024) diskSize := int64(s.DiskSize * 1024 * 1024) - err := driver.CreateVirtualMachine(s.VMName, path, harddrivePath, vhdPath, ramSize, diskSize, s.SwitchName, s.Generation) + err := driver.CreateVirtualMachine(s.VMName, path, harddrivePath, vhdPath, ramSize, diskSize, s.SwitchName, s.Generation, s.DifferencingDisk) if err != nil { err := fmt.Errorf("Error creating virtual machine: %s", err) state.Put("error", err) diff --git a/builder/hyperv/iso/builder.go b/builder/hyperv/iso/builder.go index b3c1e1f61..849cbcacf 100644 --- a/builder/hyperv/iso/builder.go +++ b/builder/hyperv/iso/builder.go @@ -94,6 +94,9 @@ type Config struct { SkipCompaction bool `mapstructure:"skip_compaction"` + // Use differencing disk + DifferencingDisk bool `mapstructure:"differencing_disk"` + ctx interpolate.Context } @@ -353,6 +356,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe EnableSecureBoot: b.config.EnableSecureBoot, EnableVirtualizationExtensions: b.config.EnableVirtualizationExtensions, AdditionalDiskSize: b.config.AdditionalDiskSize, + DifferencingDisk: b.config.DifferencingDisk, }, &hypervcommon.StepEnableIntegrationService{}, diff --git a/builder/hyperv/vmcx/builder.go b/builder/hyperv/vmcx/builder.go index 2a9459ace..6a976b3df 100644 --- a/builder/hyperv/vmcx/builder.go +++ b/builder/hyperv/vmcx/builder.go @@ -76,6 +76,9 @@ type Config struct { // By default this is "packer-BUILDNAME", where "BUILDNAME" is the name of the build. VMName string `mapstructure:"vm_name"` + // Use differencing disk + DifferencingDisk bool `mapstructure:"differencing_disk"` + BootCommand []string `mapstructure:"boot_command"` SwitchName string `mapstructure:"switch_name"` SwitchVlanId string `mapstructure:"switch_vlan_id"` diff --git a/common/powershell/hyperv/hyperv.go b/common/powershell/hyperv/hyperv.go index b793e171a..25073d238 100644 --- a/common/powershell/hyperv/hyperv.go +++ b/common/powershell/hyperv/hyperv.go @@ -187,40 +187,49 @@ Set-VMFloppyDiskDrive -VMName $vmName -Path $null return err } -func CreateVirtualMachine(vmName string, path string, harddrivePath string, vhdRoot string, ram int64, diskSize int64, switchName string, generation uint) error { +func CreateVirtualMachine(vmName string, path string, harddrivePath string, vhdRoot string, ram int64, diskSize int64, switchName string, generation uint, diffDisks bool) error { if generation == 2 { var script = ` -param([string]$vmName, [string]$path, [string]$harddrivePath, [string]$vhdRoot, [long]$memoryStartupBytes, [long]$newVHDSizeBytes, [string]$switchName, [int]$generation) +param([string]$vmName, [string]$path, [string]$harddrivePath, [string]$vhdRoot, [long]$memoryStartupBytes, [long]$newVHDSizeBytes, [string]$switchName, [int]$generation, [string]$diffDisks) $vhdx = $vmName + '.vhdx' $vhdPath = Join-Path -Path $vhdRoot -ChildPath $vhdx if ($harddrivePath){ - Copy-Item -Path $harddrivePath -Destination $vhdPath + if($diffDisks -eq "true"){ + New-VHD -Path $vhdPath -ParentPath $harddrivePath -Differencing + } else { + Copy-Item -Path $harddrivePath -Destination $vhdPath + } New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -VHDPath $vhdPath -SwitchName $switchName -Generation $generation } else { New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -NewVHDPath $vhdPath -NewVHDSizeBytes $newVHDSizeBytes -SwitchName $switchName -Generation $generation } ` var ps powershell.PowerShellCmd - if err := ps.Run(script, vmName, path, harddrivePath, vhdRoot, strconv.FormatInt(ram, 10), strconv.FormatInt(diskSize, 10), switchName, strconv.FormatInt(int64(generation), 10)); err != nil { + if err := ps.Run(script, vmName, path, harddrivePath, vhdRoot, strconv.FormatInt(ram, 10), strconv.FormatInt(diskSize, 10), switchName, strconv.FormatInt(int64(generation), 10), strconv.FormatBool(diffDisks)); err != nil { return err } return DisableAutomaticCheckpoints(vmName) } else { var script = ` -param([string]$vmName, [string]$path, [string]$harddrivePath, [string]$vhdRoot, [long]$memoryStartupBytes, [long]$newVHDSizeBytes, [string]$switchName) +param([string]$vmName, [string]$path, [string]$harddrivePath, [string]$vhdRoot, [long]$memoryStartupBytes, [long]$newVHDSizeBytes, [string]$switchName, [string]$diffDisks) $vhdx = $vmName + '.vhdx' $vhdPath = Join-Path -Path $vhdRoot -ChildPath $vhdx if ($harddrivePath){ - Copy-Item -Path $harddrivePath -Destination $vhdPath + if($diffDisks -eq "true"){ + New-VHD -Path $vhdPath -ParentPath $harddrivePath -Differencing + } + else{ + Copy-Item -Path $harddrivePath -Destination $vhdPath + } New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -VHDPath $vhdPath -SwitchName $switchName } else { New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -NewVHDPath $vhdPath -NewVHDSizeBytes $newVHDSizeBytes -SwitchName $switchName } ` var ps powershell.PowerShellCmd - if err := ps.Run(script, vmName, path, harddrivePath, vhdRoot, strconv.FormatInt(ram, 10), strconv.FormatInt(diskSize, 10), switchName); err != nil { + if err := ps.Run(script, vmName, path, harddrivePath, vhdRoot, strconv.FormatInt(ram, 10), strconv.FormatInt(diskSize, 10), switchName, strconv.FormatBool(diffDisks)); err != nil { return err } From 3d5303c60d389d95c5b54b1332dfb10a2cf9e2a8 Mon Sep 17 00:00:00 2001 From: Vijaya Bhaskar Reddy Kondreddi Date: Fri, 24 Nov 2017 13:24:44 +0530 Subject: [PATCH 0097/1216] Fix tests --- builder/hyperv/common/driver_mock.go | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/builder/hyperv/common/driver_mock.go b/builder/hyperv/common/driver_mock.go index dcdd857a5..3823cbd12 100644 --- a/builder/hyperv/common/driver_mock.go +++ b/builder/hyperv/common/driver_mock.go @@ -110,16 +110,17 @@ type DriverMock struct { AddVirtualMachineHardDrive_ControllerType string AddVirtualMachineHardDrive_Err error - CreateVirtualMachine_Called bool - CreateVirtualMachine_VmName string - CreateVirtualMachine_Path string - CreateVirtualMachine_HarddrivePath string - CreateVirtualMachine_VhdPath string - CreateVirtualMachine_Ram int64 - CreateVirtualMachine_DiskSize int64 - CreateVirtualMachine_SwitchName string - CreateVirtualMachine_Generation uint - CreateVirtualMachine_Err error + CreateVirtualMachine_Called bool + CreateVirtualMachine_VmName string + CreateVirtualMachine_Path string + CreateVirtualMachine_HarddrivePath string + CreateVirtualMachine_VhdPath string + CreateVirtualMachine_Ram int64 + CreateVirtualMachine_DiskSize int64 + CreateVirtualMachine_SwitchName string + CreateVirtualMachine_Generation uint + CreateVirtualMachine_DifferentialDisk bool + CreateVirtualMachine_Err error CloneVirtualMachine_Called bool CloneVirtualMachine_CloneFromVmxcPath string @@ -374,7 +375,7 @@ func (d *DriverMock) AddVirtualMachineHardDrive(vmName string, vhdFile string, v return d.AddVirtualMachineHardDrive_Err } -func (d *DriverMock) CreateVirtualMachine(vmName string, path string, harddrivePath string, vhdPath string, ram int64, diskSize int64, switchName string, generation uint) error { +func (d *DriverMock) CreateVirtualMachine(vmName string, path string, harddrivePath string, vhdPath string, ram int64, diskSize int64, switchName string, generation uint, diffDisks bool) error { d.CreateVirtualMachine_Called = true d.CreateVirtualMachine_VmName = vmName d.CreateVirtualMachine_Path = path @@ -384,6 +385,7 @@ func (d *DriverMock) CreateVirtualMachine(vmName string, path string, harddriveP d.CreateVirtualMachine_DiskSize = diskSize d.CreateVirtualMachine_SwitchName = switchName d.CreateVirtualMachine_Generation = generation + d.CreateVirtualMachine_DifferentialDisk = diffDisks return d.CreateVirtualMachine_Err } From af8a0c46c540ac5bd28ed6e7ff3ae09df1e86a0b Mon Sep 17 00:00:00 2001 From: Vladislav Rassokhin Date: Sun, 26 Nov 2017 00:02:54 +0300 Subject: [PATCH 0098/1216] Do not re-download iso multiple times from different urls In case of two or more iso_urls checks for downloaded files prior to downloading them. Speedups case when some iso already downloaded and another url prepended to iso_urls list. --- common/step_download.go | 45 ++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/common/step_download.go b/common/step_download.go index f63070a04..765a07d23 100644 --- a/common/step_download.go +++ b/common/step_download.go @@ -61,10 +61,12 @@ func (s *StepDownload) Run(state multistep.StateBag) multistep.StepAction { ui.Say(fmt.Sprintf("Downloading or copying %s", s.Description)) - var finalPath string - for _, url := range s.Url { - ui.Message(fmt.Sprintf("Downloading or copying: %s", url)) + // First try to use any already downloaded file + // If it fails, proceed to regualar download logic + var downloadConfigs = make([]*DownloadConfig, len(s.Url)) + var finalPath string + for i, url := range s.Url { targetPath := s.TargetPath if targetPath == "" { // Determine a cache key. This is normally just the URL but @@ -90,22 +92,37 @@ func (s *StepDownload) Run(state multistep.StateBag) multistep.StepAction { Checksum: checksum, UserAgent: "Packer", } + downloadConfigs[i] = config - path, err, retry := s.download(config, state) - if err != nil { - ui.Message(fmt.Sprintf("Error downloading: %s", err)) - } - - if !retry { - return multistep.ActionHalt - } - - if err == nil { - finalPath = path + if match, _ := NewDownloadClient(config).VerifyChecksum(config.TargetPath); match { + ui.Message(fmt.Sprintf("Found already downloaded, initial checksum matched, no download needed: %s", url)) + finalPath = config.TargetPath break } } + if finalPath == "" { + for i, url := range s.Url { + ui.Message(fmt.Sprintf("Downloading or copying: %s", url)) + + config := downloadConfigs[i] + + path, err, retry := s.download(config, state) + if err != nil { + ui.Message(fmt.Sprintf("Error downloading: %s", err)) + } + + if !retry { + return multistep.ActionHalt + } + + if err == nil { + finalPath = path + break + } + } + } + if finalPath == "" { err := fmt.Errorf("%s download failed.", s.Description) state.Put("error", err) From 0c787ec9df19a69f3968835c4f138c62b654a582 Mon Sep 17 00:00:00 2001 From: John Davies-Colley Date: Tue, 28 Nov 2017 11:46:01 +1300 Subject: [PATCH 0099/1216] =?UTF-8?q?Valadating=20early=20=E2=8F=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- builder/amazon/common/run_config.go | 13 +++++++++++++ builder/amazon/common/ssh.go | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/builder/amazon/common/run_config.go b/builder/amazon/common/run_config.go index 48f8337f8..7e523740a 100644 --- a/builder/amazon/common/run_config.go +++ b/builder/amazon/common/run_config.go @@ -76,11 +76,24 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { c.RunTags = make(map[string]string) } + if c.SSHPrivateIp && c.SSHInterface { + errs = append(errs, errors.New("ssh_interface and ssh_private_ip should not both be specified")) + } + // Legacy configurable if c.SSHPrivateIp { c.SSHInterface = "private_ip" } + // Valadating ssh_interface + if c.SSHInterface != "public_ip" || + c.SSHInterface != "private_ip" || + c.SSHInterface != "public_dns" || + c.SSHInterface != "private_dns" || + c.SSHInterface != "" { + errs = append(errs, errors.New("Unknown interface type: %s", SSHInterface)) + } + // Validation errs := c.Comm.Prepare(ctx) if c.SSHKeyPairName != "" { diff --git a/builder/amazon/common/ssh.go b/builder/amazon/common/ssh.go index 34fb64ce1..be414a8a0 100644 --- a/builder/amazon/common/ssh.go +++ b/builder/amazon/common/ssh.go @@ -51,7 +51,7 @@ func SSHHost(e ec2Describer, sshInterface string) func(multistep.StateBag) (stri host = *i.PrivateDnsName } default: - return "", fmt.Errorf("unknown interface type: %s", sshInterface) + panic(fmt.Sprintf("Unknown interface type: %s", sshInterface)) } } else if i.VpcId != nil && *i.VpcId != "" { if i.PublicIpAddress != nil && *i.PublicIpAddress != "" { From 52f5f2b895257265916c8a67963231dd0e47d19d Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Mon, 27 Nov 2017 15:10:29 -0800 Subject: [PATCH 0100/1216] clarify where ansible-local runs --- .../docs/provisioners/ansible-local.html.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/website/source/docs/provisioners/ansible-local.html.md b/website/source/docs/provisioners/ansible-local.html.md index 92d46fb3d..439abb63a 100644 --- a/website/source/docs/provisioners/ansible-local.html.md +++ b/website/source/docs/provisioners/ansible-local.html.md @@ -1,8 +1,10 @@ --- description: | - The ansible-local Packer provisioner configures Ansible to run on the - machine by Packer from local Playbook and Role files. Playbooks and Roles can - be uploaded from your local machine to the remote machine. + The ansible-local Packer provisioner will run ansible "locally" on the + remote/guest VM using Playbook and Role files that exist on the guest VM. + This means ansible must be installed on the remote/guest VM. Playbooks and + Roles can be uploaded from your local machine (the one running Packer) to + the vm. layout: docs page_title: 'Ansible Local - Provisioners' sidebar_current: 'docs-provisioners-ansible-local' @@ -12,15 +14,17 @@ sidebar_current: 'docs-provisioners-ansible-local' Type: `ansible-local` -The `ansible-local` Packer provisioner configures Ansible to run on the machine -by Packer from local Playbook and Role files. Playbooks and Roles can be -uploaded from your local machine to the remote machine. Ansible is run in [local +The `ansible-local` Packer provisioner will run ansible "locally" on the + remote/guest VM using Playbook and Role files that exist on the guest VM. + This means ansible must be installed on the remote/guest VM. Playbooks and + Roles can be uploaded from your local machine (the one running Packer) to + the vm. Ansible is run on the guest machine in [local mode](https://docs.ansible.com/ansible/playbooks_delegation.html#local-playbooks) via the `ansible-playbook` command. -> **Note:** Ansible will *not* be installed automatically by this provisioner. This provisioner expects that Ansible is already installed on the -machine. It is common practice to use the [shell +guest/remote machine. It is common practice to use the [shell provisioner](/docs/provisioners/shell.html) before the Ansible provisioner to do this. From 150bf1f6dab86fdfc4d0bcbd8fac9b2fe3a7404c Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Mon, 27 Nov 2017 15:15:52 -0800 Subject: [PATCH 0101/1216] clarify shell-local location --- .../source/docs/provisioners/shell-local.html.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/website/source/docs/provisioners/shell-local.html.md b/website/source/docs/provisioners/shell-local.html.md index bb9022453..e812f3228 100644 --- a/website/source/docs/provisioners/shell-local.html.md +++ b/website/source/docs/provisioners/shell-local.html.md @@ -1,8 +1,9 @@ --- description: | - The shell Packer provisioner provisions machines built by Packer using shell - scripts. Shell provisioning is the easiest way to get software installed and - configured on a machine. + shell-local will run a shell script of your choosing on the machine where Packer + is being run - in other words, it shell-local will run the shell script on your + build server, or your desktop, etc., rather than the remote/guest machine being + provisioned by Packer. layout: docs page_title: 'Shell (Local) - Provisioners' sidebar_current: 'docs-provisioners-shell-local' @@ -12,8 +13,12 @@ sidebar_current: 'docs-provisioners-shell-local' Type: `shell-local` -The local shell provisioner executes a local shell script on the machine running -Packer. The [remote shell](/docs/provisioners/shell.html) provisioner executes +shell-local will run a shell script of your choosing on the machine where Packer +is being run - in other words, it shell-local will run the shell script on your +build server, or your desktop, etc., rather than the remote/guest machine being +provisioned by Packer. + +The [remote shell](/docs/provisioners/shell.html) provisioner executes shell scripts on a remote machine. ## Basic Example From df492d35bfb608ed5f5087c9d3bb4e5a57630742 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Mon, 27 Nov 2017 15:17:54 -0800 Subject: [PATCH 0102/1216] remove confusing use of local. dang. --- .../source/docs/provisioners/ansible-local.html.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/website/source/docs/provisioners/ansible-local.html.md b/website/source/docs/provisioners/ansible-local.html.md index 439abb63a..cec5eee17 100644 --- a/website/source/docs/provisioners/ansible-local.html.md +++ b/website/source/docs/provisioners/ansible-local.html.md @@ -14,12 +14,12 @@ sidebar_current: 'docs-provisioners-ansible-local' Type: `ansible-local` -The `ansible-local` Packer provisioner will run ansible "locally" on the - remote/guest VM using Playbook and Role files that exist on the guest VM. - This means ansible must be installed on the remote/guest VM. Playbooks and - Roles can be uploaded from your local machine (the one running Packer) to - the vm. Ansible is run on the guest machine in [local -mode](https://docs.ansible.com/ansible/playbooks_delegation.html#local-playbooks) via the +The `ansible-local` Packer provisioner will run ansible in ansible's "local" + mode on the remote/guest VM using Playbook and Role files that exist on the + guest VM. This means ansible must be installed on the remote/guest VM. + Playbooks and Roles can be uploaded from your build machine + (the one running Packer) to the vm. Ansible is then run on the guest machine + in [local mode](https://docs.ansible.com/ansible/playbooks_delegation.html#local-playbooks) via the `ansible-playbook` command. -> **Note:** Ansible will *not* be installed automatically by this From a325780aa59da7122d98858d7cb953ee026611a9 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Mon, 27 Nov 2017 15:18:58 -0800 Subject: [PATCH 0103/1216] make description match --- website/source/docs/provisioners/ansible-local.html.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/website/source/docs/provisioners/ansible-local.html.md b/website/source/docs/provisioners/ansible-local.html.md index cec5eee17..b6e7028d9 100644 --- a/website/source/docs/provisioners/ansible-local.html.md +++ b/website/source/docs/provisioners/ansible-local.html.md @@ -1,10 +1,10 @@ --- description: | - The ansible-local Packer provisioner will run ansible "locally" on the - remote/guest VM using Playbook and Role files that exist on the guest VM. - This means ansible must be installed on the remote/guest VM. Playbooks and - Roles can be uploaded from your local machine (the one running Packer) to - the vm. + The ansible-local Packer provisioner will run ansible in ansible's "local" + mode on the remote/guest VM using Playbook and Role files that exist on the + guest VM. This means ansible must be installed on the remote/guest VM. + Playbooks and Roles can be uploaded from your build machine + (the one running Packer) to the vm. layout: docs page_title: 'Ansible Local - Provisioners' sidebar_current: 'docs-provisioners-ansible-local' From be3fe340c8f60db399f11878c457695d2e5d05b7 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Mon, 27 Nov 2017 17:23:31 -0800 Subject: [PATCH 0104/1216] add a section about uploading files that don't exist. --- website/source/docs/provisioners/file.html.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/website/source/docs/provisioners/file.html.md b/website/source/docs/provisioners/file.html.md index 26e2fc239..0405e8db5 100644 --- a/website/source/docs/provisioners/file.html.md +++ b/website/source/docs/provisioners/file.html.md @@ -47,9 +47,6 @@ The available configuration options are listed below. All elements are required. - `direction` (string) - The direction of the file transfer. This defaults to "upload". If it is set to "download" then the file "source" in the machine will be downloaded locally to "destination" - -The source file must exist before running Packer, this means that creating the -file with a provisioner does not work. ## Directory Uploads @@ -78,6 +75,17 @@ directly. This behavior was adopted from the standard behavior of rsync. Note that under the covers, rsync may or may not be used. +## Uploading files that don't exist before Packer starts + +In general, local files used as the source **must** exist before Packer is run. +This is great for catching typos and ensuring that once a build is started, +that it will succeed. However, this also means that you can't generate a file +during your build and then upload it using the file provisioner later. +A convenient workaround is to upload a directory instead of a file. The +directory still must exist, but its contents don't. You can write your +generated file to the directory during the Packer run, and have it be uploaded +later. + ## Symbolic link uploads The behavior when uploading symbolic links depends on the communicator. The From ff3efb4641411d1cf693d647b652ef08716a4dcc Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Mon, 27 Nov 2017 17:26:03 -0800 Subject: [PATCH 0105/1216] remove trailing spaces --- .../source/docs/builders/amazon-ebs.html.md | 2 +- website/source/docs/builders/azure.html.md | 2 +- website/source/docs/builders/docker.html.md | 2 +- .../source/docs/builders/hyperv-iso.html.md | 10 +++--- .../source/docs/builders/hyperv-vmcx.html.md | 36 +++++++++---------- website/source/docs/builders/hyperv.html.md | 4 +-- .../post-processors/vsphere-template.html.md | 14 ++++---- .../docs/provisioners/ansible-local.html.md | 22 ++++++------ .../source/docs/provisioners/ansible.html.md | 2 +- .../docs/provisioners/shell-local.html.md | 12 +++---- 10 files changed, 53 insertions(+), 53 deletions(-) diff --git a/website/source/docs/builders/amazon-ebs.html.md b/website/source/docs/builders/amazon-ebs.html.md index c11525dd0..df165ea4b 100644 --- a/website/source/docs/builders/amazon-ebs.html.md +++ b/website/source/docs/builders/amazon-ebs.html.md @@ -237,7 +237,7 @@ builder. - `temporary_security_group_source_cidr` (string) - An IPv4 CIDR block to be authorized access to the instance, when packer is creating a temporary security group. - The default is `0.0.0.0/0` (ie, allow any IPv4 source). This is only used + The default is `0.0.0.0/0` (ie, allow any IPv4 source). This is only used when `security_group_id` or `security_group_ids` is not specified. - `shutdown_behavior` (string) - Automatically terminate instances on shutdown diff --git a/website/source/docs/builders/azure.html.md b/website/source/docs/builders/azure.html.md index a4068702a..f673d34fd 100644 --- a/website/source/docs/builders/azure.html.md +++ b/website/source/docs/builders/azure.html.md @@ -134,7 +134,7 @@ When creating a managed image the following two options are required. assigned. Knowing the resource group and VM name allows one to execute commands to update the VM during a Packer build, e.g. attach a resource disk to the VM. -- `temp_resource_group_name` (string) name assigned to the temporary resource group created during the build. If this +- `temp_resource_group_name` (string) name assigned to the temporary resource group created during the build. If this value is not set, a random value will be assigned. This resource group is deleted at the end of the build. Cannot be used together with `build_resource_group_name`. diff --git a/website/source/docs/builders/docker.html.md b/website/source/docs/builders/docker.html.md index 8b8e684cd..e1390ff12 100644 --- a/website/source/docs/builders/docker.html.md +++ b/website/source/docs/builders/docker.html.md @@ -212,7 +212,7 @@ You must specify (only) one of `commit`, `discard`, or `export_path`. - `container_dir` (string) - The directory inside container to mount temp directory from host server for work [file provisioner](/docs/provisioners/file.html). By default this is set to `/packer-files`. - + - `fix_upload_owner` (boolean) - If true, files uploaded to the container will be owned by the user the container is running as. If false, the owner will depend on the version of docker installed in the system. Defaults to true. diff --git a/website/source/docs/builders/hyperv-iso.html.md b/website/source/docs/builders/hyperv-iso.html.md index b5b6a3826..ff267392c 100644 --- a/website/source/docs/builders/hyperv-iso.html.md +++ b/website/source/docs/builders/hyperv-iso.html.md @@ -54,20 +54,20 @@ can be configured for this builder. ### Required: - `iso_checksum` (string) - The checksum for the OS ISO file or virtual - harddrive file. Because these files are so large, this is required and - Packer will verify it prior to booting a virtual machine with the ISO or - virtual harddrive attached. The type of the checksum is specified with + harddrive file. Because these files are so large, this is required and + Packer will verify it prior to booting a virtual machine with the ISO or + virtual harddrive attached. The type of the checksum is specified with `iso_checksum_type`, documented below. - `iso_checksum_type` (string) - The type of the checksum specified in `iso_checksum`. Valid values are "none", "md5", "sha1", "sha256", or "sha512" currently. While "none" will skip checksumming, this is not - recommended since ISO files and virtual harddrive files are generally large + recommended since ISO files and virtual harddrive files are generally large and corruption does happen from time to time. - `iso_url` (string) - A URL to the ISO containing the installation image or virtual harddrive vhd or vhdx file to clone. This URL can be either an HTTP - URL or a file URL (or path to a file). If this is an HTTP URL, Packer will + URL or a file URL (or path to a file). If this is an HTTP URL, Packer will download the file and cache it between runs. ### Optional: diff --git a/website/source/docs/builders/hyperv-vmcx.html.md b/website/source/docs/builders/hyperv-vmcx.html.md index d3236a4fe..1aac29fc1 100644 --- a/website/source/docs/builders/hyperv-vmcx.html.md +++ b/website/source/docs/builders/hyperv-vmcx.html.md @@ -10,13 +10,13 @@ page_title: "Hyper-V Builder (from an vmcx)" Type: `hyperv-vmcx` -The Hyper-V Packer builder is able to use exported virtual machines or clone existing +The Hyper-V Packer builder is able to use exported virtual machines or clone existing [Hyper-V](https://www.microsoft.com/en-us/server-cloud/solutions/virtualization.aspx) virtual machines. -The builder imports a virtual machine or clones an existing virtual machine boots it, -and provisioning software within the OS, then shutting it down. The result of the -Hyper-V builder is a directory containing all the files necessary to run the virtual +The builder imports a virtual machine or clones an existing virtual machine boots it, +and provisioning software within the OS, then shutting it down. The result of the +Hyper-V builder is a directory containing all the files necessary to run the virtual machine portably. ## Basic Example @@ -193,7 +193,7 @@ can be configured for this builder. * `secondary_iso_images` (array of strings) - A list of iso paths to attached to a VM when it is booted. This is most useful for unattended Windows installs, which look for an `Autounattend.xml` file on removable media. By - default, no secondary iso will be attached. + default, no secondary iso will be attached. - `shutdown_command` (string) - The command to use to gracefully shut down the machine once all the provisioning is done. By default this is an empty @@ -275,7 +275,7 @@ will be replaced by the proper key: - `` `` - Simulates pressing and holding the alt key. -- `` `` - Simulates pressing and holding the ctrl key. +- `` `` - Simulates pressing and holding the ctrl key. - `` `` - Simulates pressing and holding the shift key. @@ -289,7 +289,7 @@ will be replaced by the proper key: sending any additional keys. This is useful if you have to generally wait for the UI to update before typing more. -When using modifier keys `ctrl`, `alt`, `shift` ensure that you release them, otherwise they will be held down until the machine reboots. Use lowercase characters as well inside modifiers. For example: to simulate ctrl+c use `c`. +When using modifier keys `ctrl`, `alt`, `shift` ensure that you release them, otherwise they will be held down until the machine reboots. Use lowercase characters as well inside modifiers. For example: to simulate ctrl+c use `c`. In addition to the special keys, each command to type is treated as a [configuration template](/docs/templates/configuration-templates.html). @@ -324,15 +324,15 @@ for the version of Hyper-V that is running. ## Generation 1 vs Generation 2 -Floppy drives are no longer supported by generation 2 machines. This requires you to +Floppy drives are no longer supported by generation 2 machines. This requires you to take another approach when dealing with preseed or answer files. Two possible options are using virtual dvd drives or using the built in web server. -When dealing with Windows you need to enable UEFI drives for generation 2 virtual machines. +When dealing with Windows you need to enable UEFI drives for generation 2 virtual machines. ## Creating iso from directory -Programs like mkisofs can be used to create an iso from a directory. +Programs like mkisofs can be used to create an iso from a directory. There is a [windows version of mkisofs](http://opensourcepack.blogspot.co.uk/p/cdrtools.html). Example powershell script. This is an actually working powershell script used to create a Windows answer iso: @@ -357,7 +357,7 @@ copy windows\common\win-updates.ps1 $isoFolder\ copy windows\common\run-sysprep.ps1 $isoFolder\ copy windows\common\run-sysprep.cmd $isoFolder\ -$textFile = "$isoFolder\Autounattend.xml" +$textFile = "$isoFolder\Autounattend.xml" $c = Get-Content -Encoding UTF8 $textFile @@ -399,7 +399,7 @@ Packer config: "winrm_username": "vagrant", "winrm_password": "vagrant", "winrm_timeout" : "4h", - "shutdown_command": "f:\\run-sysprep.cmd", + "shutdown_command": "f:\\run-sysprep.cmd", "ram_size": 4096, "cpu": 4, "generation": 2, @@ -514,10 +514,10 @@ autounattend.xml: 3 128 MSR - + 4 - true + true Primary @@ -609,8 +609,8 @@ autounattend.xml: 0 true cache-proxy:3142 - -Finish Setup cache proxy during installation --> + +Finish Setup cache proxy during installation --> @@ -842,13 +842,13 @@ sysprep-unattend.xml: - +Finish proxy after sysprep --> 0809:00000809 en-GB diff --git a/website/source/docs/builders/hyperv.html.md b/website/source/docs/builders/hyperv.html.md index 57f2015a9..58358f1e9 100644 --- a/website/source/docs/builders/hyperv.html.md +++ b/website/source/docs/builders/hyperv.html.md @@ -18,6 +18,6 @@ virtual machines and export them. an image. This is best for people who want to start from scratch. - [hyperv-vmcx](/docs/builders/hyperv-vmcx.html) - Clones an - an existing virtual machine, provisions software within the OS, - then exports that machine to create an image. This is best for + an existing virtual machine, provisions software within the OS, + then exports that machine to create an image. This is best for people who have existing base images and want to customize them. \ No newline at end of file diff --git a/website/source/docs/post-processors/vsphere-template.html.md b/website/source/docs/post-processors/vsphere-template.html.md index e72dc19cc..a78fade3e 100644 --- a/website/source/docs/post-processors/vsphere-template.html.md +++ b/website/source/docs/post-processors/vsphere-template.html.md @@ -1,7 +1,7 @@ --- description: | The Packer vSphere Template post-processor takes an artifact from the VMware-iso builder built on ESXi (i.e. remote) - and allows to mark a VM as a template and leaving it in a path of choice. + and allows to mark a VM as a template and leaving it in a path of choice. layout: docs page_title: 'vSphere Template - Post-Processors' sidebar_current: 'docs-post-processors-vSphere-template' @@ -19,14 +19,14 @@ allows to mark a VM as a template and leaving it in a path of choice. An example is shown below, showing only the post-processor configuration: ``` json -{ +{ "type": "vsphere-template", "host": "vcenter.local", "insecure": true, "username": "root", - "password": "secret", + "password": "secret", "datacenter": "mydatacenter", - "folder": "/packer-templates/os/distro-7" + "folder": "/packer-templates/os/distro-7" } ``` @@ -38,7 +38,7 @@ each category, the available configuration keys are alphabetized. Required: -- `host` (string) - The vSphere host that contains the VM built by the vmware-iso. +- `host` (string) - The vSphere host that contains the VM built by the vmware-iso. - `password` (string) - Password to use to authenticate to the vSphere endpoint. @@ -48,6 +48,6 @@ Optional: - `datacenter` (string) - If you have more than one, you will need to specify which one the ESXi used. -- `folder` (string) - Target path where the template will be created. +- `folder` (string) - Target path where the template will be created. -- `insecure` (boolean) - If it's true skip verification of server certificate. Default is false +- `insecure` (boolean) - If it's true skip verification of server certificate. Default is false diff --git a/website/source/docs/provisioners/ansible-local.html.md b/website/source/docs/provisioners/ansible-local.html.md index b6e7028d9..5e7467084 100644 --- a/website/source/docs/provisioners/ansible-local.html.md +++ b/website/source/docs/provisioners/ansible-local.html.md @@ -1,10 +1,10 @@ --- description: | - The ansible-local Packer provisioner will run ansible in ansible's "local" - mode on the remote/guest VM using Playbook and Role files that exist on the - guest VM. This means ansible must be installed on the remote/guest VM. - Playbooks and Roles can be uploaded from your build machine - (the one running Packer) to the vm. + The ansible-local Packer provisioner will run ansible in ansible's "local" + mode on the remote/guest VM using Playbook and Role files that exist on the + guest VM. This means ansible must be installed on the remote/guest VM. + Playbooks and Roles can be uploaded from your build machine + (the one running Packer) to the vm. layout: docs page_title: 'Ansible Local - Provisioners' sidebar_current: 'docs-provisioners-ansible-local' @@ -14,11 +14,11 @@ sidebar_current: 'docs-provisioners-ansible-local' Type: `ansible-local` -The `ansible-local` Packer provisioner will run ansible in ansible's "local" - mode on the remote/guest VM using Playbook and Role files that exist on the - guest VM. This means ansible must be installed on the remote/guest VM. - Playbooks and Roles can be uploaded from your build machine - (the one running Packer) to the vm. Ansible is then run on the guest machine +The `ansible-local` Packer provisioner will run ansible in ansible's "local" + mode on the remote/guest VM using Playbook and Role files that exist on the + guest VM. This means ansible must be installed on the remote/guest VM. + Playbooks and Roles can be uploaded from your build machine + (the one running Packer) to the vm. Ansible is then run on the guest machine in [local mode](https://docs.ansible.com/ansible/playbooks_delegation.html#local-playbooks) via the `ansible-playbook` command. @@ -118,7 +118,7 @@ chi-appservers cli](http://docs.ansible.com/ansible/galaxy.html#the-ansible-galaxy-command-line-tool) on the remote machine. By default, this is empty. -- `galaxycommand` (string) - The command to invoke ansible-galaxy. +- `galaxycommand` (string) - The command to invoke ansible-galaxy. By default, this is ansible-galaxy. - `group_vars` (string) - a path to the directory containing ansible group diff --git a/website/source/docs/provisioners/ansible.html.md b/website/source/docs/provisioners/ansible.html.md index 5ea4de0f2..5c9f1c31c 100644 --- a/website/source/docs/provisioners/ansible.html.md +++ b/website/source/docs/provisioners/ansible.html.md @@ -250,7 +250,7 @@ SSH servers only allow you to attempt to authenticate a certain number of times. googlecompute: fatal: [default]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: Warning: Permanently added '[127.0.0.1]:62684' (RSA) to the list of known hosts.\r\nReceived disconnect from 127.0.0.1 port 62684:2: too many authentication failures\r\nAuthentication failed.\r\n", "unreachable": true} ``` -To unload all keys from your `ssh-agent`, run: +To unload all keys from your `ssh-agent`, run: ```console $ ssh-add -D diff --git a/website/source/docs/provisioners/shell-local.html.md b/website/source/docs/provisioners/shell-local.html.md index e812f3228..c52bcd893 100644 --- a/website/source/docs/provisioners/shell-local.html.md +++ b/website/source/docs/provisioners/shell-local.html.md @@ -1,8 +1,8 @@ --- description: | - shell-local will run a shell script of your choosing on the machine where Packer - is being run - in other words, it shell-local will run the shell script on your - build server, or your desktop, etc., rather than the remote/guest machine being + shell-local will run a shell script of your choosing on the machine where Packer + is being run - in other words, it shell-local will run the shell script on your + build server, or your desktop, etc., rather than the remote/guest machine being provisioned by Packer. layout: docs page_title: 'Shell (Local) - Provisioners' @@ -13,9 +13,9 @@ sidebar_current: 'docs-provisioners-shell-local' Type: `shell-local` -shell-local will run a shell script of your choosing on the machine where Packer -is being run - in other words, it shell-local will run the shell script on your -build server, or your desktop, etc., rather than the remote/guest machine being +shell-local will run a shell script of your choosing on the machine where Packer +is being run - in other words, it shell-local will run the shell script on your +build server, or your desktop, etc., rather than the remote/guest machine being provisioned by Packer. The [remote shell](/docs/provisioners/shell.html) provisioner executes From 10aaa49bebf7edbb4c50005aa5b19d5d4f3d434d Mon Sep 17 00:00:00 2001 From: John Davies-Colley Date: Tue, 28 Nov 2017 14:26:55 +1300 Subject: [PATCH 0106/1216] =?UTF-8?q?fixing=20tests=20and=20funky=20logic?= =?UTF-8?q?=20=F0=9F=92=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- builder/amazon/common/run_config.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/builder/amazon/common/run_config.go b/builder/amazon/common/run_config.go index 7e523740a..4f20ed3b4 100644 --- a/builder/amazon/common/run_config.go +++ b/builder/amazon/common/run_config.go @@ -76,7 +76,9 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { c.RunTags = make(map[string]string) } - if c.SSHPrivateIp && c.SSHInterface { + // Validation + errs := c.Comm.Prepare(ctx) + if c.SSHPrivateIp && c.SSHInterface != "" { errs = append(errs, errors.New("ssh_interface and ssh_private_ip should not both be specified")) } @@ -86,16 +88,14 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { } // Valadating ssh_interface - if c.SSHInterface != "public_ip" || - c.SSHInterface != "private_ip" || - c.SSHInterface != "public_dns" || - c.SSHInterface != "private_dns" || + if c.SSHInterface != "public_ip" && + c.SSHInterface != "private_ip" && + c.SSHInterface != "public_dns" && + c.SSHInterface != "private_dns" && c.SSHInterface != "" { - errs = append(errs, errors.New("Unknown interface type: %s", SSHInterface)) + errs = append(errs, errors.New(fmt.Sprintf("Unknown interface type: %s", c.SSHInterface))) } - // Validation - errs := c.Comm.Prepare(ctx) if c.SSHKeyPairName != "" { if c.Comm.Type == "winrm" && c.Comm.WinRMPassword == "" && c.Comm.SSHPrivateKey == "" { errs = append(errs, errors.New("A private_key_file must be provided to retrieve the winrm password when using ssh_keypair_name.")) From de0017c37a5cee90a73fc35ff872ef13eeebec0f Mon Sep 17 00:00:00 2001 From: zhuzhih2017 Date: Tue, 28 Nov 2017 10:22:16 +0800 Subject: [PATCH 0107/1216] Add security token supported and TLS handshake timeout support --- builder/alicloud/ecs/access_config.go | 8 +- examples/alicloud/basic/alicloud.json | 2 +- .../basic/alicloud_with_data_disk.json | 2 +- .../denverdino/aliyungo/common/client.go | 159 +++++++++++- .../denverdino/aliyungo/common/regions.go | 3 +- .../denverdino/aliyungo/common/request.go | 6 +- .../denverdino/aliyungo/ecs/client.go | 75 ++++-- .../denverdino/aliyungo/ecs/disks.go | 25 ++ .../denverdino/aliyungo/ecs/instances.go | 66 ++++- .../denverdino/aliyungo/ecs/networks.go | 4 +- .../denverdino/aliyungo/ecs/route_tables.go | 1 + .../aliyungo/ecs/router_interface.go | 227 ++++++++++++++++++ .../denverdino/aliyungo/ram/client.go | 21 +- .../denverdino/aliyungo/ram/group.go | 2 + .../denverdino/aliyungo/ram/policy.go | 14 +- .../denverdino/aliyungo/util/encoding.go | 12 +- vendor/vendor.json | 28 +-- .../source/docs/builders/alicloud-ecs.html.md | 6 + 18 files changed, 597 insertions(+), 64 deletions(-) create mode 100644 vendor/github.com/denverdino/aliyungo/ecs/router_interface.go diff --git a/builder/alicloud/ecs/access_config.go b/builder/alicloud/ecs/access_config.go index bcc7ba76f..b49bd0bbf 100644 --- a/builder/alicloud/ecs/access_config.go +++ b/builder/alicloud/ecs/access_config.go @@ -15,6 +15,7 @@ type AlicloudAccessConfig struct { AlicloudSecretKey string `mapstructure:"secret_key"` AlicloudRegion string `mapstructure:"region"` AlicloudSkipValidation bool `mapstructure:"skip_region_validation"` + SecurityToken string `mapstructure:"security_token"` } // Client for AlicloudClient @@ -22,7 +23,12 @@ func (c *AlicloudAccessConfig) Client() (*ecs.Client, error) { if err := c.loadAndValidate(); err != nil { return nil, err } - client := ecs.NewECSClient(c.AlicloudAccessKey, c.AlicloudSecretKey, common.Region(c.AlicloudRegion)) + if c.SecurityToken == "" { + c.SecurityToken = os.Getenv("SECURITY_TOKEN") + } + client := ecs.NewECSClientWithSecurityToken(c.AlicloudAccessKey, c.AlicloudSecretKey, + c.SecurityToken, common.Region(c.AlicloudRegion)) + client.SetBusinessInfo("Packer") if _, err := client.DescribeRegions(); err != nil { return nil, err diff --git a/examples/alicloud/basic/alicloud.json b/examples/alicloud/basic/alicloud.json index 8f83262fe..d0c643f48 100644 --- a/examples/alicloud/basic/alicloud.json +++ b/examples/alicloud/basic/alicloud.json @@ -9,7 +9,7 @@ "secret_key":"{{user `secret_key`}}", "region":"cn-beijing", "image_name":"packer_basic", - "source_image":"centos_7_2_64_40G_base_20170222.vhd", + "source_image":"centos_7_03_64_20G_alibase_20170818.vhd", "ssh_username":"root", "instance_type":"ecs.n1.tiny", "internet_charge_type":"PayByTraffic", diff --git a/examples/alicloud/basic/alicloud_with_data_disk.json b/examples/alicloud/basic/alicloud_with_data_disk.json index 31f10ab70..dcc8c70ef 100644 --- a/examples/alicloud/basic/alicloud_with_data_disk.json +++ b/examples/alicloud/basic/alicloud_with_data_disk.json @@ -9,7 +9,7 @@ "secret_key":"{{user `secret_key`}}", "region":"cn-beijing", "image_name":"packer_with_data_disk", - "source_image":"centos_7_2_64_40G_base_20170222.vhd", + "source_image":"centos_7_03_64_20G_alibase_20170818.vhd", "ssh_username":"root", "instance_type":"ecs.n1.tiny", "internet_charge_type":"PayByTraffic", diff --git a/vendor/github.com/denverdino/aliyungo/common/client.go b/vendor/github.com/denverdino/aliyungo/common/client.go index a59789fee..10dcd9000 100755 --- a/vendor/github.com/denverdino/aliyungo/common/client.go +++ b/vendor/github.com/denverdino/aliyungo/common/client.go @@ -3,12 +3,16 @@ package common import ( "bytes" "encoding/json" + "errors" + "fmt" "io/ioutil" "log" "net/http" "net/url" "strings" "time" + "os" + "strconv" "github.com/denverdino/aliyungo/util" ) @@ -25,6 +29,7 @@ type UnderlineString string type Client struct { AccessKeyId string //Access Key Id AccessKeySecret string //Access Key Secret + securityToken string debug bool httpClient *http.Client endpoint string @@ -32,19 +37,30 @@ type Client struct { serviceCode string regionID Region businessInfo string - userAgent string + userAgent string } -// NewClient creates a new instance of ECS client +// Initialize properties of a client instance func (client *Client) Init(endpoint, version, accessKeyId, accessKeySecret string) { client.AccessKeyId = accessKeyId client.AccessKeySecret = accessKeySecret + "&" client.debug = false - client.httpClient = &http.Client{} + handshakeTimeout, err := strconv.Atoi(os.Getenv("TLSHandshakeTimeout")) + if err != nil { + handshakeTimeout = 0 + } + if handshakeTimeout == 0 { + client.httpClient = &http.Client{} + } else { + t := &http.Transport{ + TLSHandshakeTimeout: time.Duration(handshakeTimeout) * time.Second,} + client.httpClient = &http.Client{Transport: t,} + } client.endpoint = endpoint client.version = version } +// Initialize properties of a client instance including regionID func (client *Client) NewInit(endpoint, version, accessKeyId, accessKeySecret, serviceCode string, regionID Region) { client.Init(endpoint, version, accessKeyId, accessKeySecret) client.serviceCode = serviceCode @@ -52,6 +68,24 @@ func (client *Client) NewInit(endpoint, version, accessKeyId, accessKeySecret, s client.setEndpointByLocation(regionID, serviceCode, accessKeyId, accessKeySecret) } +// Intialize client object when all properties are ready +func (client *Client) InitClient() *Client { + client.debug = false + handshakeTimeout, err := strconv.Atoi(os.Getenv("TLSHandshakeTimeout")) + if err != nil { + handshakeTimeout = 0 + } + if handshakeTimeout == 0 { + client.httpClient = &http.Client{} + } else { + t := &http.Transport{ + TLSHandshakeTimeout: time.Duration(handshakeTimeout) * time.Second,} + client.httpClient = &http.Client{Transport: t,} + } + client.setEndpointByLocation(client.regionID, client.serviceCode, client.AccessKeyId, client.AccessKeySecret) + return client +} + //NewClient using location service func (client *Client) setEndpointByLocation(region Region, serviceCode, accessKeyId, accessKeySecret string) { locationClient := NewLocationClient(accessKeyId, accessKeySecret) @@ -65,6 +99,95 @@ func (client *Client) setEndpointByLocation(region Region, serviceCode, accessKe } } +// Ensure all necessary properties are valid +func (client *Client) ensureProperties() error { + var msg string + + if client.endpoint == "" { + msg = fmt.Sprintf("endpoint cannot be empty!") + } else if client.version == "" { + msg = fmt.Sprintf("version cannot be empty!") + } else if client.AccessKeyId == "" { + msg = fmt.Sprintf("AccessKeyId cannot be empty!") + } else if client.AccessKeySecret == "" { + msg = fmt.Sprintf("AccessKeySecret cannot be empty!") + } + + if msg != "" { + return errors.New(msg) + } + + return nil +} + +// ---------------------------------------------------- +// WithXXX methods +// ---------------------------------------------------- + +// WithEndpoint sets custom endpoint +func (client *Client) WithEndpoint(endpoint string) *Client { + client.SetEndpoint(endpoint) + return client +} + +// WithVersion sets custom version +func (client *Client) WithVersion(version string) *Client { + client.SetVersion(version) + return client +} + +// WithRegionID sets Region ID +func (client *Client) WithRegionID(regionID Region) *Client { + client.SetRegionID(regionID) + return client +} + +//WithServiceCode sets serviceCode +func (client *Client) WithServiceCode(serviceCode string) *Client { + client.SetServiceCode(serviceCode) + return client +} + +// WithAccessKeyId sets new AccessKeyId +func (client *Client) WithAccessKeyId(id string) *Client { + client.SetAccessKeyId(id) + return client +} + +// WithAccessKeySecret sets new AccessKeySecret +func (client *Client) WithAccessKeySecret(secret string) *Client { + client.SetAccessKeySecret(secret) + return client +} + +// WithSecurityToken sets securityToken +func (client *Client) WithSecurityToken(securityToken string) *Client { + client.SetSecurityToken(securityToken) + return client +} + +// WithDebug sets debug mode to log the request/response message +func (client *Client) WithDebug(debug bool) *Client { + client.SetDebug(debug) + return client +} + +// WithBusinessInfo sets business info to log the request/response message +func (client *Client) WithBusinessInfo(businessInfo string) *Client { + client.SetBusinessInfo(businessInfo) + return client +} + +// WithUserAgent sets user agent to the request/response message +func (client *Client) WithUserAgent(userAgent string) *Client { + client.SetUserAgent(userAgent) + return client +} + +// ---------------------------------------------------- +// SetXXX methods +// ---------------------------------------------------- + // SetEndpoint sets custom endpoint func (client *Client) SetEndpoint(endpoint string) { client.endpoint = endpoint @@ -75,6 +198,7 @@ func (client *Client) SetVersion(version string) { client.version = version } +// SetEndpoint sets Region ID func (client *Client) SetRegionID(regionID Region) { client.regionID = regionID } @@ -94,6 +218,11 @@ func (client *Client) SetAccessKeySecret(secret string) { client.AccessKeySecret = secret + "&" } +// SetAccessKeySecret sets securityToken +func (client *Client) SetSecurityToken(securityToken string) { + client.securityToken = securityToken +} + // SetDebug sets debug mode to log the request/response message func (client *Client) SetDebug(debug bool) { client.debug = debug @@ -115,9 +244,12 @@ func (client *Client) SetUserAgent(userAgent string) { // Invoke sends the raw HTTP request for ECS services func (client *Client) Invoke(action string, args interface{}, response interface{}) error { + if err := client.ensureProperties(); err != nil { + return err + } request := Request{} - request.init(client.version, action, client.AccessKeyId) + request.init(client.version, action, client.AccessKeyId, client.securityToken, client.regionID) query := util.ConvertToQueryValues(request) util.SetQueryValues(args, &query) @@ -136,8 +268,7 @@ func (client *Client) Invoke(action string, args interface{}, response interface // TODO move to util and add build val flag httpReq.Header.Set("X-SDK-Client", `AliyunGO/`+Version+client.businessInfo) - - httpReq.Header.Set("User-Agent", httpReq.UserAgent()+ " " +client.userAgent) + httpReq.Header.Set("User-Agent", httpReq.UserAgent()+" "+client.userAgent) t0 := time.Now() httpResp, err := client.httpClient.Do(httpReq) @@ -185,9 +316,12 @@ func (client *Client) Invoke(action string, args interface{}, response interface // Invoke sends the raw HTTP request for ECS services func (client *Client) InvokeByFlattenMethod(action string, args interface{}, response interface{}) error { + if err := client.ensureProperties(); err != nil { + return err + } request := Request{} - request.init(client.version, action, client.AccessKeyId) + request.init(client.version, action, client.AccessKeyId, client.securityToken, client.regionID) query := util.ConvertToQueryValues(request) @@ -207,8 +341,7 @@ func (client *Client) InvokeByFlattenMethod(action string, args interface{}, res // TODO move to util and add build val flag httpReq.Header.Set("X-SDK-Client", `AliyunGO/`+Version+client.businessInfo) - - httpReq.Header.Set("User-Agent", httpReq.UserAgent()+ " " +client.userAgent) + httpReq.Header.Set("User-Agent", httpReq.UserAgent()+" "+client.userAgent) t0 := time.Now() httpResp, err := client.httpClient.Do(httpReq) @@ -258,9 +391,12 @@ func (client *Client) InvokeByFlattenMethod(action string, args interface{}, res //改进了一下上面那个方法,可以使用各种Http方法 //2017.1.30 增加了一个path参数,用来拓展访问的地址 func (client *Client) InvokeByAnyMethod(method, action, path string, args interface{}, response interface{}) error { + if err := client.ensureProperties(); err != nil { + return err + } request := Request{} - request.init(client.version, action, client.AccessKeyId) + request.init(client.version, action, client.AccessKeyId, client.securityToken, client.regionID) data := util.ConvertToQueryValues(request) util.SetQueryValues(args, &data) @@ -290,8 +426,7 @@ func (client *Client) InvokeByAnyMethod(method, action, path string, args interf // TODO move to util and add build val flag httpReq.Header.Set("X-SDK-Client", `AliyunGO/`+Version+client.businessInfo) - - httpReq.Header.Set("User-Agent", httpReq.Header.Get("User-Agent")+ " " +client.userAgent) + httpReq.Header.Set("User-Agent", httpReq.Header.Get("User-Agent")+" "+client.userAgent) t0 := time.Now() httpResp, err := client.httpClient.Do(httpReq) diff --git a/vendor/github.com/denverdino/aliyungo/common/regions.go b/vendor/github.com/denverdino/aliyungo/common/regions.go index 62e6e9d81..c87fe0691 100644 --- a/vendor/github.com/denverdino/aliyungo/common/regions.go +++ b/vendor/github.com/denverdino/aliyungo/common/regions.go @@ -16,6 +16,7 @@ const ( APSouthEast1 = Region("ap-southeast-1") APNorthEast1 = Region("ap-northeast-1") APSouthEast2 = Region("ap-southeast-2") + APSouthEast3 = Region("ap-southeast-3") USWest1 = Region("us-west-1") USEast1 = Region("us-east-1") @@ -28,7 +29,7 @@ const ( var ValidRegions = []Region{ Hangzhou, Qingdao, Beijing, Shenzhen, Hongkong, Shanghai, Zhangjiakou, USWest1, USEast1, - APNorthEast1, APSouthEast1, APSouthEast2, + APNorthEast1, APSouthEast1, APSouthEast2, APSouthEast3, MEEast1, EUCentral1, } diff --git a/vendor/github.com/denverdino/aliyungo/common/request.go b/vendor/github.com/denverdino/aliyungo/common/request.go index 2a883f19b..f35c2990d 100644 --- a/vendor/github.com/denverdino/aliyungo/common/request.go +++ b/vendor/github.com/denverdino/aliyungo/common/request.go @@ -20,7 +20,9 @@ const ( type Request struct { Format string Version string + RegionId Region AccessKeyId string + SecurityToken string Signature string SignatureMethod string Timestamp util.ISO6801Time @@ -30,7 +32,7 @@ type Request struct { Action string } -func (request *Request) init(version string, action string, AccessKeyId string) { +func (request *Request) init(version string, action string, AccessKeyId string, securityToken string, regionId Region) { request.Format = JSONResponseFormat request.Timestamp = util.NewISO6801Time(time.Now().UTC()) request.Version = version @@ -39,6 +41,8 @@ func (request *Request) init(version string, action string, AccessKeyId string) request.SignatureNonce = util.CreateRandomString() request.Action = action request.AccessKeyId = AccessKeyId + request.SecurityToken = securityToken + request.RegionId = regionId } type Response struct { diff --git a/vendor/github.com/denverdino/aliyungo/ecs/client.go b/vendor/github.com/denverdino/aliyungo/ecs/client.go index d70a1554e..117483c35 100644 --- a/vendor/github.com/denverdino/aliyungo/ecs/client.go +++ b/vendor/github.com/denverdino/aliyungo/ecs/client.go @@ -20,8 +20,7 @@ const ( // ECSDefaultEndpoint is the default API endpoint of ECS services ECSDefaultEndpoint = "https://ecs-cn-hangzhou.aliyuncs.com" ECSAPIVersion = "2014-05-26" - - ECSServiceCode = "ecs" + ECSServiceCode = "ecs" VPCDefaultEndpoint = "https://vpc.aliyuncs.com" VPCAPIVersion = "2016-04-28" @@ -37,38 +36,80 @@ func NewClient(accessKeyId, accessKeySecret string) *Client { return NewClientWithEndpoint(endpoint, accessKeyId, accessKeySecret) } -func NewECSClient(accessKeyId, accessKeySecret string, regionID common.Region) *Client { - endpoint := os.Getenv("ECS_ENDPOINT") - if endpoint == "" { - endpoint = ECSDefaultEndpoint - } - - return NewClientWithRegion(endpoint, accessKeyId, accessKeySecret, regionID) -} - -func NewClientWithRegion(endpoint string, accessKeyId, accessKeySecret string, regionID common.Region) *Client { +func NewClientWithRegion(endpoint string, accessKeyId string, accessKeySecret string, regionID common.Region) *Client { client := &Client{} client.NewInit(endpoint, ECSAPIVersion, accessKeyId, accessKeySecret, ECSServiceCode, regionID) return client } -func NewClientWithEndpoint(endpoint string, accessKeyId, accessKeySecret string) *Client { +func NewClientWithEndpoint(endpoint string, accessKeyId string, accessKeySecret string) *Client { client := &Client{} client.Init(endpoint, ECSAPIVersion, accessKeyId, accessKeySecret) return client } -func NewVPCClient(accessKeyId, accessKeySecret string, regionID common.Region) *Client { +// --------------------------------------- +// NewECSClient creates a new instance of ECS client +// --------------------------------------- +func NewECSClient(accessKeyId, accessKeySecret string, regionID common.Region) *Client { + return NewECSClientWithSecurityToken(accessKeyId, accessKeySecret, "", regionID) +} + +func NewECSClientWithSecurityToken(accessKeyId string, accessKeySecret string, securityToken string, regionID common.Region) *Client { + endpoint := os.Getenv("ECS_ENDPOINT") + if endpoint == "" { + endpoint = ECSDefaultEndpoint + } + + return NewECSClientWithEndpointAndSecurityToken(endpoint, accessKeyId, accessKeySecret, securityToken, regionID) +} + +func NewECSClientWithEndpoint(endpoint string, accessKeyId string, accessKeySecret string, regionID common.Region) *Client { + return NewECSClientWithEndpointAndSecurityToken(endpoint, accessKeyId, accessKeySecret, "", regionID) +} + +func NewECSClientWithEndpointAndSecurityToken(endpoint string, accessKeyId string, accessKeySecret string, securityToken string, regionID common.Region) *Client { + client := &Client{} + client.WithEndpoint(endpoint). + WithVersion(ECSAPIVersion). + WithAccessKeyId(accessKeyId). + WithAccessKeySecret(accessKeySecret). + WithSecurityToken(securityToken). + WithServiceCode(ECSServiceCode). + WithRegionID(regionID). + InitClient() + return client +} + +// --------------------------------------- +// NewVPCClient creates a new instance of VPC client +// --------------------------------------- +func NewVPCClient(accessKeyId string, accessKeySecret string, regionID common.Region) *Client { + return NewVPCClientWithSecurityToken(accessKeyId, accessKeySecret, "", regionID) +} + +func NewVPCClientWithSecurityToken(accessKeyId string, accessKeySecret string, securityToken string, regionID common.Region) *Client { endpoint := os.Getenv("VPC_ENDPOINT") if endpoint == "" { endpoint = VPCDefaultEndpoint } - return NewVPCClientWithRegion(endpoint, accessKeyId, accessKeySecret, regionID) + return NewVPCClientWithEndpointAndSecurityToken(endpoint, accessKeyId, accessKeySecret, securityToken, regionID) } -func NewVPCClientWithRegion(endpoint string, accessKeyId, accessKeySecret string, regionID common.Region) *Client { +func NewVPCClientWithEndpoint(endpoint string, accessKeyId string, accessKeySecret string, regionID common.Region) *Client { + return NewVPCClientWithEndpointAndSecurityToken(endpoint, accessKeyId, accessKeySecret, "", regionID) +} + +func NewVPCClientWithEndpointAndSecurityToken(endpoint string, accessKeyId string, accessKeySecret string, securityToken string, regionID common.Region) *Client { client := &Client{} - client.NewInit(endpoint, VPCAPIVersion, accessKeyId, accessKeySecret, VPCServiceCode, regionID) + client.WithEndpoint(endpoint). + WithVersion(VPCAPIVersion). + WithAccessKeyId(accessKeyId). + WithAccessKeySecret(accessKeySecret). + WithSecurityToken(securityToken). + WithServiceCode(VPCServiceCode). + WithRegionID(regionID). + InitClient() return client } diff --git a/vendor/github.com/denverdino/aliyungo/ecs/disks.go b/vendor/github.com/denverdino/aliyungo/ecs/disks.go index 7a67c380d..6b898c60d 100644 --- a/vendor/github.com/denverdino/aliyungo/ecs/disks.go +++ b/vendor/github.com/denverdino/aliyungo/ecs/disks.go @@ -135,6 +135,7 @@ type CreateDiskArgs struct { ZoneId string DiskName string Description string + Encrypted bool DiskCategory DiskCategory Size int SnapshotId string @@ -240,6 +241,29 @@ func (client *Client) DetachDisk(instanceId string, diskId string) error { return err } +type ResizeDiskArgs struct { + DiskId string + NewSize int +} + +type ResizeDiskResponse struct { + common.Response +} + +// +// ResizeDisk can only support to enlarge disk size +// You can read doc at https://help.aliyun.com/document_detail/25522.html +func (client *Client) ResizeDisk(diskId string, sizeGB int) error { + args := ResizeDiskArgs{ + DiskId:diskId, + NewSize:sizeGB, + } + response := ResizeDiskResponse{} + err := client.Invoke("ResizeDisk", &args, &response) + return err +} + + type ResetDiskArgs struct { DiskId string SnapshotId string @@ -249,6 +273,7 @@ type ResetDiskResponse struct { common.Response } + // ResetDisk resets disk to original status // // You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/disk&resetdisk diff --git a/vendor/github.com/denverdino/aliyungo/ecs/instances.go b/vendor/github.com/denverdino/aliyungo/ecs/instances.go index 4eb999537..a08ac1244 100644 --- a/vendor/github.com/denverdino/aliyungo/ecs/instances.go +++ b/vendor/github.com/denverdino/aliyungo/ecs/instances.go @@ -224,6 +224,7 @@ type SpotStrategyType string const ( NoSpot = SpotStrategyType("NoSpot") SpotWithPriceLimit = SpotStrategyType("SpotWithPriceLimit") + SpotAsPriceGo = SpotStrategyType("SpotAsPriceGo") ) // @@ -244,7 +245,7 @@ type InstanceAttributesType struct { SerialNumber string Status InstanceStatus OperationLocks OperationLocksType - SecurityGroupIds struct { + SecurityGroupIds struct { SecurityGroupId []string } PublicIpAddress IpAddressSetType @@ -259,11 +260,12 @@ type InstanceAttributesType struct { IoOptimized StringOrBool InstanceChargeType common.InstanceChargeType ExpiredTime util.ISO6801Time - Tags struct { + Tags struct { Tag []TagItemType } - SpotStrategy SpotStrategyType - KeyPairName string + SpotStrategy SpotStrategyType + SpotPriceLimit float64 + KeyPairName string } type DescribeInstanceAttributeResponse struct { @@ -535,7 +537,9 @@ type CreateInstanceArgs struct { AutoRenew bool AutoRenewPeriod int SpotStrategy SpotStrategyType + SpotPriceLimit float64 KeyPairName string + RamRoleName string } type CreateInstanceResponse struct { @@ -624,3 +628,57 @@ func (client *Client) LeaveSecurityGroup(instanceId string, securityGroupId stri err := client.Invoke("LeaveSecurityGroup", &args, &response) return err } + +type AttachInstancesArgs struct { + RegionId common.Region + RamRoleName string + InstanceIds string +} + +// AttachInstanceRamRole attach instances to ram role +// +// You can read doc at https://help.aliyun.com/document_detail/54244.html?spm=5176.doc54245.6.811.zEJcS5 +func (client *Client) AttachInstanceRamRole(args *AttachInstancesArgs) (err error) { + response := common.Response{} + err = client.Invoke("AttachInstanceRamRole", args, &response) + if err != nil { + return err + } + return nil +} + +// DetachInstanceRamRole detach instances from ram role +// +// You can read doc at https://help.aliyun.com/document_detail/54245.html?spm=5176.doc54243.6.813.bt8RB3 +func (client *Client) DetachInstanceRamRole(args *AttachInstancesArgs) (err error) { + response := common.Response{} + err = client.Invoke("DetachInstanceRamRole", args, &response) + if err != nil { + return err + } + return nil +} + +type DescribeInstanceRamRoleResponse struct { + common.Response + InstanceRamRoleSets struct { + InstanceRamRoleSet []InstanceRamRoleSetType + } +} + +type InstanceRamRoleSetType struct { + InstanceId string + RamRoleName string +} + +// DescribeInstanceRamRole +// +// You can read doc at https://help.aliyun.com/document_detail/54243.html?spm=5176.doc54245.6.812.RgNCoi +func (client *Client) DescribeInstanceRamRole(args *AttachInstancesArgs) (resp *DescribeInstanceRamRoleResponse, err error) { + response := &DescribeInstanceRamRoleResponse{} + err = client.Invoke("DescribeInstanceRamRole", args, response) + if err != nil { + return response, err + } + return response, nil +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/networks.go b/vendor/github.com/denverdino/aliyungo/ecs/networks.go index 100835c37..4c4cd7150 100644 --- a/vendor/github.com/denverdino/aliyungo/ecs/networks.go +++ b/vendor/github.com/denverdino/aliyungo/ecs/networks.go @@ -36,8 +36,8 @@ func (client *Client) AllocatePublicIpAddress(instanceId string) (ipAddress stri type ModifyInstanceNetworkSpec struct { InstanceId string - InternetMaxBandwidthOut *int - InternetMaxBandwidthIn *int + InternetMaxBandwidthOut int + InternetMaxBandwidthIn int } type ModifyInstanceNetworkSpecResponse struct { diff --git a/vendor/github.com/denverdino/aliyungo/ecs/route_tables.go b/vendor/github.com/denverdino/aliyungo/ecs/route_tables.go index cc85cb129..f8531a20b 100644 --- a/vendor/github.com/denverdino/aliyungo/ecs/route_tables.go +++ b/vendor/github.com/denverdino/aliyungo/ecs/route_tables.go @@ -102,6 +102,7 @@ type NextHopType string const ( NextHopIntance = NextHopType("Instance") //Default NextHopTunnel = NextHopType("Tunnel") + NextHopTunnelRouterInterface = NextHopType("RouterInterface") ) type CreateRouteEntryArgs struct { diff --git a/vendor/github.com/denverdino/aliyungo/ecs/router_interface.go b/vendor/github.com/denverdino/aliyungo/ecs/router_interface.go new file mode 100644 index 000000000..62af67860 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/router_interface.go @@ -0,0 +1,227 @@ +package ecs + +import ( + "github.com/denverdino/aliyungo/common" +) + +type EcsCommonResponse struct { + common.Response +} +type RouterType string +type InterfaceStatus string +type Role string +type Spec string + +const ( + VRouter = RouterType("VRouter") + VBR = RouterType("VBR") + + Idl = InterfaceStatus("Idl") + Active = InterfaceStatus("Active") + Inactive = InterfaceStatus("Inactive") + + InitiatingSide = Role("InitiatingSide") + AcceptingSide = Role("AcceptingSide") + + Small1 = Spec("Small.1") + Small2 = Spec("Small.2") + Small5 = Spec("Small.5") + Middle1 = Spec("Middle.1") + Middle2 = Spec("Middle.2") + Middle5 = Spec("Middle.5") + Large1 = Spec("Large.1") + Large2 = Spec("Large.2") +) + +type CreateRouterInterfaceArgs struct { + RegionId common.Region + OppositeRegionId common.Region + RouterType RouterType + OppositeRouterType RouterType + RouterId string + OppositeRouterId string + Role Role + Spec Spec + AccessPointId string + OppositeAccessPointId string + OppositeInterfaceId string + OppositeInterfaceOwnerId string + Name string + Description string + HealthCheckSourceIp string + HealthCheckTargetIp string +} + +type CreateRouterInterfaceResponse struct { + common.Response + RouterInterfaceId string +} + +// CreateRouterInterface create Router interface +// +// You can read doc at https://help.aliyun.com/document_detail/36032.html?spm=5176.product27706.6.664.EbBsxC +func (client *Client) CreateRouterInterface(args *CreateRouterInterfaceArgs) (response *CreateRouterInterfaceResponse, err error) { + response = &CreateRouterInterfaceResponse{} + err = client.Invoke("CreateRouterInterface", args, &response) + if err != nil { + return response, err + } + return response, nil +} + +type Filter struct { + Key string + Value []string +} + +type DescribeRouterInterfacesArgs struct { + RegionId common.Region + common.Pagination + Filter []Filter +} + +type RouterInterfaceItemType struct { + ChargeType string + RouterInterfaceId string + AccessPointId string + OppositeRegionId string + OppositeAccessPointId string + Role Role + Spec Spec + Name string + Description string + RouterId string + RouterType RouterType + CreationTime string + Status string + BusinessStatus string + ConnectedTime string + OppositeInterfaceId string + OppositeInterfaceSpec string + OppositeInterfaceStatus string + OppositeInterfaceBusinessStatus string + OppositeRouterId string + OppositeRouterType RouterType + OppositeInterfaceOwnerId string + HealthCheckSourceIp string + HealthCheckTargetIp string +} + +type DescribeRouterInterfacesResponse struct { + RouterInterfaceSet struct { + RouterInterfaceType []RouterInterfaceItemType + } + common.PaginationResult +} + +// DescribeRouterInterfaces describe Router interfaces +// +// You can read doc at https://help.aliyun.com/document_detail/36032.html?spm=5176.product27706.6.664.EbBsxC +func (client *Client) DescribeRouterInterfaces(args *DescribeRouterInterfacesArgs) (response *DescribeRouterInterfacesResponse, err error) { + response = &DescribeRouterInterfacesResponse{} + err = client.Invoke("DescribeRouterInterfaces", args, &response) + if err != nil { + return response, err + } + return response, nil +} + +type OperateRouterInterfaceArgs struct { + RegionId common.Region + RouterInterfaceId string +} + +// ConnectRouterInterface +// +// You can read doc at https://help.aliyun.com/document_detail/36031.html?spm=5176.doc36035.6.666.wkyljN +func (client *Client) ConnectRouterInterface(args *OperateRouterInterfaceArgs) (response *EcsCommonResponse, err error) { + response = &EcsCommonResponse{} + err = client.Invoke("ConnectRouterInterface", args, &response) + if err != nil { + return response, err + } + return response, nil +} + +// ActivateRouterInterface active Router Interface +// +// You can read doc at https://help.aliyun.com/document_detail/36030.html?spm=5176.doc36031.6.667.DAuZLD +func (client *Client) ActivateRouterInterface(args *OperateRouterInterfaceArgs) (response *EcsCommonResponse, err error) { + response = &EcsCommonResponse{} + err = client.Invoke("ActivateRouterInterface", args, &response) + if err != nil { + return response, err + } + return response, nil +} + +// DeactivateRouterInterface deactivate Router Interface +// +// You can read doc at https://help.aliyun.com/document_detail/36033.html?spm=5176.doc36030.6.668.JqCWUz +func (client *Client) DeactivateRouterInterface(args *OperateRouterInterfaceArgs) (response *EcsCommonResponse, err error) { + response = &EcsCommonResponse{} + err = client.Invoke("DeactivateRouterInterface", args, &response) + if err != nil { + return response, err + } + return response, nil +} + +type ModifyRouterInterfaceSpecArgs struct { + RegionId common.Region + RouterInterfaceId string + Spec Spec +} + +type ModifyRouterInterfaceSpecResponse struct { + common.Response + Spec Spec +} + +// ModifyRouterInterfaceSpec +// +// You can read doc at https://help.aliyun.com/document_detail/36037.html?spm=5176.doc36036.6.669.McKiye +func (client *Client) ModifyRouterInterfaceSpec(args *ModifyRouterInterfaceSpecArgs) (response *ModifyRouterInterfaceSpecResponse, err error) { + response = &ModifyRouterInterfaceSpecResponse{} + err = client.Invoke("ModifyRouterInterfaceSpec", args, &response) + if err != nil { + return response, err + } + return response, nil +} + +type ModifyRouterInterfaceAttributeArgs struct { + RegionId common.Region + RouterInterfaceId string + Name string + Description string + OppositeInterfaceId string + OppositeRouterId string + OppositeInterfaceOwnerId string + HealthCheckSourceIp string + HealthCheckTargetIp string +} + +// ModifyRouterInterfaceAttribute +// +// You can read doc at https://help.aliyun.com/document_detail/36036.html?spm=5176.doc36037.6.670.Dcz3xS +func (client *Client) ModifyRouterInterfaceAttribute(args *ModifyRouterInterfaceAttributeArgs) (response *EcsCommonResponse, err error) { + response = &EcsCommonResponse{} + err = client.Invoke("ModifyRouterInterfaceAttribute", args, &response) + if err != nil { + return response, err + } + return response, nil +} + +// DeleteRouterInterface delete Router Interface +// +// You can read doc at https://help.aliyun.com/document_detail/36034.html?spm=5176.doc36036.6.671.y2xpNt +func (client *Client) DeleteRouterInterface(args *OperateRouterInterfaceArgs) (response *EcsCommonResponse, err error) { + response = &EcsCommonResponse{} + err = client.Invoke("DeleteRouterInterface", args, &response) + if err != nil { + return response, err + } + return response, nil +} diff --git a/vendor/github.com/denverdino/aliyungo/ram/client.go b/vendor/github.com/denverdino/aliyungo/ram/client.go index 974bc023f..2a29bd804 100644 --- a/vendor/github.com/denverdino/aliyungo/ram/client.go +++ b/vendor/github.com/denverdino/aliyungo/ram/client.go @@ -1,8 +1,9 @@ package ram import ( - "github.com/denverdino/aliyungo/common" "os" + + "github.com/denverdino/aliyungo/common" ) const ( @@ -16,15 +17,29 @@ type RamClient struct { } func NewClient(accessKeyId string, accessKeySecret string) RamClientInterface { + return NewClientWithSecurityToken(accessKeyId, accessKeySecret, "") +} + +func NewClientWithSecurityToken(accessKeyId string, accessKeySecret string, securityToken string) RamClientInterface { endpoint := os.Getenv("RAM_ENDPOINT") if endpoint == "" { endpoint = RAMDefaultEndpoint } - return NewClientWithEndpoint(endpoint, accessKeyId, accessKeySecret) + + return NewClientWithEndpointAndSecurityToken(endpoint, accessKeyId, accessKeySecret, securityToken) } func NewClientWithEndpoint(endpoint string, accessKeyId string, accessKeySecret string) RamClientInterface { + return NewClientWithEndpointAndSecurityToken(endpoint, accessKeyId, accessKeySecret, "") +} + +func NewClientWithEndpointAndSecurityToken(endpoint string, accessKeyId string, accessKeySecret string, securityToken string) RamClientInterface { client := &RamClient{} - client.Init(endpoint, RAMAPIVersion, accessKeyId, accessKeySecret) + client.WithEndpoint(endpoint). + WithVersion(RAMAPIVersion). + WithAccessKeyId(accessKeyId). + WithAccessKeySecret(accessKeySecret). + WithSecurityToken(securityToken). + InitClient() return client } diff --git a/vendor/github.com/denverdino/aliyungo/ram/group.go b/vendor/github.com/denverdino/aliyungo/ram/group.go index 6f1224f5f..491e6931d 100644 --- a/vendor/github.com/denverdino/aliyungo/ram/group.go +++ b/vendor/github.com/denverdino/aliyungo/ram/group.go @@ -31,6 +31,8 @@ type GroupResponse struct { type GroupListResponse struct { RamCommonResponse + IsTruncated bool + Marker string Groups struct { Group []Group } diff --git a/vendor/github.com/denverdino/aliyungo/ram/policy.go b/vendor/github.com/denverdino/aliyungo/ram/policy.go index b0a84b86d..0acfd71a7 100644 --- a/vendor/github.com/denverdino/aliyungo/ram/policy.go +++ b/vendor/github.com/denverdino/aliyungo/ram/policy.go @@ -1,8 +1,15 @@ package ram +type Type string + +const ( + Custom Type = "Custom" + System Type = "System" +) + type PolicyRequest struct { PolicyName string - PolicyType string + PolicyType Type Description string PolicyDocument string SetAsDefault string @@ -21,15 +28,16 @@ type PolicyResponse struct { } type PolicyQueryRequest struct { - PolicyType string + PolicyType Type Marker string MaxItems int8 } type PolicyQueryResponse struct { + RamCommonResponse IsTruncated bool Marker string - Policies struct { + Policies struct { Policy []Policy } } diff --git a/vendor/github.com/denverdino/aliyungo/util/encoding.go b/vendor/github.com/denverdino/aliyungo/util/encoding.go index 8cb588288..99a508f5b 100644 --- a/vendor/github.com/denverdino/aliyungo/util/encoding.go +++ b/vendor/github.com/denverdino/aliyungo/util/encoding.go @@ -66,24 +66,26 @@ func setQueryValues(i interface{}, values *url.Values, prefix string) { // TODO Use Tag for validation // tag := typ.Field(i).Tag.Get("tagname") kind := field.Kind() + isPtr := false if (kind == reflect.Ptr || kind == reflect.Array || kind == reflect.Slice || kind == reflect.Map || kind == reflect.Chan) && field.IsNil() { continue } if kind == reflect.Ptr { field = field.Elem() kind = field.Kind() + isPtr = true } var value string //switch field.Interface().(type) { switch kind { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: i := field.Int() - if i != 0 { + if i != 0 || isPtr { value = strconv.FormatInt(i, 10) } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: i := field.Uint() - if i != 0 { + if i != 0 || isPtr { value = strconv.FormatUint(i, 10) } case reflect.Float32: @@ -197,12 +199,14 @@ func setQueryValuesByFlattenMethod(i interface{}, values *url.Values, prefix str // tag := typ.Field(i).Tag.Get("tagname") kind := field.Kind() + isPtr := false if (kind == reflect.Ptr || kind == reflect.Array || kind == reflect.Slice || kind == reflect.Map || kind == reflect.Chan) && field.IsNil() { continue } if kind == reflect.Ptr { field = field.Elem() kind = field.Kind() + isPtr = true } var value string @@ -210,12 +214,12 @@ func setQueryValuesByFlattenMethod(i interface{}, values *url.Values, prefix str switch kind { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: i := field.Int() - if i != 0 { + if i != 0 || isPtr { value = strconv.FormatInt(i, 10) } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: i := field.Uint() - if i != 0 { + if i != 0 || isPtr { value = strconv.FormatUint(i, 10) } case reflect.Float32: diff --git a/vendor/vendor.json b/vendor/vendor.json index 3a5b35714..ddc99ef45 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -396,34 +396,34 @@ "revision": "6d212800a42e8ab5c146b8ace3490ee17e5225f9" }, { - "checksumSHA1": "b2m7aICkBOz9JqmnX2kdDN4wgjI=", + "checksumSHA1": "2+1TPdvFj4W1QS5drkFr+ibM3G0=", "path": "github.com/denverdino/aliyungo/common", - "revision": "b8a81c0f54003ea306ef566807919955d639cd48", - "revisionTime": "2017-08-02T08:24:47Z" + "revision": "ec0e57291175fc9b06c62977f384756642285aab", + "revisionTime": "2017-11-27T16:20:29Z" }, { - "checksumSHA1": "5UYxIc/DZ5g0SM7qwXskoyNOc78=", + "checksumSHA1": "y4Ay4E5HqT25sweC/Bl9/odNWaI=", "path": "github.com/denverdino/aliyungo/ecs", - "revision": "b8a81c0f54003ea306ef566807919955d639cd48", - "revisionTime": "2017-08-02T08:24:47Z" + "revision": "ec0e57291175fc9b06c62977f384756642285aab", + "revisionTime": "2017-11-27T16:20:29Z" }, { - "checksumSHA1": "VfIjW9Tf2eLmHrgrvk1wRnBaxTE=", + "checksumSHA1": "sievsBvgtVF2iZ2FjmDZppH3+Ro=", "path": "github.com/denverdino/aliyungo/ram", - "revision": "b8a81c0f54003ea306ef566807919955d639cd48", - "revisionTime": "2017-08-02T08:24:47Z" + "revision": "ec0e57291175fc9b06c62977f384756642285aab", + "revisionTime": "2017-11-27T16:20:29Z" }, { "checksumSHA1": "pQHH9wpyS0e4wpW0erxe3D7OILM=", "path": "github.com/denverdino/aliyungo/slb", - "revision": "b8a81c0f54003ea306ef566807919955d639cd48", - "revisionTime": "2017-08-02T08:24:47Z" + "revision": "ec0e57291175fc9b06c62977f384756642285aab", + "revisionTime": "2017-11-27T16:20:29Z" }, { - "checksumSHA1": "piZlmhWPLGxYkXLysTrjcXllO4c=", + "checksumSHA1": "cKVBRn7GKT+0IqfGUc/NnKDWzCw=", "path": "github.com/denverdino/aliyungo/util", - "revision": "b8a81c0f54003ea306ef566807919955d639cd48", - "revisionTime": "2017-08-02T08:24:47Z" + "revision": "ec0e57291175fc9b06c62977f384756642285aab", + "revisionTime": "2017-11-27T16:20:29Z" }, { "checksumSHA1": "D37uI+U+FYvTJIdG2TTozXe7i7U=", diff --git a/website/source/docs/builders/alicloud-ecs.html.md b/website/source/docs/builders/alicloud-ecs.html.md index e634e8840..500d4debb 100644 --- a/website/source/docs/builders/alicloud-ecs.html.md +++ b/website/source/docs/builders/alicloud-ecs.html.md @@ -182,6 +182,12 @@ builder. generate. By default, Packer generates a name that looks like `packer_`, where `` is a 36 character unique identifier. +- `security_token` (string) - STS access token, can be set through template or by exporting + as environment vairalbe such "export SecurityToken=value". + +- `TLSHandshakeTimeout` (int) - When happen "net/http: TLS handshake timeout" problem, set this environment variable + to a bigger such as "export TLSHandshakeTimeout=30", it will set the TLS handshake timeout value to 30s. + ## Basic Example Here is a basic example for Alicloud. From 2f7b9640d9badc085cdaf73502360ea109a6b645 Mon Sep 17 00:00:00 2001 From: zhuzhih2017 Date: Tue, 28 Nov 2017 11:16:14 +0800 Subject: [PATCH 0108/1216] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 889ed4d44..8cb17b49e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ ## (UNRELEASED) ### IMPROVEMENTS: +* builder/alicloud-ecs: Add security token support and set tls handshake timeout + through environment variable. [GH-5641] ### BUG FIXES: From 8f02150178236e7a1215a3b3e9d52312436cea56 Mon Sep 17 00:00:00 2001 From: Casey Robertson Date: Tue, 28 Nov 2017 14:23:12 -0800 Subject: [PATCH 0109/1216] Changes Linux install URL to omnitruck. Changes powershell install to use omnitruck rather than hard-coded 32-bit url --- provisioner/chef-client/provisioner.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/provisioner/chef-client/provisioner.go b/provisioner/chef-client/provisioner.go index 3853613fc..c7bd865dd 100644 --- a/provisioner/chef-client/provisioner.go +++ b/provisioner/chef-client/provisioner.go @@ -30,13 +30,13 @@ type guestOSTypeConfig struct { var guestOSTypeConfigs = map[string]guestOSTypeConfig{ provisioner.UnixOSType: { executeCommand: "{{if .Sudo}}sudo {{end}}chef-client --no-color -c {{.ConfigPath}} -j {{.JsonPath}}", - installCommand: "curl -L https://www.chef.io/chef/install.sh | {{if .Sudo}}sudo {{end}}bash", + installCommand: "curl -L https://omnitruck.chef.io/install.sh | {{if .Sudo}}sudo {{end}}bash", knifeCommand: "{{if .Sudo}}sudo {{end}}knife {{.Args}} {{.Flags}}", stagingDir: "/tmp/packer-chef-client", }, provisioner.WindowsOSType: { executeCommand: "c:/opscode/chef/bin/chef-client.bat --no-color -c {{.ConfigPath}} -j {{.JsonPath}}", - installCommand: "powershell.exe -Command \"(New-Object System.Net.WebClient).DownloadFile('http://chef.io/chef/install.msi', 'C:\\Windows\\Temp\\chef.msi');Start-Process 'msiexec' -ArgumentList '/qb /i C:\\Windows\\Temp\\chef.msi' -NoNewWindow -Wait\"", + installCommand: "powershell.exe -Command \". { iwr -useb https://omnitruck.chef.io/install.ps1 } | iex; install\"", knifeCommand: "c:/opscode/chef/bin/knife.bat {{.Args}} {{.Flags}}", stagingDir: "C:/Windows/Temp/packer-chef-client", }, From b66426e668cda04beffe4464c6782cd71e6deb94 Mon Sep 17 00:00:00 2001 From: Hariharan Jayaraman Date: Tue, 28 Nov 2017 16:23:49 -0800 Subject: [PATCH 0110/1216] Updates to Docs to ensure permission issues are clearer --- website/source/docs/builders/azure.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/builders/azure.html.md b/website/source/docs/builders/azure.html.md index f673d34fd..779a374b8 100644 --- a/website/source/docs/builders/azure.html.md +++ b/website/source/docs/builders/azure.html.md @@ -29,7 +29,7 @@ builder. - `client_secret` (string) The password or secret for your service principal. -- `subscription_id` (string) Subscription under which the build will be performed. **The service principal specified in `client_id` must have full access to this subscription.** +- `subscription_id` (string) Subscription under which the build will be performed. **The service principal specified in `client_id` must have full access to this subscription, unless build_resource_group_name option is specified in which case it needs to have owner access to the existing resource group specified in build_resource_group_name parameter.** - `capture_container_name` (string) Destination container name. Essentially the "directory" where your VHD will be organized in Azure. The captured VHD's URL will be `https://.blob.core.windows.net/system/Microsoft.Compute/Images//.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.vhd`. - `image_publisher` (string) PublisherName for your base image. See [documentation](https://azure.microsoft.com/en-us/documentation/articles/resource-groups-vm-searching/) for details. From da0c13f622234be9c70183d32fcbcf4fa9ec0e4a Mon Sep 17 00:00:00 2001 From: Christopher Boumenot Date: Thu, 30 Nov 2017 00:11:17 -0800 Subject: [PATCH 0111/1216] azure: delete keyvault deployment --- builder/azure/arm/azure_client.go | 15 +++ builder/azure/arm/builder.go | 12 +- .../azure/arm/step_delete_resource_group.go | 110 +++++++++++------- builder/azure/arm/step_deploy_template.go | 15 ++- .../azure/arm/step_deploy_template_test.go | 4 +- builder/azure/common/constants/stateBag.go | 1 + builder/azure/common/vault.go | 78 ++++++++++++- 7 files changed, 178 insertions(+), 57 deletions(-) diff --git a/builder/azure/arm/azure_client.go b/builder/azure/arm/azure_client.go index 08dd95eae..c4d8b544d 100644 --- a/builder/azure/arm/azure_client.go +++ b/builder/azure/arm/azure_client.go @@ -48,6 +48,7 @@ type AzureClient struct { InspectorMaxLength int Template *CaptureTemplate LastError azureErrorResponse + VaultClientDelete common.VaultClient } func getCaptureResponse(body string) *CaptureTemplate { @@ -209,6 +210,20 @@ func NewAzureClient(subscriptionID, resourceGroupName, storageAccountName string azureClient.VaultClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) azureClient.VaultClient.UserAgent += packerUserAgent + // TODO(boumenot) - SDK still does not have a full KeyVault client. + // There are two ways that KeyVault has to be accessed, and each one has their own SPN. An authenticated SPN + // is tied to the URL, and the URL associated with getting the secret is different than the URL + // associated with deleting the KeyVault. As a result, I need to have *two* different clients to + // access KeyVault. I did not want to split it into two separate files, so I am starting with this. + // + // I do not like this implementation. It is getting long in the tooth, and should be re-examined now + // that we have a "working" solution. + azureClient.VaultClientDelete = common.NewVaultClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) + azureClient.VaultClientDelete.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) + azureClient.VaultClientDelete.RequestInspector = withInspection(maxlen) + azureClient.VaultClientDelete.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) + azureClient.VaultClientDelete.UserAgent += packerUserAgent + // If this is a managed disk build, this should be ignored. if resourceGroupName != "" && storageAccountName != "" { accountKeys, err := azureClient.AccountsClient.ListKeys(resourceGroupName, storageAccountName) diff --git a/builder/azure/arm/builder.go b/builder/azure/arm/builder.go index 3874cdb05..c6b8bcbe1 100644 --- a/builder/azure/arm/builder.go +++ b/builder/azure/arm/builder.go @@ -127,11 +127,13 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe b.setImageParameters(b.stateBag) var steps []multistep.Step + deploymentName := b.stateBag.Get(constants.ArmDeploymentName).(string) + if b.config.OSType == constants.Target_Linux { steps = []multistep.Step{ NewStepCreateResourceGroup(azureClient, ui), NewStepValidateTemplate(azureClient, ui, b.config, GetVirtualMachineDeployment), - NewStepDeployTemplate(azureClient, ui, b.config, GetVirtualMachineDeployment), + NewStepDeployTemplate(azureClient, ui, b.config, deploymentName, GetVirtualMachineDeployment), NewStepGetIPAddress(azureClient, ui, endpointConnectType), &communicator.StepConnectSSH{ Config: &b.config.Comm, @@ -146,14 +148,15 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe NewStepDeleteOSDisk(azureClient, ui), } } else if b.config.OSType == constants.Target_Windows { + keyVaultDeploymentName := b.stateBag.Get(constants.ArmKeyVaultDeploymentName).(string) steps = []multistep.Step{ NewStepCreateResourceGroup(azureClient, ui), NewStepValidateTemplate(azureClient, ui, b.config, GetKeyVaultDeployment), - NewStepDeployTemplate(azureClient, ui, b.config, GetKeyVaultDeployment), + NewStepDeployTemplate(azureClient, ui, b.config, keyVaultDeploymentName, GetKeyVaultDeployment), NewStepGetCertificate(azureClient, ui), NewStepSetCertificate(b.config, ui), NewStepValidateTemplate(azureClient, ui, b.config, GetVirtualMachineDeployment), - NewStepDeployTemplate(azureClient, ui, b.config, GetVirtualMachineDeployment), + NewStepDeployTemplate(azureClient, ui, b.config, deploymentName, GetVirtualMachineDeployment), NewStepGetIPAddress(azureClient, ui, endpointConnectType), &communicator.StepConnectWinRM{ Config: &b.config.Comm, @@ -283,6 +286,9 @@ func (b *Builder) configureStateBag(stateBag multistep.StateBag) { stateBag.Put(constants.ArmTags, &b.config.AzureTags) stateBag.Put(constants.ArmComputeName, b.config.tmpComputeName) stateBag.Put(constants.ArmDeploymentName, b.config.tmpDeploymentName) + if b.config.OSType == constants.Target_Windows { + stateBag.Put(constants.ArmKeyVaultDeploymentName, fmt.Sprintf("kv%s", b.config.tmpDeploymentName)) + } stateBag.Put(constants.ArmKeyVaultName, b.config.tmpKeyVaultName) stateBag.Put(constants.ArmLocation, b.config.Location) stateBag.Put(constants.ArmNicName, DefaultNicName) diff --git a/builder/azure/arm/step_delete_resource_group.go b/builder/azure/arm/step_delete_resource_group.go index 147a33349..c3db6de0a 100644 --- a/builder/azure/arm/step_delete_resource_group.go +++ b/builder/azure/arm/step_delete_resource_group.go @@ -37,54 +37,19 @@ func (s *StepDeleteResourceGroup) deleteResourceGroup(state multistep.StateBag, if state.Get(constants.ArmIsExistingResourceGroup).(bool) { s.say("\nThe resource group was not created by Packer, only deleting individual resources ...") var deploymentName = state.Get(constants.ArmDeploymentName).(string) - if deploymentName != "" { - maxResources := int32(maxResourcesToDelete) - deploymentOperations, err := s.client.DeploymentOperationsClient.List(resourceGroupName, deploymentName, &maxResources) + err = s.deleteDeploymentResources(deploymentName, resourceGroupName) + if err != nil { + return err + } + + if keyVaultDeploymentName, ok := state.GetOk(constants.ArmKeyVaultDeploymentName); ok { + err = s.deleteDeploymentResources(keyVaultDeploymentName.(string), resourceGroupName) if err != nil { - s.say(fmt.Sprintf("Error deleting resources. Please delete them manually.\n\n"+ - "Name: %s\n"+ - "Error: %s", resourceGroupName, err)) - s.error(err) - } - for _, deploymentOperation := range *deploymentOperations.Value { - // Sometimes an empty operation is added to the list by Azure - if deploymentOperation.Properties.TargetResource == nil { - continue - } - s.say(fmt.Sprintf(" -> %s : '%s'", - *deploymentOperation.Properties.TargetResource.ResourceType, - *deploymentOperation.Properties.TargetResource.ResourceName)) - var networkDeleteFunction func(string, string, <-chan struct{}) (<-chan autorest.Response, <-chan error) - switch *deploymentOperation.Properties.TargetResource.ResourceType { - case "Microsoft.Compute/virtualMachines": - _, errChan := s.client.VirtualMachinesClient.Delete(resourceGroupName, *deploymentOperation.Properties.TargetResource.ResourceName, nil) - err := <-errChan - if err != nil { - s.say(fmt.Sprintf("Error deleting resource. Please delete manually.\n\n"+ - "Name: %s\n"+ - "Error: %s", *deploymentOperation.Properties.TargetResource.ResourceName, err.Error())) - s.error(err) - } - case "Microsoft.Network/networkInterfaces": - networkDeleteFunction = s.client.InterfacesClient.Delete - case "Microsoft.Network/virtualNetworks": - networkDeleteFunction = s.client.VirtualNetworksClient.Delete - case "Microsoft.Network/publicIPAddresses": - networkDeleteFunction = s.client.PublicIPAddressesClient.Delete - } - if networkDeleteFunction != nil { - _, errChan := networkDeleteFunction(resourceGroupName, *deploymentOperation.Properties.TargetResource.ResourceName, nil) - err := <-errChan - if err != nil { - s.say(fmt.Sprintf("Error deleting resource. Please delete manually.\n\n"+ - "Name: %s\n"+ - "Error: %s", *deploymentOperation.Properties.TargetResource.ResourceName, err.Error())) - s.error(err) - } - } + return err } } - return err + + return nil } else { s.say("\nThe resource group was created by Packer, deleting ...") _, errChan := s.client.GroupsClient.Delete(resourceGroupName, cancelCh) @@ -97,6 +62,61 @@ func (s *StepDeleteResourceGroup) deleteResourceGroup(state multistep.StateBag, } } +func (s *StepDeleteResourceGroup) deleteDeploymentResources(deploymentName, resourceGroupName string) error { + maxResources := int32(maxResourcesToDelete) + + deploymentOperations, err := s.client.DeploymentOperationsClient.List(resourceGroupName, deploymentName, &maxResources) + if err != nil { + s.reportIfError(err, resourceGroupName) + return err + } + + for _, deploymentOperation := range *deploymentOperations.Value { + // Sometimes an empty operation is added to the list by Azure + if deploymentOperation.Properties.TargetResource == nil { + continue + } + s.say(fmt.Sprintf(" -> %s : '%s'", + *deploymentOperation.Properties.TargetResource.ResourceType, + *deploymentOperation.Properties.TargetResource.ResourceName)) + + var networkDeleteFunction func(string, string, <-chan struct{}) (<-chan autorest.Response, <-chan error) + resourceName := *deploymentOperation.Properties.TargetResource.ResourceName + + switch *deploymentOperation.Properties.TargetResource.ResourceType { + case "Microsoft.Compute/virtualMachines": + _, errChan := s.client.VirtualMachinesClient.Delete(resourceGroupName, resourceName, nil) + err := <-errChan + s.reportIfError(err, resourceName) + case "Microsoft.KeyVault/vaults": + _, err := s.client.VaultClientDelete.Delete(resourceGroupName, resourceName) + s.reportIfError(err, resourceName) + case "Microsoft.Network/networkInterfaces": + networkDeleteFunction = s.client.InterfacesClient.Delete + case "Microsoft.Network/virtualNetworks": + networkDeleteFunction = s.client.VirtualNetworksClient.Delete + case "Microsoft.Network/publicIPAddresses": + networkDeleteFunction = s.client.PublicIPAddressesClient.Delete + } + if networkDeleteFunction != nil { + _, errChan := networkDeleteFunction(resourceGroupName, resourceName, nil) + err := <-errChan + s.reportIfError(err, resourceName) + } + } + + return nil +} + +func (s *StepDeleteResourceGroup) reportIfError(err error, resourceName string) { + if err != nil { + s.say(fmt.Sprintf("Error deleting resource. Please delete manually.\n\n"+ + "Name: %s\n"+ + "Error: %s", resourceName, err.Error())) + s.error(err) + } +} + func (s *StepDeleteResourceGroup) Run(state multistep.StateBag) multistep.StepAction { s.say("Deleting resource group ...") diff --git a/builder/azure/arm/step_deploy_template.go b/builder/azure/arm/step_deploy_template.go index 53b639d51..b67dd5c1d 100644 --- a/builder/azure/arm/step_deploy_template.go +++ b/builder/azure/arm/step_deploy_template.go @@ -22,15 +22,17 @@ type StepDeployTemplate struct { error func(e error) config *Config factory templateFactoryFunc + name string } -func NewStepDeployTemplate(client *AzureClient, ui packer.Ui, config *Config, factory templateFactoryFunc) *StepDeployTemplate { +func NewStepDeployTemplate(client *AzureClient, ui packer.Ui, config *Config, deploymentName string, factory templateFactoryFunc) *StepDeployTemplate { var step = &StepDeployTemplate{ client: client, say: func(message string) { ui.Say(message) }, error: func(e error) { ui.Error(e.Error()) }, config: config, factory: factory, + name: deploymentName, } step.deploy = step.deployTemplate @@ -59,15 +61,14 @@ func (s *StepDeployTemplate) Run(state multistep.StateBag) multistep.StepAction s.say("Deploying deployment template ...") var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) - var deploymentName = state.Get(constants.ArmDeploymentName).(string) s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName)) - s.say(fmt.Sprintf(" -> DeploymentName : '%s'", deploymentName)) + s.say(fmt.Sprintf(" -> DeploymentName : '%s'", s.name)) result := common.StartInterruptibleTask( func() bool { return common.IsStateCancelled(state) }, func(cancelCh <-chan struct{}) error { - return s.deploy(resourceGroupName, deploymentName, cancelCh) + return s.deploy(resourceGroupName, s.name, cancelCh) }, ) @@ -104,6 +105,9 @@ func (s *StepDeployTemplate) deleteOperationResource(resourceType string, resour return err } + case "Microsoft.KeyVault/vaults": + _, err := s.client.VaultClientDelete.Delete(resourceGroupName, resourceName) + return err case "Microsoft.Network/networkInterfaces": networkDeleteFunction = s.client.InterfacesClient.Delete case "Microsoft.Network/virtualNetworks": @@ -142,7 +146,6 @@ func (s *StepDeployTemplate) deleteImage(imageType string, imageName string, res blob := s.client.BlobStorageClient.GetContainerReference(storageAccountName).GetBlobReference(blobName) err = blob.Delete(nil) return err - } func (s *StepDeployTemplate) Cleanup(state multistep.StateBag) { @@ -158,7 +161,7 @@ func (s *StepDeployTemplate) Cleanup(state multistep.StateBag) { var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) var computeName = state.Get(constants.ArmComputeName).(string) - var deploymentName = state.Get(constants.ArmDeploymentName).(string) + var deploymentName = s.name imageType, imageName, err := s.disk(resourceGroupName, computeName) if err != nil { ui.Error("Could not retrieve OS Image details") diff --git a/builder/azure/arm/step_deploy_template_test.go b/builder/azure/arm/step_deploy_template_test.go index 3a1d0c0bc..d812e0ffa 100644 --- a/builder/azure/arm/step_deploy_template_test.go +++ b/builder/azure/arm/step_deploy_template_test.go @@ -61,6 +61,7 @@ func TestStepDeployTemplateShouldTakeStepArgumentsFromStateBag(t *testing.T) { }, say: func(message string) {}, error: func(e error) {}, + name: "--deployment-name--", } stateBag := createTestStateBagStepValidateTemplate() @@ -70,10 +71,9 @@ func TestStepDeployTemplateShouldTakeStepArgumentsFromStateBag(t *testing.T) { t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) } - var expectedDeploymentName = stateBag.Get(constants.ArmDeploymentName).(string) var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string) - if actualDeploymentName != expectedDeploymentName { + if actualDeploymentName != "--deployment-name--" { t.Fatal("Expected StepValidateTemplate to source 'constants.ArmDeploymentName' from the state bag, but it did not.") } diff --git a/builder/azure/common/constants/stateBag.go b/builder/azure/common/constants/stateBag.go index 6d5a93068..720979b9c 100644 --- a/builder/azure/common/constants/stateBag.go +++ b/builder/azure/common/constants/stateBag.go @@ -15,6 +15,7 @@ const ( ArmComputeName string = "arm.ComputeName" ArmImageParameters string = "arm.ImageParameters" ArmCertificateUrl string = "arm.CertificateUrl" + ArmKeyVaultDeploymentName string = "arm.KeyVaultDeploymentName" ArmDeploymentName string = "arm.DeploymentName" ArmNicName string = "arm.NicName" ArmKeyVaultName string = "arm.KeyVaultName" diff --git a/builder/azure/common/vault.go b/builder/azure/common/vault.go index db5c6db0c..ab54ee493 100644 --- a/builder/azure/common/vault.go +++ b/builder/azure/common/vault.go @@ -9,15 +9,18 @@ import ( "net/url" "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" ) const ( - AzureVaultApiVersion = "2015-06-01" + AzureVaultApiVersion = "2016-10-01" ) type VaultClient struct { autorest.Client keyVaultEndpoint url.URL + SubscriptionID string + baseURI string } func NewVaultClient(keyVaultEndpoint url.URL) VaultClient { @@ -26,6 +29,13 @@ func NewVaultClient(keyVaultEndpoint url.URL) VaultClient { } } +func NewVaultClientWithBaseURI(baseURI, subscriptionID string) VaultClient { + return VaultClient{ + baseURI: baseURI, + SubscriptionID: subscriptionID, + } +} + type Secret struct { ID *string `json:"id,omitempty"` Value string `json:"value"` @@ -76,6 +86,72 @@ func (client *VaultClient) GetSecret(vaultName, secretName string) (*Secret, err return &secret, nil } +// Delete deletes the specified Azure key vault. +// +// resourceGroupName is the name of the Resource Group to which the vault belongs. vaultName is the name of the vault +// to delete +func (client *VaultClient) Delete(resourceGroupName string, vaultName string) (result autorest.Response, err error) { + req, err := client.DeletePreparer(resourceGroupName, vaultName) + if err != nil { + err = autorest.NewErrorWithError(err, "keyvault.VaultsClient", "Delete", nil, "Failure preparing request") + return + } + + resp, err := client.DeleteSender(req) + if err != nil { + result.Response = resp + err = autorest.NewErrorWithError(err, "keyvault.VaultsClient", "Delete", resp, "Failure sending request") + return + } + + result, err = client.DeleteResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "keyvault.VaultsClient", "Delete", resp, "Failure responding to request") + } + + return +} + +// DeletePreparer prepares the Delete request. +func (client *VaultClient) DeletePreparer(resourceGroupName string, vaultName string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "SubscriptionID": autorest.Encode("path", client.SubscriptionID), + "vaultName": autorest.Encode("path", vaultName), + } + + queryParameters := map[string]interface{}{ + "api-version": AzureVaultApiVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsDelete(), + autorest.WithBaseURL(client.baseURI), + autorest.WithPathParameters("/subscriptions/{SubscriptionID}/resourceGroups/{resourceGroupName}/providers/Microsoft.KeyVault/vaults/{vaultName}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// DeleteSender sends the Delete request. The method will close the +// http.Response Body if it receives an error. +func (client *VaultClient) DeleteSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, + req, + azure.DoPollForAsynchronous(client.PollingDelay)) +} + +// DeleteResponder handles the response to the Delete request. The method always +// closes the http.Response Body. +func (client *VaultClient) DeleteResponder(resp *http.Response) (result autorest.Response, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByClosing()) + result.Response = resp + return +} + func (client *VaultClient) getVaultUrl(vaultName string) string { return fmt.Sprintf("%s://%s.%s/", client.keyVaultEndpoint.Scheme, vaultName, client.keyVaultEndpoint.Host) } From 556da47d35139b653dddac1bded87cc8228f5a2d Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 30 Nov 2017 13:51:33 -0800 Subject: [PATCH 0112/1216] modify validation to allow user to have the original region in the ami_regions list --- builder/amazon/common/ami_config.go | 38 ++++++++++++----------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/builder/amazon/common/ami_config.go b/builder/amazon/common/ami_config.go index aa0792e3a..50c01975a 100644 --- a/builder/amazon/common/ami_config.go +++ b/builder/amazon/common/ami_config.go @@ -41,22 +41,20 @@ func stringInSlice(s []string, searchstr string) bool { func (c *AMIConfig) Prepare(accessConfig *AccessConfig, ctx *interpolate.Context) []error { var errs []error - if accessConfig != nil { - session, err := accessConfig.Session() - if err != nil { - errs = append(errs, err) - } else { - region := *session.Config.Region - if stringInSlice(c.AMIRegions, region) { - errs = append(errs, fmt.Errorf("Cannot copy AMI to AWS session region '%s', please remove it from `ami_regions`.", region)) - } - } - } - if c.AMIName == "" { errs = append(errs, fmt.Errorf("ami_name must be specified")) } + // Make sure that if we have region_kms_key_ids defined, + // the regions in region_kms_key_ids are also in ami_regions + if len(c.AMIRegionKMSKeyIDs) > 0 { + for kmsKeyRegion := range c.AMIRegionKMSKeyIDs { + if !stringInSlice(c.AMIRegions, kmsKeyRegion) { + errs = append(errs, fmt.Errorf("Region %s is in region_kms_key_ids but not in ami_regions", kmsKeyRegion)) + } + } + } + if len(c.AMIRegions) > 0 { regionSet := make(map[string]struct{}) regions := make([]string, 0, len(c.AMIRegions)) @@ -84,21 +82,17 @@ func (c *AMIConfig) Prepare(accessConfig *AccessConfig, ctx *interpolate.Context errs = append(errs, fmt.Errorf("Region %s is in ami_regions but not in region_kms_key_ids", region)) } } - + if (accessConfig != nil) && (region == accessConfig.RawRegion) { + // make sure we don't try to copy to the region we originally + // create the AMI in. + fmt.Printf("Cannot copy AMI to AWS session region '%s', deleting it from `ami_regions`.", region) + continue + } regions = append(regions, region) } c.AMIRegions = regions } - // Make sure that if we have region_kms_key_ids defined, - // the regions in region_kms_key_ids are also in ami_regions - if len(c.AMIRegionKMSKeyIDs) > 0 { - for kmsKeyRegion := range c.AMIRegionKMSKeyIDs { - if !stringInSlice(c.AMIRegions, kmsKeyRegion) { - errs = append(errs, fmt.Errorf("Region %s is in region_kms_key_ids but not in ami_regions", kmsKeyRegion)) - } - } - } if len(c.AMIUsers) > 0 && c.AMIEncryptBootVolume { errs = append(errs, fmt.Errorf("Cannot share AMI with encrypted boot volume")) From 1c681fc0961230775bc7e2fb409ac868ab405424 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 30 Nov 2017 14:08:48 -0800 Subject: [PATCH 0113/1216] tests --- builder/amazon/common/ami_config.go | 3 ++- builder/amazon/common/ami_config_test.go | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/builder/amazon/common/ami_config.go b/builder/amazon/common/ami_config.go index 50c01975a..9af2244ee 100644 --- a/builder/amazon/common/ami_config.go +++ b/builder/amazon/common/ami_config.go @@ -2,6 +2,7 @@ package common import ( "fmt" + "log" "github.com/hashicorp/packer/template/interpolate" ) @@ -85,7 +86,7 @@ func (c *AMIConfig) Prepare(accessConfig *AccessConfig, ctx *interpolate.Context if (accessConfig != nil) && (region == accessConfig.RawRegion) { // make sure we don't try to copy to the region we originally // create the AMI in. - fmt.Printf("Cannot copy AMI to AWS session region '%s', deleting it from `ami_regions`.", region) + log.Printf("Cannot copy AMI to AWS session region '%s', deleting it from `ami_regions`.", region) continue } regions = append(regions, region) diff --git a/builder/amazon/common/ami_config_test.go b/builder/amazon/common/ami_config_test.go index 5f130130c..120c88bfc 100644 --- a/builder/amazon/common/ami_config_test.go +++ b/builder/amazon/common/ami_config_test.go @@ -11,6 +11,12 @@ func testAMIConfig() *AMIConfig { } } +func getFakeAccessConfig(region string) *AccessConfig { + return &AccessConfig{ + RawRegion: region, + } +} + func TestAMIConfigPrepare_name(t *testing.T) { c := testAMIConfig() if err := c.Prepare(nil, nil); err != nil { @@ -118,6 +124,15 @@ func TestAMIConfigPrepare_regions(t *testing.T) { if err := c.Prepare(nil, nil); err == nil { t.Fatal("should have error b/c theres a region in in ami_regions that isn't in the key map") } + + // allow rawregion to exist in ami_regions list. + accessConf := getFakeAccessConfig("us-east-1") + c.AMIRegions = []string{"us-east-1", "us-west-1", "us-east-2"} + c.AMIRegionKMSKeyIDs = nil + if err := c.Prepare(accessConf, nil); err != nil { + t.Fatal("should allow user to have the raw region in ami_regions") + } + } func TestAMIConfigPrepare_Share_EncryptedBoot(t *testing.T) { From c28a50af168ef289ae8fa956f6cea9a538a42608 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 30 Nov 2017 16:30:28 -0800 Subject: [PATCH 0114/1216] try to fix transient test failure --- packer/provisioner_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packer/provisioner_test.go b/packer/provisioner_test.go index 97304d2a5..22d34806f 100644 --- a/packer/provisioner_test.go +++ b/packer/provisioner_test.go @@ -67,7 +67,7 @@ func TestProvisionHook_cancel(t *testing.T) { p := &MockProvisioner{ ProvFunc: func() error { - time.Sleep(50 * time.Millisecond) + time.Sleep(100 * time.Millisecond) lock.Lock() defer lock.Unlock() From 0e08640fff9dd62aab47bb4dceb3f8b6c696a968 Mon Sep 17 00:00:00 2001 From: Krzysztof Wilczynski Date: Sun, 3 Dec 2017 23:04:25 +0100 Subject: [PATCH 0115/1216] Re-factor version command to use version.FormattedVersion() function. This commit removes surplus code which is almost a duplicate of the code available in the version package by favouring the package implementation instead. Signed-off-by: Krzysztof Wilczynski --- command/version.go | 28 ++++++++-------------------- commands.go | 8 ++------ 2 files changed, 10 insertions(+), 26 deletions(-) diff --git a/command/version.go b/command/version.go index 1142e1747..3d45205bd 100644 --- a/command/version.go +++ b/command/version.go @@ -1,18 +1,16 @@ package command import ( - "bytes" "fmt" + + "github.com/hashicorp/packer/version" ) // VersionCommand is a Command implementation prints the version. type VersionCommand struct { Meta - Revision string - Version string - VersionPrerelease string - CheckFunc VersionCheckFunc + CheckFunc VersionCheckFunc } // VersionCheckFunc is the callback called by the Version command to @@ -29,25 +27,15 @@ type VersionCheckInfo struct { } func (c *VersionCommand) Help() string { - return "" + return "Prints the Packer version, and checks for new release." } func (c *VersionCommand) Run(args []string) int { - c.Ui.Machine("version", c.Version) - c.Ui.Machine("version-prelease", c.VersionPrerelease) - c.Ui.Machine("version-commit", c.Revision) + c.Ui.Machine("version", version.Version) + c.Ui.Machine("version-prelease", version.VersionPrerelease) + c.Ui.Machine("version-commit", version.GitCommit) - var versionString bytes.Buffer - fmt.Fprintf(&versionString, "Packer v%s", c.Version) - if c.VersionPrerelease != "" { - fmt.Fprintf(&versionString, "-%s", c.VersionPrerelease) - - if c.Revision != "" { - fmt.Fprintf(&versionString, " (%s)", c.Revision) - } - } - - c.Ui.Say(versionString.String()) + c.Ui.Say(fmt.Sprintf("Packer v%s", version.FormattedVersion())) // If we have a version check function, then let's check for // the latest version as well. diff --git a/commands.go b/commands.go index 17b6a7f36..47ee467a5 100644 --- a/commands.go +++ b/commands.go @@ -2,7 +2,6 @@ package main import ( "github.com/hashicorp/packer/command" - "github.com/hashicorp/packer/version" "github.com/mitchellh/cli" ) @@ -50,11 +49,8 @@ func init() { "version": func() (cli.Command, error) { return &command.VersionCommand{ - Meta: *CommandMeta, - Revision: version.GitCommit, - Version: version.Version, - VersionPrerelease: version.VersionPrerelease, - CheckFunc: commandVersionCheck, + Meta: *CommandMeta, + CheckFunc: commandVersionCheck, }, nil }, From d043c37ad41dd1df465e434a94d4c23c617d811d Mon Sep 17 00:00:00 2001 From: Jamie Lennox Date: Fri, 1 Dec 2017 11:43:35 +1100 Subject: [PATCH 0116/1216] Azure: Don't provide location for build_resource_group_name Location is required by default because you must specify where to create the resource group containing the packer resources. When using build_resource_group_name you are specifying that packer should use an existing resource group and so the location that resources are in can be determined by fetching the information from the existing group. It is forbidden to pass both variables as it is easier and more intuitive that the location comes from the group rather than ignore a parameter. Closes: #5655 --- builder/azure/arm/builder.go | 11 +++++- builder/azure/arm/builder_test.go | 1 - builder/azure/arm/config.go | 8 ++--- builder/azure/arm/config_test.go | 1 + website/source/docs/builders/azure.html.md | 40 ++++++++++++++++------ 5 files changed, 45 insertions(+), 16 deletions(-) diff --git a/builder/azure/arm/builder.go b/builder/azure/arm/builder.go index c6b8bcbe1..d1f98bbd2 100644 --- a/builder/azure/arm/builder.go +++ b/builder/azure/arm/builder.go @@ -103,6 +103,15 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe } } + if b.config.BuildResourceGroupName != "" { + group, err := azureClient.GroupsClient.Get(b.config.BuildResourceGroupName) + if err != nil { + return nil, fmt.Errorf("Cannot locate the existing build resource resource group %s.", b.config.BuildResourceGroupName) + } + + b.config.Location = *group.Location + } + if b.config.StorageAccount != "" { account, err := b.getBlobAccount(azureClient, b.config.ResourceGroupName, b.config.StorageAccount) if err != nil { @@ -290,7 +299,6 @@ func (b *Builder) configureStateBag(stateBag multistep.StateBag) { stateBag.Put(constants.ArmKeyVaultDeploymentName, fmt.Sprintf("kv%s", b.config.tmpDeploymentName)) } stateBag.Put(constants.ArmKeyVaultName, b.config.tmpKeyVaultName) - stateBag.Put(constants.ArmLocation, b.config.Location) stateBag.Put(constants.ArmNicName, DefaultNicName) stateBag.Put(constants.ArmPublicIPAddressName, DefaultPublicIPAddressName) if b.config.TempResourceGroupName != "" && b.config.BuildResourceGroupName != "" { @@ -312,6 +320,7 @@ func (b *Builder) configureStateBag(stateBag multistep.StateBag) { // Parameters that are only known at runtime after querying Azure. func (b *Builder) setRuntimeParameters(stateBag multistep.StateBag) { + stateBag.Put(constants.ArmLocation, b.config.Location) stateBag.Put(constants.ArmManagedImageLocation, b.config.manageImageLocation) } diff --git a/builder/azure/arm/builder_test.go b/builder/azure/arm/builder_test.go index a44d363d0..2db475a02 100644 --- a/builder/azure/arm/builder_test.go +++ b/builder/azure/arm/builder_test.go @@ -20,7 +20,6 @@ func TestStateBagShouldBePopulatedExpectedValues(t *testing.T) { constants.ArmTags, constants.ArmComputeName, constants.ArmDeploymentName, - constants.ArmLocation, constants.ArmNicName, constants.ArmResourceGroupName, constants.ArmStorageAccountName, diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index b2301db6c..f384c88bc 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -590,10 +590,6 @@ func assertRequiredParametersSet(c *Config, errs *packer.MultiError) { } } - if c.Location == "" { - errs = packer.MultiErrorAppend(errs, fmt.Errorf("A location must be specified")) - } - ///////////////////////////////////////////// // Deployment xor := func(a, b bool) bool { @@ -604,6 +600,10 @@ func assertRequiredParametersSet(c *Config, errs *packer.MultiError) { errs = packer.MultiErrorAppend(errs, fmt.Errorf("Specify either a VHD (storage_account and resource_group_name) or Managed Image (managed_image_resource_group_name and managed_image_name) output")) } + if !xor(c.Location != "", c.BuildResourceGroupName != "") { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("Must specify either a location to create the resource group in or an existing build_resource_group_name.")) + } + if c.ManagedImageName == "" && c.ManagedImageResourceGroupName == "" { if c.StorageAccount == "" { errs = packer.MultiErrorAppend(errs, fmt.Errorf("A storage_account must be specified")) diff --git a/builder/azure/arm/config_test.go b/builder/azure/arm/config_test.go index 75d397831..4cefd6730 100644 --- a/builder/azure/arm/config_test.go +++ b/builder/azure/arm/config_test.go @@ -972,6 +972,7 @@ func TestConfigShouldRejectInvalidResourceGroupNames(t *testing.T) { } } + delete(config, "location") // not valid for build_resource_group_name delete(config, x) } } diff --git a/website/source/docs/builders/azure.html.md b/website/source/docs/builders/azure.html.md index 779a374b8..8fa59f4e8 100644 --- a/website/source/docs/builders/azure.html.md +++ b/website/source/docs/builders/azure.html.md @@ -44,10 +44,6 @@ builder. CLI example `azure vm image list-skus -l westus -p Canonical -o UbuntuServer` -- `location` (string) Azure datacenter in which your VM will build. - - CLI example `azure location list` - #### VHD or Managed Image The Azure builder can create either a VHD, or a managed image. If you @@ -77,14 +73,42 @@ When creating a managed image the following two options are required. set. See [documentation](https://docs.microsoft.com/en-us/azure/storage/storage-managed-disks-overview#images) to learn more about managed images. +#### Resource Group Usage + +The Azure builder can either provision resources into a new resource group that +it controls (default) or an existing one. The advantage of using a packer +defined resource group is that failed resource cleanup is easier because you +can simply remove the entire resource group, however this means that the +provided credentials must have permission to create and remove resource groups. +By using an existing resource group you can scope the provided credentials to +just this group, however failed builds are more likely to leave unused +artifacts. + +To have packer create a resource group you **must** provide: + +- `location` (string) Azure datacenter in which your VM will build. + + CLI example `azure location list` + +and optionally: + +- `temp_resource_group_name` (string) name assigned to the temporary resource + group created during the build. If this value is not set, a random value will + be assigned. This resource group is deleted at the end of the build. + +To use an existing resource group you **must** provide: + +- `build_resource_group_name` (string) - Specify an existing resource group + to run the build in. + +Providing `temp_resource_group_name` or `location` in combination with `build_resource_group_name` is not allowed. + ### Optional: - `azure_tags` (object of name/value strings) - the user can define up to 15 tags. Tag names cannot exceed 512 characters, and tag values cannot exceed 256 characters. Tags are applied to every resource deployed by a Packer build, i.e. Resource Group, VM, NIC, VNET, Public IP, KeyVault, etc. -- `build_resource_group_name` (string) - Specify an existing resource group to run the build in. Cannot be used together with `temp_resource_group_name` and requires less permissions due to not creating or destroying a resource group. - - `cloud_environment_name` (string) One of `Public`, `China`, `Germany`, or `USGovernment`. Defaults to `Public`. Long forms such as `USGovernmentCloud` and `AzureUSGovernmentCloud` are also supported. @@ -134,10 +158,6 @@ When creating a managed image the following two options are required. assigned. Knowing the resource group and VM name allows one to execute commands to update the VM during a Packer build, e.g. attach a resource disk to the VM. -- `temp_resource_group_name` (string) name assigned to the temporary resource group created during the build. If this - value is not set, a random value will be assigned. This resource group is deleted at the end of the build. Cannot be - used together with `build_resource_group_name`. - - `tenant_id` (string) The account identifier with which your `client_id` and `subscription_id` are associated. If not specified, `tenant_id` will be looked up using `subscription_id`. From 89707228453e8d2f20685756e6161f8027131dcf Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Mon, 4 Dec 2017 10:43:38 -0800 Subject: [PATCH 0117/1216] use latest go for travis tests --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5e753eb01..146d5858c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ language: go go: - 1.7.4 - 1.8.3 - - 1.9 + - 1.x install: - make deps From 05327b75248f9510cc0caacaa0f6410781f49d0b Mon Sep 17 00:00:00 2001 From: Krzysztof Wilczynski Date: Sun, 3 Dec 2017 23:43:30 +0100 Subject: [PATCH 0118/1216] amazon: Remove Session Token (STS) from being shown in the log. This commit adds a change which ensures that the Session Token config struct item is removed from log output. Signed-off-by: Krzysztof Wilczynski --- builder/amazon/chroot/builder.go | 2 +- builder/amazon/ebs/builder.go | 2 +- builder/amazon/ebssurrogate/builder.go | 2 +- builder/amazon/ebsvolume/builder.go | 2 +- builder/amazon/instance/builder.go | 2 +- post-processor/amazon-import/post-processor.go | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/builder/amazon/chroot/builder.go b/builder/amazon/chroot/builder.go index b6b5088a2..388061bee 100644 --- a/builder/amazon/chroot/builder.go +++ b/builder/amazon/chroot/builder.go @@ -173,7 +173,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { return warns, errs } - log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey)) + log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey, b.config.Token)) return warns, nil } diff --git a/builder/amazon/ebs/builder.go b/builder/amazon/ebs/builder.go index 1cc346472..d30a0c0be 100644 --- a/builder/amazon/ebs/builder.go +++ b/builder/amazon/ebs/builder.go @@ -73,7 +73,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { return nil, errs } - log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey)) + log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey, b.config.Token)) return nil, nil } diff --git a/builder/amazon/ebssurrogate/builder.go b/builder/amazon/ebssurrogate/builder.go index 16e8367da..97e8e4b8d 100644 --- a/builder/amazon/ebssurrogate/builder.go +++ b/builder/amazon/ebssurrogate/builder.go @@ -88,7 +88,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { return nil, errs } - log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey)) + log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey, b.config.Token)) return nil, nil } diff --git a/builder/amazon/ebsvolume/builder.go b/builder/amazon/ebsvolume/builder.go index ea3f74b61..6da6cd938 100644 --- a/builder/amazon/ebsvolume/builder.go +++ b/builder/amazon/ebsvolume/builder.go @@ -66,7 +66,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { return nil, errs } - log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey)) + log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey, b.config.Token)) return nil, nil } diff --git a/builder/amazon/instance/builder.go b/builder/amazon/instance/builder.go index 7cee44c09..066494086 100644 --- a/builder/amazon/instance/builder.go +++ b/builder/amazon/instance/builder.go @@ -159,7 +159,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { return nil, errs } - log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey)) + log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey, b.config.Token)) return nil, nil } diff --git a/post-processor/amazon-import/post-processor.go b/post-processor/amazon-import/post-processor.go index f66c2435e..c9cbd2077 100644 --- a/post-processor/amazon-import/post-processor.go +++ b/post-processor/amazon-import/post-processor.go @@ -91,7 +91,7 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { return errs } - log.Println(common.ScrubConfig(p.config, p.config.AccessKey, p.config.SecretKey)) + log.Println(common.ScrubConfig(p.config, p.config.AccessKey, p.config.SecretKey, p.config.Token)) return nil } From 92d1bdbdabb62cb4e029fc60c7292a64d404d8cf Mon Sep 17 00:00:00 2001 From: John Davies-Colley Date: Wed, 6 Dec 2017 16:50:54 +1300 Subject: [PATCH 0119/1216] =?UTF-8?q?docs=20change=20for=20deprecation=20a?= =?UTF-8?q?nd=20proxy=20usage=20=E2=9C=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- website/source/docs/builders/amazon-ebs.html.md | 7 +++++-- website/source/docs/builders/amazon-ebssurrogate.html.md | 7 +++++-- website/source/docs/builders/amazon-ebsvolume.html.md | 7 +++++-- website/source/docs/builders/amazon-instance.html.md | 7 +++++-- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/website/source/docs/builders/amazon-ebs.html.md b/website/source/docs/builders/amazon-ebs.html.md index 4d8c56a1e..54782d9d4 100644 --- a/website/source/docs/builders/amazon-ebs.html.md +++ b/website/source/docs/builders/amazon-ebs.html.md @@ -328,8 +328,8 @@ builder. in AWS with the source instance, set the `ssh_keypair_name` field to the name of the key pair. -- `ssh_private_ip` (boolean) - If `true`, then SSH will always use the private - IP if available. Also works for WinRM. Overrides `ssh_interface`. +- `ssh_private_ip` (boolean) - *Deprecated* use `ssh_interface` instead. If `true`, + then SSH will always use the private IP if available. Also works for WinRM. - `ssh_interface` (string) - One of `public_ip`, `private_ip`, `public_dns` or `private_dns`. If set, either the public IP address, @@ -338,6 +338,9 @@ builder. otherwise the private IP address will be used. If not in a VPC the public DNS name will be used. + If packer is configured for an outbound proxy. To configure WinRM traffic to bypass the proxy + `ssh_interface` can be set to `private_dns`. + - `subnet_id` (string) - If using VPC, the ID of the subnet, such as `subnet-12345def`, where Packer will launch the EC2 instance. This field is required if you are using an non-default VPC. diff --git a/website/source/docs/builders/amazon-ebssurrogate.html.md b/website/source/docs/builders/amazon-ebssurrogate.html.md index acd976538..998733e4a 100644 --- a/website/source/docs/builders/amazon-ebssurrogate.html.md +++ b/website/source/docs/builders/amazon-ebssurrogate.html.md @@ -321,8 +321,8 @@ builder. in AWS with the source instance, set the `ssh_keypair_name` field to the name of the key pair. -- `ssh_private_ip` (boolean) - If `true`, then SSH will always use the private - IP if available. Also works for WinRM. Overrides `ssh_interface`. +- `ssh_private_ip` (boolean) - *Deprecated* use `ssh_interface` instead. If `true`, + then SSH will always use the private IP if available. Also works for WinRM. - `ssh_interface` (string) - One of `public_ip`, `private_ip`, `public_dns` or `private_dns`. If set, either the public IP address, @@ -331,6 +331,9 @@ builder. otherwise the private IP address will be used. If not in a VPC the public DNS name will be used. + If packer is configured for an outbound proxy. To configure WinRM traffic to bypass the proxy + `ssh_interface` can be set to `private_dns`. + - `subnet_id` (string) - If using VPC, the ID of the subnet, such as `subnet-12345def`, where Packer will launch the EC2 instance. This field is required if you are using an non-default VPC. diff --git a/website/source/docs/builders/amazon-ebsvolume.html.md b/website/source/docs/builders/amazon-ebsvolume.html.md index b022c9e81..a28398746 100644 --- a/website/source/docs/builders/amazon-ebsvolume.html.md +++ b/website/source/docs/builders/amazon-ebsvolume.html.md @@ -225,8 +225,8 @@ builder. [`ssh_private_key_file`](/docs/templates/communicator.html#ssh_private_key_file) must be specified with this. -- `ssh_private_ip` (boolean) - If `true`, then SSH will always use the private - IP if available. Also works for WinRM. Overrides `ssh_interface`. +- `ssh_private_ip` (boolean) - *Deprecated* use `ssh_interface` instead. If `true`, + then SSH will always use the private IP if available. Also works for WinRM. - `ssh_interface` (string) - One of `public_ip`, `private_ip`, `public_dns` or `private_dns`. If set, either the public IP address, @@ -235,6 +235,9 @@ builder. otherwise the private IP address will be used. If not in a VPC the public DNS name will be used. + If packer is configured for an outbound proxy. To configure WinRM traffic to bypass the proxy + `ssh_interface` can be set to `private_dns`. + - `subnet_id` (string) - If using VPC, the ID of the subnet, such as `subnet-12345def`, where Packer will launch the EC2 instance. This field is required if you are using an non-default VPC. diff --git a/website/source/docs/builders/amazon-instance.html.md b/website/source/docs/builders/amazon-instance.html.md index 8636b6a49..9afd894c2 100644 --- a/website/source/docs/builders/amazon-instance.html.md +++ b/website/source/docs/builders/amazon-instance.html.md @@ -329,8 +329,8 @@ builder. in AWS with the source instance, set the `ssh_keypair_name` field to the name of the key pair. -- `ssh_private_ip` (boolean) - If `true`, then SSH will always use the private - IP if available. Also works for WinRM. Overrides `ssh_interface`. +- `ssh_private_ip` (boolean) - *Deprecated* use `ssh_interface` instead. If `true`, + then SSH will always use the private IP if available. Also works for WinRM. - `ssh_interface` (string) - One of `public_ip`, `private_ip`, `public_dns` or `private_dns`. If set, either the public IP address, @@ -339,6 +339,9 @@ builder. otherwise the private IP address will be used. If not in a VPC the public DNS name will be used. + If packer is configured for an outbound proxy. To configure WinRM traffic to bypass the proxy + `ssh_interface` can be set to `private_dns`. + - `subnet_id` (string) - If using VPC, the ID of the subnet, such as `subnet-12345def`, where Packer will launch the EC2 instance. This field is required if you are using an non-default VPC. From 76ac755ed98ed28154e38de0b13247e91bdef451 Mon Sep 17 00:00:00 2001 From: John Davies-Colley Date: Wed, 6 Dec 2017 17:13:02 +1300 Subject: [PATCH 0120/1216] =?UTF-8?q?fixing=20wording=20for=20proxy=20usag?= =?UTF-8?q?e=20=F0=9F=91=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- website/source/docs/builders/amazon-ebs.html.md | 5 +++-- website/source/docs/builders/amazon-ebssurrogate.html.md | 5 +++-- website/source/docs/builders/amazon-ebsvolume.html.md | 5 +++-- website/source/docs/builders/amazon-instance.html.md | 7 ++++--- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/website/source/docs/builders/amazon-ebs.html.md b/website/source/docs/builders/amazon-ebs.html.md index 54782d9d4..c050cd070 100644 --- a/website/source/docs/builders/amazon-ebs.html.md +++ b/website/source/docs/builders/amazon-ebs.html.md @@ -338,8 +338,9 @@ builder. otherwise the private IP address will be used. If not in a VPC the public DNS name will be used. - If packer is configured for an outbound proxy. To configure WinRM traffic to bypass the proxy - `ssh_interface` can be set to `private_dns`. + Where Packer is configured for an outbound proxy but WinRM traffic should be direct + `ssh_interface` must be set to `private_dns` and `.compute.internal` included + in the `NO_PROXY` environment variable. - `subnet_id` (string) - If using VPC, the ID of the subnet, such as `subnet-12345def`, where Packer will launch the EC2 instance. This field is diff --git a/website/source/docs/builders/amazon-ebssurrogate.html.md b/website/source/docs/builders/amazon-ebssurrogate.html.md index 998733e4a..640710230 100644 --- a/website/source/docs/builders/amazon-ebssurrogate.html.md +++ b/website/source/docs/builders/amazon-ebssurrogate.html.md @@ -331,8 +331,9 @@ builder. otherwise the private IP address will be used. If not in a VPC the public DNS name will be used. - If packer is configured for an outbound proxy. To configure WinRM traffic to bypass the proxy - `ssh_interface` can be set to `private_dns`. + Where Packer is configured for an outbound proxy but WinRM traffic should be direct + `ssh_interface` must be set to `private_dns` and `.compute.internal` included + in the `NO_PROXY` environment variable. - `subnet_id` (string) - If using VPC, the ID of the subnet, such as `subnet-12345def`, where Packer will launch the EC2 instance. This field is diff --git a/website/source/docs/builders/amazon-ebsvolume.html.md b/website/source/docs/builders/amazon-ebsvolume.html.md index a28398746..2b92c6b44 100644 --- a/website/source/docs/builders/amazon-ebsvolume.html.md +++ b/website/source/docs/builders/amazon-ebsvolume.html.md @@ -235,8 +235,9 @@ builder. otherwise the private IP address will be used. If not in a VPC the public DNS name will be used. - If packer is configured for an outbound proxy. To configure WinRM traffic to bypass the proxy - `ssh_interface` can be set to `private_dns`. + Where Packer is configured for an outbound proxy but WinRM traffic should be direct + `ssh_interface` must be set to `private_dns` and `.compute.internal` included + in the `NO_PROXY` environment variable. - `subnet_id` (string) - If using VPC, the ID of the subnet, such as `subnet-12345def`, where Packer will launch the EC2 instance. This field is diff --git a/website/source/docs/builders/amazon-instance.html.md b/website/source/docs/builders/amazon-instance.html.md index 9afd894c2..198345ba7 100644 --- a/website/source/docs/builders/amazon-instance.html.md +++ b/website/source/docs/builders/amazon-instance.html.md @@ -329,7 +329,7 @@ builder. in AWS with the source instance, set the `ssh_keypair_name` field to the name of the key pair. -- `ssh_private_ip` (boolean) - *Deprecated* use `ssh_interface` instead. If `true`, +- `ssh_private_ip` (boolean) - *Deprecated* use `ssh_interface` instead. If `true`, then SSH will always use the private IP if available. Also works for WinRM. - `ssh_interface` (string) - One of `public_ip`, `private_ip`, @@ -339,8 +339,9 @@ builder. otherwise the private IP address will be used. If not in a VPC the public DNS name will be used. - If packer is configured for an outbound proxy. To configure WinRM traffic to bypass the proxy - `ssh_interface` can be set to `private_dns`. + Where Packer is configured for an outbound proxy but WinRM traffic should be direct + `ssh_interface` must be set to `private_dns` and `.compute.internal` included + in the `NO_PROXY` environment variable. - `subnet_id` (string) - If using VPC, the ID of the subnet, such as `subnet-12345def`, where Packer will launch the EC2 instance. This field is From 19c997cb0e3eb7059c1fd8a68185d54826422e57 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Wed, 6 Dec 2017 10:37:11 -0800 Subject: [PATCH 0121/1216] revert to using UI becuase the remote command syntax breaks things on linux with vmware fusion. --- provisioner/windows-restart/provisioner.go | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/provisioner/windows-restart/provisioner.go b/provisioner/windows-restart/provisioner.go index 3b03cf116..0568516aa 100644 --- a/provisioner/windows-restart/provisioner.go +++ b/provisioner/windows-restart/provisioner.go @@ -3,6 +3,7 @@ package restart import ( "bytes" "fmt" + "io" "log" "strings" @@ -212,20 +213,15 @@ var waitForCommunicator = func(p *Provisioner) error { // provisioning before powershell is actually ready. // In this next check, we parse stdout to make sure that the command is // actually running as expected. - var stdout, stderr bytes.Buffer - cmdModuleLoad := &packer.RemoteCmd{ - Command: DefaultRestartCheckCommand, - Stdin: nil, - Stdout: &stdout, - Stderr: &stderr} + cmdModuleLoad := &packer.RemoteCmd{Command: DefaultRestartCheckCommand} + var buf, buf2 bytes.Buffer + cmdModuleLoad.Stdout = &buf + cmdModuleLoad.Stdout = io.MultiWriter(cmdModuleLoad.Stdout, &buf2) - p.comm.Start(cmdModuleLoad) - cmdModuleLoad.Wait() + cmdModuleLoad.StartWithUi(p.comm, p.ui) + stdoutToRead := buf2.String() - stdoutToRead := stdout.String() - stderrToRead := stderr.String() if !strings.Contains(stdoutToRead, "restarted.") { - log.Printf("Stderr is %s", stderrToRead) log.Printf("echo didn't succeed; retrying...") continue } From 7b5c0900ef53018783d295cfca1177a52432b120 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 7 Dec 2017 11:12:57 -0800 Subject: [PATCH 0122/1216] Correctly set aws region if given in template along with a profile. --- builder/amazon/common/access_config.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/builder/amazon/common/access_config.go b/builder/amazon/common/access_config.go index 73e50b862..1ecd9dcdb 100644 --- a/builder/amazon/common/access_config.go +++ b/builder/amazon/common/access_config.go @@ -40,7 +40,9 @@ func (c *AccessConfig) Session() (*session.Session, error) { if err := os.Setenv("AWS_PROFILE", c.ProfileName); err != nil { return nil, fmt.Errorf("Set env error: %s", err) } - } else if c.RawRegion != "" { + } + + if c.RawRegion != "" { config = config.WithRegion(c.RawRegion) } else if region := c.metadataRegion(); region != "" { config = config.WithRegion(region) From a90c45d9bb3f2abd56ea77c8a456df19baaa60a7 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 7 Dec 2017 12:31:50 -0800 Subject: [PATCH 0123/1216] Wait until source instance OK before continuing --- .../amazon/common/step_run_source_instance.go | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/builder/amazon/common/step_run_source_instance.go b/builder/amazon/common/step_run_source_instance.go index a3dca8057..43a63493f 100644 --- a/builder/amazon/common/step_run_source_instance.go +++ b/builder/amazon/common/step_run_source_instance.go @@ -174,21 +174,27 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi ui.Message(fmt.Sprintf("Instance ID: %s", instanceId)) ui.Say(fmt.Sprintf("Waiting for instance (%v) to become ready...", instanceId)) - stateChange := StateChangeConf{ - Pending: []string{"pending"}, - Target: "running", - Refresh: InstanceStateRefreshFunc(ec2conn, instanceId), - StepState: state, + + describeInstanceStatus := &ec2.DescribeInstanceStatusInput{ + InstanceIds: []*string{aws.String(instanceId)}, } - latestInstance, err := WaitForState(&stateChange) - if err != nil { + if err := ec2conn.WaitUntilInstanceStatusOk(describeInstanceStatus); err != nil { err := fmt.Errorf("Error waiting for instance (%s) to become ready: %s", instanceId, err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } - instance := latestInstance.(*ec2.Instance) + r, err := ec2conn.DescribeInstances(&ec2.DescribeInstancesInput{ + InstanceIds: []*string{aws.String(instanceId)}, + }) + if err != nil || len(r.Reservations) == 0 || len(r.Reservations[0].Instances) == 0 { + err := fmt.Errorf("Error finding source instance.") + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + instance := r.Reservations[0].Instances[0] if s.Debug { if instance.PublicDnsName != nil && *instance.PublicDnsName != "" { From 4acc98a729e2545cb7285fbf262f6268ac162b25 Mon Sep 17 00:00:00 2001 From: Andrew Pennebaker Date: Thu, 7 Dec 2017 23:15:56 -0600 Subject: [PATCH 0124/1216] add super key (vmware builder) --- .../vmware/common/step_type_boot_command.go | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/builder/vmware/common/step_type_boot_command.go b/builder/vmware/common/step_type_boot_command.go index 28db3c72e..b100b609e 100644 --- a/builder/vmware/common/step_type_boot_command.go +++ b/builder/vmware/common/step_type_boot_command.go @@ -183,6 +183,8 @@ func vncSendString(c *vnc.ClientConn, original string) { special[""] = 0xFFEA special[""] = 0xFFE4 special[""] = 0xFFE2 + special[""] = 0xFFEB + special[""] = 0xFFEC shiftedChars := "~!@#$%^&*()_+{}|:\"<>?" @@ -231,6 +233,17 @@ func vncSendString(c *vnc.ClientConn, original string) { continue } + if strings.HasPrefix(original, "") { + keyCode = special[""] + original = original[len(""):] + log.Printf("Special code '' found, replacing with: %d", keyCode) + + c.KeyEvent(keyCode, true) + time.Sleep(keyInterval) + + continue + } + if strings.HasPrefix(original, "") { keyCode = special[""] original = original[len(""):] @@ -264,6 +277,17 @@ func vncSendString(c *vnc.ClientConn, original string) { continue } + if strings.HasPrefix(original, "") { + keyCode = special[""] + original = original[len(""):] + log.Printf("Special code '' found, replacing with: %d", keyCode) + + c.KeyEvent(keyCode, false) + time.Sleep(keyInterval) + + continue + } + if strings.HasPrefix(original, "") { keyCode = special[""] original = original[len(""):] @@ -297,6 +321,17 @@ func vncSendString(c *vnc.ClientConn, original string) { continue } + if strings.HasPrefix(original, "") { + keyCode = special[""] + original = original[len(""):] + log.Printf("Special code '' found, replacing with: %d", keyCode) + + c.KeyEvent(keyCode, true) + time.Sleep(keyInterval) + + continue + } + if strings.HasPrefix(original, "") { keyCode = special[""] original = original[len(""):] @@ -330,6 +365,17 @@ func vncSendString(c *vnc.ClientConn, original string) { continue } + if strings.HasPrefix(original, "") { + keyCode = special[""] + original = original[len(""):] + log.Printf("Special code '' found, replacing with: %d", keyCode) + + c.KeyEvent(keyCode, false) + time.Sleep(keyInterval) + + continue + } + if strings.HasPrefix(original, "") { log.Printf("Special code '' found, sleeping one second") time.Sleep(1 * time.Second) From 42d0e6f920217e239d8e4c49d46e1189f8fbec2e Mon Sep 17 00:00:00 2001 From: Maciej Skierkowski Date: Fri, 8 Dec 2017 08:28:27 -0800 Subject: [PATCH 0125/1216] Navigation and placeholders for guide --- .../building-image-in-cicd.html.md | 9 +++++++++ .../building-virtualbox-image.html.md | 9 +++++++++ .../source/guides/packer-on-cicd/index.html.md | 10 ++++++++++ .../packer-on-cicd/triggering-tfe.html.md | 9 +++++++++ .../uploading-images-to-artifact.html.md | 9 +++++++++ website/source/layouts/guides.erb | 17 +++++++++++++++++ 6 files changed, 63 insertions(+) create mode 100644 website/source/guides/packer-on-cicd/building-image-in-cicd.html.md create mode 100644 website/source/guides/packer-on-cicd/building-virtualbox-image.html.md create mode 100644 website/source/guides/packer-on-cicd/index.html.md create mode 100644 website/source/guides/packer-on-cicd/triggering-tfe.html.md create mode 100644 website/source/guides/packer-on-cicd/uploading-images-to-artifact.html.md diff --git a/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md b/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md new file mode 100644 index 000000000..4c234fd44 --- /dev/null +++ b/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md @@ -0,0 +1,9 @@ +--- +layout: guides +sidebar_current: guides-packer-on-cicd-build-image +page_title: Building Images in CI/CD +description: |- + ... +--- + +# Building Images in CI/CD diff --git a/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md b/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md new file mode 100644 index 000000000..dc086223c --- /dev/null +++ b/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md @@ -0,0 +1,9 @@ +--- +layout: guides +sidebar_current: guides-packer-on-cicd-build-virtualbox +page_title: Building a VirtualBox Image with Packer in TeamCity +description: |- + ... +--- + +# Building a VirtualBox Image with Packer in TeamCity diff --git a/website/source/guides/packer-on-cicd/index.html.md b/website/source/guides/packer-on-cicd/index.html.md new file mode 100644 index 000000000..32b4f33cc --- /dev/null +++ b/website/source/guides/packer-on-cicd/index.html.md @@ -0,0 +1,10 @@ +--- +layout: guides +sidebar_current: guides-packer-on-cicd-index +page_title: Building Immutable Infrastructure with Packer in CI/CD +description: |- + ... +--- + +# Building Immutable Infrastructure with Packer in CI/CD + diff --git a/website/source/guides/packer-on-cicd/triggering-tfe.html.md b/website/source/guides/packer-on-cicd/triggering-tfe.html.md new file mode 100644 index 000000000..60aa59d4d --- /dev/null +++ b/website/source/guides/packer-on-cicd/triggering-tfe.html.md @@ -0,0 +1,9 @@ +--- +layout: guides +sidebar_current: guides-packer-on-cicd-triggering-tfe-run +page_title: Triggering Terraform Enterprise runs +description: |- + ... +--- + +# Triggering Terraform Enterprise runs diff --git a/website/source/guides/packer-on-cicd/uploading-images-to-artifact.html.md b/website/source/guides/packer-on-cicd/uploading-images-to-artifact.html.md new file mode 100644 index 000000000..752a74dee --- /dev/null +++ b/website/source/guides/packer-on-cicd/uploading-images-to-artifact.html.md @@ -0,0 +1,9 @@ +--- +layout: guides +sidebar_current: guides-packer-on-cicd-upload-image-to-artifact-store +page_title: Uploading Images to Artifact Stores +description: |- + ... +--- + +# Uploading Images to Artifact Stores diff --git a/website/source/layouts/guides.erb b/website/source/layouts/guides.erb index f842f51a6..be059a632 100644 --- a/website/source/layouts/guides.erb +++ b/website/source/layouts/guides.erb @@ -4,6 +4,23 @@ > Veewee to Packer + > + Building Immutable Infrastructure with Packer in CI/CD + <% end %> From 5df2e040d03505ea150be68d8d8f95d61088a177 Mon Sep 17 00:00:00 2001 From: Ben Gnoinski Date: Fri, 8 Dec 2017 08:49:49 -0800 Subject: [PATCH 0126/1216] Update amazon run_config.go absent ssh_private_key_file error --- builder/amazon/common/run_config.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builder/amazon/common/run_config.go b/builder/amazon/common/run_config.go index 4f20ed3b4..eef56cf4c 100644 --- a/builder/amazon/common/run_config.go +++ b/builder/amazon/common/run_config.go @@ -98,9 +98,9 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { if c.SSHKeyPairName != "" { if c.Comm.Type == "winrm" && c.Comm.WinRMPassword == "" && c.Comm.SSHPrivateKey == "" { - errs = append(errs, errors.New("A private_key_file must be provided to retrieve the winrm password when using ssh_keypair_name.")) + errs = append(errs, errors.New("ssh_private_key_file must be provided to retrieve the winrm password when using ssh_keypair_name.")) } else if c.Comm.SSHPrivateKey == "" && !c.Comm.SSHAgentAuth { - errs = append(errs, errors.New("A private_key_file must be provided or ssh_agent_auth enabled when ssh_keypair_name is specified.")) + errs = append(errs, errors.New("ssh_private_key_file must be provided or ssh_agent_auth enabled when ssh_keypair_name is specified.")) } } From 59172c5a2d735422df3525bff7e4e7dec3b829b2 Mon Sep 17 00:00:00 2001 From: Maciej Skierkowski Date: Fri, 8 Dec 2017 10:31:00 -0800 Subject: [PATCH 0127/1216] Adding the CI/CD guide content --- .../building-image-in-cicd.html.md | 9 ++ .../building-virtualbox-image.html.md | 93 ++++++++++++++++++ .../images/teamcity_build_log.png | Bin 0 -> 333088 bytes .../images/teamcity_build_log_complete.png | Bin 0 -> 230524 bytes .../images/teamcity_new_build.png | Bin 0 -> 249258 bytes .../guides/packer-on-cicd/index.html.md | 5 + .../packer-on-cicd/triggering-tfe.html.md | 27 ++++- .../uploading-images-to-artifact.html.md | 16 +++ 8 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 website/source/guides/packer-on-cicd/images/teamcity_build_log.png create mode 100644 website/source/guides/packer-on-cicd/images/teamcity_build_log_complete.png create mode 100644 website/source/guides/packer-on-cicd/images/teamcity_new_build.png diff --git a/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md b/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md index 4c234fd44..3504f4cae 100644 --- a/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md +++ b/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md @@ -7,3 +7,12 @@ description: |- --- # Building Images in CI/CD + +The following guides from our amazing partners show how to use their service to build images with Packer. + +- [How to Build Immutable Infrastructure with Packer and CircleCI Workflows](https://docs.google.com/document/d/1hetlS94SpUQ979K-1At9hwn1oDNWHegBqHGNcIFLBoY/edit) +- [Using Packer and Ansible to Build Immutable Infrastructure](https://blog.codeship.com/packer-ansible/) + +For the majority of the [Packer Builders](https://www.packer.io/docs/builders/index.html) can run in a container or VM, a common model used by most CI/CD services. However, the [QEMU builder](https://www.packer.io/docs/builders/qemu.html) for [KVM](https://www.linux-kvm.org/page/Main_Page) and [Xen](https://www.xenproject.org/) virtual machine images, [VirtualBox builder](https://www.packer.io/docs/builders/virtualbox.html) for OVA or OVF virtual machines and [VMWare builder](https://www.packer.io/docs/builders/vmware.html) for use with VMware products require running on a bare-metal machine. + +[Building a VirtualBox Image with Packer in TeamCity](https://docs.google.com/document/d/1AQjn4PpApnZ6pf097HYZzZa4ZMspRATxo9wNj78hLLc/edit#) diff --git a/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md b/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md index dc086223c..df060b68f 100644 --- a/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md +++ b/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md @@ -7,3 +7,96 @@ description: |- --- # Building a VirtualBox Image with Packer in TeamCity + +This guide walks through the process of building a VirtualBox image using Packer on a new TeamCity Agent. Before getting started you should have access to a TeamCity Server. + +The Packer VirtualBox builder requires access to VirtualBox which should run on a bare-metal machine as virtual machines should not run inside other virtual machines. This is also true for the [VMWare](https://www.packer.io/docs/builders/vmware.html) and the [QEMU](https://www.packer.io/docs/builders/qemu.html) Packer builders. + +## 1. Provision a bare-metal machine + +The Packer VirtualBox builder requires running on bare-metal (hardware). If you do not have access to a bare-metal machine, we recommend using [Packet.net](https://www.packet.net/) to obtain a new machine. If you are a first time user of Packet.net, the Packet.net team has provided HashiCorp the coupon code `hash25` which you can use for $25 off to test out this guide. You can use a `baremetal_0` for testing, but for regular use the `baremetal_1` instance may be a better option. + +There is also a [Packet Provider](https://www.terraform.io/docs/providers/packet/index.html) in Terraform you can use to provision the project and instance. + +```hcl +provider "packet" { } + +resource "packet_project" "teamcity_agents" { + name = "TeamCity" +} + +resource "packet_device" "agent" { + hostname = "teamcity-agent" + plan = "baremetal_0" + facility = "ams1" + operating_system = "ubuntu_16_04" + billing_cycle = "hourly" + project_id = "${packet_project.teamcity_project.id}" +} +``` + +## 2. Install VirtualBox and TeamCity Dependencies + +VirtualBox must be installed on the new instance along and TeamCity requires the JDK prior to installation. This guide uses Ubuntu as the Linux distribution, so you may need to adjust these commands for your distribution of choice. + +**Install Teamcity Dependencies** + +```shell +apt-get upgrade +apt-get install -y zip linux-headers-generic linux-headers-4.13.0-16-generic build-essential openjdk-8-jdk +``` + +**Install VirtualBox** + +``` +curl -OL "http://download.virtualbox.org/virtualbox/5.2.2/virtualbox-5.2_5.2.2-119230~Ubuntu~xenial_amd64.deb" +dpkg -i virtualbox-5.2_5.2.2-119230~Ubuntu~xenial_amd64.deb +``` + +You can also use the [`remote-exec` provisioner](https://www.terraform.io/docs/provisioners/remote-exec.html) in your terraform configuration to automatically run these commands when provisioning the new instance. + +## 3. Install Packer + +The TeamCity Agent machine will also need Packer Installed. You can find the latest download link from the [Packer Download](https://www.packer.io/downloads.html) page. + +```shell +curl -OL "https://releases.hashicorp.com/packer/1.1.2/packer_1.1.2_linux_amd64.zip" +unzip ./packer_1.1.2_linux_amd64.zip +``` + +Packer is installed at the `/root/packer` path which is used in subsequent steps. If it is installed elsewhere, take note of the path. + +## 4. Install TeamCity Agent + +This guide assume you already have a running instance of TeamCity Server. The new TeamCity Agent can be installed by [downloading a zip file and installing manually](https://confluence.jetbrains.com/display/TCD10//Setting+up+and+Running+Additional+Build+Agents#SettingupandRunningAdditionalBuildAgents-InstallingAdditionalBuildAgents), or using [Agent Push](https://confluence.jetbrains.com/display/TCD10//Setting+up+and+Running+Additional+Build+Agents#SettingupandRunningAdditionalBuildAgents-InstallingviaAgentPush). Once it is installed it should appear in TeamCity as a new Agent. + +Create a new Agent Pool for the agents which will be responsible for the VirtualBox Packer builds and the assign the new Agent to the new Agent Pool. + +## 5. Create a new Build in TeamCity + +In TeamCity Server create a new build and configure the Version Control Settings to download the Packer build configuration from the VCS repository. + +Add one **Build Step: Command Line** to the build. + +![TeamCity screenshot: New Build](./images/teamcity_new_build.png) + +In the **Script content** field add the following: + +```shell +#!/usr/bin/env bash +/root/packer build -only=virtualbox-iso -var "headless=true" ./packer.json +``` + +This assumes that `packer.json` is the Packer build configuration file in the root path of the VCS repository. + +## 6. Run a build in TeamCity + +The entire configuration is ready for a new build. Start a new run in TeamCity by pressing “Run”. + +The new run should be triggered and the virtual box image will be built. + +![TeamCity screenshot: Build log](./images/teamcity_build_log.png) + +Once complete, the build status should be updated to complete and successful. + +![TeamCity screenshot: Build log complete](./images/teamcity_build_log_complete.png) \ No newline at end of file diff --git a/website/source/guides/packer-on-cicd/images/teamcity_build_log.png b/website/source/guides/packer-on-cicd/images/teamcity_build_log.png new file mode 100644 index 0000000000000000000000000000000000000000..13d23e19cda1ab2cd7697f44e2bde9e7729fd6e4 GIT binary patch literal 333088 zcmbrl1#le8k|-(*EV962#t}0!TFlJM%#0RUOcpcCVrI6O*eEz60$`N`41N%a5 z`R$v6NdN$#K@hSqcmwiS)eC`$NVVqcPeEZ= zVq}%G;e2pwp@BejUG&;YsJMY2Q1FokKzM4((Kd~mfNNP4!Nu3nr-MvSp!>yI>rMB~ zMg|AOcPBJ$z^I5S*f7dLctOx>Y65CPYN1>%%pkaFF4C9fOr3ap6&23!rh9J>*KZht zg%76-vl{O<@71D7V`KYZ-$jJvwhx@@hzOy;+z-O~J;9K@^42ZPvSFCNmO-8vQ6ksX zCeH1hQSYO1g^u|c6}%EtK;#OV)(=>MQA)Zp-Edhl#wcN%XcMaw1+T#@NA{UJ6C()| zq$DLFXfET`d5*d}&c0!=n6i>*WU-H4>zIfV3&zomnoPY3{Mz+TsgM~N#uBShdfJJz zu$-Lr92-VVU`D?dHIxzon6M3VKNRjB7D`kImHa=QRKR2~29g4x*RnRFFN< zr2h%A9#fn46#jLl5Zuouke8nt&0HL5NE|*m?dx`*_uE*3&{;%0DndJH3=XCP9jf~MJn-TmQew4cx0ePsBal|No$ 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

^tuQoo(Un^r~R`a}qEqS-vUEis3` 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

m z9!eCV(knDX7n>^hC&z)Lx@WZeURL$MuhC(3OuO;;hT`q{fewTNH=yC8Qg3wg+p32yZRq}@L$zyKJvQi4q}5L(KX4KUx*@ZdJx8~xhp((F>tme zZzJ^?zV6El*{IPFzkK!&us)wzZcEhPuM+ui3Sq8sVCNS!yNeUH*$-Cl?+7I8tK$-^ z6|RJa$Bf=T#Eu>qM1|0*CCD#pZ97|^RbHZWtpRZ!0?Luf-hSq_qRxd=3Y0b_nl4Ot z0u27|qv{F&iK^!i{(pq3f9W~>zk;fND4W;j6dKvH_udS|PXq;P66ZHC@cUc2sEGEm zu+V^0BTliQA5!?1Tq)VpftFJ+5dy{BRz-2+!h=?i|6dyKH%?nyE5YSB|?m! zq>*v>QbuFfYb2K9It6zqdyFxq;sw0mRR4-<>R;_ZXC3$5O{}deP^|411Adt$P;SVD zeOOz^*sL70T?~jN`A=lMEPr=HR^Z7lcFIzZ!YzU9@c`00&L`OKKN2<&lD>a!3Hw3= z^m;;4S5%c`D;nteF|Xsa=A5T zYSG9!@{#q7Hr(q|DR zMkSNu=H8B}7@tk738ft$83C597^|21pWR0{{JRv)aNd{=VjMB)a{T`VU4Lf%-$B>^ zLmzxCXtvU?-$J7^IN&pg%2ke`wFq%h-!5pkH0g!p-D|J8>*Bu-Abm~37L0`aVDm$3 zNUU_k{lZ&e7At>S-RbIH+gguS!OwBSFJE3-p^rrPLhX8fEsl78`9Xd{U!4vBy)u7- zXup5pTEf6y>xtd3YoKFh~3z`r31m>GK*@xzAs z?XU?-yMJdAKyLe^|E2x+HECSKYlgUXXE`QkY|tV`A;ug+R6;;>Y~i=4x)X|^F4gwN zvEtz&VOkf8&T|6%UblYO%=QDMi2#1MMy0T*4kBL@_ zu*n5E6OKNl{iOJZY^Z}|fB#v#GXvU*@u9RavAN92=ayu*ZB4a2-fRdCoGY*Zg03OJfK9+$+qd#7X=Tl+o9LcLA{%t>Dv zoJ-8Li>(Wp>XBGLo|?U<@rHBUPaD|)C_3b;a{=U zI-s>lx*(6$adEol9ONJdj+|B-~a}dJe+G?*A$Hu(I&D z`<3QV<(f1{oz5GACT1~ykdG1ty52%+!a%nb80IO+{Lr35qmc-M zG>9z^M4PK$09`0D4pQu4Ras~H;#-)3e3A_1lLnrqA}$*Mhl2laa_iTOe}Xm^!~D5` zQjyEGzXCs0qTPFk%AJ%gk1H;U$Zrq5f0IwMX@eY4DPbuU3?V|36%C&z0;0D5$VoXJ zPV=WCR?6U7y@jMD`OF4BE$L27$p);>Dls(IHlo{D9SR;VhZ8;WmY~`hsJ`nYIHj!R z(J}&(*;{T-7g^acBv}bgYBdJyl(J4+q8PO57j+ya;|hNcw$ zAjEp{)L*^CXg9;o%5-JYv z5!C|0Wy|cN_;+*%Tx4leoM_dV0e8_)VL?s4^s3f^t$G=g)K&KBjp9~o9{Zh^G#pA4 z73|irz--_gu9f{r_M7bD0P`?K_k9v>*|7kS1_@TUv=gwDB51f&_Iw*(?1^ETT^!(3 zAb3Z9N;_pJwwu(uxXs@T1J}MM_g8o}h*&Y_!(yswh{E=J$X28wET|@2ZTk%2|3K3< zyren+H2Pie8BXA!7{BRyNE$oP?BDQoB-WG2e+ScHPoW0Xzc~JjHNL_91TESFi%zM3 zTs$!hjMt`jmSWfRPYSDNd%WcFy0@Y9EKbN9Y%@8d`CEOJ)^#hA^BWRw9s-dV;^Rg0 zzqaZ&%jMbo2u*xZ07cy>apu9uf3I!@J|kOU%spB>5P0MI|EX(d0~ z6-(-iU*x2rP{aBnCdOAhh=0b9UayV+bxO4{cqUm;It4nT(FgN9BxX5DYP+ZF5UT1O zx|ifERDvO*VcKGPTF=5YL|~35uhsr~HGor3uzx`1xt48!Gpc_-`|}7v$johqRRZZv z1lv{LR4#e`OWE*m0d;92VBjC*66xk&p~`>9o&Tjxi&(;a?7%O`W`TeI{J(hw|MgC& zA|F&cIPd}de|N(Poe!%0fI*b$UugpW+LyoPBK+&XAyGfRA29TQe?R*F(+%BHKd5#- mxrE>Be^KZEx(nctZ^F6==I^{kT|lsphm546M5UN<@c#g31?jy2 literal 0 HcmV?d00001 diff --git a/website/source/guides/packer-on-cicd/images/teamcity_build_log_complete.png b/website/source/guides/packer-on-cicd/images/teamcity_build_log_complete.png new file mode 100644 index 0000000000000000000000000000000000000000..a05b2a685b397d8bb27bfae43ba822282f54a15e GIT binary patch literal 230524 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 eq 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�xNlGKD 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 wSz0b(J~-*0L`#o9=da?r+gryulkUtul`S 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 P&7XvrtZ4CjU7!B}WTjCQ literal 0 HcmV?d00001 diff --git a/website/source/guides/packer-on-cicd/index.html.md b/website/source/guides/packer-on-cicd/index.html.md index 32b4f33cc..c65a49a2b 100644 --- a/website/source/guides/packer-on-cicd/index.html.md +++ b/website/source/guides/packer-on-cicd/index.html.md @@ -8,3 +8,8 @@ description: |- # Building Immutable Infrastructure with Packer in CI/CD +This guide focuses on the following workflow for building immutable infrastructure. This workflow can be manual or automated and it can be implemented with a variety of technologies. The goal of this guide is to show how this workflow can be fully automated using Packer for building images from a CI/CD pipeline. + +1. [Building Images using Packer in CI/CD](./building-image-in-cicd.html) +2. [Uploading the new image to S3](./uploading-images-to-artifact.html) for future deployment or use during development +3. Provision new instances with the images using Terraform Enterprise by [creating a new Terraform Enterprise runs](./triggering-tfe.html). \ No newline at end of file diff --git a/website/source/guides/packer-on-cicd/triggering-tfe.html.md b/website/source/guides/packer-on-cicd/triggering-tfe.html.md index 60aa59d4d..4d2ef6fcc 100644 --- a/website/source/guides/packer-on-cicd/triggering-tfe.html.md +++ b/website/source/guides/packer-on-cicd/triggering-tfe.html.md @@ -6,4 +6,29 @@ description: |- ... --- -# Triggering Terraform Enterprise runs +# Creating a Terraform Enterprise runs + +Once an image is built and uploaded to an artifact store, the next step is to use this new image. In some cases the image will be downloaded by the dev team and used locally in development, like is often done with VirtualBox images with Vagrant. In most other cases, the new image will be used to provision new infrastructure. [Terraform](https://www.terraform.io/) is an open source tool that is ideal for provisioning the new infrastructure with the new image generated by Packer. + +The following is a sample terraform configuration which provisions a new AWS EC2 instance. The `aws_ami_id` is a variable which will be provided when running `terraform plan` and `terraform apply`. This variable references the latest AMI generated with the Packer build in CI/CD. + +```hcl +variable "aws_ami_id" { } + +provider "aws" { + region = "us-west-2" +} + +resource "aws_instance" "web" { + ami = "${var.aws_ami_id}" + instance_type = "t2.micro" +} +``` + +Terraform Enterprise should have a workspace with this terraform configuration and a placeholder variable `aws_ami_id`. + +**Steps to create a new run from CI/CD after a Packer build is complete and uploaded**: + +1. Add a new step to the CI/CD pipeline. +2. In the new step add a `curl` call to update the variables in the workspace using the [update variables API](https://www.terraform.io/docs/enterprise-beta/api/variables.html#update-variables) with the reference to the latest image. In the sample terraform configuration above, the “aws_ami_id” variable would be updated to the AMI ID of the latest image. +3. In that same step, add another `curl` call to [create a new run via the API](https://www.terraform.io/docs/enterprise-beta/api/run.html#create-a-run). A run performs a plan and apply on the last configuration version created and using the variables set in the workspace. In the previous step we update the variables, so the new run can be created using the previous configuration version. diff --git a/website/source/guides/packer-on-cicd/uploading-images-to-artifact.html.md b/website/source/guides/packer-on-cicd/uploading-images-to-artifact.html.md index 752a74dee..3510a1dd5 100644 --- a/website/source/guides/packer-on-cicd/uploading-images-to-artifact.html.md +++ b/website/source/guides/packer-on-cicd/uploading-images-to-artifact.html.md @@ -7,3 +7,19 @@ description: |- --- # Uploading Images to Artifact Stores + +Once the image is generated it will be used by other parts of your operations workflow. For example, it is common to build VirtualBoxes with Packer to be used as base boxes in Vagrant. + +On the agent machine install the [AWS Command Line Tool](https://aws.amazon.com/cli/). Since this is a one-time operation, this can be incorporated into the initial provisioning step when installing other dependencies. + +```shell +pip install awscli +``` + +Add an additional **Build Step: Command Line** to the build and set the **Script content** field to the following: + +```shell +awscli s3 cp . s3://bucket/ --exclude “*” --include “*.iso” +``` + +TeamCity provides a [Build Artifacts](https://confluence.jetbrains.com/display/TCD9/Build+Artifact) feature which can be used to store the newly generated image. Other CI/CD services also have similar build artifacts features built in, like [Circle CI Build Artifacts](https://circleci.com/docs/2.0/artifacts/). In addition to the built in artifact stores in CI/CD tools, there are also dedicated universal artifact storage services like [Artifactory](https://confluence.jetbrains.com/display/TCD9/Build+Artifact). All of these are great options for image artifact storage. From c4abcd9c7c83e103bf1fccda01ec8370bba107f2 Mon Sep 17 00:00:00 2001 From: Maciej Skierkowski Date: Fri, 8 Dec 2017 10:59:43 -0800 Subject: [PATCH 0128/1216] Adding more context for references to other guides --- .../guides/packer-on-cicd/building-image-in-cicd.html.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md b/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md index 3504f4cae..452b425fc 100644 --- a/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md +++ b/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md @@ -11,8 +11,8 @@ description: |- The following guides from our amazing partners show how to use their service to build images with Packer. - [How to Build Immutable Infrastructure with Packer and CircleCI Workflows](https://docs.google.com/document/d/1hetlS94SpUQ979K-1At9hwn1oDNWHegBqHGNcIFLBoY/edit) -- [Using Packer and Ansible to Build Immutable Infrastructure](https://blog.codeship.com/packer-ansible/) +- [Using Packer and Ansible to Build Immutable Infrastructure [in CodeShip]](https://blog.codeship.com/packer-ansible/) For the majority of the [Packer Builders](https://www.packer.io/docs/builders/index.html) can run in a container or VM, a common model used by most CI/CD services. However, the [QEMU builder](https://www.packer.io/docs/builders/qemu.html) for [KVM](https://www.linux-kvm.org/page/Main_Page) and [Xen](https://www.xenproject.org/) virtual machine images, [VirtualBox builder](https://www.packer.io/docs/builders/virtualbox.html) for OVA or OVF virtual machines and [VMWare builder](https://www.packer.io/docs/builders/vmware.html) for use with VMware products require running on a bare-metal machine. -[Building a VirtualBox Image with Packer in TeamCity](https://docs.google.com/document/d/1AQjn4PpApnZ6pf097HYZzZa4ZMspRATxo9wNj78hLLc/edit#) +The [Building a VirtualBox Image with Packer in TeamCity](./building-virtualbox-image.html) guide walks through creating a VirtualBox image, which requires a bare-metal machine, running in TeamCity which also supports running the scripts directly on the machine. From 79c10a251ce00cf9179cf43e829502b4b423189d Mon Sep 17 00:00:00 2001 From: Maciej Skierkowski Date: Fri, 8 Dec 2017 11:05:17 -0800 Subject: [PATCH 0129/1216] Using placeholder for now --- .../source/guides/packer-on-cicd/building-image-in-cicd.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md b/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md index 452b425fc..213fe0aa7 100644 --- a/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md +++ b/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md @@ -10,7 +10,7 @@ description: |- The following guides from our amazing partners show how to use their service to build images with Packer. -- [How to Build Immutable Infrastructure with Packer and CircleCI Workflows](https://docs.google.com/document/d/1hetlS94SpUQ979K-1At9hwn1oDNWHegBqHGNcIFLBoY/edit) +- [How to Build Immutable Infrastructure with Packer and CircleCI Workflows](#) - [Using Packer and Ansible to Build Immutable Infrastructure [in CodeShip]](https://blog.codeship.com/packer-ansible/) For the majority of the [Packer Builders](https://www.packer.io/docs/builders/index.html) can run in a container or VM, a common model used by most CI/CD services. However, the [QEMU builder](https://www.packer.io/docs/builders/qemu.html) for [KVM](https://www.linux-kvm.org/page/Main_Page) and [Xen](https://www.xenproject.org/) virtual machine images, [VirtualBox builder](https://www.packer.io/docs/builders/virtualbox.html) for OVA or OVF virtual machines and [VMWare builder](https://www.packer.io/docs/builders/vmware.html) for use with VMware products require running on a bare-metal machine. From 98420fc9f5928ee20b667a1e931d88cceccaee5b Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 8 Dec 2017 13:23:42 -0800 Subject: [PATCH 0130/1216] update changelog --- CHANGELOG.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cb17b49e..799e41f93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ * builder/amazon: Correctly deregister AMIs when `force_deregister` is set. [GH-5525] +* builder/amazon: Add a new parameter `ssh_interface`. Valid values include +`public_ip`, `private_ip`, `public_dns` or `private_dns`. [GH-5630] * builder/digitalocean: Add `ipv6` option to enable on droplet. [GH-5534] * builder/docker: Add `aws_profile` option to control the aws profile for ECR. [GH-5470] @@ -41,9 +43,30 @@ * post-processor/docker-push: Add `aws_profile` option to control the aws profile for ECR. [GH-5470] * post-processor/vsphere: Properly capture `ovftool` output. [GH-5499] +* builder/vmware-iso: Improve logging of network errors. [GH-5456] +* provisioner/ansible-local: Add ability to clean staging directory. [GH-5618] +* core: Add new `packer_version` template engine. [GH-5619] +* core: Improve logic checking for downloaded isos in case where user has +provided 2+ `iso_urls` [GH-5632] +* builder/hyper-v: Add support for differencing disk. [GH-5458] +* builder/azure: Add sanity checks for resource group names [GH-5599] ### BUG FIXES: +* builder/hyper-v: Fix interpolation context for user variables in +`boot_command` [GH-5547] +* core: Fix windows pathing regression when downloading isos. [GH-5591] +* builder/azure: Only destroy temporary resource groups if they were created by +Packer. [GH-5593] +* provisioner/chef: Fix chef installs on Windows. [GH-5649] +* builder/azure: Clean up KeyVaults when deploying Windows images that used an +existing resource group. [GH-5656] +* builder/azure: 'build_resource_group_name' is mutually exclusive with +'location' [GH-5661] +* builder/amazon: Correctly set AWS region if given in template along with a +profile. [GH-5676] +* builder/vmware: Correctly detect Windows boot on vmware workstation. [GH-5672] +* builder/amazon: Builds on new C5 instances are now possible. [GH-5678] * builder/amazon: Add a delay option to security group waiter. [GH-5536] * builder/amazon: Fix regressions relating to spot instances and EBS volumes. [GH-5495] From d7fe37b8628bd1b382908cd69799b90706aa8ee0 Mon Sep 17 00:00:00 2001 From: Nick Fagerlund Date: Fri, 8 Dec 2017 13:25:32 -0800 Subject: [PATCH 0131/1216] Delete empty descriptions --- .../source/guides/packer-on-cicd/building-image-in-cicd.html.md | 2 -- .../guides/packer-on-cicd/building-virtualbox-image.html.md | 2 -- website/source/guides/packer-on-cicd/index.html.md | 2 -- website/source/guides/packer-on-cicd/triggering-tfe.html.md | 2 -- .../guides/packer-on-cicd/uploading-images-to-artifact.html.md | 2 -- 5 files changed, 10 deletions(-) diff --git a/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md b/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md index 213fe0aa7..99607b2f8 100644 --- a/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md +++ b/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md @@ -2,8 +2,6 @@ layout: guides sidebar_current: guides-packer-on-cicd-build-image page_title: Building Images in CI/CD -description: |- - ... --- # Building Images in CI/CD diff --git a/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md b/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md index df060b68f..e06866650 100644 --- a/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md +++ b/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md @@ -2,8 +2,6 @@ layout: guides sidebar_current: guides-packer-on-cicd-build-virtualbox page_title: Building a VirtualBox Image with Packer in TeamCity -description: |- - ... --- # Building a VirtualBox Image with Packer in TeamCity diff --git a/website/source/guides/packer-on-cicd/index.html.md b/website/source/guides/packer-on-cicd/index.html.md index c65a49a2b..5c79dfd88 100644 --- a/website/source/guides/packer-on-cicd/index.html.md +++ b/website/source/guides/packer-on-cicd/index.html.md @@ -2,8 +2,6 @@ layout: guides sidebar_current: guides-packer-on-cicd-index page_title: Building Immutable Infrastructure with Packer in CI/CD -description: |- - ... --- # Building Immutable Infrastructure with Packer in CI/CD diff --git a/website/source/guides/packer-on-cicd/triggering-tfe.html.md b/website/source/guides/packer-on-cicd/triggering-tfe.html.md index 4d2ef6fcc..a241b0f15 100644 --- a/website/source/guides/packer-on-cicd/triggering-tfe.html.md +++ b/website/source/guides/packer-on-cicd/triggering-tfe.html.md @@ -2,8 +2,6 @@ layout: guides sidebar_current: guides-packer-on-cicd-triggering-tfe-run page_title: Triggering Terraform Enterprise runs -description: |- - ... --- # Creating a Terraform Enterprise runs diff --git a/website/source/guides/packer-on-cicd/uploading-images-to-artifact.html.md b/website/source/guides/packer-on-cicd/uploading-images-to-artifact.html.md index 3510a1dd5..7ca746df7 100644 --- a/website/source/guides/packer-on-cicd/uploading-images-to-artifact.html.md +++ b/website/source/guides/packer-on-cicd/uploading-images-to-artifact.html.md @@ -2,8 +2,6 @@ layout: guides sidebar_current: guides-packer-on-cicd-upload-image-to-artifact-store page_title: Uploading Images to Artifact Stores -description: |- - ... --- # Uploading Images to Artifact Stores From dada63801b2139374ae06320474fae9bfb2a69d0 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 7 Dec 2017 14:05:51 -0800 Subject: [PATCH 0132/1216] also use waiter code for spot instances --- .../amazon/common/step_run_spot_instance.go | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/builder/amazon/common/step_run_spot_instance.go b/builder/amazon/common/step_run_spot_instance.go index b34d09bcb..323032de0 100644 --- a/builder/amazon/common/step_run_spot_instance.go +++ b/builder/amazon/common/step_run_spot_instance.go @@ -231,21 +231,26 @@ func (s *StepRunSpotInstance) Run(state multistep.StateBag) multistep.StepAction ui.Message(fmt.Sprintf("Instance ID: %s", instanceId)) ui.Say(fmt.Sprintf("Waiting for instance (%v) to become ready...", instanceId)) - stateChangeSpot := StateChangeConf{ - Pending: []string{"pending"}, - Target: "running", - Refresh: InstanceStateRefreshFunc(ec2conn, instanceId), - StepState: state, + describeInstanceStatus := &ec2.DescribeInstanceStatusInput{ + InstanceIds: []*string{aws.String(instanceId)}, } - latestInstance, err := WaitForState(&stateChangeSpot) - if err != nil { + if err := ec2conn.WaitUntilInstanceStatusOk(describeInstanceStatus); err != nil { err := fmt.Errorf("Error waiting for instance (%s) to become ready: %s", instanceId, err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } - instance := latestInstance.(*ec2.Instance) + r, err := ec2conn.DescribeInstances(&ec2.DescribeInstancesInput{ + InstanceIds: []*string{aws.String(instanceId)}, + }) + if err != nil || len(r.Reservations) == 0 || len(r.Reservations[0].Instances) == 0 { + err := fmt.Errorf("Error finding source instance.") + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + instance := r.Reservations[0].Instances[0] // Retry creating tags for about 2.5 minutes err = retry.Retry(0.2, 30, 11, func(_ uint) (bool, error) { From 73b98b2a0405dfa6b3068503c2559ba45877bfd4 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 7 Dec 2017 14:05:59 -0800 Subject: [PATCH 0133/1216] use waiter to wait for ebs instances to stop --- builder/amazon/common/step_stop_ebs_instance.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/builder/amazon/common/step_stop_ebs_instance.go b/builder/amazon/common/step_stop_ebs_instance.go index 852626811..7d6b2e943 100644 --- a/builder/amazon/common/step_stop_ebs_instance.go +++ b/builder/amazon/common/step_stop_ebs_instance.go @@ -75,15 +75,12 @@ func (s *StepStopEBSBackedInstance) Run(state multistep.StateBag) multistep.Step ui.Say("Automatic instance stop disabled. Please stop instance manually.") } - // Wait for the instance to actual stop + // Wait for the instance to actually stop ui.Say("Waiting for the instance to stop...") - stateChange := StateChangeConf{ - Pending: []string{"running", "pending", "stopping"}, - Target: "stopped", - Refresh: InstanceStateRefreshFunc(ec2conn, *instance.InstanceId), - StepState: state, - } - _, err = WaitForState(&stateChange) + err = ec2conn.WaitUntilInstanceStopped(&ec2.DescribeInstancesInput{ + InstanceIds: []*string{instance.InstanceId}, + }) + if err != nil { err := fmt.Errorf("Error waiting for instance to stop: %s", err) state.Put("error", err) From 4b1d8e3fe8b4f0b490902748fd09520423a4b4a8 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 7 Dec 2017 15:27:40 -0800 Subject: [PATCH 0134/1216] don't allow enhanced networking flags for spot instances. --- builder/amazon/ebs/builder.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/builder/amazon/ebs/builder.go b/builder/amazon/ebs/builder.go index 56757b826..42c87c365 100644 --- a/builder/amazon/ebs/builder.go +++ b/builder/amazon/ebs/builder.go @@ -34,8 +34,9 @@ type Config struct { } type Builder struct { - config Config - runner multistep.Runner + config Config + runner multistep.Runner + isSpotInstance bool } func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { @@ -60,6 +61,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { if b.config.PackerConfig.PackerForce { b.config.AMIForceDeregister = true } + b.isSpotInstance = b.config.SpotPrice != "" && b.config.SpotPrice != "0" // Accumulate any errors var errs *packer.MultiError @@ -69,6 +71,10 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { errs = packer.MultiErrorAppend(errs, b.config.BlockDevices.Prepare(&b.config.ctx)...) errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...) + if b.isSpotInstance && (b.config.AMIENASupport || b.config.AMISriovNetSupport) { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("Spot instances do not support modification. Please ensure you use an AMI that supports either SR-IOV or ENA.")) + } + if errs != nil && len(errs.Errors) > 0 { return nil, errs } @@ -110,9 +116,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe state.Put("ui", ui) var instanceStep multistep.Step - isSpotInstance := b.config.SpotPrice != "" && b.config.SpotPrice != "0" - if isSpotInstance { + if b.isSpotInstance { instanceStep = &awscommon.StepRunSpotInstance{ Debug: b.config.PackerDebug, ExpectedRootDevice: "ebs", @@ -201,7 +206,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe }, &common.StepProvision{}, &awscommon.StepStopEBSBackedInstance{ - Skip: isSpotInstance, + Skip: b.isSpotInstance, DisableStopInstance: b.config.DisableStopInstance, }, &awscommon.StepModifyEBSBackedInstance{ From 6601f18c7861b2c30f7a32865a13319a6ea3f473 Mon Sep 17 00:00:00 2001 From: Nick Fagerlund Date: Fri, 8 Dec 2017 13:46:51 -0800 Subject: [PATCH 0135/1216] Minor edits --- .../building-image-in-cicd.html.md | 8 +++---- .../building-virtualbox-image.html.md | 22 +++++++++---------- .../guides/packer-on-cicd/index.html.md | 4 ++-- .../packer-on-cicd/triggering-tfe.html.md | 18 ++++++++++----- .../uploading-images-to-artifact.html.md | 14 +++++++----- 5 files changed, 38 insertions(+), 28 deletions(-) diff --git a/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md b/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md index 99607b2f8..a5bc86725 100644 --- a/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md +++ b/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md @@ -6,11 +6,11 @@ page_title: Building Images in CI/CD # Building Images in CI/CD -The following guides from our amazing partners show how to use their service to build images with Packer. +The following guides from our partners show how to use their services to build images with Packer. - [How to Build Immutable Infrastructure with Packer and CircleCI Workflows](#) -- [Using Packer and Ansible to Build Immutable Infrastructure [in CodeShip]](https://blog.codeship.com/packer-ansible/) +- [Using Packer and Ansible to Build Immutable Infrastructure in CodeShip](https://blog.codeship.com/packer-ansible/) -For the majority of the [Packer Builders](https://www.packer.io/docs/builders/index.html) can run in a container or VM, a common model used by most CI/CD services. However, the [QEMU builder](https://www.packer.io/docs/builders/qemu.html) for [KVM](https://www.linux-kvm.org/page/Main_Page) and [Xen](https://www.xenproject.org/) virtual machine images, [VirtualBox builder](https://www.packer.io/docs/builders/virtualbox.html) for OVA or OVF virtual machines and [VMWare builder](https://www.packer.io/docs/builders/vmware.html) for use with VMware products require running on a bare-metal machine. +The majority of the [Packer Builders](https://www.packer.io/docs/builders/index.html) can run in a container or VM, a common model used by most CI/CD services. However, the [QEMU builder](https://www.packer.io/docs/builders/qemu.html) for [KVM](https://www.linux-kvm.org/page/Main_Page) and [Xen](https://www.xenproject.org/) virtual machine images, [VirtualBox builder](https://www.packer.io/docs/builders/virtualbox.html) for OVA or OVF virtual machines and [VMWare builder](https://www.packer.io/docs/builders/vmware.html) for use with VMware products require running on a bare-metal machine. -The [Building a VirtualBox Image with Packer in TeamCity](./building-virtualbox-image.html) guide walks through creating a VirtualBox image, which requires a bare-metal machine, running in TeamCity which also supports running the scripts directly on the machine. +The [Building a VirtualBox Image with Packer in TeamCity](./building-virtualbox-image.html) guide shows how to create a VirtualBox image using TeamCity's support for running scripts on bare-metal machines. diff --git a/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md b/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md index e06866650..484308f8e 100644 --- a/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md +++ b/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md @@ -8,9 +8,9 @@ page_title: Building a VirtualBox Image with Packer in TeamCity This guide walks through the process of building a VirtualBox image using Packer on a new TeamCity Agent. Before getting started you should have access to a TeamCity Server. -The Packer VirtualBox builder requires access to VirtualBox which should run on a bare-metal machine as virtual machines should not run inside other virtual machines. This is also true for the [VMWare](https://www.packer.io/docs/builders/vmware.html) and the [QEMU](https://www.packer.io/docs/builders/qemu.html) Packer builders. +The Packer VirtualBox builder requires access to VirtualBox. VirtualBox should run on a bare-metal machine, as virtual machines should not run inside other virtual machines. This is also true for the [VMWare](https://www.packer.io/docs/builders/vmware.html) and the [QEMU](https://www.packer.io/docs/builders/qemu.html) Packer builders. -## 1. Provision a bare-metal machine +## 1. Provision a Bare-metal Machine The Packer VirtualBox builder requires running on bare-metal (hardware). If you do not have access to a bare-metal machine, we recommend using [Packet.net](https://www.packet.net/) to obtain a new machine. If you are a first time user of Packet.net, the Packet.net team has provided HashiCorp the coupon code `hash25` which you can use for $25 off to test out this guide. You can use a `baremetal_0` for testing, but for regular use the `baremetal_1` instance may be a better option. @@ -35,7 +35,7 @@ resource "packet_device" "agent" { ## 2. Install VirtualBox and TeamCity Dependencies -VirtualBox must be installed on the new instance along and TeamCity requires the JDK prior to installation. This guide uses Ubuntu as the Linux distribution, so you may need to adjust these commands for your distribution of choice. +VirtualBox must be installed on the new instance, and TeamCity requires the JDK prior to installation. This guide uses Ubuntu as the Linux distribution, so you may need to adjust these commands for your distribution of choice. **Install Teamcity Dependencies** @@ -51,7 +51,7 @@ curl -OL "http://download.virtualbox.org/virtualbox/5.2.2/virtualbox-5.2_5.2.2-1 dpkg -i virtualbox-5.2_5.2.2-119230~Ubuntu~xenial_amd64.deb ``` -You can also use the [`remote-exec` provisioner](https://www.terraform.io/docs/provisioners/remote-exec.html) in your terraform configuration to automatically run these commands when provisioning the new instance. +You can also use the [`remote-exec` provisioner](https://www.terraform.io/docs/provisioners/remote-exec.html) in your Terraform configuration to automatically run these commands when provisioning the new instance. ## 3. Install Packer @@ -66,13 +66,13 @@ Packer is installed at the `/root/packer` path which is used in subsequent steps ## 4. Install TeamCity Agent -This guide assume you already have a running instance of TeamCity Server. The new TeamCity Agent can be installed by [downloading a zip file and installing manually](https://confluence.jetbrains.com/display/TCD10//Setting+up+and+Running+Additional+Build+Agents#SettingupandRunningAdditionalBuildAgents-InstallingAdditionalBuildAgents), or using [Agent Push](https://confluence.jetbrains.com/display/TCD10//Setting+up+and+Running+Additional+Build+Agents#SettingupandRunningAdditionalBuildAgents-InstallingviaAgentPush). Once it is installed it should appear in TeamCity as a new Agent. +This guide assume you already have a running instance of TeamCity Server. The new TeamCity Agent can be installed by [downloading a zip file and installing manually](https://confluence.jetbrains.com/display/TCD10//Setting+up+and+Running+Additional+Build+Agents#SettingupandRunningAdditionalBuildAgents-InstallingAdditionalBuildAgents), or using [Agent Push](https://confluence.jetbrains.com/display/TCD10//Setting+up+and+Running+Additional+Build+Agents#SettingupandRunningAdditionalBuildAgents-InstallingviaAgentPush). Once it is installed it should appear in TeamCity as a new Agent. -Create a new Agent Pool for the agents which will be responsible for the VirtualBox Packer builds and the assign the new Agent to the new Agent Pool. +Create a new Agent Pool for agents responsible for VirtualBox Packer builds and assign the new Agent to it. -## 5. Create a new Build in TeamCity +## 5. Create a New Build in TeamCity -In TeamCity Server create a new build and configure the Version Control Settings to download the Packer build configuration from the VCS repository. +In TeamCity Server create a new build and configure the Version Control Settings to download the Packer build configuration from the VCS repository. Add one **Build Step: Command Line** to the build. @@ -82,14 +82,14 @@ In the **Script content** field add the following: ```shell #!/usr/bin/env bash -/root/packer build -only=virtualbox-iso -var "headless=true" ./packer.json +/root/packer build -only=virtualbox-iso -var "headless=true" ./packer.json ``` This assumes that `packer.json` is the Packer build configuration file in the root path of the VCS repository. ## 6. Run a build in TeamCity -The entire configuration is ready for a new build. Start a new run in TeamCity by pressing “Run”. +The entire configuration is ready for a new build. Start a new run in TeamCity by pressing “Run”. The new run should be triggered and the virtual box image will be built. @@ -97,4 +97,4 @@ The new run should be triggered and the virtual box image will be built. Once complete, the build status should be updated to complete and successful. -![TeamCity screenshot: Build log complete](./images/teamcity_build_log_complete.png) \ No newline at end of file +![TeamCity screenshot: Build log complete](./images/teamcity_build_log_complete.png) diff --git a/website/source/guides/packer-on-cicd/index.html.md b/website/source/guides/packer-on-cicd/index.html.md index 5c79dfd88..dce80ada2 100644 --- a/website/source/guides/packer-on-cicd/index.html.md +++ b/website/source/guides/packer-on-cicd/index.html.md @@ -6,8 +6,8 @@ page_title: Building Immutable Infrastructure with Packer in CI/CD # Building Immutable Infrastructure with Packer in CI/CD -This guide focuses on the following workflow for building immutable infrastructure. This workflow can be manual or automated and it can be implemented with a variety of technologies. The goal of this guide is to show how this workflow can be fully automated using Packer for building images from a CI/CD pipeline. +This guide focuses on the following workflow for building immutable infrastructure. This workflow can be manual or automated and it can be implemented with a variety of technologies. The goal of this guide is to show how this workflow can be fully automated using Packer for building images from a continuous integration/continuous deployment (CI/CD) pipeline. 1. [Building Images using Packer in CI/CD](./building-image-in-cicd.html) 2. [Uploading the new image to S3](./uploading-images-to-artifact.html) for future deployment or use during development -3. Provision new instances with the images using Terraform Enterprise by [creating a new Terraform Enterprise runs](./triggering-tfe.html). \ No newline at end of file +3. [Creating new Terraform Enterprise runs](./triggering-tfe.html) to provision new instances with the images diff --git a/website/source/guides/packer-on-cicd/triggering-tfe.html.md b/website/source/guides/packer-on-cicd/triggering-tfe.html.md index a241b0f15..a79af6f7a 100644 --- a/website/source/guides/packer-on-cicd/triggering-tfe.html.md +++ b/website/source/guides/packer-on-cicd/triggering-tfe.html.md @@ -4,11 +4,15 @@ sidebar_current: guides-packer-on-cicd-triggering-tfe-run page_title: Triggering Terraform Enterprise runs --- -# Creating a Terraform Enterprise runs +# Creating Terraform Enterprise Runs -Once an image is built and uploaded to an artifact store, the next step is to use this new image. In some cases the image will be downloaded by the dev team and used locally in development, like is often done with VirtualBox images with Vagrant. In most other cases, the new image will be used to provision new infrastructure. [Terraform](https://www.terraform.io/) is an open source tool that is ideal for provisioning the new infrastructure with the new image generated by Packer. +Once an image is built and uploaded to an artifact store, the next step is to use this new image. In some cases the image will be downloaded by the dev team and used locally in development, like is often done with VirtualBox images with Vagrant. In most other cases, the new image will be used to provision new infrastructure. -The following is a sample terraform configuration which provisions a new AWS EC2 instance. The `aws_ami_id` is a variable which will be provided when running `terraform plan` and `terraform apply`. This variable references the latest AMI generated with the Packer build in CI/CD. +[Terraform](https://www.terraform.io/) is an open source tool that is ideal for provisioning new infrastructure with images generated by Packer, and [Terraform Enterprise](https://www.hashicorp.com/products/terraform/) is the best way to perform automated Terraform runs. + +## Create a Terraform Configuration and Workspace + +The following is a sample Terraform configuration which provisions a new AWS EC2 instance. The `aws_ami_id` is a variable which will be provided when running `terraform plan` and `terraform apply`. This variable references the latest AMI generated with the Packer build in CI/CD. ```hcl variable "aws_ami_id" { } @@ -25,8 +29,10 @@ resource "aws_instance" "web" { Terraform Enterprise should have a workspace with this terraform configuration and a placeholder variable `aws_ami_id`. -**Steps to create a new run from CI/CD after a Packer build is complete and uploaded**: +## Include Terraform Enterprise in Your CI Builds + +Follow these steps to create a new run from CI/CD after a Packer build is complete and uploaded. 1. Add a new step to the CI/CD pipeline. -2. In the new step add a `curl` call to update the variables in the workspace using the [update variables API](https://www.terraform.io/docs/enterprise-beta/api/variables.html#update-variables) with the reference to the latest image. In the sample terraform configuration above, the “aws_ami_id” variable would be updated to the AMI ID of the latest image. -3. In that same step, add another `curl` call to [create a new run via the API](https://www.terraform.io/docs/enterprise-beta/api/run.html#create-a-run). A run performs a plan and apply on the last configuration version created and using the variables set in the workspace. In the previous step we update the variables, so the new run can be created using the previous configuration version. +2. In the new step add a `curl` call to update the variables in the workspace using the [update variables API](https://www.terraform.io/docs/enterprise-beta/api/variables.html#update-variables), so that Terraform has a reference to the latest image. For the sample configuration above, the `aws_ami_id` variable should be updated to the AMI ID of the latest image. +3. In that same step, add another `curl` call to [create a new run via the API](https://www.terraform.io/docs/enterprise-beta/api/run.html#create-a-run). A run performs a plan and apply on the last configuration version created, using the variables set in the workspace. In the previous step we update the variables, so the new run can be created using the previous configuration version. diff --git a/website/source/guides/packer-on-cicd/uploading-images-to-artifact.html.md b/website/source/guides/packer-on-cicd/uploading-images-to-artifact.html.md index 7ca746df7..3ea0ece15 100644 --- a/website/source/guides/packer-on-cicd/uploading-images-to-artifact.html.md +++ b/website/source/guides/packer-on-cicd/uploading-images-to-artifact.html.md @@ -6,18 +6,22 @@ page_title: Uploading Images to Artifact Stores # Uploading Images to Artifact Stores -Once the image is generated it will be used by other parts of your operations workflow. For example, it is common to build VirtualBoxes with Packer to be used as base boxes in Vagrant. +Once the image is generated it will be used by other parts of your operations workflow. For example, it is common to build VirtualBox images with Packer to be used as base boxes in Vagrant. -On the agent machine install the [AWS Command Line Tool](https://aws.amazon.com/cli/). Since this is a one-time operation, this can be incorporated into the initial provisioning step when installing other dependencies. +The exact process for uploading images depends on the artifact store and CI system you use. TeamCity provides a [Build Artifacts](https://confluence.jetbrains.com/display/TCD9/Build+Artifact) feature which can be used to store the newly generated image. Other CI/CD services also have similar build artifacts features built in, like [Circle CI Build Artifacts](https://circleci.com/docs/2.0/artifacts/). In addition to the built in artifact stores in CI/CD tools, there are also dedicated universal artifact storage services like [Artifactory](https://confluence.jetbrains.com/display/TCD9/Build+Artifact). All of these are great options for image artifact storage. + +The following example uses TeamCity and Amazon S3. + +## Example: Uploading to S3 in a TeamCity Build + +On the agent machine responsible for building images, install the [AWS Command Line Tool](https://aws.amazon.com/cli/). Since this is a one-time operation, this can be incorporated into the initial agent provisioning step when installing other dependencies. ```shell pip install awscli ``` -Add an additional **Build Step: Command Line** to the build and set the **Script content** field to the following: +In your build configuration in TeamCity Server, add an additional **Build Step: Command Line** and set the **Script content** field to the following: ```shell awscli s3 cp . s3://bucket/ --exclude “*” --include “*.iso” ``` - -TeamCity provides a [Build Artifacts](https://confluence.jetbrains.com/display/TCD9/Build+Artifact) feature which can be used to store the newly generated image. Other CI/CD services also have similar build artifacts features built in, like [Circle CI Build Artifacts](https://circleci.com/docs/2.0/artifacts/). In addition to the built in artifact stores in CI/CD tools, there are also dedicated universal artifact storage services like [Artifactory](https://confluence.jetbrains.com/display/TCD9/Build+Artifact). All of these are great options for image artifact storage. From f216330ba30d72ddaa44e57f3cb32c3b4b7bb651 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 8 Dec 2017 14:56:19 -0800 Subject: [PATCH 0136/1216] spot instance ena/sriov checking for all other builders --- builder/amazon/common/run_config.go | 4 ++ builder/amazon/ebs/builder.go | 81 +++++++++++++------------- builder/amazon/ebssurrogate/builder.go | 76 +++++++++++++----------- builder/amazon/ebsvolume/builder.go | 73 ++++++++++++----------- builder/amazon/instance/builder.go | 47 ++++++++------- 5 files changed, 152 insertions(+), 129 deletions(-) diff --git a/builder/amazon/common/run_config.go b/builder/amazon/common/run_config.go index eef56cf4c..1b50b4842 100644 --- a/builder/amazon/common/run_config.go +++ b/builder/amazon/common/run_config.go @@ -152,3 +152,7 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { return errs } + +func (c *RunConfig) IsSpotInstance() bool { + return c.SpotPrice != "" && c.SpotPrice != "0" +} diff --git a/builder/amazon/ebs/builder.go b/builder/amazon/ebs/builder.go index 42c87c365..255506ccc 100644 --- a/builder/amazon/ebs/builder.go +++ b/builder/amazon/ebs/builder.go @@ -34,9 +34,8 @@ type Config struct { } type Builder struct { - config Config - runner multistep.Runner - isSpotInstance bool + config Config + runner multistep.Runner } func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { @@ -61,7 +60,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { if b.config.PackerConfig.PackerForce { b.config.AMIForceDeregister = true } - b.isSpotInstance = b.config.SpotPrice != "" && b.config.SpotPrice != "0" // Accumulate any errors var errs *packer.MultiError @@ -71,8 +69,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { errs = packer.MultiErrorAppend(errs, b.config.BlockDevices.Prepare(&b.config.ctx)...) errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...) - if b.isSpotInstance && (b.config.AMIENASupport || b.config.AMISriovNetSupport) { - errs = packer.MultiErrorAppend(errs, fmt.Errorf("Spot instances do not support modification. Please ensure you use an AMI that supports either SR-IOV or ENA.")) + if b.config.IsSpotInstance() && (b.config.AMIENASupport || b.config.AMISriovNetSupport) { + errs = packer.MultiErrorAppend(errs, + fmt.Errorf("Spot instances do not support modification, which is required "+ + "when either `ena_support` or `sriov_support` are set. Please ensure "+ + "you use an AMI that already has either SR-IOV or ENA enabled.")) } if errs != nil && len(errs.Errors) > 0 { @@ -117,45 +118,45 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe var instanceStep multistep.Step - if b.isSpotInstance { + if b.config.IsSpotInstance() { instanceStep = &awscommon.StepRunSpotInstance{ - Debug: b.config.PackerDebug, - ExpectedRootDevice: "ebs", - SpotPrice: b.config.SpotPrice, - SpotPriceProduct: b.config.SpotPriceAutoProduct, - InstanceType: b.config.InstanceType, - UserData: b.config.UserData, - UserDataFile: b.config.UserDataFile, - SourceAMI: b.config.SourceAmi, - IamInstanceProfile: b.config.IamInstanceProfile, - SubnetId: b.config.SubnetId, - AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, - EbsOptimized: b.config.EbsOptimized, - AvailabilityZone: b.config.AvailabilityZone, - BlockDevices: b.config.BlockDevices, - Tags: b.config.RunTags, - VolumeTags: b.config.VolumeRunTags, - Ctx: b.config.ctx, + AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, + AvailabilityZone: b.config.AvailabilityZone, + BlockDevices: b.config.BlockDevices, + Ctx: b.config.ctx, + Debug: b.config.PackerDebug, + EbsOptimized: b.config.EbsOptimized, + ExpectedRootDevice: "ebs", + IamInstanceProfile: b.config.IamInstanceProfile, InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior, + InstanceType: b.config.InstanceType, + SourceAMI: b.config.SourceAmi, + SpotPrice: b.config.SpotPrice, + SpotPriceProduct: b.config.SpotPriceAutoProduct, + SubnetId: b.config.SubnetId, + Tags: b.config.RunTags, + UserData: b.config.UserData, + UserDataFile: b.config.UserDataFile, + VolumeTags: b.config.VolumeRunTags, } } else { instanceStep = &awscommon.StepRunSourceInstance{ - Debug: b.config.PackerDebug, - ExpectedRootDevice: "ebs", - InstanceType: b.config.InstanceType, - UserData: b.config.UserData, - UserDataFile: b.config.UserDataFile, - SourceAMI: b.config.SourceAmi, - IamInstanceProfile: b.config.IamInstanceProfile, - SubnetId: b.config.SubnetId, - AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, - EbsOptimized: b.config.EbsOptimized, - AvailabilityZone: b.config.AvailabilityZone, - BlockDevices: b.config.BlockDevices, - Tags: b.config.RunTags, - VolumeTags: b.config.VolumeRunTags, - Ctx: b.config.ctx, + AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, + AvailabilityZone: b.config.AvailabilityZone, + BlockDevices: b.config.BlockDevices, + Ctx: b.config.ctx, + Debug: b.config.PackerDebug, + EbsOptimized: b.config.EbsOptimized, + ExpectedRootDevice: "ebs", + IamInstanceProfile: b.config.IamInstanceProfile, InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior, + InstanceType: b.config.InstanceType, + SourceAMI: b.config.SourceAmi, + SubnetId: b.config.SubnetId, + Tags: b.config.RunTags, + UserData: b.config.UserData, + UserDataFile: b.config.UserDataFile, + VolumeTags: b.config.VolumeRunTags, } } @@ -206,7 +207,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe }, &common.StepProvision{}, &awscommon.StepStopEBSBackedInstance{ - Skip: b.isSpotInstance, + Skip: b.config.IsSpotInstance(), DisableStopInstance: b.config.DisableStopInstance, }, &awscommon.StepModifyEBSBackedInstance{ diff --git a/builder/amazon/ebssurrogate/builder.go b/builder/amazon/ebssurrogate/builder.go index fec997816..8cf1fff12 100644 --- a/builder/amazon/ebssurrogate/builder.go +++ b/builder/amazon/ebssurrogate/builder.go @@ -84,6 +84,13 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { errs = packer.MultiErrorAppend(errs, fmt.Errorf("no volume with name '%s' is found", b.config.RootDevice.SourceDeviceName)) } + if b.config.IsSpotInstance() && (b.config.AMIENASupport || b.config.AMISriovNetSupport) { + errs = packer.MultiErrorAppend(errs, + fmt.Errorf("Spot instances do not support modification, which is required "+ + "when either `ena_support` or `sriov_support` are set. Please ensure "+ + "you use an AMI that already has either SR-IOV or ENA enabled.")) + } + if errs != nil && len(errs.Errors) > 0 { return nil, errs } @@ -124,47 +131,46 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe state.Put("ui", ui) var instanceStep multistep.Step - isSpotInstance := b.config.SpotPrice != "" && b.config.SpotPrice != "0" - if isSpotInstance { + if b.config.IsSpotInstance() { instanceStep = &awscommon.StepRunSpotInstance{ - Debug: b.config.PackerDebug, - ExpectedRootDevice: "ebs", - SpotPrice: b.config.SpotPrice, - SpotPriceProduct: b.config.SpotPriceAutoProduct, - InstanceType: b.config.InstanceType, - UserData: b.config.UserData, - UserDataFile: b.config.UserDataFile, - SourceAMI: b.config.SourceAmi, - IamInstanceProfile: b.config.IamInstanceProfile, - SubnetId: b.config.SubnetId, - AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, - EbsOptimized: b.config.EbsOptimized, - AvailabilityZone: b.config.AvailabilityZone, - BlockDevices: b.config.BlockDevices, - Tags: b.config.RunTags, - VolumeTags: b.config.VolumeRunTags, - Ctx: b.config.ctx, + AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, + AvailabilityZone: b.config.AvailabilityZone, + BlockDevices: b.config.BlockDevices, + Ctx: b.config.ctx, + Debug: b.config.PackerDebug, + EbsOptimized: b.config.EbsOptimized, + ExpectedRootDevice: "ebs", + IamInstanceProfile: b.config.IamInstanceProfile, InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior, + InstanceType: b.config.InstanceType, + SourceAMI: b.config.SourceAmi, + SpotPrice: b.config.SpotPrice, + SpotPriceProduct: b.config.SpotPriceAutoProduct, + SubnetId: b.config.SubnetId, + Tags: b.config.RunTags, + UserData: b.config.UserData, + UserDataFile: b.config.UserDataFile, + VolumeTags: b.config.VolumeRunTags, } } else { instanceStep = &awscommon.StepRunSourceInstance{ - Debug: b.config.PackerDebug, - ExpectedRootDevice: "ebs", - InstanceType: b.config.InstanceType, - UserData: b.config.UserData, - UserDataFile: b.config.UserDataFile, - SourceAMI: b.config.SourceAmi, - IamInstanceProfile: b.config.IamInstanceProfile, - SubnetId: b.config.SubnetId, - AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, - EbsOptimized: b.config.EbsOptimized, - AvailabilityZone: b.config.AvailabilityZone, - BlockDevices: b.config.BlockDevices, - Tags: b.config.RunTags, - VolumeTags: b.config.VolumeRunTags, - Ctx: b.config.ctx, + AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, + AvailabilityZone: b.config.AvailabilityZone, + BlockDevices: b.config.BlockDevices, + Ctx: b.config.ctx, + Debug: b.config.PackerDebug, + EbsOptimized: b.config.EbsOptimized, + ExpectedRootDevice: "ebs", + IamInstanceProfile: b.config.IamInstanceProfile, InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior, + InstanceType: b.config.InstanceType, + SourceAMI: b.config.SourceAmi, + SubnetId: b.config.SubnetId, + Tags: b.config.RunTags, + UserData: b.config.UserData, + UserDataFile: b.config.UserDataFile, + VolumeTags: b.config.VolumeRunTags, } } @@ -212,7 +218,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe }, &common.StepProvision{}, &awscommon.StepStopEBSBackedInstance{ - Skip: isSpotInstance, + Skip: b.config.IsSpotInstance(), DisableStopInstance: b.config.DisableStopInstance, }, &awscommon.StepModifyEBSBackedInstance{ diff --git a/builder/amazon/ebsvolume/builder.go b/builder/amazon/ebsvolume/builder.go index 6bd8927ee..e7384784b 100644 --- a/builder/amazon/ebsvolume/builder.go +++ b/builder/amazon/ebsvolume/builder.go @@ -62,6 +62,13 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { errs = packer.MultiErrorAppend(errs, err) } + if b.config.IsSpotInstance() && (b.config.AMIENASupport || b.config.AMISriovNetSupport) { + errs = packer.MultiErrorAppend(errs, + fmt.Errorf("Spot instances do not support modification, which is required "+ + "when either `ena_support` or `sriov_support` are set. Please ensure "+ + "you use an AMI that already has either SR-IOV or ENA enabled.")) + } + if errs != nil && len(errs.Errors) > 0 { return nil, errs } @@ -103,45 +110,43 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe var instanceStep multistep.Step - isSpotInstance := b.config.SpotPrice != "" && b.config.SpotPrice != "0" - - if isSpotInstance { + if b.config.IsSpotInstance() { instanceStep = &awscommon.StepRunSpotInstance{ - Debug: b.config.PackerDebug, - ExpectedRootDevice: "ebs", - SpotPrice: b.config.SpotPrice, - SpotPriceProduct: b.config.SpotPriceAutoProduct, - InstanceType: b.config.InstanceType, - UserData: b.config.UserData, - UserDataFile: b.config.UserDataFile, - SourceAMI: b.config.SourceAmi, - IamInstanceProfile: b.config.IamInstanceProfile, - SubnetId: b.config.SubnetId, - AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, - EbsOptimized: b.config.EbsOptimized, - AvailabilityZone: b.config.AvailabilityZone, - BlockDevices: b.config.launchBlockDevices, - Tags: b.config.RunTags, - Ctx: b.config.ctx, + AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, + AvailabilityZone: b.config.AvailabilityZone, + BlockDevices: b.config.launchBlockDevices, + Ctx: b.config.ctx, + Debug: b.config.PackerDebug, + EbsOptimized: b.config.EbsOptimized, + ExpectedRootDevice: "ebs", + IamInstanceProfile: b.config.IamInstanceProfile, InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior, + InstanceType: b.config.InstanceType, + SourceAMI: b.config.SourceAmi, + SpotPrice: b.config.SpotPrice, + SpotPriceProduct: b.config.SpotPriceAutoProduct, + SubnetId: b.config.SubnetId, + Tags: b.config.RunTags, + UserData: b.config.UserData, + UserDataFile: b.config.UserDataFile, } } else { instanceStep = &awscommon.StepRunSourceInstance{ - Debug: b.config.PackerDebug, - ExpectedRootDevice: "ebs", - InstanceType: b.config.InstanceType, - UserData: b.config.UserData, - UserDataFile: b.config.UserDataFile, - SourceAMI: b.config.SourceAmi, - IamInstanceProfile: b.config.IamInstanceProfile, - SubnetId: b.config.SubnetId, - AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, - EbsOptimized: b.config.EbsOptimized, - AvailabilityZone: b.config.AvailabilityZone, - BlockDevices: b.config.launchBlockDevices, - Tags: b.config.RunTags, - Ctx: b.config.ctx, + AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, + AvailabilityZone: b.config.AvailabilityZone, + BlockDevices: b.config.launchBlockDevices, + Ctx: b.config.ctx, + Debug: b.config.PackerDebug, + EbsOptimized: b.config.EbsOptimized, + ExpectedRootDevice: "ebs", + IamInstanceProfile: b.config.IamInstanceProfile, InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior, + InstanceType: b.config.InstanceType, + SourceAMI: b.config.SourceAmi, + SubnetId: b.config.SubnetId, + Tags: b.config.RunTags, + UserData: b.config.UserData, + UserDataFile: b.config.UserDataFile, } } @@ -189,7 +194,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe }, &common.StepProvision{}, &awscommon.StepStopEBSBackedInstance{ - Skip: isSpotInstance, + Skip: b.config.IsSpotInstance(), DisableStopInstance: b.config.DisableStopInstance, }, &awscommon.StepModifyEBSBackedInstance{ diff --git a/builder/amazon/instance/builder.go b/builder/amazon/instance/builder.go index 220ee7b10..ccef52d1a 100644 --- a/builder/amazon/instance/builder.go +++ b/builder/amazon/instance/builder.go @@ -155,6 +155,13 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { errs, fmt.Errorf("x509_key_path points to bad file: %s", err)) } + if b.config.IsSpotInstance() && (b.config.AMIENASupport || b.config.AMISriovNetSupport) { + errs = packer.MultiErrorAppend(errs, + fmt.Errorf("Spot instances do not support modification, which is required "+ + "when either `ena_support` or `sriov_support` are set. Please ensure "+ + "you use an AMI that already has either SR-IOV or ENA enabled.")) + } + if errs != nil && len(errs.Errors) > 0 { return nil, errs } @@ -196,39 +203,39 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe var instanceStep multistep.Step - if b.config.SpotPrice == "" || b.config.SpotPrice == "0" { - instanceStep = &awscommon.StepRunSourceInstance{ - Debug: b.config.PackerDebug, - InstanceType: b.config.InstanceType, - UserData: b.config.UserData, - UserDataFile: b.config.UserDataFile, - SourceAMI: b.config.SourceAmi, - IamInstanceProfile: b.config.IamInstanceProfile, - SubnetId: b.config.SubnetId, + if b.config.IsSpotInstance() { + instanceStep = &awscommon.StepRunSpotInstance{ AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, - EbsOptimized: b.config.EbsOptimized, AvailabilityZone: b.config.AvailabilityZone, BlockDevices: b.config.BlockDevices, - Tags: b.config.RunTags, Ctx: b.config.ctx, - } - } else { - instanceStep = &awscommon.StepRunSpotInstance{ Debug: b.config.PackerDebug, + EbsOptimized: b.config.EbsOptimized, + IamInstanceProfile: b.config.IamInstanceProfile, + InstanceType: b.config.InstanceType, + SourceAMI: b.config.SourceAmi, SpotPrice: b.config.SpotPrice, SpotPriceProduct: b.config.SpotPriceAutoProduct, - InstanceType: b.config.InstanceType, + SubnetId: b.config.SubnetId, + Tags: b.config.RunTags, UserData: b.config.UserData, UserDataFile: b.config.UserDataFile, - SourceAMI: b.config.SourceAmi, - IamInstanceProfile: b.config.IamInstanceProfile, - SubnetId: b.config.SubnetId, + } + } else { + instanceStep = &awscommon.StepRunSourceInstance{ AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, - EbsOptimized: b.config.EbsOptimized, AvailabilityZone: b.config.AvailabilityZone, BlockDevices: b.config.BlockDevices, - Tags: b.config.RunTags, Ctx: b.config.ctx, + Debug: b.config.PackerDebug, + EbsOptimized: b.config.EbsOptimized, + IamInstanceProfile: b.config.IamInstanceProfile, + InstanceType: b.config.InstanceType, + SourceAMI: b.config.SourceAmi, + SubnetId: b.config.SubnetId, + Tags: b.config.RunTags, + UserData: b.config.UserData, + UserDataFile: b.config.UserDataFile, } } From 3710c34eb5771ed0bd9713d85f4559b34729d2f2 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 8 Dec 2017 15:17:32 -0800 Subject: [PATCH 0137/1216] Revert "update changelog" This reverts commit 98420fc9f5928ee20b667a1e931d88cceccaee5b. --- CHANGELOG.md | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 799e41f93..8cb17b49e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,8 +14,6 @@ * builder/amazon: Correctly deregister AMIs when `force_deregister` is set. [GH-5525] -* builder/amazon: Add a new parameter `ssh_interface`. Valid values include -`public_ip`, `private_ip`, `public_dns` or `private_dns`. [GH-5630] * builder/digitalocean: Add `ipv6` option to enable on droplet. [GH-5534] * builder/docker: Add `aws_profile` option to control the aws profile for ECR. [GH-5470] @@ -43,30 +41,9 @@ * post-processor/docker-push: Add `aws_profile` option to control the aws profile for ECR. [GH-5470] * post-processor/vsphere: Properly capture `ovftool` output. [GH-5499] -* builder/vmware-iso: Improve logging of network errors. [GH-5456] -* provisioner/ansible-local: Add ability to clean staging directory. [GH-5618] -* core: Add new `packer_version` template engine. [GH-5619] -* core: Improve logic checking for downloaded isos in case where user has -provided 2+ `iso_urls` [GH-5632] -* builder/hyper-v: Add support for differencing disk. [GH-5458] -* builder/azure: Add sanity checks for resource group names [GH-5599] ### BUG FIXES: -* builder/hyper-v: Fix interpolation context for user variables in -`boot_command` [GH-5547] -* core: Fix windows pathing regression when downloading isos. [GH-5591] -* builder/azure: Only destroy temporary resource groups if they were created by -Packer. [GH-5593] -* provisioner/chef: Fix chef installs on Windows. [GH-5649] -* builder/azure: Clean up KeyVaults when deploying Windows images that used an -existing resource group. [GH-5656] -* builder/azure: 'build_resource_group_name' is mutually exclusive with -'location' [GH-5661] -* builder/amazon: Correctly set AWS region if given in template along with a -profile. [GH-5676] -* builder/vmware: Correctly detect Windows boot on vmware workstation. [GH-5672] -* builder/amazon: Builds on new C5 instances are now possible. [GH-5678] * builder/amazon: Add a delay option to security group waiter. [GH-5536] * builder/amazon: Fix regressions relating to spot instances and EBS volumes. [GH-5495] From cad5ccfa64e85c14fcf3ae8f3611796a687ed39f Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 8 Dec 2017 15:24:37 -0800 Subject: [PATCH 0138/1216] update changelog --- CHANGELOG.md | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cb17b49e..8a5864a7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,41 @@ ## (UNRELEASED) ### IMPROVEMENTS: -* builder/alicloud-ecs: Add security token support and set tls handshake timeout - through environment variable. [GH-5641] + +* builder/alicloud-ecs: Add security token support and set TLS handshake + timeout through environment variable. [GH-5641] +* builder/amazon: Add a new parameter `ssh_interface`. Valid values include + `public_ip`, `private_ip`, `public_dns` or `private_dns`. [GH-5630] +* builder/azure: Add sanity checks for resource group names [GH-5599] +* builder/hyper-v: Add support for differencing disk. [GH-5458] +* builder/vmware-iso: Improve logging of network errors. [GH-5456] +* core: Add new `packer_version` template engine. [GH-5619] +* core: Improve logic checking for downloaded ISOs in case where user has + provided more than one URL in `iso_urls` [GH-5632] +* provisioner/ansible-local: Add ability to clean staging directory. [GH-5618] ### BUG FIXES: -* builder/qemu: Set default disk size to 40960 MB to prevent boot failures. [GH-5588] +* builder/amazon: Allow `region` to appear in `ami_regions`. [GH-5660] +* builder/amazon: `C5` instance types now build more reliably. [GH-5678] +* builder/amazon: Correctly set AWS region if given in template along with a + profile. [GH-5676] +* builder/amazon: Prevent `sriov_support` and `ena_support` from being used + with spot instances, which would cause a build failure. [GH-5679] +* builder/azure: `build_resource_group_name` is mutually exclusive with + `location` [GH-5661] +* builder/azure: Clean up KeyVaults when deploying Windows images that used an + existing resource group. [GH-5656] +* builder/azure: Only destroy temporary resource groups if they were created by + Packer. [GH-5593] +* builder/hyper-v: Fix interpolation context for user variables in + `boot_command` [GH-5547] +* builder/qemu: Set default disk size to 40960 MB to prevent boot failures. + [GH-5588] +* builder/vmware: Correctly detect Windows boot on vmware workstation. + [GH-5672] +* core: Fix windows path regression when downloading ISOs. [GH-5591] +* provisioner/chef: Fix chef installs on Windows. [GH-5649] ## 1.1.2 (November 15, 2017) From 4a864d59d72806b739031a9c539a97f32e0958a9 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 8 Dec 2017 15:26:17 -0800 Subject: [PATCH 0139/1216] Prepare for 1.1.3 --- CHANGELOG.md | 2 +- version/version.go | 2 +- website/config.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a5864a7c..e1a6958e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## (UNRELEASED) +## 1.1.3 (December 8, 2017) ### IMPROVEMENTS: diff --git a/version/version.go b/version/version.go index 7ab8bb073..1cbe0158f 100644 --- a/version/version.go +++ b/version/version.go @@ -14,7 +14,7 @@ const Version = "1.1.3" // A pre-release marker for the version. If this is "" (empty string) // then it means that it is a final release. Otherwise, this is a pre-release // such as "dev" (in development), "beta", "rc1", etc. -const VersionPrerelease = "dev" +const VersionPrerelease = "" func FormattedVersion() string { var versionString bytes.Buffer diff --git a/website/config.rb b/website/config.rb index b3a77be6a..fc5caf4e5 100644 --- a/website/config.rb +++ b/website/config.rb @@ -2,7 +2,7 @@ set :base_url, "https://www.packer.io/" activate :hashicorp do |h| h.name = "packer" - h.version = "1.1.2" + h.version = "1.1.3" h.github_slug = "hashicorp/packer" h.website_root = "website" end From 3818ca8347182d770b8e5d6702c5fa43ce3a34ea Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 8 Dec 2017 15:45:12 -0800 Subject: [PATCH 0141/1216] prepare for next version --- CHANGELOG.md | 3 +++ version/version.go | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1a6958e8..62b92ca3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## (UNRELEASED) + + ## 1.1.3 (December 8, 2017) ### IMPROVEMENTS: diff --git a/version/version.go b/version/version.go index 1cbe0158f..03ee6f644 100644 --- a/version/version.go +++ b/version/version.go @@ -9,12 +9,12 @@ import ( var GitCommit string // The main version number that is being run at the moment. -const Version = "1.1.3" +const Version = "1.1.4" // A pre-release marker for the version. If this is "" (empty string) // then it means that it is a final release. Otherwise, this is a pre-release // such as "dev" (in development), "beta", "rc1", etc. -const VersionPrerelease = "" +const VersionPrerelease = "dev" func FormattedVersion() string { var versionString bytes.Buffer From 203f29a95e3c69c9d363fb87a0c708c7025b73a4 Mon Sep 17 00:00:00 2001 From: Maciej Skierkowski Date: Fri, 8 Dec 2017 15:49:26 -0800 Subject: [PATCH 0142/1216] Moving images to assets folder --- .../images/guides}/teamcity_build_log.png | Bin .../images/guides}/teamcity_build_log_complete.png | Bin .../images/guides}/teamcity_new_build.png | Bin .../building-virtualbox-image.html.md | 6 +++--- 4 files changed, 3 insertions(+), 3 deletions(-) rename website/source/{guides/packer-on-cicd/images => assets/images/guides}/teamcity_build_log.png (100%) rename website/source/{guides/packer-on-cicd/images => assets/images/guides}/teamcity_build_log_complete.png (100%) rename website/source/{guides/packer-on-cicd/images => assets/images/guides}/teamcity_new_build.png (100%) diff --git a/website/source/guides/packer-on-cicd/images/teamcity_build_log.png b/website/source/assets/images/guides/teamcity_build_log.png similarity index 100% rename from website/source/guides/packer-on-cicd/images/teamcity_build_log.png rename to website/source/assets/images/guides/teamcity_build_log.png diff --git a/website/source/guides/packer-on-cicd/images/teamcity_build_log_complete.png b/website/source/assets/images/guides/teamcity_build_log_complete.png similarity index 100% rename from website/source/guides/packer-on-cicd/images/teamcity_build_log_complete.png rename to website/source/assets/images/guides/teamcity_build_log_complete.png diff --git a/website/source/guides/packer-on-cicd/images/teamcity_new_build.png b/website/source/assets/images/guides/teamcity_new_build.png similarity index 100% rename from website/source/guides/packer-on-cicd/images/teamcity_new_build.png rename to website/source/assets/images/guides/teamcity_new_build.png diff --git a/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md b/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md index 484308f8e..d6b23dc42 100644 --- a/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md +++ b/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md @@ -76,7 +76,7 @@ In TeamCity Server create a new build and configure the Version Control Settings Add one **Build Step: Command Line** to the build. -![TeamCity screenshot: New Build](./images/teamcity_new_build.png) +![TeamCity screenshot: New Build](/assets/images/guides/teamcity_new_build.png) In the **Script content** field add the following: @@ -93,8 +93,8 @@ The entire configuration is ready for a new build. Start a new run in TeamCity b The new run should be triggered and the virtual box image will be built. -![TeamCity screenshot: Build log](./images/teamcity_build_log.png) +![TeamCity screenshot: Build log](/assets/images/guides/teamcity_build_log.png) Once complete, the build status should be updated to complete and successful. -![TeamCity screenshot: Build log complete](./images/teamcity_build_log_complete.png) +![TeamCity screenshot: Build log complete](/assets/images/guides/teamcity_build_log_complete.png) From 9c90744d3e29f2b60b9d841be893982e761d3ee5 Mon Sep 17 00:00:00 2001 From: Maciej Skierkowski Date: Fri, 8 Dec 2017 15:55:22 -0800 Subject: [PATCH 0143/1216] Clarifying virtualization phrasing for VirtualBox --- .../guides/packer-on-cicd/building-virtualbox-image.html.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md b/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md index d6b23dc42..7d1bb5067 100644 --- a/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md +++ b/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md @@ -8,7 +8,7 @@ page_title: Building a VirtualBox Image with Packer in TeamCity This guide walks through the process of building a VirtualBox image using Packer on a new TeamCity Agent. Before getting started you should have access to a TeamCity Server. -The Packer VirtualBox builder requires access to VirtualBox. VirtualBox should run on a bare-metal machine, as virtual machines should not run inside other virtual machines. This is also true for the [VMWare](https://www.packer.io/docs/builders/vmware.html) and the [QEMU](https://www.packer.io/docs/builders/qemu.html) Packer builders. +The Packer VirtualBox builder requires access to VirtualBox, which needs to run on a bare-metal machine as virtualization is generally not supported on cloud instances. This is also true for the [VMWare](https://www.packer.io/docs/builders/vmware.html) and the [QEMU](https://www.packer.io/docs/builders/qemu.html) Packer builders. ## 1. Provision a Bare-metal Machine @@ -33,7 +33,7 @@ resource "packet_device" "agent" { } ``` -## 2. Install VirtualBox and TeamCity Dependencies +## 2. Install VirtualBox and TeamCity dependencies VirtualBox must be installed on the new instance, and TeamCity requires the JDK prior to installation. This guide uses Ubuntu as the Linux distribution, so you may need to adjust these commands for your distribution of choice. From e0d5e184507f091955cf770617240268d5e2d56f Mon Sep 17 00:00:00 2001 From: Maciej Skierkowski Date: Fri, 8 Dec 2017 15:57:39 -0800 Subject: [PATCH 0144/1216] Calling out AWS CLI dependencies --- .../guides/packer-on-cicd/uploading-images-to-artifact.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/guides/packer-on-cicd/uploading-images-to-artifact.html.md b/website/source/guides/packer-on-cicd/uploading-images-to-artifact.html.md index 3ea0ece15..d981a8b81 100644 --- a/website/source/guides/packer-on-cicd/uploading-images-to-artifact.html.md +++ b/website/source/guides/packer-on-cicd/uploading-images-to-artifact.html.md @@ -14,7 +14,7 @@ The following example uses TeamCity and Amazon S3. ## Example: Uploading to S3 in a TeamCity Build -On the agent machine responsible for building images, install the [AWS Command Line Tool](https://aws.amazon.com/cli/). Since this is a one-time operation, this can be incorporated into the initial agent provisioning step when installing other dependencies. +On the agent machine responsible for building images, install the [AWS Command Line Tool](https://aws.amazon.com/cli/). Since this is a one-time operation, this can be incorporated into the initial agent provisioning step when installing other dependencies. The AWS Command Line tool may require installing additional [dependencies](http://docs.aws.amazon.com/cli/latest/userguide/installing.html) prior. ```shell pip install awscli From 3be55d20bec96a146d368b1f52395ff03ef5c914 Mon Sep 17 00:00:00 2001 From: Maciej Skierkowski Date: Fri, 8 Dec 2017 16:01:18 -0800 Subject: [PATCH 0145/1216] Updating section title to be specific to VirtualBox and S3 --- .../packer-on-cicd/uploading-images-to-artifact.html.md | 4 ++-- website/source/layouts/guides.erb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/website/source/guides/packer-on-cicd/uploading-images-to-artifact.html.md b/website/source/guides/packer-on-cicd/uploading-images-to-artifact.html.md index d981a8b81..e14d94ac4 100644 --- a/website/source/guides/packer-on-cicd/uploading-images-to-artifact.html.md +++ b/website/source/guides/packer-on-cicd/uploading-images-to-artifact.html.md @@ -1,10 +1,10 @@ --- layout: guides sidebar_current: guides-packer-on-cicd-upload-image-to-artifact-store -page_title: Uploading Images to Artifact Stores +page_title: Uploading VirtualBox Image to S3 --- -# Uploading Images to Artifact Stores +# Uploading VirtualBox Image to S3 Once the image is generated it will be used by other parts of your operations workflow. For example, it is common to build VirtualBox images with Packer to be used as base boxes in Vagrant. diff --git a/website/source/layouts/guides.erb b/website/source/layouts/guides.erb index be059a632..b9ea693ec 100644 --- a/website/source/layouts/guides.erb +++ b/website/source/layouts/guides.erb @@ -14,7 +14,7 @@ Building a VirtualBox Image with Packer in TeamCity > - Uploading Images to Artifact Store + Uploading VirtualBox Image to S3 > Triggering Terraform Enterprise runs From fadda1fe6d7711b05cd21bebc818e9894deffec2 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 8 Dec 2017 16:08:36 -0800 Subject: [PATCH 0146/1216] shameful ex post facto changelog fix. --- CHANGELOG.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62b92ca3b..a0b095d29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ * builder/amazon: Add a new parameter `ssh_interface`. Valid values include `public_ip`, `private_ip`, `public_dns` or `private_dns`. [GH-5630] * builder/azure: Add sanity checks for resource group names [GH-5599] +* builder/azure: Allow users to specify an existing resource group to use, + instead of creating a new one for every run. [GH-5548] * builder/hyper-v: Add support for differencing disk. [GH-5458] * builder/vmware-iso: Improve logging of network errors. [GH-5456] * core: Add new `packer_version` template engine. [GH-5619] @@ -25,12 +27,6 @@ profile. [GH-5676] * builder/amazon: Prevent `sriov_support` and `ena_support` from being used with spot instances, which would cause a build failure. [GH-5679] -* builder/azure: `build_resource_group_name` is mutually exclusive with - `location` [GH-5661] -* builder/azure: Clean up KeyVaults when deploying Windows images that used an - existing resource group. [GH-5656] -* builder/azure: Only destroy temporary resource groups if they were created by - Packer. [GH-5593] * builder/hyper-v: Fix interpolation context for user variables in `boot_command` [GH-5547] * builder/qemu: Set default disk size to 40960 MB to prevent boot failures. From d069dc5b7c18e39ac332ab12566cdf801b14bd32 Mon Sep 17 00:00:00 2001 From: Andrew Pennebaker Date: Sat, 9 Dec 2017 12:02:20 -0600 Subject: [PATCH 0147/1216] handle holding a-z keys, such as for boot options (vmware builder) --- .../vmware/common/step_type_boot_command.go | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/builder/vmware/common/step_type_boot_command.go b/builder/vmware/common/step_type_boot_command.go index b100b609e..520ed4320 100644 --- a/builder/vmware/common/step_type_boot_command.go +++ b/builder/vmware/common/step_type_boot_command.go @@ -5,6 +5,7 @@ import ( "log" "net" "os" + "regexp" "runtime" "strings" "time" @@ -195,6 +196,9 @@ func vncSendString(c *vnc.ClientConn, original string) { keyInterval = delay } + azOnRegex := regexp.MustCompile("^<(?P[a-zA-Z])On>") + azOffRegex := regexp.MustCompile("^<(?P[a-zA-Z])Off>") + // TODO(mitchellh): Ripe for optimizations of some point, perhaps. for len(original) > 0 { var keyCode uint32 @@ -244,6 +248,25 @@ func vncSendString(c *vnc.ClientConn, original string) { continue } + if azOnRegex.MatchString(original) { + m := azOnRegex.FindStringSubmatch(original) + r, _ := utf8.DecodeRuneInString(m[1]) + original = original[len(""):] + keyCode = uint32(r) + keyShift = unicode.IsUpper(r) || strings.ContainsRune(shiftedChars, r) + + log.Printf("Special code '%s' found, replacing with %d, shift %v", m[0], keyCode, keyShift) + + if keyShift { + c.KeyEvent(KeyLeftShift, true) + } + + c.KeyEvent(keyCode, true) + time.Sleep(keyInterval) + + continue + } + if strings.HasPrefix(original, "") { keyCode = special[""] original = original[len(""):] @@ -288,6 +311,25 @@ func vncSendString(c *vnc.ClientConn, original string) { continue } + if azOffRegex.MatchString(original) { + m := azOffRegex.FindStringSubmatch(original) + r, _ := utf8.DecodeRuneInString(m[1]) + original = original[len(""):] + keyCode = uint32(r) + keyShift = unicode.IsUpper(r) || strings.ContainsRune(shiftedChars, r) + + log.Printf("Special code '%s' found, replacing with %d, shift %v", m[0], keyCode, keyShift) + + if keyShift { + c.KeyEvent(KeyLeftShift, false) + } + + c.KeyEvent(keyCode, false) + time.Sleep(keyInterval) + + continue + } + if strings.HasPrefix(original, "") { keyCode = special[""] original = original[len(""):] From 567b566c23bf46261a360a4d046ef7bfccede62b Mon Sep 17 00:00:00 2001 From: Krzysztof Wilczynski Date: Mon, 4 Dec 2017 14:27:56 +0100 Subject: [PATCH 0148/1216] docker: Remove AWS credentials and Session Token from being shown in the log. Signed-off-by: Krzysztof Wilczynski --- builder/docker/driver_docker.go | 62 +++++++++++++++++++++++++-------- builder/docker/exec.go | 13 ++++++- 2 files changed, 59 insertions(+), 16 deletions(-) diff --git a/builder/docker/driver_docker.go b/builder/docker/driver_docker.go index c3db3dda6..50a2b9920 100644 --- a/builder/docker/driver_docker.go +++ b/builder/docker/driver_docker.go @@ -150,24 +150,56 @@ func (d *DockerDriver) IPAddress(id string) (string, error) { func (d *DockerDriver) Login(repo, user, pass string) error { d.l.Lock() - args := []string{"login"} - if user != "" { - args = append(args, "-u", user) - } - if pass != "" { - args = append(args, "-p", pass) - } - if repo != "" { - args = append(args, repo) - } - - cmd := exec.Command("docker", args...) - err := runAndStream(cmd, d.Ui) + version_running, err := d.Version() if err != nil { d.l.Unlock() + return err } - return err + // Version 17.07.0 of Docker adds support for the new + // `--password-stdin` option which can be used to offer + // password via the standard input, rather than passing + // the password and/or token using a command line switch. + constraint, err := version.NewConstraint(">= 17.07.0") + if err != nil { + d.l.Unlock() + return err + } + + cmd := exec.Command("docker") + cmd.Args = append(cmd.Args, "login") + + if user != "" { + cmd.Args = append(cmd.Args, "-u", user) + } + + if pass != "" { + if constraint.Check(version_running) { + cmd.Args = append(cmd.Args, "--password-stdin") + + stdin, err := cmd.StdinPipe() + if err != nil { + d.l.Unlock() + return err + } + io.WriteString(stdin, pass) + stdin.Close() + } else { + cmd.Args = append(cmd.Args, "-p", pass) + } + } + + if repo != "" { + cmd.Args = append(cmd.Args, repo) + } + + err = runAndStream(cmd, d.Ui) + if err != nil { + d.l.Unlock() + return err + } + + return nil } func (d *DockerDriver) Logout(repo string) error { @@ -292,7 +324,7 @@ func (d *DockerDriver) TagImage(id string, repo string, force bool) error { return err } - version_deprecated, err := version.NewVersion(string("1.12.0")) + version_deprecated, err := version.NewVersion("1.12.0") if err != nil { // should never reach this line return err diff --git a/builder/docker/exec.go b/builder/docker/exec.go index 201055b74..95752382f 100644 --- a/builder/docker/exec.go +++ b/builder/docker/exec.go @@ -19,7 +19,18 @@ func runAndStream(cmd *exec.Cmd, ui packer.Ui) error { defer stdout_w.Close() defer stderr_w.Close() - log.Printf("Executing: %s %v", cmd.Path, cmd.Args[1:]) + args := make([]string, len(cmd.Args)-1) + copy(args, cmd.Args[1:]) + + // Scrub password from the log output. + for i, v := range args { + if v == "-p" || v == "--password" { + args[i+1] = "" + break + } + } + + log.Printf("Executing: %s %v", cmd.Path, args) cmd.Stdout = stdout_w cmd.Stderr = stderr_w if err := cmd.Start(); err != nil { From 54f059d3d4006cc1481709d8485ff14c67c03f43 Mon Sep 17 00:00:00 2001 From: Vijaya Bhaskar Reddy Kondreddi Date: Fri, 13 Oct 2017 00:46:24 +0530 Subject: [PATCH 0149/1216] Add support for skip export --- builder/hyperv/common/step_create_vm.go | 8 +++++ builder/hyperv/common/step_export_vm.go | 46 +++++++++++++------------ builder/hyperv/iso/builder.go | 5 +++ builder/hyperv/vmcx/builder.go | 3 ++ 4 files changed, 40 insertions(+), 22 deletions(-) diff --git a/builder/hyperv/common/step_create_vm.go b/builder/hyperv/common/step_create_vm.go index 02171d9c6..852880911 100644 --- a/builder/hyperv/common/step_create_vm.go +++ b/builder/hyperv/common/step_create_vm.go @@ -28,6 +28,8 @@ type StepCreateVM struct { EnableVirtualizationExtensions bool AdditionalDiskSize []uint DifferencingDisk bool + SkipExport bool + OutputDir string } func (s *StepCreateVM) Run(state multistep.StateBag) multistep.StepAction { @@ -51,6 +53,12 @@ func (s *StepCreateVM) Run(state multistep.StateBag) multistep.StepAction { } vhdPath := state.Get("packerVhdTempDir").(string) + + // inline vhd path if export is skipped + if s.SkipExport { + vhdPath = filepath.Join(s.OutputDir, "Virtual Hard Disks") + } + // convert the MB to bytes ramSize := int64(s.RamSize * 1024 * 1024) diskSize := int64(s.DiskSize * 1024 * 1024) diff --git a/builder/hyperv/common/step_export_vm.go b/builder/hyperv/common/step_export_vm.go index 8c32052a3..a9c2097a5 100644 --- a/builder/hyperv/common/step_export_vm.go +++ b/builder/hyperv/common/step_export_vm.go @@ -17,18 +17,19 @@ const ( type StepExportVm struct { OutputDir string SkipCompaction bool + SkipExport bool } func (s *StepExportVm) Run(state multistep.StateBag) multistep.StepAction { driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) - var err error var errorMsg string vmName := state.Get("vmName").(string) tmpPath := state.Get("packerTempDir").(string) outputPath := s.OutputDir + expPath := s.OutputDir // create temp path to export vm errorMsg = "Error creating temp export path: %s" @@ -39,21 +40,21 @@ func (s *StepExportVm) Run(state multistep.StateBag) multistep.StepAction { ui.Error(err.Error()) return multistep.ActionHalt } + if !s.SkipExport { + ui.Say("Exporting vm...") - ui.Say("Exporting vm...") - - err = driver.ExportVirtualMachine(vmName, vmExportPath) - if err != nil { - errorMsg = "Error exporting vm: %s" - err := fmt.Errorf(errorMsg, err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt + err = driver.ExportVirtualMachine(vmName, vmExportPath) + if err != nil { + errorMsg = "Error exporting vm: %s" + err := fmt.Errorf(errorMsg, err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + // copy to output dir + expPath = filepath.Join(vmExportPath, vmName) } - // copy to output dir - expPath := filepath.Join(vmExportPath, vmName) - if s.SkipCompaction { ui.Say("Skipping disk compaction...") } else { @@ -68,16 +69,17 @@ func (s *StepExportVm) Run(state multistep.StateBag) multistep.StepAction { } } - ui.Say("Copying to output dir...") - err = driver.CopyExportedVirtualMachine(expPath, outputPath, vhdDir, vmDir) - if err != nil { - errorMsg = "Error exporting vm: %s" - err := fmt.Errorf(errorMsg, err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt + if !s.SkipExport { + ui.Say("Copying to output dir...") + err = driver.CopyExportedVirtualMachine(expPath, outputPath, vhdDir, vmDir) + if err != nil { + errorMsg = "Error exporting vm: %s" + err := fmt.Errorf(errorMsg, err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } } - return multistep.ActionContinue } diff --git a/builder/hyperv/iso/builder.go b/builder/hyperv/iso/builder.go index 849cbcacf..35ed2c698 100644 --- a/builder/hyperv/iso/builder.go +++ b/builder/hyperv/iso/builder.go @@ -94,6 +94,8 @@ type Config struct { SkipCompaction bool `mapstructure:"skip_compaction"` + SkipExport bool `mapstructure:"skip_export"` + // Use differencing disk DifferencingDisk bool `mapstructure:"differencing_disk"` @@ -357,6 +359,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe EnableVirtualizationExtensions: b.config.EnableVirtualizationExtensions, AdditionalDiskSize: b.config.AdditionalDiskSize, DifferencingDisk: b.config.DifferencingDisk, + SkipExport: b.config.SkipExport, + OutputDir: b.config.OutputDir, }, &hypervcommon.StepEnableIntegrationService{}, @@ -422,6 +426,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe &hypervcommon.StepExportVm{ OutputDir: b.config.OutputDir, SkipCompaction: b.config.SkipCompaction, + SkipExport: b.config.SkipExport, }, // the clean up actions for each step will be executed reverse order diff --git a/builder/hyperv/vmcx/builder.go b/builder/hyperv/vmcx/builder.go index 6a976b3df..51997a384 100644 --- a/builder/hyperv/vmcx/builder.go +++ b/builder/hyperv/vmcx/builder.go @@ -94,6 +94,8 @@ type Config struct { SkipCompaction bool `mapstructure:"skip_compaction"` + SkipExport bool `mapstructure:"skip_export"` + ctx interpolate.Context } @@ -469,6 +471,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe &hypervcommon.StepExportVm{ OutputDir: b.config.OutputDir, SkipCompaction: b.config.SkipCompaction, + SkipExport: b.config.SkipExport, }, // the clean up actions for each step will be executed reverse order From 5346583df96f06b888b6a2a69a652c61605751df Mon Sep 17 00:00:00 2001 From: Vijaya Bhaskar Reddy Kondreddi Date: Tue, 28 Nov 2017 14:56:58 +0530 Subject: [PATCH 0150/1216] Update docs --- website/source/docs/builders/hyperv-iso.html.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/website/source/docs/builders/hyperv-iso.html.md b/website/source/docs/builders/hyperv-iso.html.md index ff267392c..24fee6f05 100644 --- a/website/source/docs/builders/hyperv-iso.html.md +++ b/website/source/docs/builders/hyperv-iso.html.md @@ -97,6 +97,12 @@ can be configured for this builder. - `disk_size` (number) - The size, in megabytes, of the hard disk to create for the VM. By default, this is 40 GB. +- `differencing_disk` (boolean) - If true enables differencing disks. Only the changes will be written to the new disk. This is especially useful if your + source is a vhd/vhdx. This defaults to false. + +- `skip_export` (boolean) - If true skips VM export. If you are interested only in the vhd/vhdx files, you can enable this option. This will create + inline disks which improves the build performance. There will not be any copying of source vhds to temp directory. This defauls to false. + - `enable_dynamic_memory` (boolean) - If true enable dynamic memory for virtual machine. This defaults to false. From dc96e7315181ebd2c8f18e3ba3bcac8bb80e2afb Mon Sep 17 00:00:00 2001 From: Maciej Skierkowski Date: Mon, 11 Dec 2017 09:08:12 -0800 Subject: [PATCH 0151/1216] Adding more info about the options for Packer --- .../guides/packer-on-cicd/building-virtualbox-image.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md b/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md index 7d1bb5067..28848f584 100644 --- a/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md +++ b/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md @@ -85,7 +85,7 @@ In the **Script content** field add the following: /root/packer build -only=virtualbox-iso -var "headless=true" ./packer.json ``` -This assumes that `packer.json` is the Packer build configuration file in the root path of the VCS repository. +This will use the `build` command in Packer to build the image defined in `./packer.json`. It assumes that `packer.json` is the Packer build configuration file in the root path of the VCS repository. Packer defaults to building VirtualBox virtual machines by launching a GUI that shows the console, since this will run in CI/CD, the the [`headless` variable](https://www.packer.io/docs/builders/virtualbox-iso.html#headless) instructs Packer to start the machine without the console. Packer can build multiple image types, so the [`-only=virtualbox-iso` option] instructs Packer to only build the builds with the name `virtualbox-iso`. ## 6. Run a build in TeamCity From dd5e5b8993050ca65831857d506f7f1a7e3891a7 Mon Sep 17 00:00:00 2001 From: Maciej Skierkowski Date: Mon, 11 Dec 2017 09:14:46 -0800 Subject: [PATCH 0152/1216] Using relative links for the docs/guides --- .../guides/packer-on-cicd/building-image-in-cicd.html.md | 2 +- .../guides/packer-on-cicd/building-virtualbox-image.html.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md b/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md index a5bc86725..bbfc7ec5e 100644 --- a/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md +++ b/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md @@ -11,6 +11,6 @@ The following guides from our partners show how to use their services to build i - [How to Build Immutable Infrastructure with Packer and CircleCI Workflows](#) - [Using Packer and Ansible to Build Immutable Infrastructure in CodeShip](https://blog.codeship.com/packer-ansible/) -The majority of the [Packer Builders](https://www.packer.io/docs/builders/index.html) can run in a container or VM, a common model used by most CI/CD services. However, the [QEMU builder](https://www.packer.io/docs/builders/qemu.html) for [KVM](https://www.linux-kvm.org/page/Main_Page) and [Xen](https://www.xenproject.org/) virtual machine images, [VirtualBox builder](https://www.packer.io/docs/builders/virtualbox.html) for OVA or OVF virtual machines and [VMWare builder](https://www.packer.io/docs/builders/vmware.html) for use with VMware products require running on a bare-metal machine. +The majority of the [Packer Builders](../../docs/builders/index.html) can run in a container or VM, a common model used by most CI/CD services. However, the [QEMU builder](../../docs/builders/qemu.html) for [KVM](https://www.linux-kvm.org/page/Main_Page) and [Xen](https://www.xenproject.org/) virtual machine images, [VirtualBox builder](../../docs/builders/virtualbox.html) for OVA or OVF virtual machines and [VMWare builder](../../docs/builders/vmware.html) for use with VMware products require running on a bare-metal machine. The [Building a VirtualBox Image with Packer in TeamCity](./building-virtualbox-image.html) guide shows how to create a VirtualBox image using TeamCity's support for running scripts on bare-metal machines. diff --git a/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md b/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md index 28848f584..f2859f095 100644 --- a/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md +++ b/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md @@ -8,7 +8,7 @@ page_title: Building a VirtualBox Image with Packer in TeamCity This guide walks through the process of building a VirtualBox image using Packer on a new TeamCity Agent. Before getting started you should have access to a TeamCity Server. -The Packer VirtualBox builder requires access to VirtualBox, which needs to run on a bare-metal machine as virtualization is generally not supported on cloud instances. This is also true for the [VMWare](https://www.packer.io/docs/builders/vmware.html) and the [QEMU](https://www.packer.io/docs/builders/qemu.html) Packer builders. +The Packer VirtualBox builder requires access to VirtualBox, which needs to run on a bare-metal machine as virtualization is generally not supported on cloud instances. This is also true for the [VMWare](../../docs/builders/vmware.html) and the [QEMU](../../docs/builders/qemu.html) Packer builders. ## 1. Provision a Bare-metal Machine @@ -85,7 +85,7 @@ In the **Script content** field add the following: /root/packer build -only=virtualbox-iso -var "headless=true" ./packer.json ``` -This will use the `build` command in Packer to build the image defined in `./packer.json`. It assumes that `packer.json` is the Packer build configuration file in the root path of the VCS repository. Packer defaults to building VirtualBox virtual machines by launching a GUI that shows the console, since this will run in CI/CD, the the [`headless` variable](https://www.packer.io/docs/builders/virtualbox-iso.html#headless) instructs Packer to start the machine without the console. Packer can build multiple image types, so the [`-only=virtualbox-iso` option] instructs Packer to only build the builds with the name `virtualbox-iso`. +This will use the `build` command in Packer to build the image defined in `./packer.json`. It assumes that `packer.json` is the Packer build configuration file in the root path of the VCS repository. Packer defaults to building VirtualBox virtual machines by launching a GUI that shows the console, since this will run in CI/CD, the the [`headless` variable](../../docs/builders/virtualbox-iso.html#headless) instructs Packer to start the machine without the console. Packer can build multiple image types, so the [`-only=virtualbox-iso` option](../../docs/commands/build.html#only-foo-bar-baz) instructs Packer to only build the builds with the name `virtualbox-iso`. ## 6. Run a build in TeamCity From 004a43492857f337936c9c64dbaadf37e6de85f4 Mon Sep 17 00:00:00 2001 From: Maciej Skierkowski Date: Mon, 11 Dec 2017 11:48:34 -0800 Subject: [PATCH 0153/1216] Add "coming soon" --- .../source/guides/packer-on-cicd/building-image-in-cicd.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md b/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md index bbfc7ec5e..16cb5aa7d 100644 --- a/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md +++ b/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md @@ -8,7 +8,7 @@ page_title: Building Images in CI/CD The following guides from our partners show how to use their services to build images with Packer. -- [How to Build Immutable Infrastructure with Packer and CircleCI Workflows](#) +- How to Build Immutable Infrastructure with Packer and CircleCI Workflows (coming soon) - [Using Packer and Ansible to Build Immutable Infrastructure in CodeShip](https://blog.codeship.com/packer-ansible/) The majority of the [Packer Builders](../../docs/builders/index.html) can run in a container or VM, a common model used by most CI/CD services. However, the [QEMU builder](../../docs/builders/qemu.html) for [KVM](https://www.linux-kvm.org/page/Main_Page) and [Xen](https://www.xenproject.org/) virtual machine images, [VirtualBox builder](../../docs/builders/virtualbox.html) for OVA or OVF virtual machines and [VMWare builder](../../docs/builders/vmware.html) for use with VMware products require running on a bare-metal machine. From be3f0a121a1b97ecb115cd463fc8811ffe64db2e Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Mon, 11 Dec 2017 14:31:44 -0800 Subject: [PATCH 0154/1216] guides should use infinitive verbs --- ...e-in-cicd.html.md => build-image-in-cicd.html.md} | 4 ++-- ...-image.html.md => build-virtualbox-image.html.md} | 4 ++-- website/source/guides/packer-on-cicd/index.html.md | 10 +++++----- .../{triggering-tfe.html.md => trigger-tfe.html.md} | 6 +++--- ...act.html.md => upload-images-to-artifact.html.md} | 4 ++-- website/source/layouts/guides.erb | 12 ++++++------ 6 files changed, 20 insertions(+), 20 deletions(-) rename website/source/guides/packer-on-cicd/{building-image-in-cicd.html.md => build-image-in-cicd.html.md} (94%) rename website/source/guides/packer-on-cicd/{building-virtualbox-image.html.md => build-virtualbox-image.html.md} (97%) rename website/source/guides/packer-on-cicd/{triggering-tfe.html.md => trigger-tfe.html.md} (93%) rename website/source/guides/packer-on-cicd/{uploading-images-to-artifact.html.md => upload-images-to-artifact.html.md} (95%) diff --git a/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md b/website/source/guides/packer-on-cicd/build-image-in-cicd.html.md similarity index 94% rename from website/source/guides/packer-on-cicd/building-image-in-cicd.html.md rename to website/source/guides/packer-on-cicd/build-image-in-cicd.html.md index 16cb5aa7d..f35f7168a 100644 --- a/website/source/guides/packer-on-cicd/building-image-in-cicd.html.md +++ b/website/source/guides/packer-on-cicd/build-image-in-cicd.html.md @@ -1,10 +1,10 @@ --- layout: guides sidebar_current: guides-packer-on-cicd-build-image -page_title: Building Images in CI/CD +page_title: Build Images in CI/CD --- -# Building Images in CI/CD +# Build Images in CI/CD The following guides from our partners show how to use their services to build images with Packer. diff --git a/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md b/website/source/guides/packer-on-cicd/build-virtualbox-image.html.md similarity index 97% rename from website/source/guides/packer-on-cicd/building-virtualbox-image.html.md rename to website/source/guides/packer-on-cicd/build-virtualbox-image.html.md index f2859f095..d4c80bdd1 100644 --- a/website/source/guides/packer-on-cicd/building-virtualbox-image.html.md +++ b/website/source/guides/packer-on-cicd/build-virtualbox-image.html.md @@ -1,10 +1,10 @@ --- layout: guides sidebar_current: guides-packer-on-cicd-build-virtualbox -page_title: Building a VirtualBox Image with Packer in TeamCity +page_title: Build a VirtualBox Image with Packer in TeamCity --- -# Building a VirtualBox Image with Packer in TeamCity +# Build a VirtualBox Image with Packer in TeamCity This guide walks through the process of building a VirtualBox image using Packer on a new TeamCity Agent. Before getting started you should have access to a TeamCity Server. diff --git a/website/source/guides/packer-on-cicd/index.html.md b/website/source/guides/packer-on-cicd/index.html.md index dce80ada2..97e4a1391 100644 --- a/website/source/guides/packer-on-cicd/index.html.md +++ b/website/source/guides/packer-on-cicd/index.html.md @@ -1,13 +1,13 @@ --- layout: guides sidebar_current: guides-packer-on-cicd-index -page_title: Building Immutable Infrastructure with Packer in CI/CD +page_title: Build Immutable Infrastructure with Packer in CI/CD --- -# Building Immutable Infrastructure with Packer in CI/CD +# Build Immutable Infrastructure with Packer in CI/CD This guide focuses on the following workflow for building immutable infrastructure. This workflow can be manual or automated and it can be implemented with a variety of technologies. The goal of this guide is to show how this workflow can be fully automated using Packer for building images from a continuous integration/continuous deployment (CI/CD) pipeline. -1. [Building Images using Packer in CI/CD](./building-image-in-cicd.html) -2. [Uploading the new image to S3](./uploading-images-to-artifact.html) for future deployment or use during development -3. [Creating new Terraform Enterprise runs](./triggering-tfe.html) to provision new instances with the images +1. [Build Images using Packer in CI/CD](./build-image-in-cicd.html) +2. [Upload the new image to S3](./upload-images-to-artifact.html) for future deployment or use during development +3. [Create new Terraform Enterprise runs](./triggering-tfe.html) to provision new instances with the images diff --git a/website/source/guides/packer-on-cicd/triggering-tfe.html.md b/website/source/guides/packer-on-cicd/trigger-tfe.html.md similarity index 93% rename from website/source/guides/packer-on-cicd/triggering-tfe.html.md rename to website/source/guides/packer-on-cicd/trigger-tfe.html.md index a79af6f7a..0200bd571 100644 --- a/website/source/guides/packer-on-cicd/triggering-tfe.html.md +++ b/website/source/guides/packer-on-cicd/trigger-tfe.html.md @@ -1,10 +1,10 @@ --- layout: guides -sidebar_current: guides-packer-on-cicd-triggering-tfe-run -page_title: Triggering Terraform Enterprise runs +sidebar_current: guides-packer-on-cicd-trigger-tfe-run +page_title: Trigger Terraform Enterprise runs --- -# Creating Terraform Enterprise Runs +# Create Terraform Enterprise Runs Once an image is built and uploaded to an artifact store, the next step is to use this new image. In some cases the image will be downloaded by the dev team and used locally in development, like is often done with VirtualBox images with Vagrant. In most other cases, the new image will be used to provision new infrastructure. diff --git a/website/source/guides/packer-on-cicd/uploading-images-to-artifact.html.md b/website/source/guides/packer-on-cicd/upload-images-to-artifact.html.md similarity index 95% rename from website/source/guides/packer-on-cicd/uploading-images-to-artifact.html.md rename to website/source/guides/packer-on-cicd/upload-images-to-artifact.html.md index e14d94ac4..b7f034e95 100644 --- a/website/source/guides/packer-on-cicd/uploading-images-to-artifact.html.md +++ b/website/source/guides/packer-on-cicd/upload-images-to-artifact.html.md @@ -1,10 +1,10 @@ --- layout: guides sidebar_current: guides-packer-on-cicd-upload-image-to-artifact-store -page_title: Uploading VirtualBox Image to S3 +page_title: Upload VirtualBox Image to S3 --- -# Uploading VirtualBox Image to S3 +# Upload VirtualBox Image to S3 Once the image is generated it will be used by other parts of your operations workflow. For example, it is common to build VirtualBox images with Packer to be used as base boxes in Vagrant. diff --git a/website/source/layouts/guides.erb b/website/source/layouts/guides.erb index b9ea693ec..1d5653c04 100644 --- a/website/source/layouts/guides.erb +++ b/website/source/layouts/guides.erb @@ -5,19 +5,19 @@ Veewee to Packer > - Building Immutable Infrastructure with Packer in CI/CD + Build Immutable Infrastructure with Packer in CI/CD

+ > + Scaleway + > CloudStack From e46108298ccf90d155630f77d74d328142cebd60 Mon Sep 17 00:00:00 2001 From: Edouard BONLIEU Date: Thu, 6 Apr 2017 16:31:35 +0200 Subject: [PATCH 0474/1216] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5249f515e..1c30fd877 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ comes out of the box with support for the following platforms: * Parallels * ProfitBricks * QEMU. Both KVM and Xen images. +* Scaleway * Triton (Joyent Public Cloud) * VMware * VirtualBox From 9b611af7e6dd6e32b823751b47bbc8ea5593e769 Mon Sep 17 00:00:00 2001 From: Edouard BONLIEU Date: Thu, 6 Apr 2017 16:37:06 +0200 Subject: [PATCH 0475/1216] Allow token and organization id to be passed via env vars --- builder/scaleway/config.go | 9 +++++++++ website/source/docs/builders/scaleway.html.md | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/builder/scaleway/config.go b/builder/scaleway/config.go index eb9575140..040b39891 100644 --- a/builder/scaleway/config.go +++ b/builder/scaleway/config.go @@ -3,6 +3,7 @@ package scaleway import ( "errors" "fmt" + "os" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/uuid" @@ -51,6 +52,14 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { c.UserAgent = "Packer - Scaleway builder" + if c.Organization == "" { + c.Organization = os.Getenv("SCALEWAY_API_ORGANIZATION") + } + + if c.Token == "" { + c.Token = os.Getenv("SCALEWAY_API_TOKEN") + } + if c.SnapshotName == "" { def, err := interpolate.Render("packer-{{timestamp}}", nil) if err != nil { diff --git a/website/source/docs/builders/scaleway.html.md b/website/source/docs/builders/scaleway.html.md index 01852c70c..1df9dd6ec 100644 --- a/website/source/docs/builders/scaleway.html.md +++ b/website/source/docs/builders/scaleway.html.md @@ -37,8 +37,12 @@ builder. ### Required: - `api_organization` (string) - The organization ID to use to access your account. + It can also be specified via + environment variable `SCALEWAY_API_ORGANIZATION`. - `api_token` (string) - The organization TOKEN to use to access your account. + It can also be specified via + environment variable `SCALEWAY_API_TOKEN`. - `image` (string) - The UUID of the base image to use. This is the image that will be used to launch a new server and provision it. See From 1fb13cc23eeebf7e3c57f47657d663dc6a13e21c Mon Sep 17 00:00:00 2001 From: Edouard BONLIEU Date: Tue, 11 Apr 2017 12:19:28 +0200 Subject: [PATCH 0476/1216] Add image creation from snapshot Rename organization_id / access_key Update test / doc --- builder/scaleway/artifact.go | 25 ++++++--- builder/scaleway/artifact_test.go | 8 +-- builder/scaleway/builder.go | 5 +- builder/scaleway/builder_test.go | 12 ++--- builder/scaleway/config.go | 16 ++++-- builder/scaleway/step_create_image.go | 53 +++++++++++++++++++ builder/scaleway/step_create_server.go | 11 ++-- builder/scaleway/step_shutdown.go | 6 +-- builder/scaleway/step_snapshot.go | 13 +---- builder/scaleway/step_terminate.go | 4 +- website/source/docs/builders/scaleway.html.md | 15 ++++-- 11 files changed, 122 insertions(+), 46 deletions(-) create mode 100644 builder/scaleway/step_create_image.go diff --git a/builder/scaleway/artifact.go b/builder/scaleway/artifact.go index fe1efb96f..b5aea88da 100644 --- a/builder/scaleway/artifact.go +++ b/builder/scaleway/artifact.go @@ -8,11 +8,17 @@ import ( ) type Artifact struct { + // The name of the image + imageName string + + // The ID of the image + imageID string + // The name of the snapshot snapshotName string // The ID of the snapshot - snapshotId string + snapshotID string // The name of the region regionName string @@ -31,11 +37,12 @@ func (*Artifact) Files() []string { } func (a *Artifact) Id() string { - return fmt.Sprintf("%s:%s", a.regionName, a.snapshotId) + return fmt.Sprintf("%s:%s", a.regionName, a.imageID) } func (a *Artifact) String() string { - return fmt.Sprintf("A snapshot was created: '%v' (ID: %v) in region '%v'", a.snapshotName, a.snapshotId, a.regionName) + return fmt.Sprintf("An image was created: '%v' (ID: %v) in region '%v' based on snapshot '%v' (ID: %v)", + a.imageName, a.imageID, a.regionName, a.snapshotName, a.snapshotID) } func (a *Artifact) State(name string) interface{} { @@ -43,7 +50,13 @@ func (a *Artifact) State(name string) interface{} { } func (a *Artifact) Destroy() error { - log.Printf("Destroying image: %s (%s)", a.snapshotId, a.snapshotName) - err := a.client.DeleteSnapshot(a.snapshotId) - return err + log.Printf("Destroying image: %s (%s)", a.imageID, a.imageName) + if err := a.client.DeleteImage(a.imageID); err != nil { + return err + } + log.Printf("Destroying snapshot: %s (%s)", a.snapshotID, a.snapshotName) + if err := a.client.DeleteSnapshot(a.snapshotID); err != nil { + return err + } + return nil } diff --git a/builder/scaleway/artifact_test.go b/builder/scaleway/artifact_test.go index 8805ad686..8f158ef5f 100644 --- a/builder/scaleway/artifact_test.go +++ b/builder/scaleway/artifact_test.go @@ -15,8 +15,8 @@ func TestArtifact_Impl(t *testing.T) { } func TestArtifactId(t *testing.T) { - a := &Artifact{"packer-foobar", "cc586e45-5156-4f71-b223-cf406b10dd1c", "ams1", nil} - expected := "ams1:cc586e45-5156-4f71-b223-cf406b10dd1c" + a := &Artifact{"packer-foobar-image", "cc586e45-5156-4f71-b223-cf406b10dd1d", "packer-foobar-snapshot", "cc586e45-5156-4f71-b223-cf406b10dd1c", "ams1", nil} + expected := "ams1:cc586e45-5156-4f71-b223-cf406b10dd1d" if a.Id() != expected { t.Fatalf("artifact ID should match: %v", expected) @@ -24,8 +24,8 @@ func TestArtifactId(t *testing.T) { } func TestArtifactString(t *testing.T) { - a := &Artifact{"packer-foobar", "cc586e45-5156-4f71-b223-cf406b10dd1c", "ams1", nil} - expected := "A snapshot was created: 'packer-foobar' (ID: cc586e45-5156-4f71-b223-cf406b10dd1c) in region 'ams1'" + a := &Artifact{"packer-foobar-image", "cc586e45-5156-4f71-b223-cf406b10dd1d", "packer-foobar-snapshot", "cc586e45-5156-4f71-b223-cf406b10dd1c", "ams1", nil} + expected := "An image was created: 'packer-foobar-image' (ID: cc586e45-5156-4f71-b223-cf406b10dd1d) in region 'ams1' based on snapshot 'packer-foobar-snapshot' (ID: cc586e45-5156-4f71-b223-cf406b10dd1c)" if a.String() != expected { t.Fatalf("artifact string should match: %v", expected) diff --git a/builder/scaleway/builder.go b/builder/scaleway/builder.go index 2c92e31b6..e6265395a 100644 --- a/builder/scaleway/builder.go +++ b/builder/scaleway/builder.go @@ -56,6 +56,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe new(common.StepProvision), new(stepShutdown), new(stepSnapshot), + new(stepImage), new(stepTerminate), } @@ -72,8 +73,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe } artifact := &Artifact{ + imageName: state.Get("image_name").(string), + imageID: state.Get("image_id").(string), snapshotName: state.Get("snapshot_name").(string), - snapshotId: state.Get("snapshot_id").(string), + snapshotID: state.Get("snapshot_id").(string), regionName: state.Get("region").(string), client: client, } diff --git a/builder/scaleway/builder_test.go b/builder/scaleway/builder_test.go index ff83e1556..230c9c0c6 100644 --- a/builder/scaleway/builder_test.go +++ b/builder/scaleway/builder_test.go @@ -9,12 +9,12 @@ import ( func testConfig() map[string]interface{} { return map[string]interface{}{ - "api_organization": "foo", - "api_token": "bar", - "region": "ams1", - "commercial_type": "VC1S", - "ssh_username": "root", - "image": "image-uuid", + "api_access_key": "foo", + "api_token": "bar", + "region": "ams1", + "commercial_type": "VC1S", + "ssh_username": "root", + "image": "image-uuid", } } diff --git a/builder/scaleway/config.go b/builder/scaleway/config.go index 040b39891..f965e1ce0 100644 --- a/builder/scaleway/config.go +++ b/builder/scaleway/config.go @@ -19,13 +19,14 @@ type Config struct { Comm communicator.Config `mapstructure:",squash"` Token string `mapstructure:"api_token"` - Organization string `mapstructure:"api_organization"` + Organization string `mapstructure:"api_access_key"` Region string `mapstructure:"region"` Image string `mapstructure:"image"` CommercialType string `mapstructure:"commercial_type"` SnapshotName string `mapstructure:"snapshot_name"` + ImageName string `mapstructure:"image_name"` ServerName string `mapstructure:"server_name"` UserAgent string @@ -53,7 +54,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { c.UserAgent = "Packer - Scaleway builder" if c.Organization == "" { - c.Organization = os.Getenv("SCALEWAY_API_ORGANIZATION") + c.Organization = os.Getenv("SCALEWAY_API_ACCESS_KEY") } if c.Token == "" { @@ -61,7 +62,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } if c.SnapshotName == "" { - def, err := interpolate.Render("packer-{{timestamp}}", nil) + def, err := interpolate.Render("snapshot-packer-{{timestamp}}", nil) if err != nil { panic(err) } @@ -69,6 +70,15 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { c.SnapshotName = def } + if c.ImageName == "" { + def, err := interpolate.Render("image-packer-{{timestamp}}", nil) + if err != nil { + panic(err) + } + + c.ImageName = def + } + if c.ServerName == "" { // Default to packer-[time-ordered-uuid] c.ServerName = fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID()) diff --git a/builder/scaleway/step_create_image.go b/builder/scaleway/step_create_image.go new file mode 100644 index 000000000..313f2e421 --- /dev/null +++ b/builder/scaleway/step_create_image.go @@ -0,0 +1,53 @@ +package scaleway + +import ( + "fmt" + "log" + + "github.com/hashicorp/packer/packer" + "github.com/mitchellh/multistep" + "github.com/scaleway/scaleway-cli/pkg/api" +) + +type stepImage struct{} + +func (s *stepImage) Run(state multistep.StateBag) multistep.StepAction { + client := state.Get("client").(*api.ScalewayAPI) + ui := state.Get("ui").(packer.Ui) + c := state.Get("config").(Config) + snapshotID := state.Get("snapshot_id").(string) + bootscriptID := "" + + ui.Say(fmt.Sprintf("Creating image: %v", c.ImageName)) + + image, err := client.GetImage(c.Image) + if err != nil { + err := fmt.Errorf("Error getting initial image info: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + + if image.DefaultBootscript != nil { + bootscriptID = image.DefaultBootscript.Identifier + } + + imageID, err := client.PostImage(snapshotID, c.ImageName, bootscriptID, image.Arch) + if err != nil { + err := fmt.Errorf("Error creating image: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + + log.Printf("Image ID: %s", imageID) + state.Put("image_id", imageID) + state.Put("image_name", c.ImageName) + state.Put("region", c.Region) + + return multistep.ActionContinue +} + +func (s *stepImage) Cleanup(state multistep.StateBag) { + // no cleanup +} diff --git a/builder/scaleway/step_create_server.go b/builder/scaleway/step_create_server.go index c961b6110..e1e8b88c6 100644 --- a/builder/scaleway/step_create_server.go +++ b/builder/scaleway/step_create_server.go @@ -2,14 +2,15 @@ package scaleway import ( "fmt" + "strings" + "github.com/hashicorp/packer/packer" "github.com/mitchellh/multistep" "github.com/scaleway/scaleway-cli/pkg/api" - "strings" ) type stepCreateServer struct { - serverId string + serverID string } func (s *stepCreateServer) Run(state multistep.StateBag) multistep.StepAction { @@ -37,7 +38,7 @@ func (s *stepCreateServer) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionHalt } - s.serverId = server + s.serverID = server state.Put("server_id", server) @@ -45,7 +46,7 @@ func (s *stepCreateServer) Run(state multistep.StateBag) multistep.StepAction { } func (s *stepCreateServer) Cleanup(state multistep.StateBag) { - if s.serverId != "" { + if s.serverID != "" { return } @@ -53,7 +54,7 @@ func (s *stepCreateServer) Cleanup(state multistep.StateBag) { ui := state.Get("ui").(packer.Ui) ui.Say("Destroying server...") - err := client.PostServerAction(s.serverId, "terminate") + err := client.PostServerAction(s.serverID, "terminate") if err != nil { ui.Error(fmt.Sprintf( "Error destroying server. Please destroy it manually: %s", err)) diff --git a/builder/scaleway/step_shutdown.go b/builder/scaleway/step_shutdown.go index 8d246974d..b8e45be51 100644 --- a/builder/scaleway/step_shutdown.go +++ b/builder/scaleway/step_shutdown.go @@ -13,11 +13,11 @@ type stepShutdown struct{} func (s *stepShutdown) Run(state multistep.StateBag) multistep.StepAction { client := state.Get("client").(*api.ScalewayAPI) ui := state.Get("ui").(packer.Ui) - serverId := state.Get("server_id").(string) + serverID := state.Get("server_id").(string) ui.Say("Shutting down server...") - err := client.PostServerAction(serverId, "poweroff") + err := client.PostServerAction(serverID, "poweroff") if err != nil { err := fmt.Errorf("Error stopping server: %s", err) @@ -26,7 +26,7 @@ func (s *stepShutdown) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionHalt } - _, err = api.WaitForServerState(client, serverId, "stopped") + _, err = api.WaitForServerState(client, serverID, "stopped") if err != nil { err := fmt.Errorf("Error shutting down server: %s", err) diff --git a/builder/scaleway/step_snapshot.go b/builder/scaleway/step_snapshot.go index 26bfa6934..20301a444 100644 --- a/builder/scaleway/step_snapshot.go +++ b/builder/scaleway/step_snapshot.go @@ -15,10 +15,10 @@ func (s *stepSnapshot) Run(state multistep.StateBag) multistep.StepAction { client := state.Get("client").(*api.ScalewayAPI) ui := state.Get("ui").(packer.Ui) c := state.Get("config").(Config) - volumeId := state.Get("root_volume_id").(string) + volumeID := state.Get("root_volume_id").(string) ui.Say(fmt.Sprintf("Creating snapshot: %v", c.SnapshotName)) - snapshot, err := client.PostSnapshot(volumeId, c.SnapshotName) + snapshot, err := client.PostSnapshot(volumeID, c.SnapshotName) if err != nil { err := fmt.Errorf("Error creating snapshot: %s", err) state.Put("error", err) @@ -26,15 +26,6 @@ func (s *stepSnapshot) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionHalt } - log.Printf("Looking up snapshot ID for snapshot: %s", c.SnapshotName) - _, err = client.GetSnapshot(snapshot) - if err != nil { - err := fmt.Errorf("Error looking up snapshot ID: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - log.Printf("Snapshot ID: %s", snapshot) state.Put("snapshot_id", snapshot) state.Put("snapshot_name", c.SnapshotName) diff --git a/builder/scaleway/step_terminate.go b/builder/scaleway/step_terminate.go index 0951b6a94..154750931 100644 --- a/builder/scaleway/step_terminate.go +++ b/builder/scaleway/step_terminate.go @@ -13,11 +13,11 @@ type stepTerminate struct{} func (s *stepTerminate) Run(state multistep.StateBag) multistep.StepAction { client := state.Get("client").(*api.ScalewayAPI) ui := state.Get("ui").(packer.Ui) - serverId := state.Get("server_id").(string) + serverID := state.Get("server_id").(string) ui.Say("Terminating server...") - err := client.DeleteServerForce(serverId) + err := client.DeleteServerForce(serverID) if err != nil { err := fmt.Errorf("Error terminating server: %s", err) diff --git a/website/source/docs/builders/scaleway.html.md b/website/source/docs/builders/scaleway.html.md index 1df9dd6ec..4e0a513fc 100644 --- a/website/source/docs/builders/scaleway.html.md +++ b/website/source/docs/builders/scaleway.html.md @@ -3,7 +3,7 @@ layout: docs sidebar_current: docs-builders-scaleway page_title: Scaleway - Builders description: |- - The Scaleway Packer builder is able to create new snapshots for use with + The Scaleway Packer builder is able to create new images for use with Scaleway BareMetal and Virtual cloud server. The builder takes a source image, runs any provisioning necessary on the image after launching it, then snapshots it into a reusable image. This reusable image can then be used as the foundation of new servers @@ -15,7 +15,7 @@ description: |- Type: `scaleway` -The `scaleway` Packer builder is able to create new snapshots for use with +The `scaleway` Packer builder is able to create new images for use with [Scaleway](https://www.scaleway.com). The builder takes a source image, runs any provisioning necessary on the image after launching it, then snapshots it into a reusable image. This reusable image can then be used as the foundation @@ -36,13 +36,15 @@ builder. ### Required: -- `api_organization` (string) - The organization ID to use to access your account. +- `api_access_key` (string) - The api_access_key to use to access your account. It can also be specified via - environment variable `SCALEWAY_API_ORGANIZATION`. + environment variable `SCALEWAY_API_ACCESS_KEY`. + Your access key is available in the ["Credentials" section](https://cloud.scaleway.com/#/credentials) of the control panel. - `api_token` (string) - The organization TOKEN to use to access your account. It can also be specified via environment variable `SCALEWAY_API_TOKEN`. + Your tokens are available in the ["Credentials" section](https://cloud.scaleway.com/#/credentials) of the control panel. - `image` (string) - The UUID of the base image to use. This is the image that will be used to launch a new server and provision it. See @@ -60,6 +62,9 @@ builder. - `server_name` (string) - The name assigned to the server. +- `image_name` (string) - The name of the resulting image that will + appear in your account. + - `snapshot_name` (string) - The name of the resulting snapshot that will appear in your account. @@ -71,7 +76,7 @@ access tokens: ```json { "type": "scaleway", - "api_organization": "YOUR ORGANIZATION KEY", + "api_access_key": "YOUR API ACCESS KEY", "api_token": "YOUR TOKEN", "image": "f01f8a48-c026-48ac-9771-a70eaac0890e", "region": "par1", From 2de93c5ae699838cccb9720f912fc55a2a3e8c9b Mon Sep 17 00:00:00 2001 From: Edouard BONLIEU Date: Wed, 19 Apr 2017 11:10:52 +0200 Subject: [PATCH 0477/1216] Add existing SSH key support Update documentation --- builder/scaleway/builder.go | 5 +++-- builder/scaleway/step_create_server.go | 7 ++++++- builder/scaleway/step_create_ssh_key.go | 21 +++++++++++++++++-- website/source/docs/builders/scaleway.html.md | 8 ++++++- 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/builder/scaleway/builder.go b/builder/scaleway/builder.go index e6265395a..2277808e0 100644 --- a/builder/scaleway/builder.go +++ b/builder/scaleway/builder.go @@ -43,8 +43,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe steps := []multistep.Step{ &stepCreateSSHKey{ - Debug: b.config.PackerDebug, - DebugKeyPath: fmt.Sprintf("scw_%s.pem", b.config.PackerBuildName), + Debug: b.config.PackerDebug, + DebugKeyPath: fmt.Sprintf("scw_%s.pem", b.config.PackerBuildName), + PrivateKeyFile: b.config.Comm.SSHPrivateKey, }, new(stepCreateServer), new(stepServerInfo), diff --git a/builder/scaleway/step_create_server.go b/builder/scaleway/step_create_server.go index e1e8b88c6..3cf46e1ed 100644 --- a/builder/scaleway/step_create_server.go +++ b/builder/scaleway/step_create_server.go @@ -18,15 +18,20 @@ func (s *stepCreateServer) Run(state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) c := state.Get("config").(Config) sshPubKey := state.Get("ssh_pubkey").(string) + tags := []string{} ui.Say("Creating server...") + if sshPubKey != "" { + tags = []string{fmt.Sprintf("AUTHORIZED_KEY=%s", strings.TrimSpace(sshPubKey))} + } + server, err := client.PostServer(api.ScalewayServerDefinition{ Name: c.ServerName, Image: &c.Image, Organization: c.Organization, CommercialType: c.CommercialType, - Tags: []string{fmt.Sprintf("AUTHORIZED_KEY=%s", strings.TrimSpace(sshPubKey))}, + Tags: tags, }) err = client.PostServerAction(server, "poweron") diff --git a/builder/scaleway/step_create_ssh_key.go b/builder/scaleway/step_create_ssh_key.go index 8a3618619..dab8f710b 100644 --- a/builder/scaleway/step_create_ssh_key.go +++ b/builder/scaleway/step_create_ssh_key.go @@ -6,6 +6,7 @@ import ( "crypto/x509" "encoding/pem" "fmt" + "io/ioutil" "log" "os" "runtime" @@ -17,13 +18,29 @@ import ( ) type stepCreateSSHKey struct { - Debug bool - DebugKeyPath string + Debug bool + DebugKeyPath string + PrivateKeyFile string } func (s *stepCreateSSHKey) Run(state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) + if s.PrivateKeyFile != "" { + ui.Say("Using existing SSH private key") + privateKeyBytes, err := ioutil.ReadFile(s.PrivateKeyFile) + if err != nil { + state.Put("error", fmt.Errorf( + "Error loading configured private key file: %s", err)) + return multistep.ActionHalt + } + + state.Put("privateKey", string(privateKeyBytes)) + state.Put("ssh_pubkey", "") + + return multistep.ActionContinue + } + ui.Say("Creating temporary ssh key for server...") priv, err := rsa.GenerateKey(rand.Reader, 2014) diff --git a/website/source/docs/builders/scaleway.html.md b/website/source/docs/builders/scaleway.html.md index 4e0a513fc..fa1edd827 100644 --- a/website/source/docs/builders/scaleway.html.md +++ b/website/source/docs/builders/scaleway.html.md @@ -68,6 +68,8 @@ builder. - `snapshot_name` (string) - The name of the resulting snapshot that will appear in your account. +- `ssh_private_key_file` (string) - Path to a PEM encoded private key file to use to authentiate with SSH. + ## Basic Example Here is a basic example. It is completely valid as soon as you enter your own @@ -81,6 +83,10 @@ access tokens: "image": "f01f8a48-c026-48ac-9771-a70eaac0890e", "region": "par1", "commercial_type": "X64-2GB", - "ssh_username": "root" + "ssh_username": "root", + "ssh_private_key_file": "~/.ssh/id_rsa", } ``` + +When you do not specified the `ssh_private_key_file`, a temporarily SSH keypair is generated to connect the server. +This key will only allows the `root` user to connect the server. \ No newline at end of file From edf9dd15174b08c13c98cafbf1085431521c632d Mon Sep 17 00:00:00 2001 From: Edouard BONLIEU Date: Wed, 19 Apr 2017 11:37:57 +0200 Subject: [PATCH 0478/1216] Fix doc --- website/source/docs/builders/scaleway.html.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/website/source/docs/builders/scaleway.html.md b/website/source/docs/builders/scaleway.html.md index fa1edd827..88f14a155 100644 --- a/website/source/docs/builders/scaleway.html.md +++ b/website/source/docs/builders/scaleway.html.md @@ -68,8 +68,6 @@ builder. - `snapshot_name` (string) - The name of the resulting snapshot that will appear in your account. -- `ssh_private_key_file` (string) - Path to a PEM encoded private key file to use to authentiate with SSH. - ## Basic Example Here is a basic example. It is completely valid as soon as you enter your own From 09805911b44b6eabcddd72e0d8adfea9844c086f Mon Sep 17 00:00:00 2001 From: Edouard BONLIEU Date: Tue, 11 Jul 2017 08:43:04 +0200 Subject: [PATCH 0479/1216] Fix builder unique id Add new ARM64 commercial types DOC - Add default value for optional settings DOC - Fix typo --- builder/scaleway/builder.go | 2 +- website/source/docs/builders/scaleway.html.md | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/builder/scaleway/builder.go b/builder/scaleway/builder.go index 2277808e0..14fee93a0 100644 --- a/builder/scaleway/builder.go +++ b/builder/scaleway/builder.go @@ -15,7 +15,7 @@ import ( ) // The unique id for the builder -const BuilderId = "pearkes.scaleway" +const BuilderId = "hashicorp.scaleway" type Builder struct { config Config diff --git a/website/source/docs/builders/scaleway.html.md b/website/source/docs/builders/scaleway.html.md index 88f14a155..e045f27bd 100644 --- a/website/source/docs/builders/scaleway.html.md +++ b/website/source/docs/builders/scaleway.html.md @@ -56,17 +56,17 @@ builder. be available. - `commercial_type` (string) - The name of the server commercial type: `C1`, `C2S`, `C2M`, - `C2L`, `X64-2GB`, `X64-4GB`, `X64-8GB`, `X64-15GB`, `X64-30GB`, `X64-60GB`, `X64-120GB` + `C2L`, `X64-2GB`, `X64-4GB`, `X64-8GB`, `X64-15GB`, `X64-30GB`, `X64-60GB`, `X64-120GB`, `ARM64-2GB`, `ARM64-4GB`, `ARM64-8GB`, `ARM64-16GB`, `ARM64-32GB`, `ARM64-64GB`, `ARM64-128GB` ### Optional: -- `server_name` (string) - The name assigned to the server. +- `server_name` (string) - The name assigned to the server. Default `packer-UUID` - `image_name` (string) - The name of the resulting image that will - appear in your account. + appear in your account. Default `packer-TIMESTAMP` - `snapshot_name` (string) - The name of the resulting snapshot that will - appear in your account. + appear in your account. Default `packer-TIMESTAMP ## Basic Example @@ -78,13 +78,14 @@ access tokens: "type": "scaleway", "api_access_key": "YOUR API ACCESS KEY", "api_token": "YOUR TOKEN", - "image": "f01f8a48-c026-48ac-9771-a70eaac0890e", + "image": "UUID OF THE BASE IMAGE", "region": "par1", "commercial_type": "X64-2GB", "ssh_username": "root", "ssh_private_key_file": "~/.ssh/id_rsa", + Extra, } ``` When you do not specified the `ssh_private_key_file`, a temporarily SSH keypair is generated to connect the server. -This key will only allows the `root` user to connect the server. \ No newline at end of file +This key will only allow the `root` user to connect the server. From ae18995ca1e7a4c038b2f26c012359ff15cfac63 Mon Sep 17 00:00:00 2001 From: Edouard BONLIEU Date: Tue, 11 Jul 2017 08:46:55 +0200 Subject: [PATCH 0480/1216] Fix builder id --- post-processor/vagrant/post-processor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/post-processor/vagrant/post-processor.go b/post-processor/vagrant/post-processor.go index 18357923f..f3bb6e346 100644 --- a/post-processor/vagrant/post-processor.go +++ b/post-processor/vagrant/post-processor.go @@ -26,7 +26,7 @@ var builtins = map[string]string{ "mitchellh.vmware-esx": "vmware", "pearkes.digitalocean": "digitalocean", "packer.googlecompute": "google", - "pearkes.scaleway": "scaleway", + "hashicorp.scaleway": "scaleway", "packer.parallels": "parallels", "MSOpenTech.hyperv": "hyperv", "transcend.qemu": "libvirt", From 520433c0b8ea5f9fc123e620109933bada6c9351 Mon Sep 17 00:00:00 2001 From: Edouard BONLIEU Date: Tue, 11 Jul 2017 14:06:53 +0200 Subject: [PATCH 0481/1216] Cleanup documentation --- website/source/docs/builders/scaleway.html.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/website/source/docs/builders/scaleway.html.md b/website/source/docs/builders/scaleway.html.md index e045f27bd..efa26dbee 100644 --- a/website/source/docs/builders/scaleway.html.md +++ b/website/source/docs/builders/scaleway.html.md @@ -48,7 +48,7 @@ builder. - `image` (string) - The UUID of the base image to use. This is the image that will be used to launch a new server and provision it. See - [https://api-marketplace.scaleway.com/images](https://api-marketplace.scaleway.com/images) to + [https://api-marketplace.scaleway.com/images](https://api-marketplace.scaleway.com/images) get the complete list of the accepted image UUID. - `region` (string) - The name of the region to launch the @@ -66,7 +66,7 @@ builder. appear in your account. Default `packer-TIMESTAMP` - `snapshot_name` (string) - The name of the resulting snapshot that will - appear in your account. Default `packer-TIMESTAMP + appear in your account. Default `packer-TIMESTAMP` ## Basic Example @@ -82,8 +82,7 @@ access tokens: "region": "par1", "commercial_type": "X64-2GB", "ssh_username": "root", - "ssh_private_key_file": "~/.ssh/id_rsa", - Extra, + "ssh_private_key_file": "~/.ssh/id_rsa" } ``` From b44798b38deb5a9ac86e3e153269d36587c7ba4d Mon Sep 17 00:00:00 2001 From: Edouard BONLIEU Date: Tue, 11 Jul 2017 16:15:00 +0200 Subject: [PATCH 0482/1216] Raise error in case of create server failure --- builder/scaleway/step_create_server.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/builder/scaleway/step_create_server.go b/builder/scaleway/step_create_server.go index 3cf46e1ed..11fe284ab 100644 --- a/builder/scaleway/step_create_server.go +++ b/builder/scaleway/step_create_server.go @@ -34,10 +34,17 @@ func (s *stepCreateServer) Run(state multistep.StateBag) multistep.StepAction { Tags: tags, }) + if err != nil { + err := fmt.Errorf("Error creating server: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + err = client.PostServerAction(server, "poweron") if err != nil { - err := fmt.Errorf("Error creating server: %s", err) + err := fmt.Errorf("Error starting server: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt @@ -51,7 +58,7 @@ func (s *stepCreateServer) Run(state multistep.StateBag) multistep.StepAction { } func (s *stepCreateServer) Cleanup(state multistep.StateBag) { - if s.serverID != "" { + if s.serverID == "" { return } From eb56b1b70e38b3c0dd3f0850321a3ab0822cd37c Mon Sep 17 00:00:00 2001 From: Edouard BONLIEU Date: Fri, 21 Jul 2017 12:26:20 +0200 Subject: [PATCH 0483/1216] Fix terminate error --- builder/scaleway/builder.go | 7 ++++-- builder/scaleway/step_create_server.go | 5 +++- builder/scaleway/step_terminate.go | 34 -------------------------- 3 files changed, 9 insertions(+), 37 deletions(-) delete mode 100644 builder/scaleway/step_terminate.go diff --git a/builder/scaleway/builder.go b/builder/scaleway/builder.go index 14fee93a0..f2266576e 100644 --- a/builder/scaleway/builder.go +++ b/builder/scaleway/builder.go @@ -33,7 +33,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { - client, _ := api.NewScalewayAPI(b.config.Organization, b.config.Token, b.config.UserAgent, b.config.Region) + client, err := api.NewScalewayAPI(b.config.Organization, b.config.Token, b.config.UserAgent, b.config.Region) + + if err != nil { + return nil, err + } state := new(multistep.BasicStateBag) state.Put("config", b.config) @@ -58,7 +62,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe new(stepShutdown), new(stepSnapshot), new(stepImage), - new(stepTerminate), } b.runner = common.NewRunner(steps, b.config.PackerConfig, ui) diff --git a/builder/scaleway/step_create_server.go b/builder/scaleway/step_create_server.go index 11fe284ab..ddd06fdf5 100644 --- a/builder/scaleway/step_create_server.go +++ b/builder/scaleway/step_create_server.go @@ -66,9 +66,12 @@ func (s *stepCreateServer) Cleanup(state multistep.StateBag) { ui := state.Get("ui").(packer.Ui) ui.Say("Destroying server...") - err := client.PostServerAction(s.serverID, "terminate") + + err := client.DeleteServerForce(s.serverID) + if err != nil { ui.Error(fmt.Sprintf( "Error destroying server. Please destroy it manually: %s", err)) } + } diff --git a/builder/scaleway/step_terminate.go b/builder/scaleway/step_terminate.go deleted file mode 100644 index 154750931..000000000 --- a/builder/scaleway/step_terminate.go +++ /dev/null @@ -1,34 +0,0 @@ -package scaleway - -import ( - "fmt" - - "github.com/hashicorp/packer/packer" - "github.com/mitchellh/multistep" - "github.com/scaleway/scaleway-cli/pkg/api" -) - -type stepTerminate struct{} - -func (s *stepTerminate) Run(state multistep.StateBag) multistep.StepAction { - client := state.Get("client").(*api.ScalewayAPI) - ui := state.Get("ui").(packer.Ui) - serverID := state.Get("server_id").(string) - - ui.Say("Terminating server...") - - err := client.DeleteServerForce(serverID) - - if err != nil { - err := fmt.Errorf("Error terminating server: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - return multistep.ActionContinue -} - -func (s *stepTerminate) Cleanup(state multistep.StateBag) { - // no cleanup -} From fc7d89eb79887f21cdb67a1e97f3e6e848134ce0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loi=CC=88c=20Carr?= Date: Fri, 5 Jan 2018 09:43:52 +0100 Subject: [PATCH 0484/1216] builder/scaleway: support password auth --- builder/scaleway/ssh.go | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/builder/scaleway/ssh.go b/builder/scaleway/ssh.go index 017138022..d1ac5dfc8 100644 --- a/builder/scaleway/ssh.go +++ b/builder/scaleway/ssh.go @@ -2,9 +2,10 @@ package scaleway import ( "fmt" - "golang.org/x/crypto/ssh" + packerssh "github.com/hashicorp/packer/communicator/ssh" "github.com/mitchellh/multistep" + "golang.org/x/crypto/ssh" ) func commHost(state multistep.StateBag) (string, error) { @@ -14,17 +15,32 @@ func commHost(state multistep.StateBag) (string, error) { func sshConfig(state multistep.StateBag) (*ssh.ClientConfig, error) { config := state.Get("config").(Config) - privateKey := state.Get("privateKey").(string) + var privateKey string - signer, err := ssh.ParsePrivateKey([]byte(privateKey)) - if err != nil { - return nil, fmt.Errorf("Error setting up SSH config: %s", err) + var auth []ssh.AuthMethod + + if config.Comm.SSHPassword != "" { + auth = []ssh.AuthMethod{ + ssh.Password(config.Comm.SSHPassword), + ssh.KeyboardInteractive( + packerssh.PasswordKeyboardInteractive(config.Comm.SSHPassword)), + } + } + + if config.Comm.SSHPrivateKey != "" { + if priv, ok := state.GetOk("privateKey"); ok { + privateKey = priv.(string) + } + signer, err := ssh.ParsePrivateKey([]byte(privateKey)) + if err != nil { + return nil, fmt.Errorf("Error setting up SSH config: %s", err) + } + auth = append(auth, ssh.PublicKeys(signer)) } return &ssh.ClientConfig{ - User: config.Comm.SSHUsername, - Auth: []ssh.AuthMethod{ - ssh.PublicKeys(signer), - }, + User: config.Comm.SSHUsername, + Auth: auth, + HostKeyCallback: ssh.InsecureIgnoreHostKey(), }, nil } From 7f8ed28bc6d89a8ce24e382caa09ffacc6a48288 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loi=CC=88c=20Carr?= Date: Fri, 5 Jan 2018 09:57:59 +0100 Subject: [PATCH 0485/1216] builder/scaleway: Make use of NewRunnerWithPauseFn --- builder/scaleway/builder.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/builder/scaleway/builder.go b/builder/scaleway/builder.go index f2266576e..266b9e41b 100644 --- a/builder/scaleway/builder.go +++ b/builder/scaleway/builder.go @@ -4,6 +4,7 @@ package scaleway import ( + "errors" "fmt" "log" @@ -64,16 +65,24 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe new(stepImage), } - b.runner = common.NewRunner(steps, b.config.PackerConfig, ui) + b.runner = common.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state) b.runner.Run(state) if rawErr, ok := state.GetOk("error"); ok { return nil, rawErr.(error) } + // If we were interrupted or cancelled, then just exit. + if _, ok := state.GetOk(multistep.StateCancelled); ok { + return nil, errors.New("Build was cancelled.") + } + + if _, ok := state.GetOk(multistep.StateHalted); ok { + return nil, errors.New("Build was halted.") + } + if _, ok := state.GetOk("snapshot_name"); !ok { - log.Println("Failed to find snapshot_name in state. Bug?") - return nil, nil + return nil, errors.New("Cannot find snapshot_name in state.") } artifact := &Artifact{ From 1f7c32db98036f1c149813b0c2f5165928350cbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loi=CC=88c=20Carr?= Date: Fri, 5 Jan 2018 10:07:03 +0100 Subject: [PATCH 0486/1216] builder/scaleway: report to ui scw api startup error --- builder/scaleway/builder.go | 1 + 1 file changed, 1 insertion(+) diff --git a/builder/scaleway/builder.go b/builder/scaleway/builder.go index 266b9e41b..8c5d79fde 100644 --- a/builder/scaleway/builder.go +++ b/builder/scaleway/builder.go @@ -37,6 +37,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe client, err := api.NewScalewayAPI(b.config.Organization, b.config.Token, b.config.UserAgent, b.config.Region) if err != nil { + ui.Error(err.Error()) return nil, err } From 22b12432db290af7589dcdcd49eba39c95470c61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loi=CC=88c=20Carr?= Date: Sat, 6 Jan 2018 14:56:46 +0100 Subject: [PATCH 0487/1216] builder/scaleway: support ssh agent authentication --- builder/scaleway/ssh.go | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/builder/scaleway/ssh.go b/builder/scaleway/ssh.go index d1ac5dfc8..2e566936e 100644 --- a/builder/scaleway/ssh.go +++ b/builder/scaleway/ssh.go @@ -2,10 +2,13 @@ package scaleway import ( "fmt" + "net" + "os" packerssh "github.com/hashicorp/packer/communicator/ssh" "github.com/mitchellh/multistep" "golang.org/x/crypto/ssh" + "golang.org/x/crypto/ssh/agent" ) func commHost(state multistep.StateBag) (string, error) { @@ -19,12 +22,27 @@ func sshConfig(state multistep.StateBag) (*ssh.ClientConfig, error) { var auth []ssh.AuthMethod - if config.Comm.SSHPassword != "" { + if config.Comm.SSHAgentAuth { + authSock := os.Getenv("SSH_AUTH_SOCK") + if authSock == "" { + return nil, fmt.Errorf("SSH_AUTH_SOCK is not set") + } + + sshAgent, err := net.Dial("unix", authSock) + if err != nil { + return nil, fmt.Errorf("Cannot connect to SSH Agent socket %q: %s", authSock, err) + } auth = []ssh.AuthMethod{ + ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers), + } + } + + if config.Comm.SSHPassword != "" { + auth = append(auth, ssh.Password(config.Comm.SSHPassword), ssh.KeyboardInteractive( packerssh.PasswordKeyboardInteractive(config.Comm.SSHPassword)), - } + ) } if config.Comm.SSHPrivateKey != "" { From e752e3a01847351130f3854d77a3d00ccd5dca4b Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Mon, 5 Feb 2018 16:50:32 -0800 Subject: [PATCH 0488/1216] use new internal multistep helper --- builder/scaleway/builder.go | 2 +- builder/scaleway/ssh.go | 2 +- builder/scaleway/step_create_image.go | 5 +++-- builder/scaleway/step_create_server.go | 5 +++-- builder/scaleway/step_create_ssh_key.go | 5 +++-- builder/scaleway/step_server_info.go | 5 +++-- builder/scaleway/step_shutdown.go | 5 +++-- builder/scaleway/step_snapshot.go | 5 +++-- 8 files changed, 20 insertions(+), 14 deletions(-) diff --git a/builder/scaleway/builder.go b/builder/scaleway/builder.go index 8c5d79fde..457002c74 100644 --- a/builder/scaleway/builder.go +++ b/builder/scaleway/builder.go @@ -10,8 +10,8 @@ import ( "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" + "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" - "github.com/mitchellh/multistep" "github.com/scaleway/scaleway-cli/pkg/api" ) diff --git a/builder/scaleway/ssh.go b/builder/scaleway/ssh.go index 2e566936e..a2c9b8f16 100644 --- a/builder/scaleway/ssh.go +++ b/builder/scaleway/ssh.go @@ -6,7 +6,7 @@ import ( "os" packerssh "github.com/hashicorp/packer/communicator/ssh" - "github.com/mitchellh/multistep" + "github.com/hashicorp/packer/helper/multistep" "golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/agent" ) diff --git a/builder/scaleway/step_create_image.go b/builder/scaleway/step_create_image.go index 313f2e421..16345e74d 100644 --- a/builder/scaleway/step_create_image.go +++ b/builder/scaleway/step_create_image.go @@ -1,17 +1,18 @@ package scaleway import ( + "context" "fmt" "log" + "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" - "github.com/mitchellh/multistep" "github.com/scaleway/scaleway-cli/pkg/api" ) type stepImage struct{} -func (s *stepImage) Run(state multistep.StateBag) multistep.StepAction { +func (s *stepImage) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { client := state.Get("client").(*api.ScalewayAPI) ui := state.Get("ui").(packer.Ui) c := state.Get("config").(Config) diff --git a/builder/scaleway/step_create_server.go b/builder/scaleway/step_create_server.go index ddd06fdf5..57511d149 100644 --- a/builder/scaleway/step_create_server.go +++ b/builder/scaleway/step_create_server.go @@ -1,11 +1,12 @@ package scaleway import ( + "context" "fmt" "strings" + "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" - "github.com/mitchellh/multistep" "github.com/scaleway/scaleway-cli/pkg/api" ) @@ -13,7 +14,7 @@ type stepCreateServer struct { serverID string } -func (s *stepCreateServer) Run(state multistep.StateBag) multistep.StepAction { +func (s *stepCreateServer) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { client := state.Get("client").(*api.ScalewayAPI) ui := state.Get("ui").(packer.Ui) c := state.Get("config").(Config) diff --git a/builder/scaleway/step_create_ssh_key.go b/builder/scaleway/step_create_ssh_key.go index dab8f710b..c5405ccd6 100644 --- a/builder/scaleway/step_create_ssh_key.go +++ b/builder/scaleway/step_create_ssh_key.go @@ -1,6 +1,7 @@ package scaleway import ( + "context" "crypto/rand" "crypto/rsa" "crypto/x509" @@ -12,8 +13,8 @@ import ( "runtime" "strings" + "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" - "github.com/mitchellh/multistep" "golang.org/x/crypto/ssh" ) @@ -23,7 +24,7 @@ type stepCreateSSHKey struct { PrivateKeyFile string } -func (s *stepCreateSSHKey) Run(state multistep.StateBag) multistep.StepAction { +func (s *stepCreateSSHKey) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) if s.PrivateKeyFile != "" { diff --git a/builder/scaleway/step_server_info.go b/builder/scaleway/step_server_info.go index 38502fe4d..7ab14658f 100644 --- a/builder/scaleway/step_server_info.go +++ b/builder/scaleway/step_server_info.go @@ -1,16 +1,17 @@ package scaleway import ( + "context" "fmt" + "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" - "github.com/mitchellh/multistep" "github.com/scaleway/scaleway-cli/pkg/api" ) type stepServerInfo struct{} -func (s *stepServerInfo) Run(state multistep.StateBag) multistep.StepAction { +func (s *stepServerInfo) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { client := state.Get("client").(*api.ScalewayAPI) ui := state.Get("ui").(packer.Ui) serverID := state.Get("server_id").(string) diff --git a/builder/scaleway/step_shutdown.go b/builder/scaleway/step_shutdown.go index b8e45be51..a8a029259 100644 --- a/builder/scaleway/step_shutdown.go +++ b/builder/scaleway/step_shutdown.go @@ -1,16 +1,17 @@ package scaleway import ( + "context" "fmt" + "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" - "github.com/mitchellh/multistep" "github.com/scaleway/scaleway-cli/pkg/api" ) type stepShutdown struct{} -func (s *stepShutdown) Run(state multistep.StateBag) multistep.StepAction { +func (s *stepShutdown) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { client := state.Get("client").(*api.ScalewayAPI) ui := state.Get("ui").(packer.Ui) serverID := state.Get("server_id").(string) diff --git a/builder/scaleway/step_snapshot.go b/builder/scaleway/step_snapshot.go index 20301a444..fd0dcb593 100644 --- a/builder/scaleway/step_snapshot.go +++ b/builder/scaleway/step_snapshot.go @@ -1,17 +1,18 @@ package scaleway import ( + "context" "fmt" "log" + "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" - "github.com/mitchellh/multistep" "github.com/scaleway/scaleway-cli/pkg/api" ) type stepSnapshot struct{} -func (s *stepSnapshot) Run(state multistep.StateBag) multistep.StepAction { +func (s *stepSnapshot) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { client := state.Get("client").(*api.ScalewayAPI) ui := state.Get("ui").(packer.Ui) c := state.Get("config").(Config) From 8b7982480fcf5772aed796753eedf5a537885c66 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Mon, 5 Feb 2018 16:51:54 -0800 Subject: [PATCH 0489/1216] fix sidebar placement --- website/source/layouts/docs.erb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/website/source/layouts/docs.erb b/website/source/layouts/docs.erb index 271de572f..5f089e041 100644 --- a/website/source/layouts/docs.erb +++ b/website/source/layouts/docs.erb @@ -90,9 +90,6 @@ - > - Scaleway - > CloudStack @@ -165,6 +162,9 @@ > QEMU + > + Scaleway + > Triton From 44647ea185befa9b21668b011d0c17ffe0a212a9 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Mon, 5 Feb 2018 16:52:44 -0800 Subject: [PATCH 0490/1216] add vendor deps --- .../github.com/Sirupsen/logrus/CHANGELOG.md | 94 + vendor/github.com/Sirupsen/logrus/LICENSE | 21 + vendor/github.com/Sirupsen/logrus/README.md | 479 +++ vendor/github.com/Sirupsen/logrus/alt_exit.go | 64 + vendor/github.com/Sirupsen/logrus/doc.go | 26 + vendor/github.com/Sirupsen/logrus/entry.go | 275 ++ vendor/github.com/Sirupsen/logrus/exported.go | 193 ++ .../github.com/Sirupsen/logrus/formatter.go | 45 + vendor/github.com/Sirupsen/logrus/hooks.go | 34 + .../Sirupsen/logrus/json_formatter.go | 74 + vendor/github.com/Sirupsen/logrus/logger.go | 308 ++ vendor/github.com/Sirupsen/logrus/logrus.go | 143 + .../Sirupsen/logrus/terminal_bsd.go | 10 + .../Sirupsen/logrus/terminal_linux.go | 14 + .../Sirupsen/logrus/terminal_notwindows.go | 28 + .../Sirupsen/logrus/terminal_solaris.go | 21 + .../Sirupsen/logrus/terminal_windows.go | 33 + .../Sirupsen/logrus/text_formatter.go | 189 ++ vendor/github.com/Sirupsen/logrus/writer.go | 62 + vendor/github.com/creack/goselect/Dockerfile | 5 + vendor/github.com/creack/goselect/LICENSE | 22 + vendor/github.com/creack/goselect/README.md | 30 + vendor/github.com/creack/goselect/fdset.go | 33 + vendor/github.com/creack/goselect/fdset_32.go | 10 + vendor/github.com/creack/goselect/fdset_64.go | 11 + .../github.com/creack/goselect/fdset_doc.go | 93 + .../creack/goselect/fdset_freebsd.go | 33 + .../creack/goselect/fdset_unsupported.go | 20 + .../creack/goselect/fdset_windows.go | 57 + vendor/github.com/creack/goselect/select.go | 28 + .../creack/goselect/select_linux.go | 10 + .../creack/goselect/select_other.go | 9 + .../creack/goselect/select_unsupported.go | 16 + .../creack/goselect/select_windows.go | 13 + .../creack/goselect/test_crosscompile.sh | 17 + .../creack/goselect/zselect_windows.go | 41 + vendor/github.com/docker/docker/LICENSE | 191 ++ vendor/github.com/docker/docker/NOTICE | 19 + .../pkg/namesgenerator/names-generator.go | 608 ++++ .../docker/docker/pkg/random/random.go | 71 + vendor/github.com/dustin/go-humanize/LICENSE | 21 + .../dustin/go-humanize/README.markdown | 92 + vendor/github.com/dustin/go-humanize/big.go | 31 + .../github.com/dustin/go-humanize/bigbytes.go | 173 ++ vendor/github.com/dustin/go-humanize/bytes.go | 143 + vendor/github.com/dustin/go-humanize/comma.go | 108 + .../github.com/dustin/go-humanize/commaf.go | 40 + vendor/github.com/dustin/go-humanize/ftoa.go | 23 + .../github.com/dustin/go-humanize/humanize.go | 8 + .../github.com/dustin/go-humanize/number.go | 192 ++ .../github.com/dustin/go-humanize/ordinals.go | 25 + vendor/github.com/dustin/go-humanize/si.go | 113 + vendor/github.com/dustin/go-humanize/times.go | 117 + vendor/github.com/gorilla/websocket/AUTHORS | 8 + vendor/github.com/gorilla/websocket/LICENSE | 22 + vendor/github.com/gorilla/websocket/README.md | 64 + vendor/github.com/gorilla/websocket/client.go | 392 +++ .../gorilla/websocket/client_clone.go | 16 + .../gorilla/websocket/client_clone_legacy.go | 38 + .../gorilla/websocket/compression.go | 148 + vendor/github.com/gorilla/websocket/conn.go | 1149 +++++++ .../github.com/gorilla/websocket/conn_read.go | 18 + .../gorilla/websocket/conn_read_legacy.go | 21 + vendor/github.com/gorilla/websocket/doc.go | 180 ++ vendor/github.com/gorilla/websocket/json.go | 55 + vendor/github.com/gorilla/websocket/mask.go | 55 + .../github.com/gorilla/websocket/prepared.go | 103 + vendor/github.com/gorilla/websocket/server.go | 291 ++ vendor/github.com/gorilla/websocket/util.go | 214 ++ vendor/github.com/moul/anonuuid/LICENSE | 22 + vendor/github.com/moul/anonuuid/Makefile | 73 + vendor/github.com/moul/anonuuid/README.md | 170 + vendor/github.com/moul/anonuuid/anonuuid.go | 229 ++ vendor/github.com/moul/gotty-client/LICENSE | 22 + vendor/github.com/moul/gotty-client/Makefile | 81 + vendor/github.com/moul/gotty-client/README.md | 201 ++ vendor/github.com/moul/gotty-client/arch.go | 34 + .../moul/gotty-client/arch_windows.go | 16 + .../github.com/moul/gotty-client/glide.lock | 37 + .../github.com/moul/gotty-client/glide.yaml | 35 + .../moul/gotty-client/gotty-client.go | 469 +++ .../github.com/renstrom/fuzzysearch/LICENSE | 21 + .../renstrom/fuzzysearch/fuzzy/fuzzy.go | 167 + .../renstrom/fuzzysearch/fuzzy/levenshtein.go | 43 + .../scaleway/scaleway-cli/LICENSE.md | 22 + .../scaleway/scaleway-cli/pkg/api/README.md | 25 + .../scaleway/scaleway-cli/pkg/api/api.go | 2754 +++++++++++++++++ .../scaleway/scaleway-cli/pkg/api/cache.go | 814 +++++ .../scaleway/scaleway-cli/pkg/api/helpers.go | 685 ++++ .../scaleway/scaleway-cli/pkg/api/logger.go | 77 + .../scaleway-cli/pkg/sshcommand/sshcommand.go | 124 + .../scaleway/scaleway-cli/pkg/utils/quiet.go | 30 + .../scaleway/scaleway-cli/pkg/utils/utils.go | 253 ++ .../x/crypto/ssh/terminal/terminal.go | 951 ++++++ .../golang.org/x/crypto/ssh/terminal/util.go | 119 + .../x/crypto/ssh/terminal/util_bsd.go | 12 + .../x/crypto/ssh/terminal/util_linux.go | 11 + .../x/crypto/ssh/terminal/util_plan9.go | 58 + .../x/crypto/ssh/terminal/util_solaris.go | 73 + .../x/crypto/ssh/terminal/util_windows.go | 155 + vendor/golang.org/x/sync/LICENSE | 27 + vendor/golang.org/x/sync/PATENTS | 22 + vendor/golang.org/x/sync/errgroup/errgroup.go | 67 + 103 files changed, 15209 insertions(+) create mode 100644 vendor/github.com/Sirupsen/logrus/CHANGELOG.md create mode 100644 vendor/github.com/Sirupsen/logrus/LICENSE create mode 100644 vendor/github.com/Sirupsen/logrus/README.md create mode 100644 vendor/github.com/Sirupsen/logrus/alt_exit.go create mode 100644 vendor/github.com/Sirupsen/logrus/doc.go create mode 100644 vendor/github.com/Sirupsen/logrus/entry.go create mode 100644 vendor/github.com/Sirupsen/logrus/exported.go create mode 100644 vendor/github.com/Sirupsen/logrus/formatter.go create mode 100644 vendor/github.com/Sirupsen/logrus/hooks.go create mode 100644 vendor/github.com/Sirupsen/logrus/json_formatter.go create mode 100644 vendor/github.com/Sirupsen/logrus/logger.go create mode 100644 vendor/github.com/Sirupsen/logrus/logrus.go create mode 100644 vendor/github.com/Sirupsen/logrus/terminal_bsd.go create mode 100644 vendor/github.com/Sirupsen/logrus/terminal_linux.go create mode 100644 vendor/github.com/Sirupsen/logrus/terminal_notwindows.go create mode 100644 vendor/github.com/Sirupsen/logrus/terminal_solaris.go create mode 100644 vendor/github.com/Sirupsen/logrus/terminal_windows.go create mode 100644 vendor/github.com/Sirupsen/logrus/text_formatter.go create mode 100644 vendor/github.com/Sirupsen/logrus/writer.go create mode 100644 vendor/github.com/creack/goselect/Dockerfile create mode 100644 vendor/github.com/creack/goselect/LICENSE create mode 100644 vendor/github.com/creack/goselect/README.md create mode 100644 vendor/github.com/creack/goselect/fdset.go create mode 100644 vendor/github.com/creack/goselect/fdset_32.go create mode 100644 vendor/github.com/creack/goselect/fdset_64.go create mode 100644 vendor/github.com/creack/goselect/fdset_doc.go create mode 100644 vendor/github.com/creack/goselect/fdset_freebsd.go create mode 100644 vendor/github.com/creack/goselect/fdset_unsupported.go create mode 100644 vendor/github.com/creack/goselect/fdset_windows.go create mode 100644 vendor/github.com/creack/goselect/select.go create mode 100644 vendor/github.com/creack/goselect/select_linux.go create mode 100644 vendor/github.com/creack/goselect/select_other.go create mode 100644 vendor/github.com/creack/goselect/select_unsupported.go create mode 100644 vendor/github.com/creack/goselect/select_windows.go create mode 100755 vendor/github.com/creack/goselect/test_crosscompile.sh create mode 100644 vendor/github.com/creack/goselect/zselect_windows.go create mode 100644 vendor/github.com/docker/docker/LICENSE create mode 100644 vendor/github.com/docker/docker/NOTICE create mode 100644 vendor/github.com/docker/docker/pkg/namesgenerator/names-generator.go create mode 100644 vendor/github.com/docker/docker/pkg/random/random.go create mode 100644 vendor/github.com/dustin/go-humanize/LICENSE create mode 100644 vendor/github.com/dustin/go-humanize/README.markdown create mode 100644 vendor/github.com/dustin/go-humanize/big.go create mode 100644 vendor/github.com/dustin/go-humanize/bigbytes.go create mode 100644 vendor/github.com/dustin/go-humanize/bytes.go create mode 100644 vendor/github.com/dustin/go-humanize/comma.go create mode 100644 vendor/github.com/dustin/go-humanize/commaf.go create mode 100644 vendor/github.com/dustin/go-humanize/ftoa.go create mode 100644 vendor/github.com/dustin/go-humanize/humanize.go create mode 100644 vendor/github.com/dustin/go-humanize/number.go create mode 100644 vendor/github.com/dustin/go-humanize/ordinals.go create mode 100644 vendor/github.com/dustin/go-humanize/si.go create mode 100644 vendor/github.com/dustin/go-humanize/times.go create mode 100644 vendor/github.com/gorilla/websocket/AUTHORS create mode 100644 vendor/github.com/gorilla/websocket/LICENSE create mode 100644 vendor/github.com/gorilla/websocket/README.md create mode 100644 vendor/github.com/gorilla/websocket/client.go create mode 100644 vendor/github.com/gorilla/websocket/client_clone.go create mode 100644 vendor/github.com/gorilla/websocket/client_clone_legacy.go create mode 100644 vendor/github.com/gorilla/websocket/compression.go create mode 100644 vendor/github.com/gorilla/websocket/conn.go create mode 100644 vendor/github.com/gorilla/websocket/conn_read.go create mode 100644 vendor/github.com/gorilla/websocket/conn_read_legacy.go create mode 100644 vendor/github.com/gorilla/websocket/doc.go create mode 100644 vendor/github.com/gorilla/websocket/json.go create mode 100644 vendor/github.com/gorilla/websocket/mask.go create mode 100644 vendor/github.com/gorilla/websocket/prepared.go create mode 100644 vendor/github.com/gorilla/websocket/server.go create mode 100644 vendor/github.com/gorilla/websocket/util.go create mode 100644 vendor/github.com/moul/anonuuid/LICENSE create mode 100644 vendor/github.com/moul/anonuuid/Makefile create mode 100644 vendor/github.com/moul/anonuuid/README.md create mode 100644 vendor/github.com/moul/anonuuid/anonuuid.go create mode 100644 vendor/github.com/moul/gotty-client/LICENSE create mode 100644 vendor/github.com/moul/gotty-client/Makefile create mode 100644 vendor/github.com/moul/gotty-client/README.md create mode 100644 vendor/github.com/moul/gotty-client/arch.go create mode 100644 vendor/github.com/moul/gotty-client/arch_windows.go create mode 100644 vendor/github.com/moul/gotty-client/glide.lock create mode 100644 vendor/github.com/moul/gotty-client/glide.yaml create mode 100644 vendor/github.com/moul/gotty-client/gotty-client.go create mode 100644 vendor/github.com/renstrom/fuzzysearch/LICENSE create mode 100644 vendor/github.com/renstrom/fuzzysearch/fuzzy/fuzzy.go create mode 100644 vendor/github.com/renstrom/fuzzysearch/fuzzy/levenshtein.go create mode 100644 vendor/github.com/scaleway/scaleway-cli/LICENSE.md create mode 100644 vendor/github.com/scaleway/scaleway-cli/pkg/api/README.md create mode 100644 vendor/github.com/scaleway/scaleway-cli/pkg/api/api.go create mode 100644 vendor/github.com/scaleway/scaleway-cli/pkg/api/cache.go create mode 100644 vendor/github.com/scaleway/scaleway-cli/pkg/api/helpers.go create mode 100644 vendor/github.com/scaleway/scaleway-cli/pkg/api/logger.go create mode 100644 vendor/github.com/scaleway/scaleway-cli/pkg/sshcommand/sshcommand.go create mode 100644 vendor/github.com/scaleway/scaleway-cli/pkg/utils/quiet.go create mode 100644 vendor/github.com/scaleway/scaleway-cli/pkg/utils/utils.go create mode 100644 vendor/golang.org/x/crypto/ssh/terminal/terminal.go create mode 100644 vendor/golang.org/x/crypto/ssh/terminal/util.go create mode 100644 vendor/golang.org/x/crypto/ssh/terminal/util_bsd.go create mode 100644 vendor/golang.org/x/crypto/ssh/terminal/util_linux.go create mode 100644 vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go create mode 100644 vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go create mode 100644 vendor/golang.org/x/crypto/ssh/terminal/util_windows.go create mode 100644 vendor/golang.org/x/sync/LICENSE create mode 100644 vendor/golang.org/x/sync/PATENTS create mode 100644 vendor/golang.org/x/sync/errgroup/errgroup.go diff --git a/vendor/github.com/Sirupsen/logrus/CHANGELOG.md b/vendor/github.com/Sirupsen/logrus/CHANGELOG.md new file mode 100644 index 000000000..747e4d89a --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/CHANGELOG.md @@ -0,0 +1,94 @@ +# 0.11.5 + +* feature: add writer and writerlevel to entry (#372) + +# 0.11.4 + +* bug: fix undefined variable on solaris (#493) + +# 0.11.3 + +* formatter: configure quoting of empty values (#484) +* formatter: configure quoting character (default is `"`) (#484) +* bug: fix not importing io correctly in non-linux environments (#481) + +# 0.11.2 + +* bug: fix windows terminal detection (#476) + +# 0.11.1 + +* bug: fix tty detection with custom out (#471) + +# 0.11.0 + +* performance: Use bufferpool to allocate (#370) +* terminal: terminal detection for app-engine (#343) +* feature: exit handler (#375) + +# 0.10.0 + +* feature: Add a test hook (#180) +* feature: `ParseLevel` is now case-insensitive (#326) +* feature: `FieldLogger` interface that generalizes `Logger` and `Entry` (#308) +* performance: avoid re-allocations on `WithFields` (#335) + +# 0.9.0 + +* logrus/text_formatter: don't emit empty msg +* logrus/hooks/airbrake: move out of main repository +* logrus/hooks/sentry: move out of main repository +* logrus/hooks/papertrail: move out of main repository +* logrus/hooks/bugsnag: move out of main repository +* logrus/core: run tests with `-race` +* logrus/core: detect TTY based on `stderr` +* logrus/core: support `WithError` on logger +* logrus/core: Solaris support + +# 0.8.7 + +* logrus/core: fix possible race (#216) +* logrus/doc: small typo fixes and doc improvements + + +# 0.8.6 + +* hooks/raven: allow passing an initialized client + +# 0.8.5 + +* logrus/core: revert #208 + +# 0.8.4 + +* formatter/text: fix data race (#218) + +# 0.8.3 + +* logrus/core: fix entry log level (#208) +* logrus/core: improve performance of text formatter by 40% +* logrus/core: expose `LevelHooks` type +* logrus/core: add support for DragonflyBSD and NetBSD +* formatter/text: print structs more verbosely + +# 0.8.2 + +* logrus: fix more Fatal family functions + +# 0.8.1 + +* logrus: fix not exiting on `Fatalf` and `Fatalln` + +# 0.8.0 + +* logrus: defaults to stderr instead of stdout +* hooks/sentry: add special field for `*http.Request` +* formatter/text: ignore Windows for colors + +# 0.7.3 + +* formatter/\*: allow configuration of timestamp layout + +# 0.7.2 + +* formatter/text: Add configuration option for time format (#158) diff --git a/vendor/github.com/Sirupsen/logrus/LICENSE b/vendor/github.com/Sirupsen/logrus/LICENSE new file mode 100644 index 000000000..f090cb42f --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Simon Eskildsen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/Sirupsen/logrus/README.md b/vendor/github.com/Sirupsen/logrus/README.md new file mode 100644 index 000000000..c32287611 --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/README.md @@ -0,0 +1,479 @@ +# Logrus :walrus: [![Build Status](https://travis-ci.org/Sirupsen/logrus.svg?branch=master)](https://travis-ci.org/Sirupsen/logrus) [![GoDoc](https://godoc.org/github.com/Sirupsen/logrus?status.svg)](https://godoc.org/github.com/Sirupsen/logrus) + +**Seeing weird case-sensitive problems?** See [this +issue](https://github.com/sirupsen/logrus/issues/451#issuecomment-264332021). +This change has been reverted. I apologize for causing this. I greatly +underestimated the impact this would have. Logrus strives for stability and +backwards compatibility and failed to provide that. + +Logrus is a structured logger for Go (golang), completely API compatible with +the standard library logger. [Godoc][godoc]. **Please note the Logrus API is not +yet stable (pre 1.0). Logrus itself is completely stable and has been used in +many large deployments. The core API is unlikely to change much but please +version control your Logrus to make sure you aren't fetching latest `master` on +every build.** + +Nicely color-coded in development (when a TTY is attached, otherwise just +plain text): + +![Colored](http://i.imgur.com/PY7qMwd.png) + +With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash +or Splunk: + +```json +{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the +ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"} + +{"level":"warning","msg":"The group's number increased tremendously!", +"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"} + +{"animal":"walrus","level":"info","msg":"A giant walrus appears!", +"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"} + +{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.", +"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"} + +{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true, +"time":"2014-03-10 19:57:38.562543128 -0400 EDT"} +``` + +With the default `log.SetFormatter(&log.TextFormatter{})` when a TTY is not +attached, the output is compatible with the +[logfmt](http://godoc.org/github.com/kr/logfmt) format: + +```text +time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8 +time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10 +time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true +time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4 +time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009 +time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true +exit status 1 +``` + +#### Example + +The simplest way to use Logrus is simply the package-level exported logger: + +```go +package main + +import ( + log "github.com/Sirupsen/logrus" +) + +func main() { + log.WithFields(log.Fields{ + "animal": "walrus", + }).Info("A walrus appears") +} +``` + +Note that it's completely api-compatible with the stdlib logger, so you can +replace your `log` imports everywhere with `log "github.com/Sirupsen/logrus"` +and you'll now have the flexibility of Logrus. You can customize it all you +want: + +```go +package main + +import ( + "os" + log "github.com/Sirupsen/logrus" +) + +func init() { + // Log as JSON instead of the default ASCII formatter. + log.SetFormatter(&log.JSONFormatter{}) + + // Output to stdout instead of the default stderr + // Can be any io.Writer, see below for File example + log.SetOutput(os.Stdout) + + // Only log the warning severity or above. + log.SetLevel(log.WarnLevel) +} + +func main() { + log.WithFields(log.Fields{ + "animal": "walrus", + "size": 10, + }).Info("A group of walrus emerges from the ocean") + + log.WithFields(log.Fields{ + "omg": true, + "number": 122, + }).Warn("The group's number increased tremendously!") + + log.WithFields(log.Fields{ + "omg": true, + "number": 100, + }).Fatal("The ice breaks!") + + // A common pattern is to re-use fields between logging statements by re-using + // the logrus.Entry returned from WithFields() + contextLogger := log.WithFields(log.Fields{ + "common": "this is a common field", + "other": "I also should be logged always", + }) + + contextLogger.Info("I'll be logged with common and other field") + contextLogger.Info("Me too") +} +``` + +For more advanced usage such as logging to multiple locations from the same +application, you can also create an instance of the `logrus` Logger: + +```go +package main + +import ( + "os" + "github.com/Sirupsen/logrus" +) + +// Create a new instance of the logger. You can have any number of instances. +var log = logrus.New() + +func main() { + // The API for setting attributes is a little different than the package level + // exported logger. See Godoc. + log.Out = os.Stdout + + // You could set this to any `io.Writer` such as a file + // file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666) + // if err == nil { + // log.Out = file + // } else { + // log.Info("Failed to log to file, using default stderr") + // } + + log.WithFields(logrus.Fields{ + "animal": "walrus", + "size": 10, + }).Info("A group of walrus emerges from the ocean") +} +``` + +#### Fields + +Logrus encourages careful, structured logging though logging fields instead of +long, unparseable error messages. For example, instead of: `log.Fatalf("Failed +to send event %s to topic %s with key %d")`, you should log the much more +discoverable: + +```go +log.WithFields(log.Fields{ + "event": event, + "topic": topic, + "key": key, +}).Fatal("Failed to send event") +``` + +We've found this API forces you to think about logging in a way that produces +much more useful logging messages. We've been in countless situations where just +a single added field to a log statement that was already there would've saved us +hours. The `WithFields` call is optional. + +In general, with Logrus using any of the `printf`-family functions should be +seen as a hint you should add a field, however, you can still use the +`printf`-family functions with Logrus. + +#### Default Fields + +Often it's helpful to have fields _always_ attached to log statements in an +application or parts of one. For example, you may want to always log the +`request_id` and `user_ip` in the context of a request. Instead of writing +`log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})` on +every line, you can create a `logrus.Entry` to pass around instead: + +```go +requestLogger := log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip}) +requestLogger.Info("something happened on that request") # will log request_id and user_ip +requestLogger.Warn("something not great happened") +``` + +#### Hooks + +You can add hooks for logging levels. For example to send errors to an exception +tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to +multiple places simultaneously, e.g. syslog. + +Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in +`init`: + +```go +import ( + log "github.com/Sirupsen/logrus" + "gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "aibrake" + logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog" + "log/syslog" +) + +func init() { + + // Use the Airbrake hook to report errors that have Error severity or above to + // an exception tracker. You can create custom hooks, see the Hooks section. + log.AddHook(airbrake.NewHook(123, "xyz", "production")) + + hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "") + if err != nil { + log.Error("Unable to connect to local syslog daemon") + } else { + log.AddHook(hook) + } +} +``` +Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md). + +| Hook | Description | +| ----- | ----------- | +| [Airbrake "legacy"](https://github.com/gemnasium/logrus-airbrake-legacy-hook) | Send errors to an exception tracking service compatible with the Airbrake API V2. Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. | +| [Airbrake](https://github.com/gemnasium/logrus-airbrake-hook) | Send errors to the Airbrake API V3. Uses the official [`gobrake`](https://github.com/airbrake/gobrake) behind the scenes. | +| [Amazon Kinesis](https://github.com/evalphobia/logrus_kinesis) | Hook for logging to [Amazon Kinesis](https://aws.amazon.com/kinesis/) | +| [Amqp-Hook](https://github.com/vladoatanasov/logrus_amqp) | Hook for logging to Amqp broker (Like RabbitMQ) | +| [Bugsnag](https://github.com/Shopify/logrus-bugsnag/blob/master/bugsnag.go) | Send errors to the Bugsnag exception tracking service. | +| [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic | +| [Discordrus](https://github.com/kz/discordrus) | Hook for logging to [Discord](https://discordapp.com/) | +| [ElasticSearch](https://github.com/sohlich/elogrus) | Hook for logging to ElasticSearch| +| [Firehose](https://github.com/beaubrewer/firehose) | Hook for logging to [Amazon Firehose](https://aws.amazon.com/kinesis/firehose/) +| [Fluentd](https://github.com/evalphobia/logrus_fluent) | Hook for logging to fluentd | +| [Go-Slack](https://github.com/multiplay/go-slack) | Hook for logging to [Slack](https://slack.com) | +| [Graylog](https://github.com/gemnasium/logrus-graylog-hook) | Hook for logging to [Graylog](http://graylog2.org/) | +| [Hiprus](https://github.com/nubo/hiprus) | Send errors to a channel in hipchat. | +| [Honeybadger](https://github.com/agonzalezro/logrus_honeybadger) | Hook for sending exceptions to Honeybadger | +| [InfluxDB](https://github.com/Abramovic/logrus_influxdb) | Hook for logging to influxdb | +| [Influxus] (http://github.com/vlad-doru/influxus) | Hook for concurrently logging to [InfluxDB] (http://influxdata.com/) | +| [Journalhook](https://github.com/wercker/journalhook) | Hook for logging to `systemd-journald` | +| [KafkaLogrus](https://github.com/goibibo/KafkaLogrus) | Hook for logging to kafka | +| [LFShook](https://github.com/rifflock/lfshook) | Hook for logging to the local filesystem | +| [Logentries](https://github.com/jcftang/logentriesrus) | Hook for logging to [Logentries](https://logentries.com/) | +| [Logentrus](https://github.com/puddingfactory/logentrus) | Hook for logging to [Logentries](https://logentries.com/) | +| [Logmatic.io](https://github.com/logmatic/logmatic-go) | Hook for logging to [Logmatic.io](http://logmatic.io/) | +| [Logrusly](https://github.com/sebest/logrusly) | Send logs to [Loggly](https://www.loggly.com/) | +| [Logstash](https://github.com/bshuster-repo/logrus-logstash-hook) | Hook for logging to [Logstash](https://www.elastic.co/products/logstash) | +| [Mail](https://github.com/zbindenren/logrus_mail) | Hook for sending exceptions via mail | +| [Mongodb](https://github.com/weekface/mgorus) | Hook for logging to mongodb | +| [NATS-Hook](https://github.com/rybit/nats_logrus_hook) | Hook for logging to [NATS](https://nats.io) | +| [Octokit](https://github.com/dorajistyle/logrus-octokit-hook) | Hook for logging to github via octokit | +| [Papertrail](https://github.com/polds/logrus-papertrail-hook) | Send errors to the [Papertrail](https://papertrailapp.com) hosted logging service via UDP. | +| [PostgreSQL](https://github.com/gemnasium/logrus-postgresql-hook) | Send logs to [PostgreSQL](http://postgresql.org) | +| [Pushover](https://github.com/toorop/logrus_pushover) | Send error via [Pushover](https://pushover.net) | +| [Raygun](https://github.com/squirkle/logrus-raygun-hook) | Hook for logging to [Raygun.io](http://raygun.io/) | +| [Redis-Hook](https://github.com/rogierlommers/logrus-redis-hook) | Hook for logging to a ELK stack (through Redis) | +| [Rollrus](https://github.com/heroku/rollrus) | Hook for sending errors to rollbar | +| [Scribe](https://github.com/sagar8192/logrus-scribe-hook) | Hook for logging to [Scribe](https://github.com/facebookarchive/scribe)| +| [Sentry](https://github.com/evalphobia/logrus_sentry) | Send errors to the Sentry error logging and aggregation service. | +| [Slackrus](https://github.com/johntdyer/slackrus) | Hook for Slack chat. | +| [Stackdriver](https://github.com/knq/sdhook) | Hook for logging to [Google Stackdriver](https://cloud.google.com/logging/) | +| [Sumorus](https://github.com/doublefree/sumorus) | Hook for logging to [SumoLogic](https://www.sumologic.com/)| +| [Syslog](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go) | Send errors to remote syslog server. Uses standard library `log/syslog` behind the scenes. | +| [TraceView](https://github.com/evalphobia/logrus_appneta) | Hook for logging to [AppNeta TraceView](https://www.appneta.com/products/traceview/) | +| [Typetalk](https://github.com/dragon3/logrus-typetalk-hook) | Hook for logging to [Typetalk](https://www.typetalk.in/) | +| [logz.io](https://github.com/ripcurld00d/logrus-logzio-hook) | Hook for logging to [logz.io](https://logz.io), a Log as a Service using Logstash | + +#### Level logging + +Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic. + +```go +log.Debug("Useful debugging information.") +log.Info("Something noteworthy happened!") +log.Warn("You should probably take a look at this.") +log.Error("Something failed but I'm not quitting.") +// Calls os.Exit(1) after logging +log.Fatal("Bye.") +// Calls panic() after logging +log.Panic("I'm bailing.") +``` + +You can set the logging level on a `Logger`, then it will only log entries with +that severity or anything above it: + +```go +// Will log anything that is info or above (warn, error, fatal, panic). Default. +log.SetLevel(log.InfoLevel) +``` + +It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose +environment if your application has that. + +#### Entries + +Besides the fields added with `WithField` or `WithFields` some fields are +automatically added to all logging events: + +1. `time`. The timestamp when the entry was created. +2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after + the `AddFields` call. E.g. `Failed to send event.` +3. `level`. The logging level. E.g. `info`. + +#### Environments + +Logrus has no notion of environment. + +If you wish for hooks and formatters to only be used in specific environments, +you should handle that yourself. For example, if your application has a global +variable `Environment`, which is a string representation of the environment you +could do: + +```go +import ( + log "github.com/Sirupsen/logrus" +) + +init() { + // do something here to set environment depending on an environment variable + // or command-line flag + if Environment == "production" { + log.SetFormatter(&log.JSONFormatter{}) + } else { + // The TextFormatter is default, you don't actually have to do this. + log.SetFormatter(&log.TextFormatter{}) + } +} +``` + +This configuration is how `logrus` was intended to be used, but JSON in +production is mostly only useful if you do log aggregation with tools like +Splunk or Logstash. + +#### Formatters + +The built-in logging formatters are: + +* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise + without colors. + * *Note:* to force colored output when there is no TTY, set the `ForceColors` + field to `true`. To force no colored output even if there is a TTY set the + `DisableColors` field to `true`. For Windows, see + [github.com/mattn/go-colorable](https://github.com/mattn/go-colorable). + * All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#TextFormatter). +* `logrus.JSONFormatter`. Logs fields as JSON. + * All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#JSONFormatter). + +Third party logging formatters: + +* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events. +* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout. +* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦. + +You can define your formatter by implementing the `Formatter` interface, +requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a +`Fields` type (`map[string]interface{}`) with all your fields as well as the +default ones (see Entries section above): + +```go +type MyJSONFormatter struct { +} + +log.SetFormatter(new(MyJSONFormatter)) + +func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) { + // Note this doesn't include Time, Level and Message which are available on + // the Entry. Consult `godoc` on information about those fields or read the + // source of the official loggers. + serialized, err := json.Marshal(entry.Data) + if err != nil { + return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) + } + return append(serialized, '\n'), nil +} +``` + +#### Logger as an `io.Writer` + +Logrus can be transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it. + +```go +w := logger.Writer() +defer w.Close() + +srv := http.Server{ + // create a stdlib log.Logger that writes to + // logrus.Logger. + ErrorLog: log.New(w, "", 0), +} +``` + +Each line written to that writer will be printed the usual way, using formatters +and hooks. The level for those entries is `info`. + +This means that we can override the standard library logger easily: + +```go +logger := logrus.New() +logger.Formatter = &logrus.JSONFormatter{} + +// Use logrus for standard log output +// Note that `log` here references stdlib's log +// Not logrus imported under the name `log`. +log.SetOutput(logger.Writer()) +``` + +#### Rotation + +Log rotation is not provided with Logrus. Log rotation should be done by an +external program (like `logrotate(8)`) that can compress and delete old log +entries. It should not be a feature of the application-level logger. + +#### Tools + +| Tool | Description | +| ---- | ----------- | +|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will generated with different config at different environment.| +|[Logrus Viper Helper](https://github.com/heirko/go-contrib/tree/master/logrusHelper)|An Helper around Logrus to wrap with spf13/Viper to load configuration with fangs! And to simplify Logrus configuration use some behavior of [Logrus Mate](https://github.com/gogap/logrus_mate). [sample](https://github.com/heirko/iris-contrib/blob/master/middleware/logrus-logger/example) | + +#### Testing + +Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides: + +* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just add the `test` hook +* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any): + +```go +logger, hook := NewNullLogger() +logger.Error("Hello error") + +assert.Equal(1, len(hook.Entries)) +assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level) +assert.Equal("Hello error", hook.LastEntry().Message) + +hook.Reset() +assert.Nil(hook.LastEntry()) +``` + +#### Fatal handlers + +Logrus can register one or more functions that will be called when any `fatal` +level message is logged. The registered handlers will be executed before +logrus performs a `os.Exit(1)`. This behavior may be helpful if callers need +to gracefully shutdown. Unlike a `panic("Something went wrong...")` call which can be intercepted with a deferred `recover` a call to `os.Exit(1)` can not be intercepted. + +``` +... +handler := func() { + // gracefully shutdown something... +} +logrus.RegisterExitHandler(handler) +... +``` + +#### Thread safety + +By default Logger is protected by mutex for concurrent writes, this mutex is invoked when calling hooks and writing logs. +If you are sure such locking is not needed, you can call logger.SetNoLock() to disable the locking. + +Situation when locking is not needed includes: + +* You have no hooks registered, or hooks calling is already thread-safe. + +* Writing to logger.Out is already thread-safe, for example: + + 1) logger.Out is protected by locks. + + 2) logger.Out is a os.File handler opened with `O_APPEND` flag, and every write is smaller than 4k. (This allow multi-thread/multi-process writing) + + (Refer to http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/) diff --git a/vendor/github.com/Sirupsen/logrus/alt_exit.go b/vendor/github.com/Sirupsen/logrus/alt_exit.go new file mode 100644 index 000000000..8af90637a --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/alt_exit.go @@ -0,0 +1,64 @@ +package logrus + +// The following code was sourced and modified from the +// https://github.com/tebeka/atexit package governed by the following license: +// +// Copyright (c) 2012 Miki Tebeka . +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import ( + "fmt" + "os" +) + +var handlers = []func(){} + +func runHandler(handler func()) { + defer func() { + if err := recover(); err != nil { + fmt.Fprintln(os.Stderr, "Error: Logrus exit handler error:", err) + } + }() + + handler() +} + +func runHandlers() { + for _, handler := range handlers { + runHandler(handler) + } +} + +// Exit runs all the Logrus atexit handlers and then terminates the program using os.Exit(code) +func Exit(code int) { + runHandlers() + os.Exit(code) +} + +// RegisterExitHandler adds a Logrus Exit handler, call logrus.Exit to invoke +// all handlers. The handlers will also be invoked when any Fatal log entry is +// made. +// +// This method is useful when a caller wishes to use logrus to log a fatal +// message but also needs to gracefully shutdown. An example usecase could be +// closing database connections, or sending a alert that the application is +// closing. +func RegisterExitHandler(handler func()) { + handlers = append(handlers, handler) +} diff --git a/vendor/github.com/Sirupsen/logrus/doc.go b/vendor/github.com/Sirupsen/logrus/doc.go new file mode 100644 index 000000000..dddd5f877 --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/doc.go @@ -0,0 +1,26 @@ +/* +Package logrus is a structured logger for Go, completely API compatible with the standard library logger. + + +The simplest way to use Logrus is simply the package-level exported logger: + + package main + + import ( + log "github.com/Sirupsen/logrus" + ) + + func main() { + log.WithFields(log.Fields{ + "animal": "walrus", + "number": 1, + "size": 10, + }).Info("A walrus appears") + } + +Output: + time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10 + +For a full guide visit https://github.com/Sirupsen/logrus +*/ +package logrus diff --git a/vendor/github.com/Sirupsen/logrus/entry.go b/vendor/github.com/Sirupsen/logrus/entry.go new file mode 100644 index 000000000..4edbe7a2d --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/entry.go @@ -0,0 +1,275 @@ +package logrus + +import ( + "bytes" + "fmt" + "os" + "sync" + "time" +) + +var bufferPool *sync.Pool + +func init() { + bufferPool = &sync.Pool{ + New: func() interface{} { + return new(bytes.Buffer) + }, + } +} + +// Defines the key when adding errors using WithError. +var ErrorKey = "error" + +// An entry is the final or intermediate Logrus logging entry. It contains all +// the fields passed with WithField{,s}. It's finally logged when Debug, Info, +// Warn, Error, Fatal or Panic is called on it. These objects can be reused and +// passed around as much as you wish to avoid field duplication. +type Entry struct { + Logger *Logger + + // Contains all the fields set by the user. + Data Fields + + // Time at which the log entry was created + Time time.Time + + // Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic + Level Level + + // Message passed to Debug, Info, Warn, Error, Fatal or Panic + Message string + + // When formatter is called in entry.log(), an Buffer may be set to entry + Buffer *bytes.Buffer +} + +func NewEntry(logger *Logger) *Entry { + return &Entry{ + Logger: logger, + // Default is three fields, give a little extra room + Data: make(Fields, 5), + } +} + +// Returns the string representation from the reader and ultimately the +// formatter. +func (entry *Entry) String() (string, error) { + serialized, err := entry.Logger.Formatter.Format(entry) + if err != nil { + return "", err + } + str := string(serialized) + return str, nil +} + +// Add an error as single field (using the key defined in ErrorKey) to the Entry. +func (entry *Entry) WithError(err error) *Entry { + return entry.WithField(ErrorKey, err) +} + +// Add a single field to the Entry. +func (entry *Entry) WithField(key string, value interface{}) *Entry { + return entry.WithFields(Fields{key: value}) +} + +// Add a map of fields to the Entry. +func (entry *Entry) WithFields(fields Fields) *Entry { + data := make(Fields, len(entry.Data)+len(fields)) + for k, v := range entry.Data { + data[k] = v + } + for k, v := range fields { + data[k] = v + } + return &Entry{Logger: entry.Logger, Data: data} +} + +// This function is not declared with a pointer value because otherwise +// race conditions will occur when using multiple goroutines +func (entry Entry) log(level Level, msg string) { + var buffer *bytes.Buffer + entry.Time = time.Now() + entry.Level = level + entry.Message = msg + + if err := entry.Logger.Hooks.Fire(level, &entry); err != nil { + entry.Logger.mu.Lock() + fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) + entry.Logger.mu.Unlock() + } + buffer = bufferPool.Get().(*bytes.Buffer) + buffer.Reset() + defer bufferPool.Put(buffer) + entry.Buffer = buffer + serialized, err := entry.Logger.Formatter.Format(&entry) + entry.Buffer = nil + if err != nil { + entry.Logger.mu.Lock() + fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err) + entry.Logger.mu.Unlock() + } else { + entry.Logger.mu.Lock() + _, err = entry.Logger.Out.Write(serialized) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) + } + entry.Logger.mu.Unlock() + } + + // To avoid Entry#log() returning a value that only would make sense for + // panic() to use in Entry#Panic(), we avoid the allocation by checking + // directly here. + if level <= PanicLevel { + panic(&entry) + } +} + +func (entry *Entry) Debug(args ...interface{}) { + if entry.Logger.Level >= DebugLevel { + entry.log(DebugLevel, fmt.Sprint(args...)) + } +} + +func (entry *Entry) Print(args ...interface{}) { + entry.Info(args...) +} + +func (entry *Entry) Info(args ...interface{}) { + if entry.Logger.Level >= InfoLevel { + entry.log(InfoLevel, fmt.Sprint(args...)) + } +} + +func (entry *Entry) Warn(args ...interface{}) { + if entry.Logger.Level >= WarnLevel { + entry.log(WarnLevel, fmt.Sprint(args...)) + } +} + +func (entry *Entry) Warning(args ...interface{}) { + entry.Warn(args...) +} + +func (entry *Entry) Error(args ...interface{}) { + if entry.Logger.Level >= ErrorLevel { + entry.log(ErrorLevel, fmt.Sprint(args...)) + } +} + +func (entry *Entry) Fatal(args ...interface{}) { + if entry.Logger.Level >= FatalLevel { + entry.log(FatalLevel, fmt.Sprint(args...)) + } + Exit(1) +} + +func (entry *Entry) Panic(args ...interface{}) { + if entry.Logger.Level >= PanicLevel { + entry.log(PanicLevel, fmt.Sprint(args...)) + } + panic(fmt.Sprint(args...)) +} + +// Entry Printf family functions + +func (entry *Entry) Debugf(format string, args ...interface{}) { + if entry.Logger.Level >= DebugLevel { + entry.Debug(fmt.Sprintf(format, args...)) + } +} + +func (entry *Entry) Infof(format string, args ...interface{}) { + if entry.Logger.Level >= InfoLevel { + entry.Info(fmt.Sprintf(format, args...)) + } +} + +func (entry *Entry) Printf(format string, args ...interface{}) { + entry.Infof(format, args...) +} + +func (entry *Entry) Warnf(format string, args ...interface{}) { + if entry.Logger.Level >= WarnLevel { + entry.Warn(fmt.Sprintf(format, args...)) + } +} + +func (entry *Entry) Warningf(format string, args ...interface{}) { + entry.Warnf(format, args...) +} + +func (entry *Entry) Errorf(format string, args ...interface{}) { + if entry.Logger.Level >= ErrorLevel { + entry.Error(fmt.Sprintf(format, args...)) + } +} + +func (entry *Entry) Fatalf(format string, args ...interface{}) { + if entry.Logger.Level >= FatalLevel { + entry.Fatal(fmt.Sprintf(format, args...)) + } + Exit(1) +} + +func (entry *Entry) Panicf(format string, args ...interface{}) { + if entry.Logger.Level >= PanicLevel { + entry.Panic(fmt.Sprintf(format, args...)) + } +} + +// Entry Println family functions + +func (entry *Entry) Debugln(args ...interface{}) { + if entry.Logger.Level >= DebugLevel { + entry.Debug(entry.sprintlnn(args...)) + } +} + +func (entry *Entry) Infoln(args ...interface{}) { + if entry.Logger.Level >= InfoLevel { + entry.Info(entry.sprintlnn(args...)) + } +} + +func (entry *Entry) Println(args ...interface{}) { + entry.Infoln(args...) +} + +func (entry *Entry) Warnln(args ...interface{}) { + if entry.Logger.Level >= WarnLevel { + entry.Warn(entry.sprintlnn(args...)) + } +} + +func (entry *Entry) Warningln(args ...interface{}) { + entry.Warnln(args...) +} + +func (entry *Entry) Errorln(args ...interface{}) { + if entry.Logger.Level >= ErrorLevel { + entry.Error(entry.sprintlnn(args...)) + } +} + +func (entry *Entry) Fatalln(args ...interface{}) { + if entry.Logger.Level >= FatalLevel { + entry.Fatal(entry.sprintlnn(args...)) + } + Exit(1) +} + +func (entry *Entry) Panicln(args ...interface{}) { + if entry.Logger.Level >= PanicLevel { + entry.Panic(entry.sprintlnn(args...)) + } +} + +// Sprintlnn => Sprint no newline. This is to get the behavior of how +// fmt.Sprintln where spaces are always added between operands, regardless of +// their type. Instead of vendoring the Sprintln implementation to spare a +// string allocation, we do the simplest thing. +func (entry *Entry) sprintlnn(args ...interface{}) string { + msg := fmt.Sprintln(args...) + return msg[:len(msg)-1] +} diff --git a/vendor/github.com/Sirupsen/logrus/exported.go b/vendor/github.com/Sirupsen/logrus/exported.go new file mode 100644 index 000000000..9a0120ac1 --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/exported.go @@ -0,0 +1,193 @@ +package logrus + +import ( + "io" +) + +var ( + // std is the name of the standard logger in stdlib `log` + std = New() +) + +func StandardLogger() *Logger { + return std +} + +// SetOutput sets the standard logger output. +func SetOutput(out io.Writer) { + std.mu.Lock() + defer std.mu.Unlock() + std.Out = out +} + +// SetFormatter sets the standard logger formatter. +func SetFormatter(formatter Formatter) { + std.mu.Lock() + defer std.mu.Unlock() + std.Formatter = formatter +} + +// SetLevel sets the standard logger level. +func SetLevel(level Level) { + std.mu.Lock() + defer std.mu.Unlock() + std.Level = level +} + +// GetLevel returns the standard logger level. +func GetLevel() Level { + std.mu.Lock() + defer std.mu.Unlock() + return std.Level +} + +// AddHook adds a hook to the standard logger hooks. +func AddHook(hook Hook) { + std.mu.Lock() + defer std.mu.Unlock() + std.Hooks.Add(hook) +} + +// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key. +func WithError(err error) *Entry { + return std.WithField(ErrorKey, err) +} + +// WithField creates an entry from the standard logger and adds a field to +// it. If you want multiple fields, use `WithFields`. +// +// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal +// or Panic on the Entry it returns. +func WithField(key string, value interface{}) *Entry { + return std.WithField(key, value) +} + +// WithFields creates an entry from the standard logger and adds multiple +// fields to it. This is simply a helper for `WithField`, invoking it +// once for each field. +// +// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal +// or Panic on the Entry it returns. +func WithFields(fields Fields) *Entry { + return std.WithFields(fields) +} + +// Debug logs a message at level Debug on the standard logger. +func Debug(args ...interface{}) { + std.Debug(args...) +} + +// Print logs a message at level Info on the standard logger. +func Print(args ...interface{}) { + std.Print(args...) +} + +// Info logs a message at level Info on the standard logger. +func Info(args ...interface{}) { + std.Info(args...) +} + +// Warn logs a message at level Warn on the standard logger. +func Warn(args ...interface{}) { + std.Warn(args...) +} + +// Warning logs a message at level Warn on the standard logger. +func Warning(args ...interface{}) { + std.Warning(args...) +} + +// Error logs a message at level Error on the standard logger. +func Error(args ...interface{}) { + std.Error(args...) +} + +// Panic logs a message at level Panic on the standard logger. +func Panic(args ...interface{}) { + std.Panic(args...) +} + +// Fatal logs a message at level Fatal on the standard logger. +func Fatal(args ...interface{}) { + std.Fatal(args...) +} + +// Debugf logs a message at level Debug on the standard logger. +func Debugf(format string, args ...interface{}) { + std.Debugf(format, args...) +} + +// Printf logs a message at level Info on the standard logger. +func Printf(format string, args ...interface{}) { + std.Printf(format, args...) +} + +// Infof logs a message at level Info on the standard logger. +func Infof(format string, args ...interface{}) { + std.Infof(format, args...) +} + +// Warnf logs a message at level Warn on the standard logger. +func Warnf(format string, args ...interface{}) { + std.Warnf(format, args...) +} + +// Warningf logs a message at level Warn on the standard logger. +func Warningf(format string, args ...interface{}) { + std.Warningf(format, args...) +} + +// Errorf logs a message at level Error on the standard logger. +func Errorf(format string, args ...interface{}) { + std.Errorf(format, args...) +} + +// Panicf logs a message at level Panic on the standard logger. +func Panicf(format string, args ...interface{}) { + std.Panicf(format, args...) +} + +// Fatalf logs a message at level Fatal on the standard logger. +func Fatalf(format string, args ...interface{}) { + std.Fatalf(format, args...) +} + +// Debugln logs a message at level Debug on the standard logger. +func Debugln(args ...interface{}) { + std.Debugln(args...) +} + +// Println logs a message at level Info on the standard logger. +func Println(args ...interface{}) { + std.Println(args...) +} + +// Infoln logs a message at level Info on the standard logger. +func Infoln(args ...interface{}) { + std.Infoln(args...) +} + +// Warnln logs a message at level Warn on the standard logger. +func Warnln(args ...interface{}) { + std.Warnln(args...) +} + +// Warningln logs a message at level Warn on the standard logger. +func Warningln(args ...interface{}) { + std.Warningln(args...) +} + +// Errorln logs a message at level Error on the standard logger. +func Errorln(args ...interface{}) { + std.Errorln(args...) +} + +// Panicln logs a message at level Panic on the standard logger. +func Panicln(args ...interface{}) { + std.Panicln(args...) +} + +// Fatalln logs a message at level Fatal on the standard logger. +func Fatalln(args ...interface{}) { + std.Fatalln(args...) +} diff --git a/vendor/github.com/Sirupsen/logrus/formatter.go b/vendor/github.com/Sirupsen/logrus/formatter.go new file mode 100644 index 000000000..b5fbe934d --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/formatter.go @@ -0,0 +1,45 @@ +package logrus + +import "time" + +const DefaultTimestampFormat = time.RFC3339 + +// The Formatter interface is used to implement a custom Formatter. It takes an +// `Entry`. It exposes all the fields, including the default ones: +// +// * `entry.Data["msg"]`. The message passed from Info, Warn, Error .. +// * `entry.Data["time"]`. The timestamp. +// * `entry.Data["level"]. The level the entry was logged at. +// +// Any additional fields added with `WithField` or `WithFields` are also in +// `entry.Data`. Format is expected to return an array of bytes which are then +// logged to `logger.Out`. +type Formatter interface { + Format(*Entry) ([]byte, error) +} + +// This is to not silently overwrite `time`, `msg` and `level` fields when +// dumping it. If this code wasn't there doing: +// +// logrus.WithField("level", 1).Info("hello") +// +// Would just silently drop the user provided level. Instead with this code +// it'll logged as: +// +// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."} +// +// It's not exported because it's still using Data in an opinionated way. It's to +// avoid code duplication between the two default formatters. +func prefixFieldClashes(data Fields) { + if t, ok := data["time"]; ok { + data["fields.time"] = t + } + + if m, ok := data["msg"]; ok { + data["fields.msg"] = m + } + + if l, ok := data["level"]; ok { + data["fields.level"] = l + } +} diff --git a/vendor/github.com/Sirupsen/logrus/hooks.go b/vendor/github.com/Sirupsen/logrus/hooks.go new file mode 100644 index 000000000..3f151cdc3 --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/hooks.go @@ -0,0 +1,34 @@ +package logrus + +// A hook to be fired when logging on the logging levels returned from +// `Levels()` on your implementation of the interface. Note that this is not +// fired in a goroutine or a channel with workers, you should handle such +// functionality yourself if your call is non-blocking and you don't wish for +// the logging calls for levels returned from `Levels()` to block. +type Hook interface { + Levels() []Level + Fire(*Entry) error +} + +// Internal type for storing the hooks on a logger instance. +type LevelHooks map[Level][]Hook + +// Add a hook to an instance of logger. This is called with +// `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface. +func (hooks LevelHooks) Add(hook Hook) { + for _, level := range hook.Levels() { + hooks[level] = append(hooks[level], hook) + } +} + +// Fire all the hooks for the passed level. Used by `entry.log` to fire +// appropriate hooks for a log entry. +func (hooks LevelHooks) Fire(level Level, entry *Entry) error { + for _, hook := range hooks[level] { + if err := hook.Fire(entry); err != nil { + return err + } + } + + return nil +} diff --git a/vendor/github.com/Sirupsen/logrus/json_formatter.go b/vendor/github.com/Sirupsen/logrus/json_formatter.go new file mode 100644 index 000000000..266554e9f --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/json_formatter.go @@ -0,0 +1,74 @@ +package logrus + +import ( + "encoding/json" + "fmt" +) + +type fieldKey string +type FieldMap map[fieldKey]string + +const ( + FieldKeyMsg = "msg" + FieldKeyLevel = "level" + FieldKeyTime = "time" +) + +func (f FieldMap) resolve(key fieldKey) string { + if k, ok := f[key]; ok { + return k + } + + return string(key) +} + +type JSONFormatter struct { + // TimestampFormat sets the format used for marshaling timestamps. + TimestampFormat string + + // DisableTimestamp allows disabling automatic timestamps in output + DisableTimestamp bool + + // FieldMap allows users to customize the names of keys for various fields. + // As an example: + // formatter := &JSONFormatter{ + // FieldMap: FieldMap{ + // FieldKeyTime: "@timestamp", + // FieldKeyLevel: "@level", + // FieldKeyLevel: "@message", + // }, + // } + FieldMap FieldMap +} + +func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { + data := make(Fields, len(entry.Data)+3) + for k, v := range entry.Data { + switch v := v.(type) { + case error: + // Otherwise errors are ignored by `encoding/json` + // https://github.com/Sirupsen/logrus/issues/137 + data[k] = v.Error() + default: + data[k] = v + } + } + prefixFieldClashes(data) + + timestampFormat := f.TimestampFormat + if timestampFormat == "" { + timestampFormat = DefaultTimestampFormat + } + + if !f.DisableTimestamp { + data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat) + } + data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message + data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String() + + serialized, err := json.Marshal(data) + if err != nil { + return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) + } + return append(serialized, '\n'), nil +} diff --git a/vendor/github.com/Sirupsen/logrus/logger.go b/vendor/github.com/Sirupsen/logrus/logger.go new file mode 100644 index 000000000..b769f3d35 --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/logger.go @@ -0,0 +1,308 @@ +package logrus + +import ( + "io" + "os" + "sync" +) + +type Logger struct { + // The logs are `io.Copy`'d to this in a mutex. It's common to set this to a + // file, or leave it default which is `os.Stderr`. You can also set this to + // something more adventorous, such as logging to Kafka. + Out io.Writer + // Hooks for the logger instance. These allow firing events based on logging + // levels and log entries. For example, to send errors to an error tracking + // service, log to StatsD or dump the core on fatal errors. + Hooks LevelHooks + // All log entries pass through the formatter before logged to Out. The + // included formatters are `TextFormatter` and `JSONFormatter` for which + // TextFormatter is the default. In development (when a TTY is attached) it + // logs with colors, but to a file it wouldn't. You can easily implement your + // own that implements the `Formatter` interface, see the `README` or included + // formatters for examples. + Formatter Formatter + // The logging level the logger should log at. This is typically (and defaults + // to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be + // logged. `logrus.Debug` is useful in + Level Level + // Used to sync writing to the log. Locking is enabled by Default + mu MutexWrap + // Reusable empty entry + entryPool sync.Pool +} + +type MutexWrap struct { + lock sync.Mutex + disabled bool +} + +func (mw *MutexWrap) Lock() { + if !mw.disabled { + mw.lock.Lock() + } +} + +func (mw *MutexWrap) Unlock() { + if !mw.disabled { + mw.lock.Unlock() + } +} + +func (mw *MutexWrap) Disable() { + mw.disabled = true +} + +// Creates a new logger. Configuration should be set by changing `Formatter`, +// `Out` and `Hooks` directly on the default logger instance. You can also just +// instantiate your own: +// +// var log = &Logger{ +// Out: os.Stderr, +// Formatter: new(JSONFormatter), +// Hooks: make(LevelHooks), +// Level: logrus.DebugLevel, +// } +// +// It's recommended to make this a global instance called `log`. +func New() *Logger { + return &Logger{ + Out: os.Stderr, + Formatter: new(TextFormatter), + Hooks: make(LevelHooks), + Level: InfoLevel, + } +} + +func (logger *Logger) newEntry() *Entry { + entry, ok := logger.entryPool.Get().(*Entry) + if ok { + return entry + } + return NewEntry(logger) +} + +func (logger *Logger) releaseEntry(entry *Entry) { + logger.entryPool.Put(entry) +} + +// Adds a field to the log entry, note that it doesn't log until you call +// Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry. +// If you want multiple fields, use `WithFields`. +func (logger *Logger) WithField(key string, value interface{}) *Entry { + entry := logger.newEntry() + defer logger.releaseEntry(entry) + return entry.WithField(key, value) +} + +// Adds a struct of fields to the log entry. All it does is call `WithField` for +// each `Field`. +func (logger *Logger) WithFields(fields Fields) *Entry { + entry := logger.newEntry() + defer logger.releaseEntry(entry) + return entry.WithFields(fields) +} + +// Add an error as single field to the log entry. All it does is call +// `WithError` for the given `error`. +func (logger *Logger) WithError(err error) *Entry { + entry := logger.newEntry() + defer logger.releaseEntry(entry) + return entry.WithError(err) +} + +func (logger *Logger) Debugf(format string, args ...interface{}) { + if logger.Level >= DebugLevel { + entry := logger.newEntry() + entry.Debugf(format, args...) + logger.releaseEntry(entry) + } +} + +func (logger *Logger) Infof(format string, args ...interface{}) { + if logger.Level >= InfoLevel { + entry := logger.newEntry() + entry.Infof(format, args...) + logger.releaseEntry(entry) + } +} + +func (logger *Logger) Printf(format string, args ...interface{}) { + entry := logger.newEntry() + entry.Printf(format, args...) + logger.releaseEntry(entry) +} + +func (logger *Logger) Warnf(format string, args ...interface{}) { + if logger.Level >= WarnLevel { + entry := logger.newEntry() + entry.Warnf(format, args...) + logger.releaseEntry(entry) + } +} + +func (logger *Logger) Warningf(format string, args ...interface{}) { + if logger.Level >= WarnLevel { + entry := logger.newEntry() + entry.Warnf(format, args...) + logger.releaseEntry(entry) + } +} + +func (logger *Logger) Errorf(format string, args ...interface{}) { + if logger.Level >= ErrorLevel { + entry := logger.newEntry() + entry.Errorf(format, args...) + logger.releaseEntry(entry) + } +} + +func (logger *Logger) Fatalf(format string, args ...interface{}) { + if logger.Level >= FatalLevel { + entry := logger.newEntry() + entry.Fatalf(format, args...) + logger.releaseEntry(entry) + } + Exit(1) +} + +func (logger *Logger) Panicf(format string, args ...interface{}) { + if logger.Level >= PanicLevel { + entry := logger.newEntry() + entry.Panicf(format, args...) + logger.releaseEntry(entry) + } +} + +func (logger *Logger) Debug(args ...interface{}) { + if logger.Level >= DebugLevel { + entry := logger.newEntry() + entry.Debug(args...) + logger.releaseEntry(entry) + } +} + +func (logger *Logger) Info(args ...interface{}) { + if logger.Level >= InfoLevel { + entry := logger.newEntry() + entry.Info(args...) + logger.releaseEntry(entry) + } +} + +func (logger *Logger) Print(args ...interface{}) { + entry := logger.newEntry() + entry.Info(args...) + logger.releaseEntry(entry) +} + +func (logger *Logger) Warn(args ...interface{}) { + if logger.Level >= WarnLevel { + entry := logger.newEntry() + entry.Warn(args...) + logger.releaseEntry(entry) + } +} + +func (logger *Logger) Warning(args ...interface{}) { + if logger.Level >= WarnLevel { + entry := logger.newEntry() + entry.Warn(args...) + logger.releaseEntry(entry) + } +} + +func (logger *Logger) Error(args ...interface{}) { + if logger.Level >= ErrorLevel { + entry := logger.newEntry() + entry.Error(args...) + logger.releaseEntry(entry) + } +} + +func (logger *Logger) Fatal(args ...interface{}) { + if logger.Level >= FatalLevel { + entry := logger.newEntry() + entry.Fatal(args...) + logger.releaseEntry(entry) + } + Exit(1) +} + +func (logger *Logger) Panic(args ...interface{}) { + if logger.Level >= PanicLevel { + entry := logger.newEntry() + entry.Panic(args...) + logger.releaseEntry(entry) + } +} + +func (logger *Logger) Debugln(args ...interface{}) { + if logger.Level >= DebugLevel { + entry := logger.newEntry() + entry.Debugln(args...) + logger.releaseEntry(entry) + } +} + +func (logger *Logger) Infoln(args ...interface{}) { + if logger.Level >= InfoLevel { + entry := logger.newEntry() + entry.Infoln(args...) + logger.releaseEntry(entry) + } +} + +func (logger *Logger) Println(args ...interface{}) { + entry := logger.newEntry() + entry.Println(args...) + logger.releaseEntry(entry) +} + +func (logger *Logger) Warnln(args ...interface{}) { + if logger.Level >= WarnLevel { + entry := logger.newEntry() + entry.Warnln(args...) + logger.releaseEntry(entry) + } +} + +func (logger *Logger) Warningln(args ...interface{}) { + if logger.Level >= WarnLevel { + entry := logger.newEntry() + entry.Warnln(args...) + logger.releaseEntry(entry) + } +} + +func (logger *Logger) Errorln(args ...interface{}) { + if logger.Level >= ErrorLevel { + entry := logger.newEntry() + entry.Errorln(args...) + logger.releaseEntry(entry) + } +} + +func (logger *Logger) Fatalln(args ...interface{}) { + if logger.Level >= FatalLevel { + entry := logger.newEntry() + entry.Fatalln(args...) + logger.releaseEntry(entry) + } + Exit(1) +} + +func (logger *Logger) Panicln(args ...interface{}) { + if logger.Level >= PanicLevel { + entry := logger.newEntry() + entry.Panicln(args...) + logger.releaseEntry(entry) + } +} + +//When file is opened with appending mode, it's safe to +//write concurrently to a file (within 4k message on Linux). +//In these cases user can choose to disable the lock. +func (logger *Logger) SetNoLock() { + logger.mu.Disable() +} diff --git a/vendor/github.com/Sirupsen/logrus/logrus.go b/vendor/github.com/Sirupsen/logrus/logrus.go new file mode 100644 index 000000000..e59669111 --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/logrus.go @@ -0,0 +1,143 @@ +package logrus + +import ( + "fmt" + "log" + "strings" +) + +// Fields type, used to pass to `WithFields`. +type Fields map[string]interface{} + +// Level type +type Level uint8 + +// Convert the Level to a string. E.g. PanicLevel becomes "panic". +func (level Level) String() string { + switch level { + case DebugLevel: + return "debug" + case InfoLevel: + return "info" + case WarnLevel: + return "warning" + case ErrorLevel: + return "error" + case FatalLevel: + return "fatal" + case PanicLevel: + return "panic" + } + + return "unknown" +} + +// ParseLevel takes a string level and returns the Logrus log level constant. +func ParseLevel(lvl string) (Level, error) { + switch strings.ToLower(lvl) { + case "panic": + return PanicLevel, nil + case "fatal": + return FatalLevel, nil + case "error": + return ErrorLevel, nil + case "warn", "warning": + return WarnLevel, nil + case "info": + return InfoLevel, nil + case "debug": + return DebugLevel, nil + } + + var l Level + return l, fmt.Errorf("not a valid logrus Level: %q", lvl) +} + +// A constant exposing all logging levels +var AllLevels = []Level{ + PanicLevel, + FatalLevel, + ErrorLevel, + WarnLevel, + InfoLevel, + DebugLevel, +} + +// These are the different logging levels. You can set the logging level to log +// on your instance of logger, obtained with `logrus.New()`. +const ( + // PanicLevel level, highest level of severity. Logs and then calls panic with the + // message passed to Debug, Info, ... + PanicLevel Level = iota + // FatalLevel level. Logs and then calls `os.Exit(1)`. It will exit even if the + // logging level is set to Panic. + FatalLevel + // ErrorLevel level. Logs. Used for errors that should definitely be noted. + // Commonly used for hooks to send errors to an error tracking service. + ErrorLevel + // WarnLevel level. Non-critical entries that deserve eyes. + WarnLevel + // InfoLevel level. General operational entries about what's going on inside the + // application. + InfoLevel + // DebugLevel level. Usually only enabled when debugging. Very verbose logging. + DebugLevel +) + +// Won't compile if StdLogger can't be realized by a log.Logger +var ( + _ StdLogger = &log.Logger{} + _ StdLogger = &Entry{} + _ StdLogger = &Logger{} +) + +// StdLogger is what your logrus-enabled library should take, that way +// it'll accept a stdlib logger and a logrus logger. There's no standard +// interface, this is the closest we get, unfortunately. +type StdLogger interface { + Print(...interface{}) + Printf(string, ...interface{}) + Println(...interface{}) + + Fatal(...interface{}) + Fatalf(string, ...interface{}) + Fatalln(...interface{}) + + Panic(...interface{}) + Panicf(string, ...interface{}) + Panicln(...interface{}) +} + +// The FieldLogger interface generalizes the Entry and Logger types +type FieldLogger interface { + WithField(key string, value interface{}) *Entry + WithFields(fields Fields) *Entry + WithError(err error) *Entry + + Debugf(format string, args ...interface{}) + Infof(format string, args ...interface{}) + Printf(format string, args ...interface{}) + Warnf(format string, args ...interface{}) + Warningf(format string, args ...interface{}) + Errorf(format string, args ...interface{}) + Fatalf(format string, args ...interface{}) + Panicf(format string, args ...interface{}) + + Debug(args ...interface{}) + Info(args ...interface{}) + Print(args ...interface{}) + Warn(args ...interface{}) + Warning(args ...interface{}) + Error(args ...interface{}) + Fatal(args ...interface{}) + Panic(args ...interface{}) + + Debugln(args ...interface{}) + Infoln(args ...interface{}) + Println(args ...interface{}) + Warnln(args ...interface{}) + Warningln(args ...interface{}) + Errorln(args ...interface{}) + Fatalln(args ...interface{}) + Panicln(args ...interface{}) +} diff --git a/vendor/github.com/Sirupsen/logrus/terminal_bsd.go b/vendor/github.com/Sirupsen/logrus/terminal_bsd.go new file mode 100644 index 000000000..5f6be4d3c --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/terminal_bsd.go @@ -0,0 +1,10 @@ +// +build darwin freebsd openbsd netbsd dragonfly +// +build !appengine + +package logrus + +import "syscall" + +const ioctlReadTermios = syscall.TIOCGETA + +type Termios syscall.Termios diff --git a/vendor/github.com/Sirupsen/logrus/terminal_linux.go b/vendor/github.com/Sirupsen/logrus/terminal_linux.go new file mode 100644 index 000000000..308160ca8 --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/terminal_linux.go @@ -0,0 +1,14 @@ +// Based on ssh/terminal: +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !appengine + +package logrus + +import "syscall" + +const ioctlReadTermios = syscall.TCGETS + +type Termios syscall.Termios diff --git a/vendor/github.com/Sirupsen/logrus/terminal_notwindows.go b/vendor/github.com/Sirupsen/logrus/terminal_notwindows.go new file mode 100644 index 000000000..190297abf --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/terminal_notwindows.go @@ -0,0 +1,28 @@ +// Based on ssh/terminal: +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux darwin freebsd openbsd netbsd dragonfly +// +build !appengine + +package logrus + +import ( + "io" + "os" + "syscall" + "unsafe" +) + +// IsTerminal returns true if stderr's file descriptor is a terminal. +func IsTerminal(f io.Writer) bool { + var termios Termios + switch v := f.(type) { + case *os.File: + _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(v.Fd()), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) + return err == 0 + default: + return false + } +} diff --git a/vendor/github.com/Sirupsen/logrus/terminal_solaris.go b/vendor/github.com/Sirupsen/logrus/terminal_solaris.go new file mode 100644 index 000000000..3c86b1abe --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/terminal_solaris.go @@ -0,0 +1,21 @@ +// +build solaris,!appengine + +package logrus + +import ( + "io" + "os" + + "golang.org/x/sys/unix" +) + +// IsTerminal returns true if the given file descriptor is a terminal. +func IsTerminal(f io.Writer) bool { + switch v := f.(type) { + case *os.File: + _, err := unix.IoctlGetTermios(int(v.Fd()), unix.TCGETA) + return err == nil + default: + return false + } +} diff --git a/vendor/github.com/Sirupsen/logrus/terminal_windows.go b/vendor/github.com/Sirupsen/logrus/terminal_windows.go new file mode 100644 index 000000000..05d2f91f1 --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/terminal_windows.go @@ -0,0 +1,33 @@ +// Based on ssh/terminal: +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows,!appengine + +package logrus + +import ( + "io" + "os" + "syscall" + "unsafe" +) + +var kernel32 = syscall.NewLazyDLL("kernel32.dll") + +var ( + procGetConsoleMode = kernel32.NewProc("GetConsoleMode") +) + +// IsTerminal returns true if stderr's file descriptor is a terminal. +func IsTerminal(f io.Writer) bool { + switch v := f.(type) { + case *os.File: + var st uint32 + r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(v.Fd()), uintptr(unsafe.Pointer(&st)), 0) + return r != 0 && e == 0 + default: + return false + } +} diff --git a/vendor/github.com/Sirupsen/logrus/text_formatter.go b/vendor/github.com/Sirupsen/logrus/text_formatter.go new file mode 100644 index 000000000..ba8885406 --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/text_formatter.go @@ -0,0 +1,189 @@ +package logrus + +import ( + "bytes" + "fmt" + "sort" + "strings" + "sync" + "time" +) + +const ( + nocolor = 0 + red = 31 + green = 32 + yellow = 33 + blue = 34 + gray = 37 +) + +var ( + baseTimestamp time.Time +) + +func init() { + baseTimestamp = time.Now() +} + +type TextFormatter struct { + // Set to true to bypass checking for a TTY before outputting colors. + ForceColors bool + + // Force disabling colors. + DisableColors bool + + // Disable timestamp logging. useful when output is redirected to logging + // system that already adds timestamps. + DisableTimestamp bool + + // Enable logging the full timestamp when a TTY is attached instead of just + // the time passed since beginning of execution. + FullTimestamp bool + + // TimestampFormat to use for display when a full timestamp is printed + TimestampFormat string + + // The fields are sorted by default for a consistent output. For applications + // that log extremely frequently and don't use the JSON formatter this may not + // be desired. + DisableSorting bool + + // QuoteEmptyFields will wrap empty fields in quotes if true + QuoteEmptyFields bool + + // QuoteCharacter can be set to the override the default quoting character " + // with something else. For example: ', or `. + QuoteCharacter string + + // Whether the logger's out is to a terminal + isTerminal bool + + sync.Once +} + +func (f *TextFormatter) init(entry *Entry) { + if len(f.QuoteCharacter) == 0 { + f.QuoteCharacter = "\"" + } + if entry.Logger != nil { + f.isTerminal = IsTerminal(entry.Logger.Out) + } +} + +func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { + var b *bytes.Buffer + keys := make([]string, 0, len(entry.Data)) + for k := range entry.Data { + keys = append(keys, k) + } + + if !f.DisableSorting { + sort.Strings(keys) + } + if entry.Buffer != nil { + b = entry.Buffer + } else { + b = &bytes.Buffer{} + } + + prefixFieldClashes(entry.Data) + + f.Do(func() { f.init(entry) }) + + isColored := (f.ForceColors || f.isTerminal) && !f.DisableColors + + timestampFormat := f.TimestampFormat + if timestampFormat == "" { + timestampFormat = DefaultTimestampFormat + } + if isColored { + f.printColored(b, entry, keys, timestampFormat) + } else { + if !f.DisableTimestamp { + f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat)) + } + f.appendKeyValue(b, "level", entry.Level.String()) + if entry.Message != "" { + f.appendKeyValue(b, "msg", entry.Message) + } + for _, key := range keys { + f.appendKeyValue(b, key, entry.Data[key]) + } + } + + b.WriteByte('\n') + return b.Bytes(), nil +} + +func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) { + var levelColor int + switch entry.Level { + case DebugLevel: + levelColor = gray + case WarnLevel: + levelColor = yellow + case ErrorLevel, FatalLevel, PanicLevel: + levelColor = red + default: + levelColor = blue + } + + levelText := strings.ToUpper(entry.Level.String())[0:4] + + if f.DisableTimestamp { + fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m %-44s ", levelColor, levelText, entry.Message) + } else if !f.FullTimestamp { + fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), entry.Message) + } else { + fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message) + } + for _, k := range keys { + v := entry.Data[k] + fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k) + f.appendValue(b, v) + } +} + +func (f *TextFormatter) needsQuoting(text string) bool { + if f.QuoteEmptyFields && len(text) == 0 { + return true + } + for _, ch := range text { + if !((ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z') || + (ch >= '0' && ch <= '9') || + ch == '-' || ch == '.') { + return true + } + } + return false +} + +func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) { + + b.WriteString(key) + b.WriteByte('=') + f.appendValue(b, value) + b.WriteByte(' ') +} + +func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) { + switch value := value.(type) { + case string: + if !f.needsQuoting(value) { + b.WriteString(value) + } else { + fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, value, f.QuoteCharacter) + } + case error: + errmsg := value.Error() + if !f.needsQuoting(errmsg) { + b.WriteString(errmsg) + } else { + fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, errmsg, f.QuoteCharacter) + } + default: + fmt.Fprint(b, value) + } +} diff --git a/vendor/github.com/Sirupsen/logrus/writer.go b/vendor/github.com/Sirupsen/logrus/writer.go new file mode 100644 index 000000000..7bdebedc6 --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/writer.go @@ -0,0 +1,62 @@ +package logrus + +import ( + "bufio" + "io" + "runtime" +) + +func (logger *Logger) Writer() *io.PipeWriter { + return logger.WriterLevel(InfoLevel) +} + +func (logger *Logger) WriterLevel(level Level) *io.PipeWriter { + return NewEntry(logger).WriterLevel(level) +} + +func (entry *Entry) Writer() *io.PipeWriter { + return entry.WriterLevel(InfoLevel) +} + +func (entry *Entry) WriterLevel(level Level) *io.PipeWriter { + reader, writer := io.Pipe() + + var printFunc func(args ...interface{}) + + switch level { + case DebugLevel: + printFunc = entry.Debug + case InfoLevel: + printFunc = entry.Info + case WarnLevel: + printFunc = entry.Warn + case ErrorLevel: + printFunc = entry.Error + case FatalLevel: + printFunc = entry.Fatal + case PanicLevel: + printFunc = entry.Panic + default: + printFunc = entry.Print + } + + go entry.writerScanner(reader, printFunc) + runtime.SetFinalizer(writer, writerFinalizer) + + return writer +} + +func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) { + scanner := bufio.NewScanner(reader) + for scanner.Scan() { + printFunc(scanner.Text()) + } + if err := scanner.Err(); err != nil { + entry.Errorf("Error while reading from Writer: %s", err) + } + reader.Close() +} + +func writerFinalizer(writer *io.PipeWriter) { + writer.Close() +} diff --git a/vendor/github.com/creack/goselect/Dockerfile b/vendor/github.com/creack/goselect/Dockerfile new file mode 100644 index 000000000..d03b5a9d9 --- /dev/null +++ b/vendor/github.com/creack/goselect/Dockerfile @@ -0,0 +1,5 @@ +FROM google/golang:stable +MAINTAINER Guillaume J. Charmes +CMD /tmp/a.out +ADD . /src +RUN cd /src && go build -o /tmp/a.out diff --git a/vendor/github.com/creack/goselect/LICENSE b/vendor/github.com/creack/goselect/LICENSE new file mode 100644 index 000000000..95c600af1 --- /dev/null +++ b/vendor/github.com/creack/goselect/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2014 Guillaume J. Charmes + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/vendor/github.com/creack/goselect/README.md b/vendor/github.com/creack/goselect/README.md new file mode 100644 index 000000000..d5d06510e --- /dev/null +++ b/vendor/github.com/creack/goselect/README.md @@ -0,0 +1,30 @@ +# go-select + +select(2) implementation in Go + +## Supported platforms + +| | 386 | amd64 | arm | arm64 | +|---------------|-----|-------|-----|-------| +| **linux** | yes | yes | yes | yes | +| **darwin** | yes | yes | n/a | ?? | +| **freebsd** | yes | yes | yes | ?? | +| **openbsd** | yes | yes | yes | ?? | +| **netbsd** | yes | yes | yes | ?? | +| **dragonfly** | n/a | yes | n/a | ?? | +| **solaris** | n/a | no | n/a | ?? | +| **plan9** | no | no | n/a | ?? | +| **windows** | yes | yes | n/a | ?? | +| **android** | n/a | n/a | no | ?? | + +*n/a: platform not supported by Go + +Go on `plan9` and `solaris` do not implement `syscall.Select` not `syscall.SYS_SELECT`. + +## Cross compile + +Using davecheney's https://github.com/davecheney/golang-crosscompile + +``` +export PLATFORMS="darwin/386 darwin/amd64 freebsd/386 freebsd/amd64 freebsd/arm linux/386 linux/amd64 linux/arm windows/386 windows/amd64 openbsd/386 openbsd/amd64 netbsd/386 netbsd/amd64 dragonfly/amd64 plan9/386 plan9/amd64 solaris/amd64" +``` diff --git a/vendor/github.com/creack/goselect/fdset.go b/vendor/github.com/creack/goselect/fdset.go new file mode 100644 index 000000000..2ff3f6c7a --- /dev/null +++ b/vendor/github.com/creack/goselect/fdset.go @@ -0,0 +1,33 @@ +// +build !freebsd,!windows,!plan9 + +package goselect + +import "syscall" + +const FD_SETSIZE = syscall.FD_SETSIZE + +// FDSet wraps syscall.FdSet with convenience methods +type FDSet syscall.FdSet + +// Set adds the fd to the set +func (fds *FDSet) Set(fd uintptr) { + fds.Bits[fd/NFDBITS] |= (1 << (fd % NFDBITS)) +} + +// Clear remove the fd from the set +func (fds *FDSet) Clear(fd uintptr) { + fds.Bits[fd/NFDBITS] &^= (1 << (fd % NFDBITS)) +} + +// IsSet check if the given fd is set +func (fds *FDSet) IsSet(fd uintptr) bool { + return fds.Bits[fd/NFDBITS]&(1<<(fd%NFDBITS)) != 0 +} + +// Keep a null set to avoid reinstatiation +var nullFdSet = &FDSet{} + +// Zero empties the Set +func (fds *FDSet) Zero() { + copy(fds.Bits[:], (nullFdSet).Bits[:]) +} diff --git a/vendor/github.com/creack/goselect/fdset_32.go b/vendor/github.com/creack/goselect/fdset_32.go new file mode 100644 index 000000000..8b90d21a9 --- /dev/null +++ b/vendor/github.com/creack/goselect/fdset_32.go @@ -0,0 +1,10 @@ +// +build darwin openbsd netbsd 386 arm + +package goselect + +// darwin, netbsd and openbsd uses uint32 on both amd64 and 386 + +const ( + // NFDBITS is the amount of bits per mask + NFDBITS = 4 * 8 +) diff --git a/vendor/github.com/creack/goselect/fdset_64.go b/vendor/github.com/creack/goselect/fdset_64.go new file mode 100644 index 000000000..4e032e4ea --- /dev/null +++ b/vendor/github.com/creack/goselect/fdset_64.go @@ -0,0 +1,11 @@ +// +build !darwin,!netbsd,!openbsd +// +build amd64 arm64 + +package goselect + +// darwin, netbsd and openbsd uses uint32 on both amd64 and 386 + +const ( + // NFDBITS is the amount of bits per mask + NFDBITS = 8 * 8 +) diff --git a/vendor/github.com/creack/goselect/fdset_doc.go b/vendor/github.com/creack/goselect/fdset_doc.go new file mode 100644 index 000000000..d9f15a1ce --- /dev/null +++ b/vendor/github.com/creack/goselect/fdset_doc.go @@ -0,0 +1,93 @@ +package goselect + +/** +From: XCode's MacOSX10.10.sdk/System/Library/Frameworks/Kernel.framework/Versions/A/Headers/sys/select.h +-- +// darwin/amd64 / 386 +sizeof(__int32_t) == 4 +-- + +typedef __int32_t __fd_mask; + +#define FD_SETSIZE 1024 +#define __NFDBITS (sizeof(__fd_mask) * 8) +#define __howmany(x, y) ((((x) % (y)) == 0) ? ((x) / (y)) : (((x) / (y)) + 1)) + +typedef struct fd_set { + __fd_mask fds_bits[__howmany(__FD_SETSIZE, __NFDBITS)]; +} fd_set; + +#define __FD_MASK(n) ((__fd_mask)1 << ((n) % __NFDBITS)) +#define FD_SET(n, p) ((p)->fds_bits[(n)/__NFDBITS] |= __FD_MASK(n)) +#define FD_CLR(n, p) ((p)->fds_bits[(n)/__NFDBITS] &= ~__FD_MASK(n)) +#define FD_ISSET(n, p) (((p)->fds_bits[(n)/__NFDBITS] & __FD_MASK(n)) != 0) +*/ + +/** +From: /usr/include/i386-linux-gnu/sys/select.h +-- +// linux/i686 +sizeof(long int) == 4 +-- + +typedef long int __fd_mask; + +#define FD_SETSIZE 1024 +#define __NFDBITS (sizeof(__fd_mask) * 8) + + +typedef struct fd_set { + __fd_mask fds_bits[__FD_SETSIZE / __NFDBITS]; +} fd_set; + +#define __FD_MASK(n) ((__fd_mask)1 << ((n) % __NFDBITS)) +#define FD_SET(n, p) ((p)->fds_bits[(n)/__NFDBITS] |= __FD_MASK(n)) +#define FD_CLR(n, p) ((p)->fds_bits[(n)/__NFDBITS] &= ~__FD_MASK(n)) +#define FD_ISSET(n, p) (((p)->fds_bits[(n)/__NFDBITS] & __FD_MASK(n)) != 0) +*/ + +/** +From: /usr/include/x86_64-linux-gnu/sys/select.h +-- +// linux/amd64 +sizeof(long int) == 8 +-- + +typedef long int __fd_mask; + +#define FD_SETSIZE 1024 +#define __NFDBITS (sizeof(__fd_mask) * 8) + + +typedef struct fd_set { + __fd_mask fds_bits[__FD_SETSIZE / __NFDBITS]; +} fd_set; + +#define __FD_MASK(n) ((__fd_mask)1 << ((n) % __NFDBITS)) +#define FD_SET(n, p) ((p)->fds_bits[(n)/__NFDBITS] |= __FD_MASK(n)) +#define FD_CLR(n, p) ((p)->fds_bits[(n)/__NFDBITS] &= ~__FD_MASK(n)) +#define FD_ISSET(n, p) (((p)->fds_bits[(n)/__NFDBITS] & __FD_MASK(n)) != 0) +*/ + +/** +From: /usr/include/sys/select.h +-- +// freebsd/amd64 +sizeof(unsigned long) == 8 +-- + +typedef unsigned long __fd_mask; + +#define FD_SETSIZE 1024U +#define __NFDBITS (sizeof(__fd_mask) * 8) +#define _howmany(x, y) (((x) + ((y) - 1)) / (y)) + +typedef struct fd_set { + __fd_mask fds_bits[_howmany(FD_SETSIZE, __NFDBITS)]; +} fd_set; + +#define __FD_MASK(n) ((__fd_mask)1 << ((n) % __NFDBITS)) +#define FD_SET(n, p) ((p)->fds_bits[(n)/__NFDBITS] |= __FD_MASK(n)) +#define FD_CLR(n, p) ((p)->fds_bits[(n)/__NFDBITS] &= ~__FD_MASK(n)) +#define FD_ISSET(n, p) (((p)->fds_bits[(n)/__NFDBITS] & __FD_MASK(n)) != 0) +*/ diff --git a/vendor/github.com/creack/goselect/fdset_freebsd.go b/vendor/github.com/creack/goselect/fdset_freebsd.go new file mode 100644 index 000000000..03f7b9127 --- /dev/null +++ b/vendor/github.com/creack/goselect/fdset_freebsd.go @@ -0,0 +1,33 @@ +// +build freebsd + +package goselect + +import "syscall" + +const FD_SETSIZE = syscall.FD_SETSIZE + +// FDSet wraps syscall.FdSet with convenience methods +type FDSet syscall.FdSet + +// Set adds the fd to the set +func (fds *FDSet) Set(fd uintptr) { + fds.X__fds_bits[fd/NFDBITS] |= (1 << (fd % NFDBITS)) +} + +// Clear remove the fd from the set +func (fds *FDSet) Clear(fd uintptr) { + fds.X__fds_bits[fd/NFDBITS] &^= (1 << (fd % NFDBITS)) +} + +// IsSet check if the given fd is set +func (fds *FDSet) IsSet(fd uintptr) bool { + return fds.X__fds_bits[fd/NFDBITS]&(1<<(fd%NFDBITS)) != 0 +} + +// Keep a null set to avoid reinstatiation +var nullFdSet = &FDSet{} + +// Zero empties the Set +func (fds *FDSet) Zero() { + copy(fds.X__fds_bits[:], (nullFdSet).X__fds_bits[:]) +} diff --git a/vendor/github.com/creack/goselect/fdset_unsupported.go b/vendor/github.com/creack/goselect/fdset_unsupported.go new file mode 100644 index 000000000..cbd8d5880 --- /dev/null +++ b/vendor/github.com/creack/goselect/fdset_unsupported.go @@ -0,0 +1,20 @@ +// +build plan9 + +package goselect + +const FD_SETSIZE = 0 + +// FDSet wraps syscall.FdSet with convenience methods +type FDSet struct{} + +// Set adds the fd to the set +func (fds *FDSet) Set(fd uintptr) {} + +// Clear remove the fd from the set +func (fds *FDSet) Clear(fd uintptr) {} + +// IsSet check if the given fd is set +func (fds *FDSet) IsSet(fd uintptr) bool { return false } + +// Zero empties the Set +func (fds *FDSet) Zero() {} diff --git a/vendor/github.com/creack/goselect/fdset_windows.go b/vendor/github.com/creack/goselect/fdset_windows.go new file mode 100644 index 000000000..ee3491975 --- /dev/null +++ b/vendor/github.com/creack/goselect/fdset_windows.go @@ -0,0 +1,57 @@ +// +build windows + +package goselect + +import "syscall" + +const FD_SETSIZE = 64 + +// FDSet extracted from mingw libs source code +type FDSet struct { + fd_count uint + fd_array [FD_SETSIZE]uintptr +} + +// Set adds the fd to the set +func (fds *FDSet) Set(fd uintptr) { + var i uint + for i = 0; i < fds.fd_count; i++ { + if fds.fd_array[i] == fd { + break + } + } + if i == fds.fd_count { + if fds.fd_count < FD_SETSIZE { + fds.fd_array[i] = fd + fds.fd_count++ + } + } +} + +// Clear remove the fd from the set +func (fds *FDSet) Clear(fd uintptr) { + var i uint + for i = 0; i < fds.fd_count; i++ { + if fds.fd_array[i] == fd { + for i < fds.fd_count-1 { + fds.fd_array[i] = fds.fd_array[i+1] + i++ + } + fds.fd_count-- + break + } + } +} + +// IsSet check if the given fd is set +func (fds *FDSet) IsSet(fd uintptr) bool { + if isset, err := __WSAFDIsSet(syscall.Handle(fd), fds); err == nil && isset != 0 { + return true + } + return false +} + +// Zero empties the Set +func (fds *FDSet) Zero() { + fds.fd_count = 0 +} diff --git a/vendor/github.com/creack/goselect/select.go b/vendor/github.com/creack/goselect/select.go new file mode 100644 index 000000000..7f2875e73 --- /dev/null +++ b/vendor/github.com/creack/goselect/select.go @@ -0,0 +1,28 @@ +package goselect + +import ( + "syscall" + "time" +) + +// Select wraps syscall.Select with Go types +func Select(n int, r, w, e *FDSet, timeout time.Duration) error { + var timeval *syscall.Timeval + if timeout >= 0 { + t := syscall.NsecToTimeval(timeout.Nanoseconds()) + timeval = &t + } + + return sysSelect(n, r, w, e, timeval) +} + +// RetrySelect wraps syscall.Select with Go types, and retries a number of times, with a given retryDelay. +func RetrySelect(n int, r, w, e *FDSet, timeout time.Duration, retries int, retryDelay time.Duration) (err error) { + for i := 0; i < retries; i++ { + if err = Select(n, r, w, e, timeout); err != syscall.EINTR { + return err + } + time.Sleep(retryDelay) + } + return err +} diff --git a/vendor/github.com/creack/goselect/select_linux.go b/vendor/github.com/creack/goselect/select_linux.go new file mode 100644 index 000000000..acd569e9d --- /dev/null +++ b/vendor/github.com/creack/goselect/select_linux.go @@ -0,0 +1,10 @@ +// +build linux + +package goselect + +import "syscall" + +func sysSelect(n int, r, w, e *FDSet, timeout *syscall.Timeval) error { + _, err := syscall.Select(n, (*syscall.FdSet)(r), (*syscall.FdSet)(w), (*syscall.FdSet)(e), timeout) + return err +} diff --git a/vendor/github.com/creack/goselect/select_other.go b/vendor/github.com/creack/goselect/select_other.go new file mode 100644 index 000000000..6c8208147 --- /dev/null +++ b/vendor/github.com/creack/goselect/select_other.go @@ -0,0 +1,9 @@ +// +build !linux,!windows,!plan9,!solaris + +package goselect + +import "syscall" + +func sysSelect(n int, r, w, e *FDSet, timeout *syscall.Timeval) error { + return syscall.Select(n, (*syscall.FdSet)(r), (*syscall.FdSet)(w), (*syscall.FdSet)(e), timeout) +} diff --git a/vendor/github.com/creack/goselect/select_unsupported.go b/vendor/github.com/creack/goselect/select_unsupported.go new file mode 100644 index 000000000..bea0ad94a --- /dev/null +++ b/vendor/github.com/creack/goselect/select_unsupported.go @@ -0,0 +1,16 @@ +// +build plan9 solaris + +package goselect + +import ( + "fmt" + "runtime" + "syscall" +) + +// ErrUnsupported . +var ErrUnsupported = fmt.Errorf("Platofrm %s/%s unsupported", runtime.GOOS, runtime.GOARCH) + +func sysSelect(n int, r, w, e *FDSet, timeout *syscall.Timeval) error { + return ErrUnsupported +} diff --git a/vendor/github.com/creack/goselect/select_windows.go b/vendor/github.com/creack/goselect/select_windows.go new file mode 100644 index 000000000..0fb70d5d2 --- /dev/null +++ b/vendor/github.com/creack/goselect/select_windows.go @@ -0,0 +1,13 @@ +// +build windows + +package goselect + +import "syscall" + +//sys _select(nfds int, readfds *FDSet, writefds *FDSet, exceptfds *FDSet, timeout *syscall.Timeval) (total int, err error) = ws2_32.select +//sys __WSAFDIsSet(handle syscall.Handle, fdset *FDSet) (isset int, err error) = ws2_32.__WSAFDIsSet + +func sysSelect(n int, r, w, e *FDSet, timeout *syscall.Timeval) error { + _, err := _select(n, r, w, e, timeout) + return err +} diff --git a/vendor/github.com/creack/goselect/test_crosscompile.sh b/vendor/github.com/creack/goselect/test_crosscompile.sh new file mode 100755 index 000000000..533ca6647 --- /dev/null +++ b/vendor/github.com/creack/goselect/test_crosscompile.sh @@ -0,0 +1,17 @@ +export GOOS=linux; export GOARCH=arm; echo $GOOS/$GOARCH; go build +export GOOS=linux; export GOARCH=arm64; echo $GOOS/$GOARCH; go build +export GOOS=linux; export GOARCH=amd64; echo $GOOS/$GOARCH; go build +export GOOS=linux; export GOARCH=386; echo $GOOS/$GOARCH; go build +export GOOS=darwin; export GOARCH=arm; echo $GOOS/$GOARCH; go build +export GOOS=darwin; export GOARCH=arm64; echo $GOOS/$GOARCH; go build +export GOOS=darwin; export GOARCH=amd64; echo $GOOS/$GOARCH; go build +export GOOS=darwin; export GOARCH=386; echo $GOOS/$GOARCH; go build +export GOOS=freebsd; export GOARCH=arm; echo $GOOS/$GOARCH; go build +export GOOS=freebsd; export GOARCH=amd64; echo $GOOS/$GOARCH; go build +export GOOS=freebsd; export GOARCH=386; echo $GOOS/$GOARCH; go build +export GOOS=openbsd; export GOARCH=arm; echo $GOOS/$GOARCH; go build +export GOOS=openbsd; export GOARCH=amd64; echo $GOOS/$GOARCH; go build +export GOOS=openbsd; export GOARCH=386; echo $GOOS/$GOARCH; go build +export GOOS=netbsd; export GOARCH=arm; echo $GOOS/$GOARCH; go build +export GOOS=netbsd; export GOARCH=amd64; echo $GOOS/$GOARCH; go build +export GOOS=netbsd; export GOARCH=386; echo $GOOS/$GOARCH; go build diff --git a/vendor/github.com/creack/goselect/zselect_windows.go b/vendor/github.com/creack/goselect/zselect_windows.go new file mode 100644 index 000000000..c3b10e1d0 --- /dev/null +++ b/vendor/github.com/creack/goselect/zselect_windows.go @@ -0,0 +1,41 @@ +// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT + +package goselect + +import "unsafe" +import "syscall" + +var _ unsafe.Pointer + +var ( + modws2_32 = syscall.NewLazyDLL("ws2_32.dll") + + procselect = modws2_32.NewProc("select") + proc__WSAFDIsSet = modws2_32.NewProc("__WSAFDIsSet") +) + +func _select(nfds int, readfds *FDSet, writefds *FDSet, exceptfds *FDSet, timeout *syscall.Timeval) (total int, err error) { + r0, _, e1 := syscall.Syscall6(procselect.Addr(), 5, uintptr(nfds), uintptr(unsafe.Pointer(readfds)), uintptr(unsafe.Pointer(writefds)), uintptr(unsafe.Pointer(exceptfds)), uintptr(unsafe.Pointer(timeout)), 0) + total = int(r0) + if total == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func __WSAFDIsSet(handle syscall.Handle, fdset *FDSet) (isset int, err error) { + r0, _, e1 := syscall.Syscall(proc__WSAFDIsSet.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(fdset)), 0) + isset = int(r0) + if isset == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} diff --git a/vendor/github.com/docker/docker/LICENSE b/vendor/github.com/docker/docker/LICENSE new file mode 100644 index 000000000..9c8e20ab8 --- /dev/null +++ b/vendor/github.com/docker/docker/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://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 + + Copyright 2013-2017 Docker, 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 + + https://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. diff --git a/vendor/github.com/docker/docker/NOTICE b/vendor/github.com/docker/docker/NOTICE new file mode 100644 index 000000000..0c74e15b0 --- /dev/null +++ b/vendor/github.com/docker/docker/NOTICE @@ -0,0 +1,19 @@ +Docker +Copyright 2012-2017 Docker, Inc. + +This product includes software developed at Docker, Inc. (https://www.docker.com). + +This product contains software (https://github.com/kr/pty) developed +by Keith Rarick, licensed under the MIT License. + +The following is courtesy of our legal counsel: + + +Use and transfer of Docker may be subject to certain restrictions by the +United States and other governments. +It is your responsibility to ensure that your use and/or transfer does not +violate applicable laws. + +For more information, please see https://www.bis.doc.gov + +See also https://www.apache.org/dev/crypto.html and/or seek legal counsel. diff --git a/vendor/github.com/docker/docker/pkg/namesgenerator/names-generator.go b/vendor/github.com/docker/docker/pkg/namesgenerator/names-generator.go new file mode 100644 index 000000000..d732a4795 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/namesgenerator/names-generator.go @@ -0,0 +1,608 @@ +package namesgenerator + +import ( + "fmt" + + "github.com/docker/docker/pkg/random" +) + +var ( + left = [...]string{ + "admiring", + "adoring", + "affectionate", + "agitated", + "amazing", + "angry", + "awesome", + "blissful", + "boring", + "brave", + "clever", + "cocky", + "compassionate", + "competent", + "condescending", + "confident", + "cranky", + "dazzling", + "determined", + "distracted", + "dreamy", + "eager", + "ecstatic", + "elastic", + "elated", + "elegant", + "eloquent", + "epic", + "fervent", + "festive", + "flamboyant", + "focused", + "friendly", + "frosty", + "gallant", + "gifted", + "goofy", + "gracious", + "happy", + "hardcore", + "heuristic", + "hopeful", + "hungry", + "infallible", + "inspiring", + "jolly", + "jovial", + "keen", + "kind", + "laughing", + "loving", + "lucid", + "mystifying", + "modest", + "musing", + "naughty", + "nervous", + "nifty", + "nostalgic", + "objective", + "optimistic", + "peaceful", + "pedantic", + "pensive", + "practical", + "priceless", + "quirky", + "quizzical", + "relaxed", + "reverent", + "romantic", + "sad", + "serene", + "sharp", + "silly", + "sleepy", + "stoic", + "stupefied", + "suspicious", + "tender", + "thirsty", + "trusting", + "unruffled", + "upbeat", + "vibrant", + "vigilant", + "vigorous", + "wizardly", + "wonderful", + "xenodochial", + "youthful", + "zealous", + "zen", + } + + // Docker, starting from 0.7.x, generates names from notable scientists and hackers. + // Please, for any amazing man that you add to the list, consider adding an equally amazing woman to it, and vice versa. + right = [...]string{ + // Muhammad ibn Jābir al-Ḥarrānī al-Battānī was a founding father of astronomy. https://en.wikipedia.org/wiki/Mu%E1%B8%A5ammad_ibn_J%C4%81bir_al-%E1%B8%A4arr%C4%81n%C4%AB_al-Batt%C4%81n%C4%AB + "albattani", + + // Frances E. Allen, became the first female IBM Fellow in 1989. In 2006, she became the first female recipient of the ACM's Turing Award. https://en.wikipedia.org/wiki/Frances_E._Allen + "allen", + + // June Almeida - Scottish virologist who took the first pictures of the rubella virus - https://en.wikipedia.org/wiki/June_Almeida + "almeida", + + // Maria Gaetana Agnesi - Italian mathematician, philosopher, theologian and humanitarian. She was the first woman to write a mathematics handbook and the first woman appointed as a Mathematics Professor at a University. https://en.wikipedia.org/wiki/Maria_Gaetana_Agnesi + "agnesi", + + // Archimedes was a physicist, engineer and mathematician who invented too many things to list them here. https://en.wikipedia.org/wiki/Archimedes + "archimedes", + + // Maria Ardinghelli - Italian translator, mathematician and physicist - https://en.wikipedia.org/wiki/Maria_Ardinghelli + "ardinghelli", + + // Aryabhata - Ancient Indian mathematician-astronomer during 476-550 CE https://en.wikipedia.org/wiki/Aryabhata + "aryabhata", + + // Wanda Austin - Wanda Austin is the President and CEO of The Aerospace Corporation, a leading architect for the US security space programs. https://en.wikipedia.org/wiki/Wanda_Austin + "austin", + + // Charles Babbage invented the concept of a programmable computer. https://en.wikipedia.org/wiki/Charles_Babbage. + "babbage", + + // Stefan Banach - Polish mathematician, was one of the founders of modern functional analysis. https://en.wikipedia.org/wiki/Stefan_Banach + "banach", + + // John Bardeen co-invented the transistor - https://en.wikipedia.org/wiki/John_Bardeen + "bardeen", + + // Jean Bartik, born Betty Jean Jennings, was one of the original programmers for the ENIAC computer. https://en.wikipedia.org/wiki/Jean_Bartik + "bartik", + + // Laura Bassi, the world's first female professor https://en.wikipedia.org/wiki/Laura_Bassi + "bassi", + + // Hugh Beaver, British engineer, founder of the Guinness Book of World Records https://en.wikipedia.org/wiki/Hugh_Beaver + "beaver", + + // Alexander Graham Bell - an eminent Scottish-born scientist, inventor, engineer and innovator who is credited with inventing the first practical telephone - https://en.wikipedia.org/wiki/Alexander_Graham_Bell + "bell", + + // Karl Friedrich Benz - a German automobile engineer. Inventor of the first practical motorcar. https://en.wikipedia.org/wiki/Karl_Benz + "benz", + + // Homi J Bhabha - was an Indian nuclear physicist, founding director, and professor of physics at the Tata Institute of Fundamental Research. Colloquially known as "father of Indian nuclear programme"- https://en.wikipedia.org/wiki/Homi_J._Bhabha + "bhabha", + + // Bhaskara II - Ancient Indian mathematician-astronomer whose work on calculus predates Newton and Leibniz by over half a millennium - https://en.wikipedia.org/wiki/Bh%C4%81skara_II#Calculus + "bhaskara", + + // Elizabeth Blackwell - American doctor and first American woman to receive a medical degree - https://en.wikipedia.org/wiki/Elizabeth_Blackwell + "blackwell", + + // Niels Bohr is the father of quantum theory. https://en.wikipedia.org/wiki/Niels_Bohr. + "bohr", + + // Kathleen Booth, she's credited with writing the first assembly language. https://en.wikipedia.org/wiki/Kathleen_Booth + "booth", + + // Anita Borg - Anita Borg was the founding director of the Institute for Women and Technology (IWT). https://en.wikipedia.org/wiki/Anita_Borg + "borg", + + // Satyendra Nath Bose - He provided the foundation for Bose–Einstein statistics and the theory of the Bose–Einstein condensate. - https://en.wikipedia.org/wiki/Satyendra_Nath_Bose + "bose", + + // Evelyn Boyd Granville - She was one of the first African-American woman to receive a Ph.D. in mathematics; she earned it in 1949 from Yale University. https://en.wikipedia.org/wiki/Evelyn_Boyd_Granville + "boyd", + + // Brahmagupta - Ancient Indian mathematician during 598-670 CE who gave rules to compute with zero - https://en.wikipedia.org/wiki/Brahmagupta#Zero + "brahmagupta", + + // Walter Houser Brattain co-invented the transistor - https://en.wikipedia.org/wiki/Walter_Houser_Brattain + "brattain", + + // Emmett Brown invented time travel. https://en.wikipedia.org/wiki/Emmett_Brown (thanks Brian Goff) + "brown", + + // Rachel Carson - American marine biologist and conservationist, her book Silent Spring and other writings are credited with advancing the global environmental movement. https://en.wikipedia.org/wiki/Rachel_Carson + "carson", + + // Subrahmanyan Chandrasekhar - Astrophysicist known for his mathematical theory on different stages and evolution in structures of the stars. He has won nobel prize for physics - https://en.wikipedia.org/wiki/Subrahmanyan_Chandrasekhar + "chandrasekhar", + + //Claude Shannon - The father of information theory and founder of digital circuit design theory. (https://en.wikipedia.org/wiki/Claude_Shannon) + "shannon", + + // Joan Clarke - Bletchley Park code breaker during the Second World War who pioneered techniques that remained top secret for decades. Also an accomplished numismatist https://en.wikipedia.org/wiki/Joan_Clarke + "clarke", + + // Jane Colden - American botanist widely considered the first female American botanist - https://en.wikipedia.org/wiki/Jane_Colden + "colden", + + // Gerty Theresa Cori - American biochemist who became the third woman—and first American woman—to win a Nobel Prize in science, and the first woman to be awarded the Nobel Prize in Physiology or Medicine. Cori was born in Prague. https://en.wikipedia.org/wiki/Gerty_Cori + "cori", + + // Seymour Roger Cray was an American electrical engineer and supercomputer architect who designed a series of computers that were the fastest in the world for decades. https://en.wikipedia.org/wiki/Seymour_Cray + "cray", + + // This entry reflects a husband and wife team who worked together: + // Joan Curran was a Welsh scientist who developed radar and invented chaff, a radar countermeasure. https://en.wikipedia.org/wiki/Joan_Curran + // Samuel Curran was an Irish physicist who worked alongside his wife during WWII and invented the proximity fuse. https://en.wikipedia.org/wiki/Samuel_Curran + "curran", + + // Marie Curie discovered radioactivity. https://en.wikipedia.org/wiki/Marie_Curie. + "curie", + + // Charles Darwin established the principles of natural evolution. https://en.wikipedia.org/wiki/Charles_Darwin. + "darwin", + + // Leonardo Da Vinci invented too many things to list here. https://en.wikipedia.org/wiki/Leonardo_da_Vinci. + "davinci", + + // Edsger Wybe Dijkstra was a Dutch computer scientist and mathematical scientist. https://en.wikipedia.org/wiki/Edsger_W._Dijkstra. + "dijkstra", + + // Donna Dubinsky - played an integral role in the development of personal digital assistants (PDAs) serving as CEO of Palm, Inc. and co-founding Handspring. https://en.wikipedia.org/wiki/Donna_Dubinsky + "dubinsky", + + // Annie Easley - She was a leading member of the team which developed software for the Centaur rocket stage and one of the first African-Americans in her field. https://en.wikipedia.org/wiki/Annie_Easley + "easley", + + // Thomas Alva Edison, prolific inventor https://en.wikipedia.org/wiki/Thomas_Edison + "edison", + + // Albert Einstein invented the general theory of relativity. https://en.wikipedia.org/wiki/Albert_Einstein + "einstein", + + // Gertrude Elion - American biochemist, pharmacologist and the 1988 recipient of the Nobel Prize in Medicine - https://en.wikipedia.org/wiki/Gertrude_Elion + "elion", + + // Douglas Engelbart gave the mother of all demos: https://en.wikipedia.org/wiki/Douglas_Engelbart + "engelbart", + + // Euclid invented geometry. https://en.wikipedia.org/wiki/Euclid + "euclid", + + // Leonhard Euler invented large parts of modern mathematics. https://de.wikipedia.org/wiki/Leonhard_Euler + "euler", + + // Pierre de Fermat pioneered several aspects of modern mathematics. https://en.wikipedia.org/wiki/Pierre_de_Fermat + "fermat", + + // Enrico Fermi invented the first nuclear reactor. https://en.wikipedia.org/wiki/Enrico_Fermi. + "fermi", + + // Richard Feynman was a key contributor to quantum mechanics and particle physics. https://en.wikipedia.org/wiki/Richard_Feynman + "feynman", + + // Benjamin Franklin is famous for his experiments in electricity and the invention of the lightning rod. + "franklin", + + // Galileo was a founding father of modern astronomy, and faced politics and obscurantism to establish scientific truth. https://en.wikipedia.org/wiki/Galileo_Galilei + "galileo", + + // William Henry "Bill" Gates III is an American business magnate, philanthropist, investor, computer programmer, and inventor. https://en.wikipedia.org/wiki/Bill_Gates + "gates", + + // Adele Goldberg, was one of the designers and developers of the Smalltalk language. https://en.wikipedia.org/wiki/Adele_Goldberg_(computer_scientist) + "goldberg", + + // Adele Goldstine, born Adele Katz, wrote the complete technical description for the first electronic digital computer, ENIAC. https://en.wikipedia.org/wiki/Adele_Goldstine + "goldstine", + + // Shafi Goldwasser is a computer scientist known for creating theoretical foundations of modern cryptography. Winner of 2012 ACM Turing Award. https://en.wikipedia.org/wiki/Shafi_Goldwasser + "goldwasser", + + // James Golick, all around gangster. + "golick", + + // Jane Goodall - British primatologist, ethologist, and anthropologist who is considered to be the world's foremost expert on chimpanzees - https://en.wikipedia.org/wiki/Jane_Goodall + "goodall", + + // Lois Haibt - American computer scientist, part of the team at IBM that developed FORTRAN - https://en.wikipedia.org/wiki/Lois_Haibt + "haibt", + + // Margaret Hamilton - Director of the Software Engineering Division of the MIT Instrumentation Laboratory, which developed on-board flight software for the Apollo space program. https://en.wikipedia.org/wiki/Margaret_Hamilton_(scientist) + "hamilton", + + // Stephen Hawking pioneered the field of cosmology by combining general relativity and quantum mechanics. https://en.wikipedia.org/wiki/Stephen_Hawking + "hawking", + + // Werner Heisenberg was a founding father of quantum mechanics. https://en.wikipedia.org/wiki/Werner_Heisenberg + "heisenberg", + + // Grete Hermann was a German philosopher noted for her philosophical work on the foundations of quantum mechanics. https://en.wikipedia.org/wiki/Grete_Hermann + "hermann", + + // Jaroslav Heyrovský was the inventor of the polarographic method, father of the electroanalytical method, and recipient of the Nobel Prize in 1959. His main field of work was polarography. https://en.wikipedia.org/wiki/Jaroslav_Heyrovsk%C3%BD + "heyrovsky", + + // Dorothy Hodgkin was a British biochemist, credited with the development of protein crystallography. She was awarded the Nobel Prize in Chemistry in 1964. https://en.wikipedia.org/wiki/Dorothy_Hodgkin + "hodgkin", + + // Erna Schneider Hoover revolutionized modern communication by inventing a computerized telephone switching method. https://en.wikipedia.org/wiki/Erna_Schneider_Hoover + "hoover", + + // Grace Hopper developed the first compiler for a computer programming language and is credited with popularizing the term "debugging" for fixing computer glitches. https://en.wikipedia.org/wiki/Grace_Hopper + "hopper", + + // Frances Hugle, she was an American scientist, engineer, and inventor who contributed to the understanding of semiconductors, integrated circuitry, and the unique electrical principles of microscopic materials. https://en.wikipedia.org/wiki/Frances_Hugle + "hugle", + + // Hypatia - Greek Alexandrine Neoplatonist philosopher in Egypt who was one of the earliest mothers of mathematics - https://en.wikipedia.org/wiki/Hypatia + "hypatia", + + // Mary Jackson, American mathematician and aerospace engineer who earned the highest title within NASA's engineering department - https://en.wikipedia.org/wiki/Mary_Jackson_(engineer) + "jackson", + + // Yeong-Sil Jang was a Korean scientist and astronomer during the Joseon Dynasty; he invented the first metal printing press and water gauge. https://en.wikipedia.org/wiki/Jang_Yeong-sil + "jang", + + // Betty Jennings - one of the original programmers of the ENIAC. https://en.wikipedia.org/wiki/ENIAC - https://en.wikipedia.org/wiki/Jean_Bartik + "jennings", + + // Mary Lou Jepsen, was the founder and chief technology officer of One Laptop Per Child (OLPC), and the founder of Pixel Qi. https://en.wikipedia.org/wiki/Mary_Lou_Jepsen + "jepsen", + + // Katherine Coleman Goble Johnson - American physicist and mathematician contributed to the NASA. https://en.wikipedia.org/wiki/Katherine_Johnson + "johnson", + + // Irène Joliot-Curie - French scientist who was awarded the Nobel Prize for Chemistry in 1935. Daughter of Marie and Pierre Curie. https://en.wikipedia.org/wiki/Ir%C3%A8ne_Joliot-Curie + "joliot", + + // Karen Spärck Jones came up with the concept of inverse document frequency, which is used in most search engines today. https://en.wikipedia.org/wiki/Karen_Sp%C3%A4rck_Jones + "jones", + + // A. P. J. Abdul Kalam - is an Indian scientist aka Missile Man of India for his work on the development of ballistic missile and launch vehicle technology - https://en.wikipedia.org/wiki/A._P._J._Abdul_Kalam + "kalam", + + // Susan Kare, created the icons and many of the interface elements for the original Apple Macintosh in the 1980s, and was an original employee of NeXT, working as the Creative Director. https://en.wikipedia.org/wiki/Susan_Kare + "kare", + + // Mary Kenneth Keller, Sister Mary Kenneth Keller became the first American woman to earn a PhD in Computer Science in 1965. https://en.wikipedia.org/wiki/Mary_Kenneth_Keller + "keller", + + // Johannes Kepler, German astronomer known for his three laws of planetary motion - https://en.wikipedia.org/wiki/Johannes_Kepler + "kepler", + + // Har Gobind Khorana - Indian-American biochemist who shared the 1968 Nobel Prize for Physiology - https://en.wikipedia.org/wiki/Har_Gobind_Khorana + "khorana", + + // Jack Kilby invented silicone integrated circuits and gave Silicon Valley its name. - https://en.wikipedia.org/wiki/Jack_Kilby + "kilby", + + // Maria Kirch - German astronomer and first woman to discover a comet - https://en.wikipedia.org/wiki/Maria_Margarethe_Kirch + "kirch", + + // Donald Knuth - American computer scientist, author of "The Art of Computer Programming" and creator of the TeX typesetting system. https://en.wikipedia.org/wiki/Donald_Knuth + "knuth", + + // Sophie Kowalevski - Russian mathematician responsible for important original contributions to analysis, differential equations and mechanics - https://en.wikipedia.org/wiki/Sofia_Kovalevskaya + "kowalevski", + + // Marie-Jeanne de Lalande - French astronomer, mathematician and cataloguer of stars - https://en.wikipedia.org/wiki/Marie-Jeanne_de_Lalande + "lalande", + + // Hedy Lamarr - Actress and inventor. The principles of her work are now incorporated into modern Wi-Fi, CDMA and Bluetooth technology. https://en.wikipedia.org/wiki/Hedy_Lamarr + "lamarr", + + // Leslie B. Lamport - American computer scientist. Lamport is best known for his seminal work in distributed systems and was the winner of the 2013 Turing Award. https://en.wikipedia.org/wiki/Leslie_Lamport + "lamport", + + // Mary Leakey - British paleoanthropologist who discovered the first fossilized Proconsul skull - https://en.wikipedia.org/wiki/Mary_Leakey + "leakey", + + // Henrietta Swan Leavitt - she was an American astronomer who discovered the relation between the luminosity and the period of Cepheid variable stars. https://en.wikipedia.org/wiki/Henrietta_Swan_Leavitt + "leavitt", + + //Daniel Lewin - Mathematician, Akamai co-founder, soldier, 9/11 victim-- Developed optimization techniques for routing traffic on the internet. Died attempting to stop the 9-11 hijackers. https://en.wikipedia.org/wiki/Daniel_Lewin + "lewin", + + // Ruth Lichterman - one of the original programmers of the ENIAC. https://en.wikipedia.org/wiki/ENIAC - https://en.wikipedia.org/wiki/Ruth_Teitelbaum + "lichterman", + + // Barbara Liskov - co-developed the Liskov substitution principle. Liskov was also the winner of the Turing Prize in 2008. - https://en.wikipedia.org/wiki/Barbara_Liskov + "liskov", + + // Ada Lovelace invented the first algorithm. https://en.wikipedia.org/wiki/Ada_Lovelace (thanks James Turnbull) + "lovelace", + + // Auguste and Louis Lumière - the first filmmakers in history - https://en.wikipedia.org/wiki/Auguste_and_Louis_Lumi%C3%A8re + "lumiere", + + // Mahavira - Ancient Indian mathematician during 9th century AD who discovered basic algebraic identities - https://en.wikipedia.org/wiki/Mah%C4%81v%C4%ABra_(mathematician) + "mahavira", + + // Maria Mayer - American theoretical physicist and Nobel laureate in Physics for proposing the nuclear shell model of the atomic nucleus - https://en.wikipedia.org/wiki/Maria_Mayer + "mayer", + + // John McCarthy invented LISP: https://en.wikipedia.org/wiki/John_McCarthy_(computer_scientist) + "mccarthy", + + // Barbara McClintock - a distinguished American cytogeneticist, 1983 Nobel Laureate in Physiology or Medicine for discovering transposons. https://en.wikipedia.org/wiki/Barbara_McClintock + "mcclintock", + + // Malcolm McLean invented the modern shipping container: https://en.wikipedia.org/wiki/Malcom_McLean + "mclean", + + // Kay McNulty - one of the original programmers of the ENIAC. https://en.wikipedia.org/wiki/ENIAC - https://en.wikipedia.org/wiki/Kathleen_Antonelli + "mcnulty", + + // Lise Meitner - Austrian/Swedish physicist who was involved in the discovery of nuclear fission. The element meitnerium is named after her - https://en.wikipedia.org/wiki/Lise_Meitner + "meitner", + + // Carla Meninsky, was the game designer and programmer for Atari 2600 games Dodge 'Em and Warlords. https://en.wikipedia.org/wiki/Carla_Meninsky + "meninsky", + + // Johanna Mestorf - German prehistoric archaeologist and first female museum director in Germany - https://en.wikipedia.org/wiki/Johanna_Mestorf + "mestorf", + + // Marvin Minsky - Pioneer in Artificial Intelligence, co-founder of the MIT's AI Lab, won the Turing Award in 1969. https://en.wikipedia.org/wiki/Marvin_Minsky + "minsky", + + // Maryam Mirzakhani - an Iranian mathematician and the first woman to win the Fields Medal. https://en.wikipedia.org/wiki/Maryam_Mirzakhani + "mirzakhani", + + // Samuel Morse - contributed to the invention of a single-wire telegraph system based on European telegraphs and was a co-developer of the Morse code - https://en.wikipedia.org/wiki/Samuel_Morse + "morse", + + // Ian Murdock - founder of the Debian project - https://en.wikipedia.org/wiki/Ian_Murdock + "murdock", + + // John von Neumann - todays computer architectures are based on the von Neumann architecture. https://en.wikipedia.org/wiki/Von_Neumann_architecture + "neumann", + + // Isaac Newton invented classic mechanics and modern optics. https://en.wikipedia.org/wiki/Isaac_Newton + "newton", + + // Florence Nightingale, more prominently known as a nurse, was also the first female member of the Royal Statistical Society and a pioneer in statistical graphics https://en.wikipedia.org/wiki/Florence_Nightingale#Statistics_and_sanitary_reform + "nightingale", + + // Alfred Nobel - a Swedish chemist, engineer, innovator, and armaments manufacturer (inventor of dynamite) - https://en.wikipedia.org/wiki/Alfred_Nobel + "nobel", + + // Emmy Noether, German mathematician. Noether's Theorem is named after her. https://en.wikipedia.org/wiki/Emmy_Noether + "noether", + + // Poppy Northcutt. Poppy Northcutt was the first woman to work as part of NASA’s Mission Control. http://www.businessinsider.com/poppy-northcutt-helped-apollo-astronauts-2014-12?op=1 + "northcutt", + + // Robert Noyce invented silicone integrated circuits and gave Silicon Valley its name. - https://en.wikipedia.org/wiki/Robert_Noyce + "noyce", + + // Panini - Ancient Indian linguist and grammarian from 4th century CE who worked on the world's first formal system - https://en.wikipedia.org/wiki/P%C4%81%E1%B9%87ini#Comparison_with_modern_formal_systems + "panini", + + // Ambroise Pare invented modern surgery. https://en.wikipedia.org/wiki/Ambroise_Par%C3%A9 + "pare", + + // Louis Pasteur discovered vaccination, fermentation and pasteurization. https://en.wikipedia.org/wiki/Louis_Pasteur. + "pasteur", + + // Cecilia Payne-Gaposchkin was an astronomer and astrophysicist who, in 1925, proposed in her Ph.D. thesis an explanation for the composition of stars in terms of the relative abundances of hydrogen and helium. https://en.wikipedia.org/wiki/Cecilia_Payne-Gaposchkin + "payne", + + // Radia Perlman is a software designer and network engineer and most famous for her invention of the spanning-tree protocol (STP). https://en.wikipedia.org/wiki/Radia_Perlman + "perlman", + + // Rob Pike was a key contributor to Unix, Plan 9, the X graphic system, utf-8, and the Go programming language. https://en.wikipedia.org/wiki/Rob_Pike + "pike", + + // Henri Poincaré made fundamental contributions in several fields of mathematics. https://en.wikipedia.org/wiki/Henri_Poincar%C3%A9 + "poincare", + + // Laura Poitras is a director and producer whose work, made possible by open source crypto tools, advances the causes of truth and freedom of information by reporting disclosures by whistleblowers such as Edward Snowden. https://en.wikipedia.org/wiki/Laura_Poitras + "poitras", + + // Claudius Ptolemy - a Greco-Egyptian writer of Alexandria, known as a mathematician, astronomer, geographer, astrologer, and poet of a single epigram in the Greek Anthology - https://en.wikipedia.org/wiki/Ptolemy + "ptolemy", + + // C. V. Raman - Indian physicist who won the Nobel Prize in 1930 for proposing the Raman effect. - https://en.wikipedia.org/wiki/C._V._Raman + "raman", + + // Srinivasa Ramanujan - Indian mathematician and autodidact who made extraordinary contributions to mathematical analysis, number theory, infinite series, and continued fractions. - https://en.wikipedia.org/wiki/Srinivasa_Ramanujan + "ramanujan", + + // Sally Kristen Ride was an American physicist and astronaut. She was the first American woman in space, and the youngest American astronaut. https://en.wikipedia.org/wiki/Sally_Ride + "ride", + + // Rita Levi-Montalcini - Won Nobel Prize in Physiology or Medicine jointly with colleague Stanley Cohen for the discovery of nerve growth factor (https://en.wikipedia.org/wiki/Rita_Levi-Montalcini) + "montalcini", + + // Dennis Ritchie - co-creator of UNIX and the C programming language. - https://en.wikipedia.org/wiki/Dennis_Ritchie + "ritchie", + + // Wilhelm Conrad Röntgen - German physicist who was awarded the first Nobel Prize in Physics in 1901 for the discovery of X-rays (Röntgen rays). https://en.wikipedia.org/wiki/Wilhelm_R%C3%B6ntgen + "roentgen", + + // Rosalind Franklin - British biophysicist and X-ray crystallographer whose research was critical to the understanding of DNA - https://en.wikipedia.org/wiki/Rosalind_Franklin + "rosalind", + + // Meghnad Saha - Indian astrophysicist best known for his development of the Saha equation, used to describe chemical and physical conditions in stars - https://en.wikipedia.org/wiki/Meghnad_Saha + "saha", + + // Jean E. Sammet developed FORMAC, the first widely used computer language for symbolic manipulation of mathematical formulas. https://en.wikipedia.org/wiki/Jean_E._Sammet + "sammet", + + // Carol Shaw - Originally an Atari employee, Carol Shaw is said to be the first female video game designer. https://en.wikipedia.org/wiki/Carol_Shaw_(video_game_designer) + "shaw", + + // Dame Stephanie "Steve" Shirley - Founded a software company in 1962 employing women working from home. https://en.wikipedia.org/wiki/Steve_Shirley + "shirley", + + // William Shockley co-invented the transistor - https://en.wikipedia.org/wiki/William_Shockley + "shockley", + + // Françoise Barré-Sinoussi - French virologist and Nobel Prize Laureate in Physiology or Medicine; her work was fundamental in identifying HIV as the cause of AIDS. https://en.wikipedia.org/wiki/Fran%C3%A7oise_Barr%C3%A9-Sinoussi + "sinoussi", + + // Betty Snyder - one of the original programmers of the ENIAC. https://en.wikipedia.org/wiki/ENIAC - https://en.wikipedia.org/wiki/Betty_Holberton + "snyder", + + // Frances Spence - one of the original programmers of the ENIAC. https://en.wikipedia.org/wiki/ENIAC - https://en.wikipedia.org/wiki/Frances_Spence + "spence", + + // Richard Matthew Stallman - the founder of the Free Software movement, the GNU project, the Free Software Foundation, and the League for Programming Freedom. He also invented the concept of copyleft to protect the ideals of this movement, and enshrined this concept in the widely-used GPL (General Public License) for software. https://en.wikiquote.org/wiki/Richard_Stallman + "stallman", + + // Michael Stonebraker is a database research pioneer and architect of Ingres, Postgres, VoltDB and SciDB. Winner of 2014 ACM Turing Award. https://en.wikipedia.org/wiki/Michael_Stonebraker + "stonebraker", + + // Janese Swanson (with others) developed the first of the Carmen Sandiego games. She went on to found Girl Tech. https://en.wikipedia.org/wiki/Janese_Swanson + "swanson", + + // Aaron Swartz was influential in creating RSS, Markdown, Creative Commons, Reddit, and much of the internet as we know it today. He was devoted to freedom of information on the web. https://en.wikiquote.org/wiki/Aaron_Swartz + "swartz", + + // Bertha Swirles was a theoretical physicist who made a number of contributions to early quantum theory. https://en.wikipedia.org/wiki/Bertha_Swirles + "swirles", + + // Nikola Tesla invented the AC electric system and every gadget ever used by a James Bond villain. https://en.wikipedia.org/wiki/Nikola_Tesla + "tesla", + + // Ken Thompson - co-creator of UNIX and the C programming language - https://en.wikipedia.org/wiki/Ken_Thompson + "thompson", + + // Linus Torvalds invented Linux and Git. https://en.wikipedia.org/wiki/Linus_Torvalds + "torvalds", + + // Alan Turing was a founding father of computer science. https://en.wikipedia.org/wiki/Alan_Turing. + "turing", + + // Varahamihira - Ancient Indian mathematician who discovered trigonometric formulae during 505-587 CE - https://en.wikipedia.org/wiki/Var%C4%81hamihira#Contributions + "varahamihira", + + // Sir Mokshagundam Visvesvaraya - is a notable Indian engineer. He is a recipient of the Indian Republic's highest honour, the Bharat Ratna, in 1955. On his birthday, 15 September is celebrated as Engineer's Day in India in his memory - https://en.wikipedia.org/wiki/Visvesvaraya + "visvesvaraya", + + // Christiane Nüsslein-Volhard - German biologist, won Nobel Prize in Physiology or Medicine in 1995 for research on the genetic control of embryonic development. https://en.wikipedia.org/wiki/Christiane_N%C3%BCsslein-Volhard + "volhard", + + // Marlyn Wescoff - one of the original programmers of the ENIAC. https://en.wikipedia.org/wiki/ENIAC - https://en.wikipedia.org/wiki/Marlyn_Meltzer + "wescoff", + + // Andrew Wiles - Notable British mathematician who proved the enigmatic Fermat's Last Theorem - https://en.wikipedia.org/wiki/Andrew_Wiles + "wiles", + + // Roberta Williams, did pioneering work in graphical adventure games for personal computers, particularly the King's Quest series. https://en.wikipedia.org/wiki/Roberta_Williams + "williams", + + // Sophie Wilson designed the first Acorn Micro-Computer and the instruction set for ARM processors. https://en.wikipedia.org/wiki/Sophie_Wilson + "wilson", + + // Jeannette Wing - co-developed the Liskov substitution principle. - https://en.wikipedia.org/wiki/Jeannette_Wing + "wing", + + // Steve Wozniak invented the Apple I and Apple II. https://en.wikipedia.org/wiki/Steve_Wozniak + "wozniak", + + // The Wright brothers, Orville and Wilbur - credited with inventing and building the world's first successful airplane and making the first controlled, powered and sustained heavier-than-air human flight - https://en.wikipedia.org/wiki/Wright_brothers + "wright", + + // Rosalyn Sussman Yalow - Rosalyn Sussman Yalow was an American medical physicist, and a co-winner of the 1977 Nobel Prize in Physiology or Medicine for development of the radioimmunoassay technique. https://en.wikipedia.org/wiki/Rosalyn_Sussman_Yalow + "yalow", + + // Ada Yonath - an Israeli crystallographer, the first woman from the Middle East to win a Nobel prize in the sciences. https://en.wikipedia.org/wiki/Ada_Yonath + "yonath", + } +) + +// GetRandomName generates a random name from the list of adjectives and surnames in this package +// formatted as "adjective_surname". For example 'focused_turing'. If retry is non-zero, a random +// integer between 0 and 10 will be added to the end of the name, e.g `focused_turing3` +func GetRandomName(retry int) string { + rnd := random.Rand +begin: + name := fmt.Sprintf("%s_%s", left[rnd.Intn(len(left))], right[rnd.Intn(len(right))]) + if name == "boring_wozniak" /* Steve Wozniak is not boring */ { + goto begin + } + + if retry > 0 { + name = fmt.Sprintf("%s%d", name, rnd.Intn(10)) + } + return name +} diff --git a/vendor/github.com/docker/docker/pkg/random/random.go b/vendor/github.com/docker/docker/pkg/random/random.go new file mode 100644 index 000000000..70de4d130 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/random/random.go @@ -0,0 +1,71 @@ +package random + +import ( + cryptorand "crypto/rand" + "io" + "math" + "math/big" + "math/rand" + "sync" + "time" +) + +// Rand is a global *rand.Rand instance, which initialized with NewSource() source. +var Rand = rand.New(NewSource()) + +// Reader is a global, shared instance of a pseudorandom bytes generator. +// It doesn't consume entropy. +var Reader io.Reader = &reader{rnd: Rand} + +// copypaste from standard math/rand +type lockedSource struct { + lk sync.Mutex + src rand.Source +} + +func (r *lockedSource) Int63() (n int64) { + r.lk.Lock() + n = r.src.Int63() + r.lk.Unlock() + return +} + +func (r *lockedSource) Seed(seed int64) { + r.lk.Lock() + r.src.Seed(seed) + r.lk.Unlock() +} + +// NewSource returns math/rand.Source safe for concurrent use and initialized +// with current unix-nano timestamp +func NewSource() rand.Source { + var seed int64 + if cryptoseed, err := cryptorand.Int(cryptorand.Reader, big.NewInt(math.MaxInt64)); err != nil { + // This should not happen, but worst-case fallback to time-based seed. + seed = time.Now().UnixNano() + } else { + seed = cryptoseed.Int64() + } + return &lockedSource{ + src: rand.NewSource(seed), + } +} + +type reader struct { + rnd *rand.Rand +} + +func (r *reader) Read(b []byte) (int, error) { + i := 0 + for { + val := r.rnd.Int63() + for val > 0 { + b[i] = byte(val) + i++ + if i == len(b) { + return i, nil + } + val >>= 8 + } + } +} diff --git a/vendor/github.com/dustin/go-humanize/LICENSE b/vendor/github.com/dustin/go-humanize/LICENSE new file mode 100644 index 000000000..8d9a94a90 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2005-2008 Dustin Sallings + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + diff --git a/vendor/github.com/dustin/go-humanize/README.markdown b/vendor/github.com/dustin/go-humanize/README.markdown new file mode 100644 index 000000000..f69d3c870 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/README.markdown @@ -0,0 +1,92 @@ +# Humane Units [![Build Status](https://travis-ci.org/dustin/go-humanize.svg?branch=master)](https://travis-ci.org/dustin/go-humanize) [![GoDoc](https://godoc.org/github.com/dustin/go-humanize?status.svg)](https://godoc.org/github.com/dustin/go-humanize) + +Just a few functions for helping humanize times and sizes. + +`go get` it as `github.com/dustin/go-humanize`, import it as +`"github.com/dustin/go-humanize"`, use it as `humanize`. + +See [godoc](https://godoc.org/github.com/dustin/go-humanize) for +complete documentation. + +## Sizes + +This lets you take numbers like `82854982` and convert them to useful +strings like, `83 MB` or `79 MiB` (whichever you prefer). + +Example: + +```go +fmt.Printf("That file is %s.", humanize.Bytes(82854982)) // That file is 83 MB. +``` + +## Times + +This lets you take a `time.Time` and spit it out in relative terms. +For example, `12 seconds ago` or `3 days from now`. + +Example: + +```go +fmt.Printf("This was touched %s.", humanize.Time(someTimeInstance)) // This was touched 7 hours ago. +``` + +Thanks to Kyle Lemons for the time implementation from an IRC +conversation one day. It's pretty neat. + +## Ordinals + +From a [mailing list discussion][odisc] where a user wanted to be able +to label ordinals. + + 0 -> 0th + 1 -> 1st + 2 -> 2nd + 3 -> 3rd + 4 -> 4th + [...] + +Example: + +```go +fmt.Printf("You're my %s best friend.", humanize.Ordinal(193)) // You are my 193rd best friend. +``` + +## Commas + +Want to shove commas into numbers? Be my guest. + + 0 -> 0 + 100 -> 100 + 1000 -> 1,000 + 1000000000 -> 1,000,000,000 + -100000 -> -100,000 + +Example: + +```go +fmt.Printf("You owe $%s.\n", humanize.Comma(6582491)) // You owe $6,582,491. +``` + +## Ftoa + +Nicer float64 formatter that removes trailing zeros. + +```go +fmt.Printf("%f", 2.24) // 2.240000 +fmt.Printf("%s", humanize.Ftoa(2.24)) // 2.24 +fmt.Printf("%f", 2.0) // 2.000000 +fmt.Printf("%s", humanize.Ftoa(2.0)) // 2 +``` + +## SI notation + +Format numbers with [SI notation][sinotation]. + +Example: + +```go +humanize.SI(0.00000000223, "M") // 2.23 nM +``` + +[odisc]: https://groups.google.com/d/topic/golang-nuts/l8NhI74jl-4/discussion +[sinotation]: http://en.wikipedia.org/wiki/Metric_prefix diff --git a/vendor/github.com/dustin/go-humanize/big.go b/vendor/github.com/dustin/go-humanize/big.go new file mode 100644 index 000000000..f49dc337d --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/big.go @@ -0,0 +1,31 @@ +package humanize + +import ( + "math/big" +) + +// order of magnitude (to a max order) +func oomm(n, b *big.Int, maxmag int) (float64, int) { + mag := 0 + m := &big.Int{} + for n.Cmp(b) >= 0 { + n.DivMod(n, b, m) + mag++ + if mag == maxmag && maxmag >= 0 { + break + } + } + return float64(n.Int64()) + (float64(m.Int64()) / float64(b.Int64())), mag +} + +// total order of magnitude +// (same as above, but with no upper limit) +func oom(n, b *big.Int) (float64, int) { + mag := 0 + m := &big.Int{} + for n.Cmp(b) >= 0 { + n.DivMod(n, b, m) + mag++ + } + return float64(n.Int64()) + (float64(m.Int64()) / float64(b.Int64())), mag +} diff --git a/vendor/github.com/dustin/go-humanize/bigbytes.go b/vendor/github.com/dustin/go-humanize/bigbytes.go new file mode 100644 index 000000000..1a2bf6172 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/bigbytes.go @@ -0,0 +1,173 @@ +package humanize + +import ( + "fmt" + "math/big" + "strings" + "unicode" +) + +var ( + bigIECExp = big.NewInt(1024) + + // BigByte is one byte in bit.Ints + BigByte = big.NewInt(1) + // BigKiByte is 1,024 bytes in bit.Ints + BigKiByte = (&big.Int{}).Mul(BigByte, bigIECExp) + // BigMiByte is 1,024 k bytes in bit.Ints + BigMiByte = (&big.Int{}).Mul(BigKiByte, bigIECExp) + // BigGiByte is 1,024 m bytes in bit.Ints + BigGiByte = (&big.Int{}).Mul(BigMiByte, bigIECExp) + // BigTiByte is 1,024 g bytes in bit.Ints + BigTiByte = (&big.Int{}).Mul(BigGiByte, bigIECExp) + // BigPiByte is 1,024 t bytes in bit.Ints + BigPiByte = (&big.Int{}).Mul(BigTiByte, bigIECExp) + // BigEiByte is 1,024 p bytes in bit.Ints + BigEiByte = (&big.Int{}).Mul(BigPiByte, bigIECExp) + // BigZiByte is 1,024 e bytes in bit.Ints + BigZiByte = (&big.Int{}).Mul(BigEiByte, bigIECExp) + // BigYiByte is 1,024 z bytes in bit.Ints + BigYiByte = (&big.Int{}).Mul(BigZiByte, bigIECExp) +) + +var ( + bigSIExp = big.NewInt(1000) + + // BigSIByte is one SI byte in big.Ints + BigSIByte = big.NewInt(1) + // BigKByte is 1,000 SI bytes in big.Ints + BigKByte = (&big.Int{}).Mul(BigSIByte, bigSIExp) + // BigMByte is 1,000 SI k bytes in big.Ints + BigMByte = (&big.Int{}).Mul(BigKByte, bigSIExp) + // BigGByte is 1,000 SI m bytes in big.Ints + BigGByte = (&big.Int{}).Mul(BigMByte, bigSIExp) + // BigTByte is 1,000 SI g bytes in big.Ints + BigTByte = (&big.Int{}).Mul(BigGByte, bigSIExp) + // BigPByte is 1,000 SI t bytes in big.Ints + BigPByte = (&big.Int{}).Mul(BigTByte, bigSIExp) + // BigEByte is 1,000 SI p bytes in big.Ints + BigEByte = (&big.Int{}).Mul(BigPByte, bigSIExp) + // BigZByte is 1,000 SI e bytes in big.Ints + BigZByte = (&big.Int{}).Mul(BigEByte, bigSIExp) + // BigYByte is 1,000 SI z bytes in big.Ints + BigYByte = (&big.Int{}).Mul(BigZByte, bigSIExp) +) + +var bigBytesSizeTable = map[string]*big.Int{ + "b": BigByte, + "kib": BigKiByte, + "kb": BigKByte, + "mib": BigMiByte, + "mb": BigMByte, + "gib": BigGiByte, + "gb": BigGByte, + "tib": BigTiByte, + "tb": BigTByte, + "pib": BigPiByte, + "pb": BigPByte, + "eib": BigEiByte, + "eb": BigEByte, + "zib": BigZiByte, + "zb": BigZByte, + "yib": BigYiByte, + "yb": BigYByte, + // Without suffix + "": BigByte, + "ki": BigKiByte, + "k": BigKByte, + "mi": BigMiByte, + "m": BigMByte, + "gi": BigGiByte, + "g": BigGByte, + "ti": BigTiByte, + "t": BigTByte, + "pi": BigPiByte, + "p": BigPByte, + "ei": BigEiByte, + "e": BigEByte, + "z": BigZByte, + "zi": BigZiByte, + "y": BigYByte, + "yi": BigYiByte, +} + +var ten = big.NewInt(10) + +func humanateBigBytes(s, base *big.Int, sizes []string) string { + if s.Cmp(ten) < 0 { + return fmt.Sprintf("%d B", s) + } + c := (&big.Int{}).Set(s) + val, mag := oomm(c, base, len(sizes)-1) + suffix := sizes[mag] + f := "%.0f %s" + if val < 10 { + f = "%.1f %s" + } + + return fmt.Sprintf(f, val, suffix) + +} + +// BigBytes produces a human readable representation of an SI size. +// +// See also: ParseBigBytes. +// +// BigBytes(82854982) -> 83 MB +func BigBytes(s *big.Int) string { + sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} + return humanateBigBytes(s, bigSIExp, sizes) +} + +// BigIBytes produces a human readable representation of an IEC size. +// +// See also: ParseBigBytes. +// +// BigIBytes(82854982) -> 79 MiB +func BigIBytes(s *big.Int) string { + sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"} + return humanateBigBytes(s, bigIECExp, sizes) +} + +// ParseBigBytes parses a string representation of bytes into the number +// of bytes it represents. +// +// See also: BigBytes, BigIBytes. +// +// ParseBigBytes("42 MB") -> 42000000, nil +// ParseBigBytes("42 mib") -> 44040192, nil +func ParseBigBytes(s string) (*big.Int, error) { + lastDigit := 0 + hasComma := false + for _, r := range s { + if !(unicode.IsDigit(r) || r == '.' || r == ',') { + break + } + if r == ',' { + hasComma = true + } + lastDigit++ + } + + num := s[:lastDigit] + if hasComma { + num = strings.Replace(num, ",", "", -1) + } + + val := &big.Rat{} + _, err := fmt.Sscanf(num, "%f", val) + if err != nil { + return nil, err + } + + extra := strings.ToLower(strings.TrimSpace(s[lastDigit:])) + if m, ok := bigBytesSizeTable[extra]; ok { + mv := (&big.Rat{}).SetInt(m) + val.Mul(val, mv) + rv := &big.Int{} + rv.Div(val.Num(), val.Denom()) + return rv, nil + } + + return nil, fmt.Errorf("unhandled size name: %v", extra) +} diff --git a/vendor/github.com/dustin/go-humanize/bytes.go b/vendor/github.com/dustin/go-humanize/bytes.go new file mode 100644 index 000000000..0b498f488 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/bytes.go @@ -0,0 +1,143 @@ +package humanize + +import ( + "fmt" + "math" + "strconv" + "strings" + "unicode" +) + +// IEC Sizes. +// kibis of bits +const ( + Byte = 1 << (iota * 10) + KiByte + MiByte + GiByte + TiByte + PiByte + EiByte +) + +// SI Sizes. +const ( + IByte = 1 + KByte = IByte * 1000 + MByte = KByte * 1000 + GByte = MByte * 1000 + TByte = GByte * 1000 + PByte = TByte * 1000 + EByte = PByte * 1000 +) + +var bytesSizeTable = map[string]uint64{ + "b": Byte, + "kib": KiByte, + "kb": KByte, + "mib": MiByte, + "mb": MByte, + "gib": GiByte, + "gb": GByte, + "tib": TiByte, + "tb": TByte, + "pib": PiByte, + "pb": PByte, + "eib": EiByte, + "eb": EByte, + // Without suffix + "": Byte, + "ki": KiByte, + "k": KByte, + "mi": MiByte, + "m": MByte, + "gi": GiByte, + "g": GByte, + "ti": TiByte, + "t": TByte, + "pi": PiByte, + "p": PByte, + "ei": EiByte, + "e": EByte, +} + +func logn(n, b float64) float64 { + return math.Log(n) / math.Log(b) +} + +func humanateBytes(s uint64, base float64, sizes []string) string { + if s < 10 { + return fmt.Sprintf("%d B", s) + } + e := math.Floor(logn(float64(s), base)) + suffix := sizes[int(e)] + val := math.Floor(float64(s)/math.Pow(base, e)*10+0.5) / 10 + f := "%.0f %s" + if val < 10 { + f = "%.1f %s" + } + + return fmt.Sprintf(f, val, suffix) +} + +// Bytes produces a human readable representation of an SI size. +// +// See also: ParseBytes. +// +// Bytes(82854982) -> 83 MB +func Bytes(s uint64) string { + sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"} + return humanateBytes(s, 1000, sizes) +} + +// IBytes produces a human readable representation of an IEC size. +// +// See also: ParseBytes. +// +// IBytes(82854982) -> 79 MiB +func IBytes(s uint64) string { + sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"} + return humanateBytes(s, 1024, sizes) +} + +// ParseBytes parses a string representation of bytes into the number +// of bytes it represents. +// +// See Also: Bytes, IBytes. +// +// ParseBytes("42 MB") -> 42000000, nil +// ParseBytes("42 mib") -> 44040192, nil +func ParseBytes(s string) (uint64, error) { + lastDigit := 0 + hasComma := false + for _, r := range s { + if !(unicode.IsDigit(r) || r == '.' || r == ',') { + break + } + if r == ',' { + hasComma = true + } + lastDigit++ + } + + num := s[:lastDigit] + if hasComma { + num = strings.Replace(num, ",", "", -1) + } + + f, err := strconv.ParseFloat(num, 64) + if err != nil { + return 0, err + } + + extra := strings.ToLower(strings.TrimSpace(s[lastDigit:])) + if m, ok := bytesSizeTable[extra]; ok { + f *= float64(m) + if f >= math.MaxUint64 { + return 0, fmt.Errorf("too large: %v", s) + } + return uint64(f), nil + } + + return 0, fmt.Errorf("unhandled size name: %v", extra) +} diff --git a/vendor/github.com/dustin/go-humanize/comma.go b/vendor/github.com/dustin/go-humanize/comma.go new file mode 100644 index 000000000..eb285cb95 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/comma.go @@ -0,0 +1,108 @@ +package humanize + +import ( + "bytes" + "math" + "math/big" + "strconv" + "strings" +) + +// Comma produces a string form of the given number in base 10 with +// commas after every three orders of magnitude. +// +// e.g. Comma(834142) -> 834,142 +func Comma(v int64) string { + sign := "" + + // minin64 can't be negated to a usable value, so it has to be special cased. + if v == math.MinInt64 { + return "-9,223,372,036,854,775,808" + } + + if v < 0 { + sign = "-" + v = 0 - v + } + + parts := []string{"", "", "", "", "", "", ""} + j := len(parts) - 1 + + for v > 999 { + parts[j] = strconv.FormatInt(v%1000, 10) + switch len(parts[j]) { + case 2: + parts[j] = "0" + parts[j] + case 1: + parts[j] = "00" + parts[j] + } + v = v / 1000 + j-- + } + parts[j] = strconv.Itoa(int(v)) + return sign + strings.Join(parts[j:], ",") +} + +// Commaf produces a string form of the given number in base 10 with +// commas after every three orders of magnitude. +// +// e.g. Commaf(834142.32) -> 834,142.32 +func Commaf(v float64) string { + buf := &bytes.Buffer{} + if v < 0 { + buf.Write([]byte{'-'}) + v = 0 - v + } + + comma := []byte{','} + + parts := strings.Split(strconv.FormatFloat(v, 'f', -1, 64), ".") + pos := 0 + if len(parts[0])%3 != 0 { + pos += len(parts[0]) % 3 + buf.WriteString(parts[0][:pos]) + buf.Write(comma) + } + for ; pos < len(parts[0]); pos += 3 { + buf.WriteString(parts[0][pos : pos+3]) + buf.Write(comma) + } + buf.Truncate(buf.Len() - 1) + + if len(parts) > 1 { + buf.Write([]byte{'.'}) + buf.WriteString(parts[1]) + } + return buf.String() +} + +// BigComma produces a string form of the given big.Int in base 10 +// with commas after every three orders of magnitude. +func BigComma(b *big.Int) string { + sign := "" + if b.Sign() < 0 { + sign = "-" + b.Abs(b) + } + + athousand := big.NewInt(1000) + c := (&big.Int{}).Set(b) + _, m := oom(c, athousand) + parts := make([]string, m+1) + j := len(parts) - 1 + + mod := &big.Int{} + for b.Cmp(athousand) >= 0 { + b.DivMod(b, athousand, mod) + parts[j] = strconv.FormatInt(mod.Int64(), 10) + switch len(parts[j]) { + case 2: + parts[j] = "0" + parts[j] + case 1: + parts[j] = "00" + parts[j] + } + j-- + } + parts[j] = strconv.Itoa(int(b.Int64())) + return sign + strings.Join(parts[j:], ",") +} diff --git a/vendor/github.com/dustin/go-humanize/commaf.go b/vendor/github.com/dustin/go-humanize/commaf.go new file mode 100644 index 000000000..620690dec --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/commaf.go @@ -0,0 +1,40 @@ +// +build go1.6 + +package humanize + +import ( + "bytes" + "math/big" + "strings" +) + +// BigCommaf produces a string form of the given big.Float in base 10 +// with commas after every three orders of magnitude. +func BigCommaf(v *big.Float) string { + buf := &bytes.Buffer{} + if v.Sign() < 0 { + buf.Write([]byte{'-'}) + v.Abs(v) + } + + comma := []byte{','} + + parts := strings.Split(v.Text('f', -1), ".") + pos := 0 + if len(parts[0])%3 != 0 { + pos += len(parts[0]) % 3 + buf.WriteString(parts[0][:pos]) + buf.Write(comma) + } + for ; pos < len(parts[0]); pos += 3 { + buf.WriteString(parts[0][pos : pos+3]) + buf.Write(comma) + } + buf.Truncate(buf.Len() - 1) + + if len(parts) > 1 { + buf.Write([]byte{'.'}) + buf.WriteString(parts[1]) + } + return buf.String() +} diff --git a/vendor/github.com/dustin/go-humanize/ftoa.go b/vendor/github.com/dustin/go-humanize/ftoa.go new file mode 100644 index 000000000..c76190b10 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/ftoa.go @@ -0,0 +1,23 @@ +package humanize + +import "strconv" + +func stripTrailingZeros(s string) string { + offset := len(s) - 1 + for offset > 0 { + if s[offset] == '.' { + offset-- + break + } + if s[offset] != '0' { + break + } + offset-- + } + return s[:offset+1] +} + +// Ftoa converts a float to a string with no trailing zeros. +func Ftoa(num float64) string { + return stripTrailingZeros(strconv.FormatFloat(num, 'f', 6, 64)) +} diff --git a/vendor/github.com/dustin/go-humanize/humanize.go b/vendor/github.com/dustin/go-humanize/humanize.go new file mode 100644 index 000000000..a2c2da31e --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/humanize.go @@ -0,0 +1,8 @@ +/* +Package humanize converts boring ugly numbers to human-friendly strings and back. + +Durations can be turned into strings such as "3 days ago", numbers +representing sizes like 82854982 into useful strings like, "83 MB" or +"79 MiB" (whichever you prefer). +*/ +package humanize diff --git a/vendor/github.com/dustin/go-humanize/number.go b/vendor/github.com/dustin/go-humanize/number.go new file mode 100644 index 000000000..dec618659 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/number.go @@ -0,0 +1,192 @@ +package humanize + +/* +Slightly adapted from the source to fit go-humanize. + +Author: https://github.com/gorhill +Source: https://gist.github.com/gorhill/5285193 + +*/ + +import ( + "math" + "strconv" +) + +var ( + renderFloatPrecisionMultipliers = [...]float64{ + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + } + + renderFloatPrecisionRounders = [...]float64{ + 0.5, + 0.05, + 0.005, + 0.0005, + 0.00005, + 0.000005, + 0.0000005, + 0.00000005, + 0.000000005, + 0.0000000005, + } +) + +// FormatFloat produces a formatted number as string based on the following user-specified criteria: +// * thousands separator +// * decimal separator +// * decimal precision +// +// Usage: s := RenderFloat(format, n) +// The format parameter tells how to render the number n. +// +// See examples: http://play.golang.org/p/LXc1Ddm1lJ +// +// Examples of format strings, given n = 12345.6789: +// "#,###.##" => "12,345.67" +// "#,###." => "12,345" +// "#,###" => "12345,678" +// "#\u202F###,##" => "12 345,68" +// "#.###,###### => 12.345,678900 +// "" (aka default format) => 12,345.67 +// +// The highest precision allowed is 9 digits after the decimal symbol. +// There is also a version for integer number, FormatInteger(), +// which is convenient for calls within template. +func FormatFloat(format string, n float64) string { + // Special cases: + // NaN = "NaN" + // +Inf = "+Infinity" + // -Inf = "-Infinity" + if math.IsNaN(n) { + return "NaN" + } + if n > math.MaxFloat64 { + return "Infinity" + } + if n < -math.MaxFloat64 { + return "-Infinity" + } + + // default format + precision := 2 + decimalStr := "." + thousandStr := "," + positiveStr := "" + negativeStr := "-" + + if len(format) > 0 { + format := []rune(format) + + // If there is an explicit format directive, + // then default values are these: + precision = 9 + thousandStr = "" + + // collect indices of meaningful formatting directives + formatIndx := []int{} + for i, char := range format { + if char != '#' && char != '0' { + formatIndx = append(formatIndx, i) + } + } + + if len(formatIndx) > 0 { + // Directive at index 0: + // Must be a '+' + // Raise an error if not the case + // index: 0123456789 + // +0.000,000 + // +000,000.0 + // +0000.00 + // +0000 + if formatIndx[0] == 0 { + if format[formatIndx[0]] != '+' { + panic("RenderFloat(): invalid positive sign directive") + } + positiveStr = "+" + formatIndx = formatIndx[1:] + } + + // Two directives: + // First is thousands separator + // Raise an error if not followed by 3-digit + // 0123456789 + // 0.000,000 + // 000,000.00 + if len(formatIndx) == 2 { + if (formatIndx[1] - formatIndx[0]) != 4 { + panic("RenderFloat(): thousands separator directive must be followed by 3 digit-specifiers") + } + thousandStr = string(format[formatIndx[0]]) + formatIndx = formatIndx[1:] + } + + // One directive: + // Directive is decimal separator + // The number of digit-specifier following the separator indicates wanted precision + // 0123456789 + // 0.00 + // 000,0000 + if len(formatIndx) == 1 { + decimalStr = string(format[formatIndx[0]]) + precision = len(format) - formatIndx[0] - 1 + } + } + } + + // generate sign part + var signStr string + if n >= 0.000000001 { + signStr = positiveStr + } else if n <= -0.000000001 { + signStr = negativeStr + n = -n + } else { + signStr = "" + n = 0.0 + } + + // split number into integer and fractional parts + intf, fracf := math.Modf(n + renderFloatPrecisionRounders[precision]) + + // generate integer part string + intStr := strconv.FormatInt(int64(intf), 10) + + // add thousand separator if required + if len(thousandStr) > 0 { + for i := len(intStr); i > 3; { + i -= 3 + intStr = intStr[:i] + thousandStr + intStr[i:] + } + } + + // no fractional part, we can leave now + if precision == 0 { + return signStr + intStr + } + + // generate fractional part + fracStr := strconv.Itoa(int(fracf * renderFloatPrecisionMultipliers[precision])) + // may need padding + if len(fracStr) < precision { + fracStr = "000000000000000"[:precision-len(fracStr)] + fracStr + } + + return signStr + intStr + decimalStr + fracStr +} + +// FormatInteger produces a formatted number as string. +// See FormatFloat. +func FormatInteger(format string, n int) string { + return FormatFloat(format, float64(n)) +} diff --git a/vendor/github.com/dustin/go-humanize/ordinals.go b/vendor/github.com/dustin/go-humanize/ordinals.go new file mode 100644 index 000000000..43d88a861 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/ordinals.go @@ -0,0 +1,25 @@ +package humanize + +import "strconv" + +// Ordinal gives you the input number in a rank/ordinal format. +// +// Ordinal(3) -> 3rd +func Ordinal(x int) string { + suffix := "th" + switch x % 10 { + case 1: + if x%100 != 11 { + suffix = "st" + } + case 2: + if x%100 != 12 { + suffix = "nd" + } + case 3: + if x%100 != 13 { + suffix = "rd" + } + } + return strconv.Itoa(x) + suffix +} diff --git a/vendor/github.com/dustin/go-humanize/si.go b/vendor/github.com/dustin/go-humanize/si.go new file mode 100644 index 000000000..b24e48169 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/si.go @@ -0,0 +1,113 @@ +package humanize + +import ( + "errors" + "math" + "regexp" + "strconv" +) + +var siPrefixTable = map[float64]string{ + -24: "y", // yocto + -21: "z", // zepto + -18: "a", // atto + -15: "f", // femto + -12: "p", // pico + -9: "n", // nano + -6: "µ", // micro + -3: "m", // milli + 0: "", + 3: "k", // kilo + 6: "M", // mega + 9: "G", // giga + 12: "T", // tera + 15: "P", // peta + 18: "E", // exa + 21: "Z", // zetta + 24: "Y", // yotta +} + +var revSIPrefixTable = revfmap(siPrefixTable) + +// revfmap reverses the map and precomputes the power multiplier +func revfmap(in map[float64]string) map[string]float64 { + rv := map[string]float64{} + for k, v := range in { + rv[v] = math.Pow(10, k) + } + return rv +} + +var riParseRegex *regexp.Regexp + +func init() { + ri := `^([\-0-9.]+)\s?([` + for _, v := range siPrefixTable { + ri += v + } + ri += `]?)(.*)` + + riParseRegex = regexp.MustCompile(ri) +} + +// ComputeSI finds the most appropriate SI prefix for the given number +// and returns the prefix along with the value adjusted to be within +// that prefix. +// +// See also: SI, ParseSI. +// +// e.g. ComputeSI(2.2345e-12) -> (2.2345, "p") +func ComputeSI(input float64) (float64, string) { + if input == 0 { + return 0, "" + } + mag := math.Abs(input) + exponent := math.Floor(logn(mag, 10)) + exponent = math.Floor(exponent/3) * 3 + + value := mag / math.Pow(10, exponent) + + // Handle special case where value is exactly 1000.0 + // Should return 1 M instead of 1000 k + if value == 1000.0 { + exponent += 3 + value = mag / math.Pow(10, exponent) + } + + value = math.Copysign(value, input) + + prefix := siPrefixTable[exponent] + return value, prefix +} + +// SI returns a string with default formatting. +// +// SI uses Ftoa to format float value, removing trailing zeros. +// +// See also: ComputeSI, ParseSI. +// +// e.g. SI(1000000, "B") -> 1 MB +// e.g. SI(2.2345e-12, "F") -> 2.2345 pF +func SI(input float64, unit string) string { + value, prefix := ComputeSI(input) + return Ftoa(value) + " " + prefix + unit +} + +var errInvalid = errors.New("invalid input") + +// ParseSI parses an SI string back into the number and unit. +// +// See also: SI, ComputeSI. +// +// e.g. ParseSI("2.2345 pF") -> (2.2345e-12, "F", nil) +func ParseSI(input string) (float64, string, error) { + found := riParseRegex.FindStringSubmatch(input) + if len(found) != 4 { + return 0, "", errInvalid + } + mag := revSIPrefixTable[found[2]] + unit := found[3] + + base, err := strconv.ParseFloat(found[1], 64) + return base * mag, unit, err +} diff --git a/vendor/github.com/dustin/go-humanize/times.go b/vendor/github.com/dustin/go-humanize/times.go new file mode 100644 index 000000000..b311f11c8 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/times.go @@ -0,0 +1,117 @@ +package humanize + +import ( + "fmt" + "math" + "sort" + "time" +) + +// Seconds-based time units +const ( + Day = 24 * time.Hour + Week = 7 * Day + Month = 30 * Day + Year = 12 * Month + LongTime = 37 * Year +) + +// Time formats a time into a relative string. +// +// Time(someT) -> "3 weeks ago" +func Time(then time.Time) string { + return RelTime(then, time.Now(), "ago", "from now") +} + +// A RelTimeMagnitude struct contains a relative time point at which +// the relative format of time will switch to a new format string. A +// slice of these in ascending order by their "D" field is passed to +// CustomRelTime to format durations. +// +// The Format field is a string that may contain a "%s" which will be +// replaced with the appropriate signed label (e.g. "ago" or "from +// now") and a "%d" that will be replaced by the quantity. +// +// The DivBy field is the amount of time the time difference must be +// divided by in order to display correctly. +// +// e.g. if D is 2*time.Minute and you want to display "%d minutes %s" +// DivBy should be time.Minute so whatever the duration is will be +// expressed in minutes. +type RelTimeMagnitude struct { + D time.Duration + Format string + DivBy time.Duration +} + +var defaultMagnitudes = []RelTimeMagnitude{ + {time.Second, "now", time.Second}, + {2 * time.Second, "1 second %s", 1}, + {time.Minute, "%d seconds %s", time.Second}, + {2 * time.Minute, "1 minute %s", 1}, + {time.Hour, "%d minutes %s", time.Minute}, + {2 * time.Hour, "1 hour %s", 1}, + {Day, "%d hours %s", time.Hour}, + {2 * Day, "1 day %s", 1}, + {Week, "%d days %s", Day}, + {2 * Week, "1 week %s", 1}, + {Month, "%d weeks %s", Week}, + {2 * Month, "1 month %s", 1}, + {Year, "%d months %s", Month}, + {18 * Month, "1 year %s", 1}, + {2 * Year, "2 years %s", 1}, + {LongTime, "%d years %s", Year}, + {math.MaxInt64, "a long while %s", 1}, +} + +// RelTime formats a time into a relative string. +// +// It takes two times and two labels. In addition to the generic time +// delta string (e.g. 5 minutes), the labels are used applied so that +// the label corresponding to the smaller time is applied. +// +// RelTime(timeInPast, timeInFuture, "earlier", "later") -> "3 weeks earlier" +func RelTime(a, b time.Time, albl, blbl string) string { + return CustomRelTime(a, b, albl, blbl, defaultMagnitudes) +} + +// CustomRelTime formats a time into a relative string. +// +// It takes two times two labels and a table of relative time formats. +// In addition to the generic time delta string (e.g. 5 minutes), the +// labels are used applied so that the label corresponding to the +// smaller time is applied. +func CustomRelTime(a, b time.Time, albl, blbl string, magnitudes []RelTimeMagnitude) string { + lbl := albl + diff := b.Sub(a) + + if a.After(b) { + lbl = blbl + diff = a.Sub(b) + } + + n := sort.Search(len(magnitudes), func(i int) bool { + return magnitudes[i].D >= diff + }) + + if n >= len(magnitudes) { + n = len(magnitudes) - 1 + } + mag := magnitudes[n] + args := []interface{}{} + escaped := false + for _, ch := range mag.Format { + if escaped { + switch ch { + case 's': + args = append(args, lbl) + case 'd': + args = append(args, diff/mag.DivBy) + } + escaped = false + } else { + escaped = ch == '%' + } + } + return fmt.Sprintf(mag.Format, args...) +} diff --git a/vendor/github.com/gorilla/websocket/AUTHORS b/vendor/github.com/gorilla/websocket/AUTHORS new file mode 100644 index 000000000..b003eca0c --- /dev/null +++ b/vendor/github.com/gorilla/websocket/AUTHORS @@ -0,0 +1,8 @@ +# This is the official list of Gorilla WebSocket authors for copyright +# purposes. +# +# Please keep the list sorted. + +Gary Burd +Joachim Bauch + diff --git a/vendor/github.com/gorilla/websocket/LICENSE b/vendor/github.com/gorilla/websocket/LICENSE new file mode 100644 index 000000000..9171c9722 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/gorilla/websocket/README.md b/vendor/github.com/gorilla/websocket/README.md new file mode 100644 index 000000000..33c3d2be3 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/README.md @@ -0,0 +1,64 @@ +# Gorilla WebSocket + +Gorilla WebSocket is a [Go](http://golang.org/) implementation of the +[WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. + +[![Build Status](https://travis-ci.org/gorilla/websocket.svg?branch=master)](https://travis-ci.org/gorilla/websocket) +[![GoDoc](https://godoc.org/github.com/gorilla/websocket?status.svg)](https://godoc.org/github.com/gorilla/websocket) + +### Documentation + +* [API Reference](http://godoc.org/github.com/gorilla/websocket) +* [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat) +* [Command example](https://github.com/gorilla/websocket/tree/master/examples/command) +* [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo) +* [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch) + +### Status + +The Gorilla WebSocket package provides a complete and tested implementation of +the [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. The +package API is stable. + +### Installation + + go get github.com/gorilla/websocket + +### Protocol Compliance + +The Gorilla WebSocket package passes the server tests in the [Autobahn Test +Suite](http://autobahn.ws/testsuite) using the application in the [examples/autobahn +subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn). + +### Gorilla WebSocket compared with other packages + + + + + + + + + + + + + + + + + + +
github.com/gorillagolang.org/x/net
RFC 6455 Features
Passes Autobahn Test SuiteYesNo
Receive fragmented messageYesNo, see note 1
Send close messageYesNo
Send pings and receive pongsYesNo
Get the type of a received data messageYesYes, see note 2
Other Features
Compression ExtensionsExperimentalNo
Read message using io.ReaderYesNo, see note 3
Write message using io.WriteCloserYesNo, see note 3
+ +Notes: + +1. Large messages are fragmented in [Chrome's new WebSocket implementation](http://www.ietf.org/mail-archive/web/hybi/current/msg10503.html). +2. The application can get the type of a received data message by implementing + a [Codec marshal](http://godoc.org/golang.org/x/net/websocket#Codec.Marshal) + function. +3. The go.net io.Reader and io.Writer operate across WebSocket frame boundaries. + Read returns when the input buffer is full or a frame boundary is + encountered. Each call to Write sends a single frame message. The Gorilla + io.Reader and io.WriteCloser operate on a single WebSocket message. + diff --git a/vendor/github.com/gorilla/websocket/client.go b/vendor/github.com/gorilla/websocket/client.go new file mode 100644 index 000000000..43a87c753 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/client.go @@ -0,0 +1,392 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bufio" + "bytes" + "crypto/tls" + "encoding/base64" + "errors" + "io" + "io/ioutil" + "net" + "net/http" + "net/url" + "strings" + "time" +) + +// ErrBadHandshake is returned when the server response to opening handshake is +// invalid. +var ErrBadHandshake = errors.New("websocket: bad handshake") + +var errInvalidCompression = errors.New("websocket: invalid compression negotiation") + +// NewClient creates a new client connection using the given net connection. +// The URL u specifies the host and request URI. Use requestHeader to specify +// the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies +// (Cookie). Use the response.Header to get the selected subprotocol +// (Sec-WebSocket-Protocol) and cookies (Set-Cookie). +// +// If the WebSocket handshake fails, ErrBadHandshake is returned along with a +// non-nil *http.Response so that callers can handle redirects, authentication, +// etc. +// +// Deprecated: Use Dialer instead. +func NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufSize, writeBufSize int) (c *Conn, response *http.Response, err error) { + d := Dialer{ + ReadBufferSize: readBufSize, + WriteBufferSize: writeBufSize, + NetDial: func(net, addr string) (net.Conn, error) { + return netConn, nil + }, + } + return d.Dial(u.String(), requestHeader) +} + +// A Dialer contains options for connecting to WebSocket server. +type Dialer struct { + // NetDial specifies the dial function for creating TCP connections. If + // NetDial is nil, net.Dial is used. + NetDial func(network, addr string) (net.Conn, error) + + // Proxy specifies a function to return a proxy for a given + // Request. If the function returns a non-nil error, the + // request is aborted with the provided error. + // If Proxy is nil or returns a nil *URL, no proxy is used. + Proxy func(*http.Request) (*url.URL, error) + + // TLSClientConfig specifies the TLS configuration to use with tls.Client. + // If nil, the default configuration is used. + TLSClientConfig *tls.Config + + // HandshakeTimeout specifies the duration for the handshake to complete. + HandshakeTimeout time.Duration + + // ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer + // size is zero, then a useful default size is used. The I/O buffer sizes + // do not limit the size of the messages that can be sent or received. + ReadBufferSize, WriteBufferSize int + + // Subprotocols specifies the client's requested subprotocols. + Subprotocols []string + + // EnableCompression specifies if the client should attempt to negotiate + // per message compression (RFC 7692). Setting this value to true does not + // guarantee that compression will be supported. Currently only "no context + // takeover" modes are supported. + EnableCompression bool + + // Jar specifies the cookie jar. + // If Jar is nil, cookies are not sent in requests and ignored + // in responses. + Jar http.CookieJar +} + +var errMalformedURL = errors.New("malformed ws or wss URL") + +// parseURL parses the URL. +// +// This function is a replacement for the standard library url.Parse function. +// In Go 1.4 and earlier, url.Parse loses information from the path. +func parseURL(s string) (*url.URL, error) { + // From the RFC: + // + // ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ] + // wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ] + var u url.URL + switch { + case strings.HasPrefix(s, "ws://"): + u.Scheme = "ws" + s = s[len("ws://"):] + case strings.HasPrefix(s, "wss://"): + u.Scheme = "wss" + s = s[len("wss://"):] + default: + return nil, errMalformedURL + } + + if i := strings.Index(s, "?"); i >= 0 { + u.RawQuery = s[i+1:] + s = s[:i] + } + + if i := strings.Index(s, "/"); i >= 0 { + u.Opaque = s[i:] + s = s[:i] + } else { + u.Opaque = "/" + } + + u.Host = s + + if strings.Contains(u.Host, "@") { + // Don't bother parsing user information because user information is + // not allowed in websocket URIs. + return nil, errMalformedURL + } + + return &u, nil +} + +func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) { + hostPort = u.Host + hostNoPort = u.Host + if i := strings.LastIndex(u.Host, ":"); i > strings.LastIndex(u.Host, "]") { + hostNoPort = hostNoPort[:i] + } else { + switch u.Scheme { + case "wss": + hostPort += ":443" + case "https": + hostPort += ":443" + default: + hostPort += ":80" + } + } + return hostPort, hostNoPort +} + +// DefaultDialer is a dialer with all fields set to the default zero values. +var DefaultDialer = &Dialer{ + Proxy: http.ProxyFromEnvironment, +} + +// Dial creates a new client connection. Use requestHeader to specify the +// origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies (Cookie). +// Use the response.Header to get the selected subprotocol +// (Sec-WebSocket-Protocol) and cookies (Set-Cookie). +// +// If the WebSocket handshake fails, ErrBadHandshake is returned along with a +// non-nil *http.Response so that callers can handle redirects, authentication, +// etcetera. The response body may not contain the entire response and does not +// need to be closed by the application. +func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) { + + if d == nil { + d = &Dialer{ + Proxy: http.ProxyFromEnvironment, + } + } + + challengeKey, err := generateChallengeKey() + if err != nil { + return nil, nil, err + } + + u, err := parseURL(urlStr) + if err != nil { + return nil, nil, err + } + + switch u.Scheme { + case "ws": + u.Scheme = "http" + case "wss": + u.Scheme = "https" + default: + return nil, nil, errMalformedURL + } + + if u.User != nil { + // User name and password are not allowed in websocket URIs. + return nil, nil, errMalformedURL + } + + req := &http.Request{ + Method: "GET", + URL: u, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + Header: make(http.Header), + Host: u.Host, + } + + // Set the cookies present in the cookie jar of the dialer + if d.Jar != nil { + for _, cookie := range d.Jar.Cookies(u) { + req.AddCookie(cookie) + } + } + + // Set the request headers using the capitalization for names and values in + // RFC examples. Although the capitalization shouldn't matter, there are + // servers that depend on it. The Header.Set method is not used because the + // method canonicalizes the header names. + req.Header["Upgrade"] = []string{"websocket"} + req.Header["Connection"] = []string{"Upgrade"} + req.Header["Sec-WebSocket-Key"] = []string{challengeKey} + req.Header["Sec-WebSocket-Version"] = []string{"13"} + if len(d.Subprotocols) > 0 { + req.Header["Sec-WebSocket-Protocol"] = []string{strings.Join(d.Subprotocols, ", ")} + } + for k, vs := range requestHeader { + switch { + case k == "Host": + if len(vs) > 0 { + req.Host = vs[0] + } + case k == "Upgrade" || + k == "Connection" || + k == "Sec-Websocket-Key" || + k == "Sec-Websocket-Version" || + k == "Sec-Websocket-Extensions" || + (k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0): + return nil, nil, errors.New("websocket: duplicate header not allowed: " + k) + default: + req.Header[k] = vs + } + } + + if d.EnableCompression { + req.Header.Set("Sec-Websocket-Extensions", "permessage-deflate; server_no_context_takeover; client_no_context_takeover") + } + + hostPort, hostNoPort := hostPortNoPort(u) + + var proxyURL *url.URL + // Check wether the proxy method has been configured + if d.Proxy != nil { + proxyURL, err = d.Proxy(req) + } + if err != nil { + return nil, nil, err + } + + var targetHostPort string + if proxyURL != nil { + targetHostPort, _ = hostPortNoPort(proxyURL) + } else { + targetHostPort = hostPort + } + + var deadline time.Time + if d.HandshakeTimeout != 0 { + deadline = time.Now().Add(d.HandshakeTimeout) + } + + netDial := d.NetDial + if netDial == nil { + netDialer := &net.Dialer{Deadline: deadline} + netDial = netDialer.Dial + } + + netConn, err := netDial("tcp", targetHostPort) + if err != nil { + return nil, nil, err + } + + defer func() { + if netConn != nil { + netConn.Close() + } + }() + + if err := netConn.SetDeadline(deadline); err != nil { + return nil, nil, err + } + + if proxyURL != nil { + connectHeader := make(http.Header) + if user := proxyURL.User; user != nil { + proxyUser := user.Username() + if proxyPassword, passwordSet := user.Password(); passwordSet { + credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword)) + connectHeader.Set("Proxy-Authorization", "Basic "+credential) + } + } + connectReq := &http.Request{ + Method: "CONNECT", + URL: &url.URL{Opaque: hostPort}, + Host: hostPort, + Header: connectHeader, + } + + connectReq.Write(netConn) + + // Read response. + // Okay to use and discard buffered reader here, because + // TLS server will not speak until spoken to. + br := bufio.NewReader(netConn) + resp, err := http.ReadResponse(br, connectReq) + if err != nil { + return nil, nil, err + } + if resp.StatusCode != 200 { + f := strings.SplitN(resp.Status, " ", 2) + return nil, nil, errors.New(f[1]) + } + } + + if u.Scheme == "https" { + cfg := cloneTLSConfig(d.TLSClientConfig) + if cfg.ServerName == "" { + cfg.ServerName = hostNoPort + } + tlsConn := tls.Client(netConn, cfg) + netConn = tlsConn + if err := tlsConn.Handshake(); err != nil { + return nil, nil, err + } + if !cfg.InsecureSkipVerify { + if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil { + return nil, nil, err + } + } + } + + conn := newConn(netConn, false, d.ReadBufferSize, d.WriteBufferSize) + + if err := req.Write(netConn); err != nil { + return nil, nil, err + } + + resp, err := http.ReadResponse(conn.br, req) + if err != nil { + return nil, nil, err + } + + if d.Jar != nil { + if rc := resp.Cookies(); len(rc) > 0 { + d.Jar.SetCookies(u, rc) + } + } + + if resp.StatusCode != 101 || + !strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") || + !strings.EqualFold(resp.Header.Get("Connection"), "upgrade") || + resp.Header.Get("Sec-Websocket-Accept") != computeAcceptKey(challengeKey) { + // Before closing the network connection on return from this + // function, slurp up some of the response to aid application + // debugging. + buf := make([]byte, 1024) + n, _ := io.ReadFull(resp.Body, buf) + resp.Body = ioutil.NopCloser(bytes.NewReader(buf[:n])) + return nil, resp, ErrBadHandshake + } + + for _, ext := range parseExtensions(resp.Header) { + if ext[""] != "permessage-deflate" { + continue + } + _, snct := ext["server_no_context_takeover"] + _, cnct := ext["client_no_context_takeover"] + if !snct || !cnct { + return nil, resp, errInvalidCompression + } + conn.newCompressionWriter = compressNoContextTakeover + conn.newDecompressionReader = decompressNoContextTakeover + break + } + + resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{})) + conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol") + + netConn.SetDeadline(time.Time{}) + netConn = nil // to avoid close in defer. + return conn, resp, nil +} diff --git a/vendor/github.com/gorilla/websocket/client_clone.go b/vendor/github.com/gorilla/websocket/client_clone.go new file mode 100644 index 000000000..4f0d94372 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/client_clone.go @@ -0,0 +1,16 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.8 + +package websocket + +import "crypto/tls" + +func cloneTLSConfig(cfg *tls.Config) *tls.Config { + if cfg == nil { + return &tls.Config{} + } + return cfg.Clone() +} diff --git a/vendor/github.com/gorilla/websocket/client_clone_legacy.go b/vendor/github.com/gorilla/websocket/client_clone_legacy.go new file mode 100644 index 000000000..babb007fb --- /dev/null +++ b/vendor/github.com/gorilla/websocket/client_clone_legacy.go @@ -0,0 +1,38 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.8 + +package websocket + +import "crypto/tls" + +// cloneTLSConfig clones all public fields except the fields +// SessionTicketsDisabled and SessionTicketKey. This avoids copying the +// sync.Mutex in the sync.Once and makes it safe to call cloneTLSConfig on a +// config in active use. +func cloneTLSConfig(cfg *tls.Config) *tls.Config { + if cfg == nil { + return &tls.Config{} + } + return &tls.Config{ + Rand: cfg.Rand, + Time: cfg.Time, + Certificates: cfg.Certificates, + NameToCertificate: cfg.NameToCertificate, + GetCertificate: cfg.GetCertificate, + RootCAs: cfg.RootCAs, + NextProtos: cfg.NextProtos, + ServerName: cfg.ServerName, + ClientAuth: cfg.ClientAuth, + ClientCAs: cfg.ClientCAs, + InsecureSkipVerify: cfg.InsecureSkipVerify, + CipherSuites: cfg.CipherSuites, + PreferServerCipherSuites: cfg.PreferServerCipherSuites, + ClientSessionCache: cfg.ClientSessionCache, + MinVersion: cfg.MinVersion, + MaxVersion: cfg.MaxVersion, + CurvePreferences: cfg.CurvePreferences, + } +} diff --git a/vendor/github.com/gorilla/websocket/compression.go b/vendor/github.com/gorilla/websocket/compression.go new file mode 100644 index 000000000..813ffb1e8 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/compression.go @@ -0,0 +1,148 @@ +// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "compress/flate" + "errors" + "io" + "strings" + "sync" +) + +const ( + minCompressionLevel = -2 // flate.HuffmanOnly not defined in Go < 1.6 + maxCompressionLevel = flate.BestCompression + defaultCompressionLevel = 1 +) + +var ( + flateWriterPools [maxCompressionLevel - minCompressionLevel + 1]sync.Pool + flateReaderPool = sync.Pool{New: func() interface{} { + return flate.NewReader(nil) + }} +) + +func decompressNoContextTakeover(r io.Reader) io.ReadCloser { + const tail = + // Add four bytes as specified in RFC + "\x00\x00\xff\xff" + + // Add final block to squelch unexpected EOF error from flate reader. + "\x01\x00\x00\xff\xff" + + fr, _ := flateReaderPool.Get().(io.ReadCloser) + fr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil) + return &flateReadWrapper{fr} +} + +func isValidCompressionLevel(level int) bool { + return minCompressionLevel <= level && level <= maxCompressionLevel +} + +func compressNoContextTakeover(w io.WriteCloser, level int) io.WriteCloser { + p := &flateWriterPools[level-minCompressionLevel] + tw := &truncWriter{w: w} + fw, _ := p.Get().(*flate.Writer) + if fw == nil { + fw, _ = flate.NewWriter(tw, level) + } else { + fw.Reset(tw) + } + return &flateWriteWrapper{fw: fw, tw: tw, p: p} +} + +// truncWriter is an io.Writer that writes all but the last four bytes of the +// stream to another io.Writer. +type truncWriter struct { + w io.WriteCloser + n int + p [4]byte +} + +func (w *truncWriter) Write(p []byte) (int, error) { + n := 0 + + // fill buffer first for simplicity. + if w.n < len(w.p) { + n = copy(w.p[w.n:], p) + p = p[n:] + w.n += n + if len(p) == 0 { + return n, nil + } + } + + m := len(p) + if m > len(w.p) { + m = len(w.p) + } + + if nn, err := w.w.Write(w.p[:m]); err != nil { + return n + nn, err + } + + copy(w.p[:], w.p[m:]) + copy(w.p[len(w.p)-m:], p[len(p)-m:]) + nn, err := w.w.Write(p[:len(p)-m]) + return n + nn, err +} + +type flateWriteWrapper struct { + fw *flate.Writer + tw *truncWriter + p *sync.Pool +} + +func (w *flateWriteWrapper) Write(p []byte) (int, error) { + if w.fw == nil { + return 0, errWriteClosed + } + return w.fw.Write(p) +} + +func (w *flateWriteWrapper) Close() error { + if w.fw == nil { + return errWriteClosed + } + err1 := w.fw.Flush() + w.p.Put(w.fw) + w.fw = nil + if w.tw.p != [4]byte{0, 0, 0xff, 0xff} { + return errors.New("websocket: internal error, unexpected bytes at end of flate stream") + } + err2 := w.tw.w.Close() + if err1 != nil { + return err1 + } + return err2 +} + +type flateReadWrapper struct { + fr io.ReadCloser +} + +func (r *flateReadWrapper) Read(p []byte) (int, error) { + if r.fr == nil { + return 0, io.ErrClosedPipe + } + n, err := r.fr.Read(p) + if err == io.EOF { + // Preemptively place the reader back in the pool. This helps with + // scenarios where the application does not call NextReader() soon after + // this final read. + r.Close() + } + return n, err +} + +func (r *flateReadWrapper) Close() error { + if r.fr == nil { + return io.ErrClosedPipe + } + err := r.fr.Close() + flateReaderPool.Put(r.fr) + r.fr = nil + return err +} diff --git a/vendor/github.com/gorilla/websocket/conn.go b/vendor/github.com/gorilla/websocket/conn.go new file mode 100644 index 000000000..97e1dbacb --- /dev/null +++ b/vendor/github.com/gorilla/websocket/conn.go @@ -0,0 +1,1149 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bufio" + "encoding/binary" + "errors" + "io" + "io/ioutil" + "math/rand" + "net" + "strconv" + "sync" + "time" + "unicode/utf8" +) + +const ( + // Frame header byte 0 bits from Section 5.2 of RFC 6455 + finalBit = 1 << 7 + rsv1Bit = 1 << 6 + rsv2Bit = 1 << 5 + rsv3Bit = 1 << 4 + + // Frame header byte 1 bits from Section 5.2 of RFC 6455 + maskBit = 1 << 7 + + maxFrameHeaderSize = 2 + 8 + 4 // Fixed header + length + mask + maxControlFramePayloadSize = 125 + + writeWait = time.Second + + defaultReadBufferSize = 4096 + defaultWriteBufferSize = 4096 + + continuationFrame = 0 + noFrame = -1 +) + +// Close codes defined in RFC 6455, section 11.7. +const ( + CloseNormalClosure = 1000 + CloseGoingAway = 1001 + CloseProtocolError = 1002 + CloseUnsupportedData = 1003 + CloseNoStatusReceived = 1005 + CloseAbnormalClosure = 1006 + CloseInvalidFramePayloadData = 1007 + ClosePolicyViolation = 1008 + CloseMessageTooBig = 1009 + CloseMandatoryExtension = 1010 + CloseInternalServerErr = 1011 + CloseServiceRestart = 1012 + CloseTryAgainLater = 1013 + CloseTLSHandshake = 1015 +) + +// The message types are defined in RFC 6455, section 11.8. +const ( + // TextMessage denotes a text data message. The text message payload is + // interpreted as UTF-8 encoded text data. + TextMessage = 1 + + // BinaryMessage denotes a binary data message. + BinaryMessage = 2 + + // CloseMessage denotes a close control message. The optional message + // payload contains a numeric code and text. Use the FormatCloseMessage + // function to format a close message payload. + CloseMessage = 8 + + // PingMessage denotes a ping control message. The optional message payload + // is UTF-8 encoded text. + PingMessage = 9 + + // PongMessage denotes a ping control message. The optional message payload + // is UTF-8 encoded text. + PongMessage = 10 +) + +// ErrCloseSent is returned when the application writes a message to the +// connection after sending a close message. +var ErrCloseSent = errors.New("websocket: close sent") + +// ErrReadLimit is returned when reading a message that is larger than the +// read limit set for the connection. +var ErrReadLimit = errors.New("websocket: read limit exceeded") + +// netError satisfies the net Error interface. +type netError struct { + msg string + temporary bool + timeout bool +} + +func (e *netError) Error() string { return e.msg } +func (e *netError) Temporary() bool { return e.temporary } +func (e *netError) Timeout() bool { return e.timeout } + +// CloseError represents close frame. +type CloseError struct { + + // Code is defined in RFC 6455, section 11.7. + Code int + + // Text is the optional text payload. + Text string +} + +func (e *CloseError) Error() string { + s := []byte("websocket: close ") + s = strconv.AppendInt(s, int64(e.Code), 10) + switch e.Code { + case CloseNormalClosure: + s = append(s, " (normal)"...) + case CloseGoingAway: + s = append(s, " (going away)"...) + case CloseProtocolError: + s = append(s, " (protocol error)"...) + case CloseUnsupportedData: + s = append(s, " (unsupported data)"...) + case CloseNoStatusReceived: + s = append(s, " (no status)"...) + case CloseAbnormalClosure: + s = append(s, " (abnormal closure)"...) + case CloseInvalidFramePayloadData: + s = append(s, " (invalid payload data)"...) + case ClosePolicyViolation: + s = append(s, " (policy violation)"...) + case CloseMessageTooBig: + s = append(s, " (message too big)"...) + case CloseMandatoryExtension: + s = append(s, " (mandatory extension missing)"...) + case CloseInternalServerErr: + s = append(s, " (internal server error)"...) + case CloseTLSHandshake: + s = append(s, " (TLS handshake error)"...) + } + if e.Text != "" { + s = append(s, ": "...) + s = append(s, e.Text...) + } + return string(s) +} + +// IsCloseError returns boolean indicating whether the error is a *CloseError +// with one of the specified codes. +func IsCloseError(err error, codes ...int) bool { + if e, ok := err.(*CloseError); ok { + for _, code := range codes { + if e.Code == code { + return true + } + } + } + return false +} + +// IsUnexpectedCloseError returns boolean indicating whether the error is a +// *CloseError with a code not in the list of expected codes. +func IsUnexpectedCloseError(err error, expectedCodes ...int) bool { + if e, ok := err.(*CloseError); ok { + for _, code := range expectedCodes { + if e.Code == code { + return false + } + } + return true + } + return false +} + +var ( + errWriteTimeout = &netError{msg: "websocket: write timeout", timeout: true, temporary: true} + errUnexpectedEOF = &CloseError{Code: CloseAbnormalClosure, Text: io.ErrUnexpectedEOF.Error()} + errBadWriteOpCode = errors.New("websocket: bad write message type") + errWriteClosed = errors.New("websocket: write closed") + errInvalidControlFrame = errors.New("websocket: invalid control frame") +) + +func newMaskKey() [4]byte { + n := rand.Uint32() + return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)} +} + +func hideTempErr(err error) error { + if e, ok := err.(net.Error); ok && e.Temporary() { + err = &netError{msg: e.Error(), timeout: e.Timeout()} + } + return err +} + +func isControl(frameType int) bool { + return frameType == CloseMessage || frameType == PingMessage || frameType == PongMessage +} + +func isData(frameType int) bool { + return frameType == TextMessage || frameType == BinaryMessage +} + +var validReceivedCloseCodes = map[int]bool{ + // see http://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number + + CloseNormalClosure: true, + CloseGoingAway: true, + CloseProtocolError: true, + CloseUnsupportedData: true, + CloseNoStatusReceived: false, + CloseAbnormalClosure: false, + CloseInvalidFramePayloadData: true, + ClosePolicyViolation: true, + CloseMessageTooBig: true, + CloseMandatoryExtension: true, + CloseInternalServerErr: true, + CloseServiceRestart: true, + CloseTryAgainLater: true, + CloseTLSHandshake: false, +} + +func isValidReceivedCloseCode(code int) bool { + return validReceivedCloseCodes[code] || (code >= 3000 && code <= 4999) +} + +// The Conn type represents a WebSocket connection. +type Conn struct { + conn net.Conn + isServer bool + subprotocol string + + // Write fields + mu chan bool // used as mutex to protect write to conn + writeBuf []byte // frame is constructed in this buffer. + writeDeadline time.Time + writer io.WriteCloser // the current writer returned to the application + isWriting bool // for best-effort concurrent write detection + + writeErrMu sync.Mutex + writeErr error + + enableWriteCompression bool + compressionLevel int + newCompressionWriter func(io.WriteCloser, int) io.WriteCloser + + // Read fields + reader io.ReadCloser // the current reader returned to the application + readErr error + br *bufio.Reader + readRemaining int64 // bytes remaining in current frame. + readFinal bool // true the current message has more frames. + readLength int64 // Message size. + readLimit int64 // Maximum message size. + readMaskPos int + readMaskKey [4]byte + handlePong func(string) error + handlePing func(string) error + handleClose func(int, string) error + readErrCount int + messageReader *messageReader // the current low-level reader + + readDecompress bool // whether last read frame had RSV1 set + newDecompressionReader func(io.Reader) io.ReadCloser +} + +func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int) *Conn { + return newConnBRW(conn, isServer, readBufferSize, writeBufferSize, nil) +} + +type writeHook struct { + p []byte +} + +func (wh *writeHook) Write(p []byte) (int, error) { + wh.p = p + return len(p), nil +} + +func newConnBRW(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int, brw *bufio.ReadWriter) *Conn { + mu := make(chan bool, 1) + mu <- true + + var br *bufio.Reader + if readBufferSize == 0 && brw != nil && brw.Reader != nil { + // Reuse the supplied bufio.Reader if the buffer has a useful size. + // This code assumes that peek on a reader returns + // bufio.Reader.buf[:0]. + brw.Reader.Reset(conn) + if p, err := brw.Reader.Peek(0); err == nil && cap(p) >= 256 { + br = brw.Reader + } + } + if br == nil { + if readBufferSize == 0 { + readBufferSize = defaultReadBufferSize + } + if readBufferSize < maxControlFramePayloadSize { + readBufferSize = maxControlFramePayloadSize + } + br = bufio.NewReaderSize(conn, readBufferSize) + } + + var writeBuf []byte + if writeBufferSize == 0 && brw != nil && brw.Writer != nil { + // Use the bufio.Writer's buffer if the buffer has a useful size. This + // code assumes that bufio.Writer.buf[:1] is passed to the + // bufio.Writer's underlying writer. + var wh writeHook + brw.Writer.Reset(&wh) + brw.Writer.WriteByte(0) + brw.Flush() + if cap(wh.p) >= maxFrameHeaderSize+256 { + writeBuf = wh.p[:cap(wh.p)] + } + } + + if writeBuf == nil { + if writeBufferSize == 0 { + writeBufferSize = defaultWriteBufferSize + } + writeBuf = make([]byte, writeBufferSize+maxFrameHeaderSize) + } + + c := &Conn{ + isServer: isServer, + br: br, + conn: conn, + mu: mu, + readFinal: true, + writeBuf: writeBuf, + enableWriteCompression: true, + compressionLevel: defaultCompressionLevel, + } + c.SetCloseHandler(nil) + c.SetPingHandler(nil) + c.SetPongHandler(nil) + return c +} + +// Subprotocol returns the negotiated protocol for the connection. +func (c *Conn) Subprotocol() string { + return c.subprotocol +} + +// Close closes the underlying network connection without sending or waiting for a close frame. +func (c *Conn) Close() error { + return c.conn.Close() +} + +// LocalAddr returns the local network address. +func (c *Conn) LocalAddr() net.Addr { + return c.conn.LocalAddr() +} + +// RemoteAddr returns the remote network address. +func (c *Conn) RemoteAddr() net.Addr { + return c.conn.RemoteAddr() +} + +// Write methods + +func (c *Conn) writeFatal(err error) error { + err = hideTempErr(err) + c.writeErrMu.Lock() + if c.writeErr == nil { + c.writeErr = err + } + c.writeErrMu.Unlock() + return err +} + +func (c *Conn) write(frameType int, deadline time.Time, bufs ...[]byte) error { + <-c.mu + defer func() { c.mu <- true }() + + c.writeErrMu.Lock() + err := c.writeErr + c.writeErrMu.Unlock() + if err != nil { + return err + } + + c.conn.SetWriteDeadline(deadline) + for _, buf := range bufs { + if len(buf) > 0 { + _, err := c.conn.Write(buf) + if err != nil { + return c.writeFatal(err) + } + } + } + + if frameType == CloseMessage { + c.writeFatal(ErrCloseSent) + } + return nil +} + +// WriteControl writes a control message with the given deadline. The allowed +// message types are CloseMessage, PingMessage and PongMessage. +func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) error { + if !isControl(messageType) { + return errBadWriteOpCode + } + if len(data) > maxControlFramePayloadSize { + return errInvalidControlFrame + } + + b0 := byte(messageType) | finalBit + b1 := byte(len(data)) + if !c.isServer { + b1 |= maskBit + } + + buf := make([]byte, 0, maxFrameHeaderSize+maxControlFramePayloadSize) + buf = append(buf, b0, b1) + + if c.isServer { + buf = append(buf, data...) + } else { + key := newMaskKey() + buf = append(buf, key[:]...) + buf = append(buf, data...) + maskBytes(key, 0, buf[6:]) + } + + d := time.Hour * 1000 + if !deadline.IsZero() { + d = deadline.Sub(time.Now()) + if d < 0 { + return errWriteTimeout + } + } + + timer := time.NewTimer(d) + select { + case <-c.mu: + timer.Stop() + case <-timer.C: + return errWriteTimeout + } + defer func() { c.mu <- true }() + + c.writeErrMu.Lock() + err := c.writeErr + c.writeErrMu.Unlock() + if err != nil { + return err + } + + c.conn.SetWriteDeadline(deadline) + _, err = c.conn.Write(buf) + if err != nil { + return c.writeFatal(err) + } + if messageType == CloseMessage { + c.writeFatal(ErrCloseSent) + } + return err +} + +func (c *Conn) prepWrite(messageType int) error { + // Close previous writer if not already closed by the application. It's + // probably better to return an error in this situation, but we cannot + // change this without breaking existing applications. + if c.writer != nil { + c.writer.Close() + c.writer = nil + } + + if !isControl(messageType) && !isData(messageType) { + return errBadWriteOpCode + } + + c.writeErrMu.Lock() + err := c.writeErr + c.writeErrMu.Unlock() + return err +} + +// NextWriter returns a writer for the next message to send. The writer's Close +// method flushes the complete message to the network. +// +// There can be at most one open writer on a connection. NextWriter closes the +// previous writer if the application has not already done so. +func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) { + if err := c.prepWrite(messageType); err != nil { + return nil, err + } + + mw := &messageWriter{ + c: c, + frameType: messageType, + pos: maxFrameHeaderSize, + } + c.writer = mw + if c.newCompressionWriter != nil && c.enableWriteCompression && isData(messageType) { + w := c.newCompressionWriter(c.writer, c.compressionLevel) + mw.compress = true + c.writer = w + } + return c.writer, nil +} + +type messageWriter struct { + c *Conn + compress bool // whether next call to flushFrame should set RSV1 + pos int // end of data in writeBuf. + frameType int // type of the current frame. + err error +} + +func (w *messageWriter) fatal(err error) error { + if w.err != nil { + w.err = err + w.c.writer = nil + } + return err +} + +// flushFrame writes buffered data and extra as a frame to the network. The +// final argument indicates that this is the last frame in the message. +func (w *messageWriter) flushFrame(final bool, extra []byte) error { + c := w.c + length := w.pos - maxFrameHeaderSize + len(extra) + + // Check for invalid control frames. + if isControl(w.frameType) && + (!final || length > maxControlFramePayloadSize) { + return w.fatal(errInvalidControlFrame) + } + + b0 := byte(w.frameType) + if final { + b0 |= finalBit + } + if w.compress { + b0 |= rsv1Bit + } + w.compress = false + + b1 := byte(0) + if !c.isServer { + b1 |= maskBit + } + + // Assume that the frame starts at beginning of c.writeBuf. + framePos := 0 + if c.isServer { + // Adjust up if mask not included in the header. + framePos = 4 + } + + switch { + case length >= 65536: + c.writeBuf[framePos] = b0 + c.writeBuf[framePos+1] = b1 | 127 + binary.BigEndian.PutUint64(c.writeBuf[framePos+2:], uint64(length)) + case length > 125: + framePos += 6 + c.writeBuf[framePos] = b0 + c.writeBuf[framePos+1] = b1 | 126 + binary.BigEndian.PutUint16(c.writeBuf[framePos+2:], uint16(length)) + default: + framePos += 8 + c.writeBuf[framePos] = b0 + c.writeBuf[framePos+1] = b1 | byte(length) + } + + if !c.isServer { + key := newMaskKey() + copy(c.writeBuf[maxFrameHeaderSize-4:], key[:]) + maskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:w.pos]) + if len(extra) > 0 { + return c.writeFatal(errors.New("websocket: internal error, extra used in client mode")) + } + } + + // Write the buffers to the connection with best-effort detection of + // concurrent writes. See the concurrency section in the package + // documentation for more info. + + if c.isWriting { + panic("concurrent write to websocket connection") + } + c.isWriting = true + + err := c.write(w.frameType, c.writeDeadline, c.writeBuf[framePos:w.pos], extra) + + if !c.isWriting { + panic("concurrent write to websocket connection") + } + c.isWriting = false + + if err != nil { + return w.fatal(err) + } + + if final { + c.writer = nil + return nil + } + + // Setup for next frame. + w.pos = maxFrameHeaderSize + w.frameType = continuationFrame + return nil +} + +func (w *messageWriter) ncopy(max int) (int, error) { + n := len(w.c.writeBuf) - w.pos + if n <= 0 { + if err := w.flushFrame(false, nil); err != nil { + return 0, err + } + n = len(w.c.writeBuf) - w.pos + } + if n > max { + n = max + } + return n, nil +} + +func (w *messageWriter) Write(p []byte) (int, error) { + if w.err != nil { + return 0, w.err + } + + if len(p) > 2*len(w.c.writeBuf) && w.c.isServer { + // Don't buffer large messages. + err := w.flushFrame(false, p) + if err != nil { + return 0, err + } + return len(p), nil + } + + nn := len(p) + for len(p) > 0 { + n, err := w.ncopy(len(p)) + if err != nil { + return 0, err + } + copy(w.c.writeBuf[w.pos:], p[:n]) + w.pos += n + p = p[n:] + } + return nn, nil +} + +func (w *messageWriter) WriteString(p string) (int, error) { + if w.err != nil { + return 0, w.err + } + + nn := len(p) + for len(p) > 0 { + n, err := w.ncopy(len(p)) + if err != nil { + return 0, err + } + copy(w.c.writeBuf[w.pos:], p[:n]) + w.pos += n + p = p[n:] + } + return nn, nil +} + +func (w *messageWriter) ReadFrom(r io.Reader) (nn int64, err error) { + if w.err != nil { + return 0, w.err + } + for { + if w.pos == len(w.c.writeBuf) { + err = w.flushFrame(false, nil) + if err != nil { + break + } + } + var n int + n, err = r.Read(w.c.writeBuf[w.pos:]) + w.pos += n + nn += int64(n) + if err != nil { + if err == io.EOF { + err = nil + } + break + } + } + return nn, err +} + +func (w *messageWriter) Close() error { + if w.err != nil { + return w.err + } + if err := w.flushFrame(true, nil); err != nil { + return err + } + w.err = errWriteClosed + return nil +} + +// WritePreparedMessage writes prepared message into connection. +func (c *Conn) WritePreparedMessage(pm *PreparedMessage) error { + frameType, frameData, err := pm.frame(prepareKey{ + isServer: c.isServer, + compress: c.newCompressionWriter != nil && c.enableWriteCompression && isData(pm.messageType), + compressionLevel: c.compressionLevel, + }) + if err != nil { + return err + } + if c.isWriting { + panic("concurrent write to websocket connection") + } + c.isWriting = true + err = c.write(frameType, c.writeDeadline, frameData, nil) + if !c.isWriting { + panic("concurrent write to websocket connection") + } + c.isWriting = false + return err +} + +// WriteMessage is a helper method for getting a writer using NextWriter, +// writing the message and closing the writer. +func (c *Conn) WriteMessage(messageType int, data []byte) error { + + if c.isServer && (c.newCompressionWriter == nil || !c.enableWriteCompression) { + // Fast path with no allocations and single frame. + + if err := c.prepWrite(messageType); err != nil { + return err + } + mw := messageWriter{c: c, frameType: messageType, pos: maxFrameHeaderSize} + n := copy(c.writeBuf[mw.pos:], data) + mw.pos += n + data = data[n:] + return mw.flushFrame(true, data) + } + + w, err := c.NextWriter(messageType) + if err != nil { + return err + } + if _, err = w.Write(data); err != nil { + return err + } + return w.Close() +} + +// SetWriteDeadline sets the write deadline on the underlying network +// connection. After a write has timed out, the websocket state is corrupt and +// all future writes will return an error. A zero value for t means writes will +// not time out. +func (c *Conn) SetWriteDeadline(t time.Time) error { + c.writeDeadline = t + return nil +} + +// Read methods + +func (c *Conn) advanceFrame() (int, error) { + + // 1. Skip remainder of previous frame. + + if c.readRemaining > 0 { + if _, err := io.CopyN(ioutil.Discard, c.br, c.readRemaining); err != nil { + return noFrame, err + } + } + + // 2. Read and parse first two bytes of frame header. + + p, err := c.read(2) + if err != nil { + return noFrame, err + } + + final := p[0]&finalBit != 0 + frameType := int(p[0] & 0xf) + mask := p[1]&maskBit != 0 + c.readRemaining = int64(p[1] & 0x7f) + + c.readDecompress = false + if c.newDecompressionReader != nil && (p[0]&rsv1Bit) != 0 { + c.readDecompress = true + p[0] &^= rsv1Bit + } + + if rsv := p[0] & (rsv1Bit | rsv2Bit | rsv3Bit); rsv != 0 { + return noFrame, c.handleProtocolError("unexpected reserved bits 0x" + strconv.FormatInt(int64(rsv), 16)) + } + + switch frameType { + case CloseMessage, PingMessage, PongMessage: + if c.readRemaining > maxControlFramePayloadSize { + return noFrame, c.handleProtocolError("control frame length > 125") + } + if !final { + return noFrame, c.handleProtocolError("control frame not final") + } + case TextMessage, BinaryMessage: + if !c.readFinal { + return noFrame, c.handleProtocolError("message start before final message frame") + } + c.readFinal = final + case continuationFrame: + if c.readFinal { + return noFrame, c.handleProtocolError("continuation after final message frame") + } + c.readFinal = final + default: + return noFrame, c.handleProtocolError("unknown opcode " + strconv.Itoa(frameType)) + } + + // 3. Read and parse frame length. + + switch c.readRemaining { + case 126: + p, err := c.read(2) + if err != nil { + return noFrame, err + } + c.readRemaining = int64(binary.BigEndian.Uint16(p)) + case 127: + p, err := c.read(8) + if err != nil { + return noFrame, err + } + c.readRemaining = int64(binary.BigEndian.Uint64(p)) + } + + // 4. Handle frame masking. + + if mask != c.isServer { + return noFrame, c.handleProtocolError("incorrect mask flag") + } + + if mask { + c.readMaskPos = 0 + p, err := c.read(len(c.readMaskKey)) + if err != nil { + return noFrame, err + } + copy(c.readMaskKey[:], p) + } + + // 5. For text and binary messages, enforce read limit and return. + + if frameType == continuationFrame || frameType == TextMessage || frameType == BinaryMessage { + + c.readLength += c.readRemaining + if c.readLimit > 0 && c.readLength > c.readLimit { + c.WriteControl(CloseMessage, FormatCloseMessage(CloseMessageTooBig, ""), time.Now().Add(writeWait)) + return noFrame, ErrReadLimit + } + + return frameType, nil + } + + // 6. Read control frame payload. + + var payload []byte + if c.readRemaining > 0 { + payload, err = c.read(int(c.readRemaining)) + c.readRemaining = 0 + if err != nil { + return noFrame, err + } + if c.isServer { + maskBytes(c.readMaskKey, 0, payload) + } + } + + // 7. Process control frame payload. + + switch frameType { + case PongMessage: + if err := c.handlePong(string(payload)); err != nil { + return noFrame, err + } + case PingMessage: + if err := c.handlePing(string(payload)); err != nil { + return noFrame, err + } + case CloseMessage: + closeCode := CloseNoStatusReceived + closeText := "" + if len(payload) >= 2 { + closeCode = int(binary.BigEndian.Uint16(payload)) + if !isValidReceivedCloseCode(closeCode) { + return noFrame, c.handleProtocolError("invalid close code") + } + closeText = string(payload[2:]) + if !utf8.ValidString(closeText) { + return noFrame, c.handleProtocolError("invalid utf8 payload in close frame") + } + } + if err := c.handleClose(closeCode, closeText); err != nil { + return noFrame, err + } + return noFrame, &CloseError{Code: closeCode, Text: closeText} + } + + return frameType, nil +} + +func (c *Conn) handleProtocolError(message string) error { + c.WriteControl(CloseMessage, FormatCloseMessage(CloseProtocolError, message), time.Now().Add(writeWait)) + return errors.New("websocket: " + message) +} + +// NextReader returns the next data message received from the peer. The +// returned messageType is either TextMessage or BinaryMessage. +// +// There can be at most one open reader on a connection. NextReader discards +// the previous message if the application has not already consumed it. +// +// Applications must break out of the application's read loop when this method +// returns a non-nil error value. Errors returned from this method are +// permanent. Once this method returns a non-nil error, all subsequent calls to +// this method return the same error. +func (c *Conn) NextReader() (messageType int, r io.Reader, err error) { + // Close previous reader, only relevant for decompression. + if c.reader != nil { + c.reader.Close() + c.reader = nil + } + + c.messageReader = nil + c.readLength = 0 + + for c.readErr == nil { + frameType, err := c.advanceFrame() + if err != nil { + c.readErr = hideTempErr(err) + break + } + if frameType == TextMessage || frameType == BinaryMessage { + c.messageReader = &messageReader{c} + c.reader = c.messageReader + if c.readDecompress { + c.reader = c.newDecompressionReader(c.reader) + } + return frameType, c.reader, nil + } + } + + // Applications that do handle the error returned from this method spin in + // tight loop on connection failure. To help application developers detect + // this error, panic on repeated reads to the failed connection. + c.readErrCount++ + if c.readErrCount >= 1000 { + panic("repeated read on failed websocket connection") + } + + return noFrame, nil, c.readErr +} + +type messageReader struct{ c *Conn } + +func (r *messageReader) Read(b []byte) (int, error) { + c := r.c + if c.messageReader != r { + return 0, io.EOF + } + + for c.readErr == nil { + + if c.readRemaining > 0 { + if int64(len(b)) > c.readRemaining { + b = b[:c.readRemaining] + } + n, err := c.br.Read(b) + c.readErr = hideTempErr(err) + if c.isServer { + c.readMaskPos = maskBytes(c.readMaskKey, c.readMaskPos, b[:n]) + } + c.readRemaining -= int64(n) + if c.readRemaining > 0 && c.readErr == io.EOF { + c.readErr = errUnexpectedEOF + } + return n, c.readErr + } + + if c.readFinal { + c.messageReader = nil + return 0, io.EOF + } + + frameType, err := c.advanceFrame() + switch { + case err != nil: + c.readErr = hideTempErr(err) + case frameType == TextMessage || frameType == BinaryMessage: + c.readErr = errors.New("websocket: internal error, unexpected text or binary in Reader") + } + } + + err := c.readErr + if err == io.EOF && c.messageReader == r { + err = errUnexpectedEOF + } + return 0, err +} + +func (r *messageReader) Close() error { + return nil +} + +// ReadMessage is a helper method for getting a reader using NextReader and +// reading from that reader to a buffer. +func (c *Conn) ReadMessage() (messageType int, p []byte, err error) { + var r io.Reader + messageType, r, err = c.NextReader() + if err != nil { + return messageType, nil, err + } + p, err = ioutil.ReadAll(r) + return messageType, p, err +} + +// SetReadDeadline sets the read deadline on the underlying network connection. +// After a read has timed out, the websocket connection state is corrupt and +// all future reads will return an error. A zero value for t means reads will +// not time out. +func (c *Conn) SetReadDeadline(t time.Time) error { + return c.conn.SetReadDeadline(t) +} + +// SetReadLimit sets the maximum size for a message read from the peer. If a +// message exceeds the limit, the connection sends a close frame to the peer +// and returns ErrReadLimit to the application. +func (c *Conn) SetReadLimit(limit int64) { + c.readLimit = limit +} + +// CloseHandler returns the current close handler +func (c *Conn) CloseHandler() func(code int, text string) error { + return c.handleClose +} + +// SetCloseHandler sets the handler for close messages received from the peer. +// The code argument to h is the received close code or CloseNoStatusReceived +// if the close message is empty. The default close handler sends a close frame +// back to the peer. +// +// The application must read the connection to process close messages as +// described in the section on Control Frames above. +// +// The connection read methods return a CloseError when a close frame is +// received. Most applications should handle close messages as part of their +// normal error handling. Applications should only set a close handler when the +// application must perform some action before sending a close frame back to +// the peer. +func (c *Conn) SetCloseHandler(h func(code int, text string) error) { + if h == nil { + h = func(code int, text string) error { + message := []byte{} + if code != CloseNoStatusReceived { + message = FormatCloseMessage(code, "") + } + c.WriteControl(CloseMessage, message, time.Now().Add(writeWait)) + return nil + } + } + c.handleClose = h +} + +// PingHandler returns the current ping handler +func (c *Conn) PingHandler() func(appData string) error { + return c.handlePing +} + +// SetPingHandler sets the handler for ping messages received from the peer. +// The appData argument to h is the PING frame application data. The default +// ping handler sends a pong to the peer. +// +// The application must read the connection to process ping messages as +// described in the section on Control Frames above. +func (c *Conn) SetPingHandler(h func(appData string) error) { + if h == nil { + h = func(message string) error { + err := c.WriteControl(PongMessage, []byte(message), time.Now().Add(writeWait)) + if err == ErrCloseSent { + return nil + } else if e, ok := err.(net.Error); ok && e.Temporary() { + return nil + } + return err + } + } + c.handlePing = h +} + +// PongHandler returns the current pong handler +func (c *Conn) PongHandler() func(appData string) error { + return c.handlePong +} + +// SetPongHandler sets the handler for pong messages received from the peer. +// The appData argument to h is the PONG frame application data. The default +// pong handler does nothing. +// +// The application must read the connection to process ping messages as +// described in the section on Control Frames above. +func (c *Conn) SetPongHandler(h func(appData string) error) { + if h == nil { + h = func(string) error { return nil } + } + c.handlePong = h +} + +// UnderlyingConn returns the internal net.Conn. This can be used to further +// modifications to connection specific flags. +func (c *Conn) UnderlyingConn() net.Conn { + return c.conn +} + +// EnableWriteCompression enables and disables write compression of +// subsequent text and binary messages. This function is a noop if +// compression was not negotiated with the peer. +func (c *Conn) EnableWriteCompression(enable bool) { + c.enableWriteCompression = enable +} + +// SetCompressionLevel sets the flate compression level for subsequent text and +// binary messages. This function is a noop if compression was not negotiated +// with the peer. See the compress/flate package for a description of +// compression levels. +func (c *Conn) SetCompressionLevel(level int) error { + if !isValidCompressionLevel(level) { + return errors.New("websocket: invalid compression level") + } + c.compressionLevel = level + return nil +} + +// FormatCloseMessage formats closeCode and text as a WebSocket close message. +func FormatCloseMessage(closeCode int, text string) []byte { + buf := make([]byte, 2+len(text)) + binary.BigEndian.PutUint16(buf, uint16(closeCode)) + copy(buf[2:], text) + return buf +} diff --git a/vendor/github.com/gorilla/websocket/conn_read.go b/vendor/github.com/gorilla/websocket/conn_read.go new file mode 100644 index 000000000..1ea15059e --- /dev/null +++ b/vendor/github.com/gorilla/websocket/conn_read.go @@ -0,0 +1,18 @@ +// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.5 + +package websocket + +import "io" + +func (c *Conn) read(n int) ([]byte, error) { + p, err := c.br.Peek(n) + if err == io.EOF { + err = errUnexpectedEOF + } + c.br.Discard(len(p)) + return p, err +} diff --git a/vendor/github.com/gorilla/websocket/conn_read_legacy.go b/vendor/github.com/gorilla/websocket/conn_read_legacy.go new file mode 100644 index 000000000..018541cf6 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/conn_read_legacy.go @@ -0,0 +1,21 @@ +// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.5 + +package websocket + +import "io" + +func (c *Conn) read(n int) ([]byte, error) { + p, err := c.br.Peek(n) + if err == io.EOF { + err = errUnexpectedEOF + } + if len(p) > 0 { + // advance over the bytes just read + io.ReadFull(c.br, p) + } + return p, err +} diff --git a/vendor/github.com/gorilla/websocket/doc.go b/vendor/github.com/gorilla/websocket/doc.go new file mode 100644 index 000000000..e291a952c --- /dev/null +++ b/vendor/github.com/gorilla/websocket/doc.go @@ -0,0 +1,180 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package websocket implements the WebSocket protocol defined in RFC 6455. +// +// Overview +// +// The Conn type represents a WebSocket connection. A server application uses +// the Upgrade function from an Upgrader object with a HTTP request handler +// to get a pointer to a Conn: +// +// var upgrader = websocket.Upgrader{ +// ReadBufferSize: 1024, +// WriteBufferSize: 1024, +// } +// +// func handler(w http.ResponseWriter, r *http.Request) { +// conn, err := upgrader.Upgrade(w, r, nil) +// if err != nil { +// log.Println(err) +// return +// } +// ... Use conn to send and receive messages. +// } +// +// Call the connection's WriteMessage and ReadMessage methods to send and +// receive messages as a slice of bytes. This snippet of code shows how to echo +// messages using these methods: +// +// for { +// messageType, p, err := conn.ReadMessage() +// if err != nil { +// return +// } +// if err = conn.WriteMessage(messageType, p); err != nil { +// return err +// } +// } +// +// In above snippet of code, p is a []byte and messageType is an int with value +// websocket.BinaryMessage or websocket.TextMessage. +// +// An application can also send and receive messages using the io.WriteCloser +// and io.Reader interfaces. To send a message, call the connection NextWriter +// method to get an io.WriteCloser, write the message to the writer and close +// the writer when done. To receive a message, call the connection NextReader +// method to get an io.Reader and read until io.EOF is returned. This snippet +// shows how to echo messages using the NextWriter and NextReader methods: +// +// for { +// messageType, r, err := conn.NextReader() +// if err != nil { +// return +// } +// w, err := conn.NextWriter(messageType) +// if err != nil { +// return err +// } +// if _, err := io.Copy(w, r); err != nil { +// return err +// } +// if err := w.Close(); err != nil { +// return err +// } +// } +// +// Data Messages +// +// The WebSocket protocol distinguishes between text and binary data messages. +// Text messages are interpreted as UTF-8 encoded text. The interpretation of +// binary messages is left to the application. +// +// This package uses the TextMessage and BinaryMessage integer constants to +// identify the two data message types. The ReadMessage and NextReader methods +// return the type of the received message. The messageType argument to the +// WriteMessage and NextWriter methods specifies the type of a sent message. +// +// It is the application's responsibility to ensure that text messages are +// valid UTF-8 encoded text. +// +// Control Messages +// +// The WebSocket protocol defines three types of control messages: close, ping +// and pong. Call the connection WriteControl, WriteMessage or NextWriter +// methods to send a control message to the peer. +// +// Connections handle received close messages by sending a close message to the +// peer and returning a *CloseError from the the NextReader, ReadMessage or the +// message Read method. +// +// Connections handle received ping and pong messages by invoking callback +// functions set with SetPingHandler and SetPongHandler methods. The callback +// functions are called from the NextReader, ReadMessage and the message Read +// methods. +// +// The default ping handler sends a pong to the peer. The application's reading +// goroutine can block for a short time while the handler writes the pong data +// to the connection. +// +// The application must read the connection to process ping, pong and close +// messages sent from the peer. If the application is not otherwise interested +// in messages from the peer, then the application should start a goroutine to +// read and discard messages from the peer. A simple example is: +// +// func readLoop(c *websocket.Conn) { +// for { +// if _, _, err := c.NextReader(); err != nil { +// c.Close() +// break +// } +// } +// } +// +// Concurrency +// +// Connections support one concurrent reader and one concurrent writer. +// +// Applications are responsible for ensuring that no more than one goroutine +// calls the write methods (NextWriter, SetWriteDeadline, WriteMessage, +// WriteJSON, EnableWriteCompression, SetCompressionLevel) concurrently and +// that no more than one goroutine calls the read methods (NextReader, +// SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, SetPingHandler) +// concurrently. +// +// The Close and WriteControl methods can be called concurrently with all other +// methods. +// +// Origin Considerations +// +// Web browsers allow Javascript applications to open a WebSocket connection to +// any host. It's up to the server to enforce an origin policy using the Origin +// request header sent by the browser. +// +// The Upgrader calls the function specified in the CheckOrigin field to check +// the origin. If the CheckOrigin function returns false, then the Upgrade +// method fails the WebSocket handshake with HTTP status 403. +// +// If the CheckOrigin field is nil, then the Upgrader uses a safe default: fail +// the handshake if the Origin request header is present and not equal to the +// Host request header. +// +// An application can allow connections from any origin by specifying a +// function that always returns true: +// +// var upgrader = websocket.Upgrader{ +// CheckOrigin: func(r *http.Request) bool { return true }, +// } +// +// The deprecated Upgrade function does not enforce an origin policy. It's the +// application's responsibility to check the Origin header before calling +// Upgrade. +// +// Compression EXPERIMENTAL +// +// Per message compression extensions (RFC 7692) are experimentally supported +// by this package in a limited capacity. Setting the EnableCompression option +// to true in Dialer or Upgrader will attempt to negotiate per message deflate +// support. +// +// var upgrader = websocket.Upgrader{ +// EnableCompression: true, +// } +// +// If compression was successfully negotiated with the connection's peer, any +// message received in compressed form will be automatically decompressed. +// All Read methods will return uncompressed bytes. +// +// Per message compression of messages written to a connection can be enabled +// or disabled by calling the corresponding Conn method: +// +// conn.EnableWriteCompression(false) +// +// Currently this package does not support compression with "context takeover". +// This means that messages must be compressed and decompressed in isolation, +// without retaining sliding window or dictionary state across messages. For +// more details refer to RFC 7692. +// +// Use of compression is experimental and may result in decreased performance. +package websocket diff --git a/vendor/github.com/gorilla/websocket/json.go b/vendor/github.com/gorilla/websocket/json.go new file mode 100644 index 000000000..4f0e36875 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/json.go @@ -0,0 +1,55 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "encoding/json" + "io" +) + +// WriteJSON is deprecated, use c.WriteJSON instead. +func WriteJSON(c *Conn, v interface{}) error { + return c.WriteJSON(v) +} + +// WriteJSON writes the JSON encoding of v to the connection. +// +// See the documentation for encoding/json Marshal for details about the +// conversion of Go values to JSON. +func (c *Conn) WriteJSON(v interface{}) error { + w, err := c.NextWriter(TextMessage) + if err != nil { + return err + } + err1 := json.NewEncoder(w).Encode(v) + err2 := w.Close() + if err1 != nil { + return err1 + } + return err2 +} + +// ReadJSON is deprecated, use c.ReadJSON instead. +func ReadJSON(c *Conn, v interface{}) error { + return c.ReadJSON(v) +} + +// ReadJSON reads the next JSON-encoded message from the connection and stores +// it in the value pointed to by v. +// +// See the documentation for the encoding/json Unmarshal function for details +// about the conversion of JSON to a Go value. +func (c *Conn) ReadJSON(v interface{}) error { + _, r, err := c.NextReader() + if err != nil { + return err + } + err = json.NewDecoder(r).Decode(v) + if err == io.EOF { + // One value is expected in the message. + err = io.ErrUnexpectedEOF + } + return err +} diff --git a/vendor/github.com/gorilla/websocket/mask.go b/vendor/github.com/gorilla/websocket/mask.go new file mode 100644 index 000000000..6a88bbc74 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/mask.go @@ -0,0 +1,55 @@ +// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of +// this source code is governed by a BSD-style license that can be found in the +// LICENSE file. + +// +build !appengine + +package websocket + +import "unsafe" + +const wordSize = int(unsafe.Sizeof(uintptr(0))) + +func maskBytes(key [4]byte, pos int, b []byte) int { + + // Mask one byte at a time for small buffers. + if len(b) < 2*wordSize { + for i := range b { + b[i] ^= key[pos&3] + pos++ + } + return pos & 3 + } + + // Mask one byte at a time to word boundary. + if n := int(uintptr(unsafe.Pointer(&b[0]))) % wordSize; n != 0 { + n = wordSize - n + for i := range b[:n] { + b[i] ^= key[pos&3] + pos++ + } + b = b[n:] + } + + // Create aligned word size key. + var k [wordSize]byte + for i := range k { + k[i] = key[(pos+i)&3] + } + kw := *(*uintptr)(unsafe.Pointer(&k)) + + // Mask one word at a time. + n := (len(b) / wordSize) * wordSize + for i := 0; i < n; i += wordSize { + *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw + } + + // Mask one byte at a time for remaining bytes. + b = b[n:] + for i := range b { + b[i] ^= key[pos&3] + pos++ + } + + return pos & 3 +} diff --git a/vendor/github.com/gorilla/websocket/prepared.go b/vendor/github.com/gorilla/websocket/prepared.go new file mode 100644 index 000000000..1efffbd1e --- /dev/null +++ b/vendor/github.com/gorilla/websocket/prepared.go @@ -0,0 +1,103 @@ +// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bytes" + "net" + "sync" + "time" +) + +// PreparedMessage caches on the wire representations of a message payload. +// Use PreparedMessage to efficiently send a message payload to multiple +// connections. PreparedMessage is especially useful when compression is used +// because the CPU and memory expensive compression operation can be executed +// once for a given set of compression options. +type PreparedMessage struct { + messageType int + data []byte + err error + mu sync.Mutex + frames map[prepareKey]*preparedFrame +} + +// prepareKey defines a unique set of options to cache prepared frames in PreparedMessage. +type prepareKey struct { + isServer bool + compress bool + compressionLevel int +} + +// preparedFrame contains data in wire representation. +type preparedFrame struct { + once sync.Once + data []byte +} + +// NewPreparedMessage returns an initialized PreparedMessage. You can then send +// it to connection using WritePreparedMessage method. Valid wire +// representation will be calculated lazily only once for a set of current +// connection options. +func NewPreparedMessage(messageType int, data []byte) (*PreparedMessage, error) { + pm := &PreparedMessage{ + messageType: messageType, + frames: make(map[prepareKey]*preparedFrame), + data: data, + } + + // Prepare a plain server frame. + _, frameData, err := pm.frame(prepareKey{isServer: true, compress: false}) + if err != nil { + return nil, err + } + + // To protect against caller modifying the data argument, remember the data + // copied to the plain server frame. + pm.data = frameData[len(frameData)-len(data):] + return pm, nil +} + +func (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) { + pm.mu.Lock() + frame, ok := pm.frames[key] + if !ok { + frame = &preparedFrame{} + pm.frames[key] = frame + } + pm.mu.Unlock() + + var err error + frame.once.Do(func() { + // Prepare a frame using a 'fake' connection. + // TODO: Refactor code in conn.go to allow more direct construction of + // the frame. + mu := make(chan bool, 1) + mu <- true + var nc prepareConn + c := &Conn{ + conn: &nc, + mu: mu, + isServer: key.isServer, + compressionLevel: key.compressionLevel, + enableWriteCompression: true, + writeBuf: make([]byte, defaultWriteBufferSize+maxFrameHeaderSize), + } + if key.compress { + c.newCompressionWriter = compressNoContextTakeover + } + err = c.WriteMessage(pm.messageType, pm.data) + frame.data = nc.buf.Bytes() + }) + return pm.messageType, frame.data, err +} + +type prepareConn struct { + buf bytes.Buffer + net.Conn +} + +func (pc *prepareConn) Write(p []byte) (int, error) { return pc.buf.Write(p) } +func (pc *prepareConn) SetWriteDeadline(t time.Time) error { return nil } diff --git a/vendor/github.com/gorilla/websocket/server.go b/vendor/github.com/gorilla/websocket/server.go new file mode 100644 index 000000000..3495e0f1a --- /dev/null +++ b/vendor/github.com/gorilla/websocket/server.go @@ -0,0 +1,291 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bufio" + "errors" + "net" + "net/http" + "net/url" + "strings" + "time" +) + +// HandshakeError describes an error with the handshake from the peer. +type HandshakeError struct { + message string +} + +func (e HandshakeError) Error() string { return e.message } + +// Upgrader specifies parameters for upgrading an HTTP connection to a +// WebSocket connection. +type Upgrader struct { + // HandshakeTimeout specifies the duration for the handshake to complete. + HandshakeTimeout time.Duration + + // ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer + // size is zero, then buffers allocated by the HTTP server are used. The + // I/O buffer sizes do not limit the size of the messages that can be sent + // or received. + ReadBufferSize, WriteBufferSize int + + // Subprotocols specifies the server's supported protocols in order of + // preference. If this field is set, then the Upgrade method negotiates a + // subprotocol by selecting the first match in this list with a protocol + // requested by the client. + Subprotocols []string + + // Error specifies the function for generating HTTP error responses. If Error + // is nil, then http.Error is used to generate the HTTP response. + Error func(w http.ResponseWriter, r *http.Request, status int, reason error) + + // CheckOrigin returns true if the request Origin header is acceptable. If + // CheckOrigin is nil, the host in the Origin header must not be set or + // must match the host of the request. + CheckOrigin func(r *http.Request) bool + + // EnableCompression specify if the server should attempt to negotiate per + // message compression (RFC 7692). Setting this value to true does not + // guarantee that compression will be supported. Currently only "no context + // takeover" modes are supported. + EnableCompression bool +} + +func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) { + err := HandshakeError{reason} + if u.Error != nil { + u.Error(w, r, status, err) + } else { + w.Header().Set("Sec-Websocket-Version", "13") + http.Error(w, http.StatusText(status), status) + } + return nil, err +} + +// checkSameOrigin returns true if the origin is not set or is equal to the request host. +func checkSameOrigin(r *http.Request) bool { + origin := r.Header["Origin"] + if len(origin) == 0 { + return true + } + u, err := url.Parse(origin[0]) + if err != nil { + return false + } + return u.Host == r.Host +} + +func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string { + if u.Subprotocols != nil { + clientProtocols := Subprotocols(r) + for _, serverProtocol := range u.Subprotocols { + for _, clientProtocol := range clientProtocols { + if clientProtocol == serverProtocol { + return clientProtocol + } + } + } + } else if responseHeader != nil { + return responseHeader.Get("Sec-Websocket-Protocol") + } + return "" +} + +// Upgrade upgrades the HTTP server connection to the WebSocket protocol. +// +// The responseHeader is included in the response to the client's upgrade +// request. Use the responseHeader to specify cookies (Set-Cookie) and the +// application negotiated subprotocol (Sec-Websocket-Protocol). +// +// If the upgrade fails, then Upgrade replies to the client with an HTTP error +// response. +func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) { + if r.Method != "GET" { + return u.returnError(w, r, http.StatusMethodNotAllowed, "websocket: not a websocket handshake: request method is not GET") + } + + if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok { + return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific 'Sec-Websocket-Extensions' headers are unsupported") + } + + if !tokenListContainsValue(r.Header, "Connection", "upgrade") { + return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'upgrade' token not found in 'Connection' header") + } + + if !tokenListContainsValue(r.Header, "Upgrade", "websocket") { + return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'websocket' token not found in 'Upgrade' header") + } + + if !tokenListContainsValue(r.Header, "Sec-Websocket-Version", "13") { + return u.returnError(w, r, http.StatusBadRequest, "websocket: unsupported version: 13 not found in 'Sec-Websocket-Version' header") + } + + checkOrigin := u.CheckOrigin + if checkOrigin == nil { + checkOrigin = checkSameOrigin + } + if !checkOrigin(r) { + return u.returnError(w, r, http.StatusForbidden, "websocket: 'Origin' header value not allowed") + } + + challengeKey := r.Header.Get("Sec-Websocket-Key") + if challengeKey == "" { + return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: `Sec-Websocket-Key' header is missing or blank") + } + + subprotocol := u.selectSubprotocol(r, responseHeader) + + // Negotiate PMCE + var compress bool + if u.EnableCompression { + for _, ext := range parseExtensions(r.Header) { + if ext[""] != "permessage-deflate" { + continue + } + compress = true + break + } + } + + var ( + netConn net.Conn + err error + ) + + h, ok := w.(http.Hijacker) + if !ok { + return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker") + } + var brw *bufio.ReadWriter + netConn, brw, err = h.Hijack() + if err != nil { + return u.returnError(w, r, http.StatusInternalServerError, err.Error()) + } + + if brw.Reader.Buffered() > 0 { + netConn.Close() + return nil, errors.New("websocket: client sent data before handshake is complete") + } + + c := newConnBRW(netConn, true, u.ReadBufferSize, u.WriteBufferSize, brw) + c.subprotocol = subprotocol + + if compress { + c.newCompressionWriter = compressNoContextTakeover + c.newDecompressionReader = decompressNoContextTakeover + } + + p := c.writeBuf[:0] + p = append(p, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "...) + p = append(p, computeAcceptKey(challengeKey)...) + p = append(p, "\r\n"...) + if c.subprotocol != "" { + p = append(p, "Sec-Websocket-Protocol: "...) + p = append(p, c.subprotocol...) + p = append(p, "\r\n"...) + } + if compress { + p = append(p, "Sec-Websocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover\r\n"...) + } + for k, vs := range responseHeader { + if k == "Sec-Websocket-Protocol" { + continue + } + for _, v := range vs { + p = append(p, k...) + p = append(p, ": "...) + for i := 0; i < len(v); i++ { + b := v[i] + if b <= 31 { + // prevent response splitting. + b = ' ' + } + p = append(p, b) + } + p = append(p, "\r\n"...) + } + } + p = append(p, "\r\n"...) + + // Clear deadlines set by HTTP server. + netConn.SetDeadline(time.Time{}) + + if u.HandshakeTimeout > 0 { + netConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout)) + } + if _, err = netConn.Write(p); err != nil { + netConn.Close() + return nil, err + } + if u.HandshakeTimeout > 0 { + netConn.SetWriteDeadline(time.Time{}) + } + + return c, nil +} + +// Upgrade upgrades the HTTP server connection to the WebSocket protocol. +// +// This function is deprecated, use websocket.Upgrader instead. +// +// The application is responsible for checking the request origin before +// calling Upgrade. An example implementation of the same origin policy is: +// +// if req.Header.Get("Origin") != "http://"+req.Host { +// http.Error(w, "Origin not allowed", 403) +// return +// } +// +// If the endpoint supports subprotocols, then the application is responsible +// for negotiating the protocol used on the connection. Use the Subprotocols() +// function to get the subprotocols requested by the client. Use the +// Sec-Websocket-Protocol response header to specify the subprotocol selected +// by the application. +// +// The responseHeader is included in the response to the client's upgrade +// request. Use the responseHeader to specify cookies (Set-Cookie) and the +// negotiated subprotocol (Sec-Websocket-Protocol). +// +// The connection buffers IO to the underlying network connection. The +// readBufSize and writeBufSize parameters specify the size of the buffers to +// use. Messages can be larger than the buffers. +// +// If the request is not a valid WebSocket handshake, then Upgrade returns an +// error of type HandshakeError. Applications should handle this error by +// replying to the client with an HTTP error response. +func Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header, readBufSize, writeBufSize int) (*Conn, error) { + u := Upgrader{ReadBufferSize: readBufSize, WriteBufferSize: writeBufSize} + u.Error = func(w http.ResponseWriter, r *http.Request, status int, reason error) { + // don't return errors to maintain backwards compatibility + } + u.CheckOrigin = func(r *http.Request) bool { + // allow all connections by default + return true + } + return u.Upgrade(w, r, responseHeader) +} + +// Subprotocols returns the subprotocols requested by the client in the +// Sec-Websocket-Protocol header. +func Subprotocols(r *http.Request) []string { + h := strings.TrimSpace(r.Header.Get("Sec-Websocket-Protocol")) + if h == "" { + return nil + } + protocols := strings.Split(h, ",") + for i := range protocols { + protocols[i] = strings.TrimSpace(protocols[i]) + } + return protocols +} + +// IsWebSocketUpgrade returns true if the client requested upgrade to the +// WebSocket protocol. +func IsWebSocketUpgrade(r *http.Request) bool { + return tokenListContainsValue(r.Header, "Connection", "upgrade") && + tokenListContainsValue(r.Header, "Upgrade", "websocket") +} diff --git a/vendor/github.com/gorilla/websocket/util.go b/vendor/github.com/gorilla/websocket/util.go new file mode 100644 index 000000000..9a4908df2 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/util.go @@ -0,0 +1,214 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "crypto/rand" + "crypto/sha1" + "encoding/base64" + "io" + "net/http" + "strings" +) + +var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11") + +func computeAcceptKey(challengeKey string) string { + h := sha1.New() + h.Write([]byte(challengeKey)) + h.Write(keyGUID) + return base64.StdEncoding.EncodeToString(h.Sum(nil)) +} + +func generateChallengeKey() (string, error) { + p := make([]byte, 16) + if _, err := io.ReadFull(rand.Reader, p); err != nil { + return "", err + } + return base64.StdEncoding.EncodeToString(p), nil +} + +// Octet types from RFC 2616. +var octetTypes [256]byte + +const ( + isTokenOctet = 1 << iota + isSpaceOctet +) + +func init() { + // From RFC 2616 + // + // OCTET = + // CHAR = + // CTL = + // CR = + // LF = + // SP = + // HT = + // <"> = + // CRLF = CR LF + // LWS = [CRLF] 1*( SP | HT ) + // TEXT = + // separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <"> + // | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT + // token = 1* + // qdtext = > + + for c := 0; c < 256; c++ { + var t byte + isCtl := c <= 31 || c == 127 + isChar := 0 <= c && c <= 127 + isSeparator := strings.IndexRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) >= 0 + if strings.IndexRune(" \t\r\n", rune(c)) >= 0 { + t |= isSpaceOctet + } + if isChar && !isCtl && !isSeparator { + t |= isTokenOctet + } + octetTypes[c] = t + } +} + +func skipSpace(s string) (rest string) { + i := 0 + for ; i < len(s); i++ { + if octetTypes[s[i]]&isSpaceOctet == 0 { + break + } + } + return s[i:] +} + +func nextToken(s string) (token, rest string) { + i := 0 + for ; i < len(s); i++ { + if octetTypes[s[i]]&isTokenOctet == 0 { + break + } + } + return s[:i], s[i:] +} + +func nextTokenOrQuoted(s string) (value string, rest string) { + if !strings.HasPrefix(s, "\"") { + return nextToken(s) + } + s = s[1:] + for i := 0; i < len(s); i++ { + switch s[i] { + case '"': + return s[:i], s[i+1:] + case '\\': + p := make([]byte, len(s)-1) + j := copy(p, s[:i]) + escape := true + for i = i + 1; i < len(s); i++ { + b := s[i] + switch { + case escape: + escape = false + p[j] = b + j += 1 + case b == '\\': + escape = true + case b == '"': + return string(p[:j]), s[i+1:] + default: + p[j] = b + j += 1 + } + } + return "", "" + } + } + return "", "" +} + +// tokenListContainsValue returns true if the 1#token header with the given +// name contains token. +func tokenListContainsValue(header http.Header, name string, value string) bool { +headers: + for _, s := range header[name] { + for { + var t string + t, s = nextToken(skipSpace(s)) + if t == "" { + continue headers + } + s = skipSpace(s) + if s != "" && s[0] != ',' { + continue headers + } + if strings.EqualFold(t, value) { + return true + } + if s == "" { + continue headers + } + s = s[1:] + } + } + return false +} + +// parseExtensiosn parses WebSocket extensions from a header. +func parseExtensions(header http.Header) []map[string]string { + + // From RFC 6455: + // + // Sec-WebSocket-Extensions = extension-list + // extension-list = 1#extension + // extension = extension-token *( ";" extension-param ) + // extension-token = registered-token + // registered-token = token + // extension-param = token [ "=" (token | quoted-string) ] + // ;When using the quoted-string syntax variant, the value + // ;after quoted-string unescaping MUST conform to the + // ;'token' ABNF. + + var result []map[string]string +headers: + for _, s := range header["Sec-Websocket-Extensions"] { + for { + var t string + t, s = nextToken(skipSpace(s)) + if t == "" { + continue headers + } + ext := map[string]string{"": t} + for { + s = skipSpace(s) + if !strings.HasPrefix(s, ";") { + break + } + var k string + k, s = nextToken(skipSpace(s[1:])) + if k == "" { + continue headers + } + s = skipSpace(s) + var v string + if strings.HasPrefix(s, "=") { + v, s = nextTokenOrQuoted(skipSpace(s[1:])) + s = skipSpace(s) + } + if s != "" && s[0] != ',' && s[0] != ';' { + continue headers + } + ext[k] = v + } + if s != "" && s[0] != ',' { + continue headers + } + result = append(result, ext) + if s == "" { + continue headers + } + s = s[1:] + } + } + return result +} diff --git a/vendor/github.com/moul/anonuuid/LICENSE b/vendor/github.com/moul/anonuuid/LICENSE new file mode 100644 index 000000000..492e2c629 --- /dev/null +++ b/vendor/github.com/moul/anonuuid/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Manfred Touron + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/vendor/github.com/moul/anonuuid/Makefile b/vendor/github.com/moul/anonuuid/Makefile new file mode 100644 index 000000000..4e2f3bb44 --- /dev/null +++ b/vendor/github.com/moul/anonuuid/Makefile @@ -0,0 +1,73 @@ +# Project-specific variables +BINARIES ?= anonuuid +CONVEY_PORT ?= 9042 + + +# Common variables +SOURCES := $(shell find . -name "*.go") +COMMANDS := $(shell go list ./... | grep -v /vendor/ | grep /cmd/) +PACKAGES := $(shell go list ./... | grep -v /vendor/ | grep -v /cmd/) +GOENV ?= GO15VENDOREXPERIMENT=1 +GO ?= $(GOENV) go +GODEP ?= $(GOENV) godep +USER ?= $(shell whoami) + + +all: build + + +.PHONY: build +build: $(BINARIES) + + +$(BINARIES): $(SOURCES) + $(GO) build -o $@ ./cmd/$@ + + +.PHONY: test +test: + $(GO) get -t . + $(GO) test -v . + + +.PHONY: godep-save +godep-save: + $(GODEP) save $(PACKAGES) $(COMMANDS) + + +.PHONY: clean +clean: + rm -f $(BINARIES) + + +.PHONY: re +re: clean all + + +.PHONY: convey +convey: + $(GO) get github.com/smartystreets/goconvey + goconvey -cover -port=$(CONVEY_PORT) -workDir="$(realpath .)" -depth=1 + + +.PHONY: cover +cover: profile.out + + +profile.out: $(SOURCES) + rm -f $@ + $(GO) test -covermode=count -coverpkg=. -coverprofile=$@ . + + +.PHONY: docker-build +docker-build: + go get github.com/laher/goxc + rm -rf contrib/docker/linux_386 + for binary in $(BINARIES); do \ + goxc -bc="linux,386" -d . -pv contrib/docker -n $$binary xc; \ + mv contrib/docker/linux_386/$$binary contrib/docker/entrypoint; \ + docker build -t $(USER)/$$binary contrib/docker; \ + docker run -it --rm $(USER)/$$binary || true; \ + docker inspect --type=image --format="{{ .Id }}" moul/$$binary || true; \ + echo "Now you can run 'docker push $(USER)/$$binary'"; \ + done diff --git a/vendor/github.com/moul/anonuuid/README.md b/vendor/github.com/moul/anonuuid/README.md new file mode 100644 index 000000000..f5c9a57eb --- /dev/null +++ b/vendor/github.com/moul/anonuuid/README.md @@ -0,0 +1,170 @@ +# AnonUUID + +[![Build Status](https://travis-ci.org/moul/anonuuid.svg)](https://travis-ci.org/moul/anonuuid) +[![GoDoc](https://godoc.org/github.com/moul/anonuuid?status.svg)](https://godoc.org/github.com/moul/anonuuid) +[![Coverage Status](https://coveralls.io/repos/moul/anonuuid/badge.svg?branch=master&service=github)](https://coveralls.io/github/moul/anonuuid?branch=master) + +:wrench: Anonymize UUIDs outputs (written in Golang) + +![AnonUUID Logo](https://raw.githubusercontent.com/moul/anonuuid/master/assets/anonuuid.png) + +**anonuuid** anonymize an input string by replacing all UUIDs by an anonymized +new one. + +The fake UUIDs are cached, so if AnonUUID encounter the same real UUIDs multiple +times, the translation will be the same. + +## Usage + +```console +$ anonuuid --help +NAME: + anonuuid - Anonymize UUIDs outputs + +USAGE: + anonuuid [global options] command [command options] [arguments...] + +VERSION: + 1.0.0-dev + +AUTHOR(S): + Manfred Touron + +COMMANDS: + help, h Shows a list of commands or help for one command + +GLOBAL OPTIONS: + --hexspeak Generate hexspeak style fake UUIDs + --random, -r Generate random fake UUIDs + --keep-beginning Keep first part of the UUID unchanged + --keep-end Keep last part of the UUID unchanged + --prefix, -p Prefix generated UUIDs + --suffix Suffix generated UUIDs + --help, -h show help + --version, -v print the version + ``` + +## Example + +Replace all UUIDs and cache the correspondance. + +```command +$ anonuuid git:(master) ✗ cat < 32 { + part = part[:32] + } + uuid := part[:8] + "-" + part[8:12] + "-1" + part[13:16] + "-" + part[16:20] + "-" + part[20:32] + + err := IsUUID(uuid) + if err != nil { + return "", err + } + + return uuid, nil +} + +// GenerateRandomUUID returns an UUID based on random strings +func GenerateRandomUUID(length int) (string, error) { + var letters = []rune("abcdef0123456789") + + b := make([]rune, length) + for i := range b { + b[i] = letters[rand.Intn(len(letters))] + } + return FormatUUID(string(b)) +} + +// GenerateHexspeakUUID returns an UUID formatted string containing hexspeak words +func GenerateHexspeakUUID(i int) (string, error) { + if i < 0 { + i = -i + } + hexspeaks := []string{ + "0ff1ce", + "31337", + "4b1d", + "badc0de", + "badcafe", + "badf00d", + "deadbabe", + "deadbeef", + "deadc0de", + "deadfeed", + "fee1bad", + } + return FormatUUID(hexspeaks[i%len(hexspeaks)]) +} + +// GenerateLenUUID returns an UUID formatted string based on an index number +func GenerateLenUUID(i int) (string, error) { + if i < 0 { + i = 2<<29 + i + } + return FormatUUID(fmt.Sprintf("%x", i)) +} diff --git a/vendor/github.com/moul/gotty-client/LICENSE b/vendor/github.com/moul/gotty-client/LICENSE new file mode 100644 index 000000000..492e2c629 --- /dev/null +++ b/vendor/github.com/moul/gotty-client/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Manfred Touron + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/vendor/github.com/moul/gotty-client/Makefile b/vendor/github.com/moul/gotty-client/Makefile new file mode 100644 index 000000000..4952ef990 --- /dev/null +++ b/vendor/github.com/moul/gotty-client/Makefile @@ -0,0 +1,81 @@ +# Project-specific variables +BINARIES ?= gotty-client +GOTTY_URL := http://localhost:8081/ +VERSION := $(shell cat .goxc.json | jq -c .PackageVersion | sed 's/"//g') + +CONVEY_PORT ?= 9042 + + +# Common variables +SOURCES := $(shell find . -type f -name "*.go") +COMMANDS := $(shell go list ./... | grep -v /vendor/ | grep /cmd/) +PACKAGES := $(shell go list ./... | grep -v /vendor/ | grep -v /cmd/) +GOENV ?= GO15VENDOREXPERIMENT=1 +GO ?= $(GOENV) go +GODEP ?= $(GOENV) godep +USER ?= $(shell whoami) + + +all: build + + +.PHONY: build +build: $(BINARIES) + + +.PHONY: install +install: + $(GO) install ./cmd/gotty-client + + +$(BINARIES): $(SOURCES) + $(GO) build -o $@ ./cmd/$@ + + +.PHONY: test +test: + $(GO) get -t . + $(GO) test -v . + + +.PHONY: godep-save +godep-save: + $(GODEP) save $(PACKAGES) $(COMMANDS) + + +.PHONY: clean +clean: + rm -f $(BINARIES) + + +.PHONY: re +re: clean all + + +.PHONY: convey +convey: + $(GO) get github.com/smartystreets/goconvey + goconvey -cover -port=$(CONVEY_PORT) -workDir="$(realpath .)" -depth=1 + + +.PHONY: cover +cover: profile.out + + +profile.out: $(SOURCES) + rm -f $@ + $(GO) test -covermode=count -coverpkg=. -coverprofile=$@ . + + +.PHONY: docker-build +docker-build: + go get github.com/laher/goxc + rm -rf contrib/docker/linux_386 + for binary in $(BINARIES); do \ + goxc -bc="linux,386" -d . -pv contrib/docker -n $$binary xc; \ + mv contrib/docker/linux_386/$$binary contrib/docker/entrypoint; \ + docker build -t $(USER)/$$binary contrib/docker; \ + docker run -it --rm $(USER)/$$binary || true; \ + docker inspect --type=image --format="{{ .Id }}" moul/$$binary || true; \ + echo "Now you can run 'docker push $(USER)/$$binary'"; \ + done diff --git a/vendor/github.com/moul/gotty-client/README.md b/vendor/github.com/moul/gotty-client/README.md new file mode 100644 index 000000000..2fb562b32 --- /dev/null +++ b/vendor/github.com/moul/gotty-client/README.md @@ -0,0 +1,201 @@ +# gotty-client +:wrench: Terminal client for [GoTTY](https://github.com/yudai/gotty). + +![](https://raw.githubusercontent.com/moul/gotty-client/master/resources/gotty-client.png) + +[![Build Status](https://travis-ci.org/moul/gotty-client.svg?branch=master)](https://travis-ci.org/moul/gotty-client) +[![GoDoc](https://godoc.org/github.com/moul/gotty-client?status.svg)](https://godoc.org/github.com/moul/gotty-client) + +```ruby + ┌─────────────────┐ + ┌──────▶│ /bin/bash │ + │ └─────────────────┘ + ┌──────────────┐ ┌──────────┐ + │ │ │ Gotty │ +┌───────┐ ┌──▶│ Browser │───────┐ │ │ +│ │ │ │ │ │ │ │ +│ │ │ └──────────────┘ │ │ │ ┌─────────────────┐ +│ Bob │───┤ websockets─▶│ │─▶│ emacs /var/www │ +│ │ │ ╔═ ══ ══ ══ ══ ╗ │ │ │ └─────────────────┘ +│ │ │ ║ ║ │ │ │ +└───────┘ └──▶║ gotty-client ───────┘ │ │ + ║ │ │ + ╚═ ══ ══ ══ ══ ╝ └──────────┘ + │ ┌─────────────────┐ + └──────▶│ tmux attach │ + └─────────────────┘ +``` + +## Example + +Server side ([GoTTY](https://github.com/yudai/gotty)) + +```console +$ gotty -p 9191 sh -c 'while true; do date; sleep 1; done' +2015/08/24 18:54:31 Server is starting with command: sh -c while true; do date; sleep 1; done +2015/08/24 18:54:31 URL: http://[::1]:9191/ +2015/08/24 18:54:34 GET /ws +2015/08/24 18:54:34 New client connected: 127.0.0.1:61811 +2015/08/24 18:54:34 Command is running for client 127.0.0.1:61811 with PID 64834 +2015/08/24 18:54:39 Command exited for: 127.0.0.1:61811 +2015/08/24 18:54:39 Connection closed: 127.0.0.1:61811 +... +``` + +**Client side** + +```console +$ gotty-client http://localhost:9191/ +INFO[0000] New title: GoTTY - sh -c while true; do date; sleep 1; done (jean-michel-van-damme.local) +WARN[0000] Unhandled protocol message: json pref: 2{} +Mon Aug 24 18:54:34 CEST 2015 +Mon Aug 24 18:54:35 CEST 2015 +Mon Aug 24 18:54:36 CEST 2015 +Mon Aug 24 18:54:37 CEST 2015 +Mon Aug 24 18:54:38 CEST 2015 +^C +``` + +## Usage + +```console +$ gotty-client -h +NAME: + gotty-client - GoTTY client for your terminal + +USAGE: + gotty-client [global options] command [command options] GOTTY_URL + +VERSION: + 1.3.0+ + +AUTHOR(S): + Manfred Touron + +COMMANDS: + help, h Shows a list of commands or help for one command + +GLOBAL OPTIONS: + --debug, -D Enable debug mode [$GOTTY_CLIENT_DEBUG] + --help, -h show help + --version, -v print the version +``` + +## Install + +Install latest version using Golang (recommended) + +```console +$ go get github.com/moul/gotty-client/cmd/gotty-client +``` + +--- + +Install latest version using Homebrew (Mac OS X) + +```console +$ brew install https://raw.githubusercontent.com/moul/gotty-client/master/contrib/homebrew/gotty-client.rb --HEAD +``` + +or the latest released version + +```console +$ brew install https://raw.githubusercontent.com/moul/gotty-client/master/contrib/homebrew/gotty-client.rb +``` + +## Changelog + +### master (unreleased) + +* No entry + +[full commits list](https://github.com/moul/gotty-client/compare/v1.6.1...master) + +### [v1.6.1](https://github.com/moul/gotty-client/releases/tag/v1.6.1) (2017-01-19) + +* Do not exit on EOF ([#45](https://github.com/moul/gotty-client/pull/45)) ([@gurjeet](https://github.com/gurjeet)) + +[full commits list](https://github.com/moul/gotty-client/compare/v1.6.0...v1.6.1) + +### [v1.6.0](https://github.com/moul/gotty-client/releases/tag/v1.6.0) (2016-05-23) + +* Support of `--use-proxy-from-env` (Add Proxy support) ([#36](https://github.com/moul/gotty-client/pull/36)) ([@byung2](https://github.com/byung2)) +* Add debug mode ([#18](https://github.com/moul/gotty-client/issues/18)) +* Fix argument passing ([#16](https://github.com/moul/gotty-client/issues/16)) +* Remove warnings + golang fixes and refactoring ([@QuentinPerez](https://github.com/QuentinPerez)) + +[full commits list](https://github.com/moul/gotty-client/compare/v1.5.0...v1.6.0) + +### [v1.5.0](https://github.com/moul/gotty-client/releases/tag/v1.5.0) (2016-02-18) + +* Add autocomplete support ([#19](https://github.com/moul/gotty-client/issues/19)) +* Switch from `Party` to `Godep` +* Fix terminal data being interpreted as format string ([#34](https://github.com/moul/gotty-client/pull/34)) ([@mickael9](https://github.com/mickael9)) + +[full commits list](https://github.com/moul/gotty-client/compare/v1.4.0...v1.5.0) + +### [v1.4.0](https://github.com/moul/gotty-client/releases/tag/v1.4.0) (2015-12-09) + +* Remove solaris,plan9,nacl for `.goxc.json` +* Add an error if the go version is lower than 1.5 +* Flexible parsing of the input URL +* Add tests +* Support of `--skip-tls-verify` + +[full commits list](https://github.com/moul/gotty-client/compare/v1.3.0...v1.4.0) + +### [v1.3.0](https://github.com/moul/gotty-client/releases/tag/v1.3.0) (2015-10-27) + +* Fix `connected` state when using `Connect()` + `Loop()` methods +* Add `ExitLoop` which allow to exit from `Loop` function + +[full commits list](https://github.com/moul/gotty-client/compare/v1.2.0...v1.3.0) + +### [v1.2.0](https://github.com/moul/gotty-client/releases/tag/v1.2.0) (2015-10-23) + +* Removed an annoying warning when exiting connection ([#22](https://github.com/moul/gotty-client/issues/22)) ([@QuentinPerez](https://github.com/QuentinPerez)) +* Add the ability to configure alternative stdout ([#21](https://github.com/moul/gotty-client/issues/21)) ([@QuentinPerez](https://github.com/QuentinPerez)) +* Refactored the goroutine system with select, improve speed and stability ([@QuentinPerez](https://github.com/QuentinPerez)) +* Add debug mode (`--debug`/`-D`) ([#18](https://github.com/moul/gotty-client/issues/18)) +* Improve error message when connecting by checking HTTP status code +* Fix arguments passing ([#16](https://github.com/moul/gotty-client/issues/16)) +* Dropped support for golang<1.5 +* Small fixes + +[full commits list](https://github.com/moul/gotty-client/compare/v1.1.0...v1.2.0) + +### [v1.1.0](https://github.com/moul/gotty-client/releases/tag/v1.1.0) (2015-10-10) + +* Handling arguments + using mutexes (thanks to [@QuentinPerez](https://github.com/QuentinPerez)) +* Add logo ([#9](https://github.com/moul/gotty-client/issues/9)) +* Using codegansta/cli for CLI parsing ([#3](https://github.com/moul/gotty-client/issues/3)) +* Fix panic when running on older GoTTY server ([#13](https://github.com/moul/gotty-client/issues/13)) +* Add 'homebrew support' ([#1](https://github.com/moul/gotty-client/issues/1)) +* Add Changelog ([#5](https://github.com/moul/gotty-client/issues/5)) +* Add GOXC configuration to build binaries for multiple architectures ([#2](https://github.com/moul/gotty-client/issues/2)) + +[full commits list](https://github.com/moul/gotty-client/compare/v1.0.1...v1.1.0) + +### [v1.0.1](https://github.com/moul/gotty-client/releases/tag/v1.0.1) (2015-09-27) + +* Using party to manage dependencies + +[full commits list](https://github.com/moul/gotty-client/compare/v1.0.0...v1.0.1) + +### [v1.0.0](https://github.com/moul/gotty-client/releases/tag/v1.0.0) (2015-09-27) + +Compatible with [GoTTY](https://github.com/yudai/gotty) version: [v0.0.10](https://github.com/yudai/gotty/releases/tag/v0.0.10) + +#### Features + +* Support **basic-auth** +* Support **terminal-(re)size** +* Support **write** +* Support **title** +* Support **custom URI** + +[full commits list](https://github.com/moul/gotty-client/compare/cf0c1146c7ce20fe0bd65764c13253bc575cd43a...v1.0.0) + +## License + +MIT diff --git a/vendor/github.com/moul/gotty-client/arch.go b/vendor/github.com/moul/gotty-client/arch.go new file mode 100644 index 000000000..d856fbd2c --- /dev/null +++ b/vendor/github.com/moul/gotty-client/arch.go @@ -0,0 +1,34 @@ +// +build !windows + +package gottyclient + +import ( + "encoding/json" + "fmt" + "os" + "os/signal" + "syscall" + "unsafe" +) + +func notifySignalSIGWINCH(c chan<- os.Signal) { + signal.Notify(c, syscall.SIGWINCH) +} + +func resetSignalSIGWINCH() { + signal.Reset(syscall.SIGWINCH) +} + +func syscallTIOCGWINSZ() ([]byte, error) { + ws := winsize{} + + syscall.Syscall(syscall.SYS_IOCTL, + uintptr(0), uintptr(syscall.TIOCGWINSZ), + uintptr(unsafe.Pointer(&ws))) + + b, err := json.Marshal(ws) + if err != nil { + return nil, fmt.Errorf("json.Marshal error: %v", err) + } + return b, err +} diff --git a/vendor/github.com/moul/gotty-client/arch_windows.go b/vendor/github.com/moul/gotty-client/arch_windows.go new file mode 100644 index 000000000..53aaffc5f --- /dev/null +++ b/vendor/github.com/moul/gotty-client/arch_windows.go @@ -0,0 +1,16 @@ +package gottyclient + +import ( + "errors" + "os" +) + +func notifySignalSIGWINCH(c chan<- os.Signal) { +} + +func resetSignalSIGWINCH() { +} + +func syscallTIOCGWINSZ() ([]byte, error) { + return nil, errors.New("SIGWINCH isn't supported on this ARCH") +} diff --git a/vendor/github.com/moul/gotty-client/glide.lock b/vendor/github.com/moul/gotty-client/glide.lock new file mode 100644 index 000000000..61d5aeee3 --- /dev/null +++ b/vendor/github.com/moul/gotty-client/glide.lock @@ -0,0 +1,37 @@ +hash: 5ba4ef945563e8e85097f2699126b1577f9c667fdbbcdd71604e553ab7dd2a03 +updated: 2017-02-04T23:03:32.03375088+01:00 +imports: +- name: github.com/codegangsta/cli + version: 2ae9042f5bcbaf15b01229f8395ba8e72e01bded +- name: github.com/creack/goselect + version: 40085cf5fd629ccd88dc328895f1f137d09a1de4 +- name: github.com/gopherjs/gopherjs + version: db27c7c470d7404b6b201f82d6fd4821260bd13e + subpackages: + - js +- name: github.com/gorilla/websocket + version: 1f512fc3f05332ba7117626cdfb4e07474e58e60 +- name: github.com/jtolds/gls + version: 8ddce2a84170772b95dd5d576c48d517b22cac63 +- name: github.com/Sirupsen/logrus + version: cd7d1bbe41066b6c1f19780f895901052150a575 +- name: github.com/smartystreets/assertions + version: 40711f7748186bbf9c99977cd89f21ce1a229447 + subpackages: + - internal/go-render/render + - internal/oglematchers +- name: github.com/smartystreets/goconvey + version: d4c757aa9afd1e2fc1832aaab209b5794eb336e1 + subpackages: + - convey + - convey/gotest + - convey/reporting +- name: golang.org/x/crypto + version: 5bcd134fee4dd1475da17714aac19c0aa0142e2f + subpackages: + - ssh/terminal +- name: golang.org/x/sys + version: d4feaf1a7e61e1d9e79e6c4e76c6349e9cab0a03 + subpackages: + - unix +testImports: [] diff --git a/vendor/github.com/moul/gotty-client/glide.yaml b/vendor/github.com/moul/gotty-client/glide.yaml new file mode 100644 index 000000000..287efb9d8 --- /dev/null +++ b/vendor/github.com/moul/gotty-client/glide.yaml @@ -0,0 +1,35 @@ +package: github.com/moul/gotty-client +import: +- package: github.com/Sirupsen/logrus + version: cd7d1bbe41066b6c1f19780f895901052150a575 +- package: github.com/codegangsta/cli + version: 2ae9042f5bcbaf15b01229f8395ba8e72e01bded +- package: github.com/creack/goselect + version: 40085cf5fd629ccd88dc328895f1f137d09a1de4 +- package: github.com/gopherjs/gopherjs + version: db27c7c470d7404b6b201f82d6fd4821260bd13e + subpackages: + - js +- package: github.com/gorilla/websocket + version: 1f512fc3f05332ba7117626cdfb4e07474e58e60 +- package: github.com/jtolds/gls + version: 8ddce2a84170772b95dd5d576c48d517b22cac63 +- package: github.com/smartystreets/assertions + version: 40711f7748186bbf9c99977cd89f21ce1a229447 + subpackages: + - internal/go-render/render + - internal/oglematchers +- package: github.com/smartystreets/goconvey + version: d4c757aa9afd1e2fc1832aaab209b5794eb336e1 + subpackages: + - convey + - convey/gotest + - convey/reporting +- package: golang.org/x/crypto + version: 5bcd134fee4dd1475da17714aac19c0aa0142e2f + subpackages: + - ssh/terminal +- package: golang.org/x/sys + version: d4feaf1a7e61e1d9e79e6c4e76c6349e9cab0a03 + subpackages: + - unix diff --git a/vendor/github.com/moul/gotty-client/gotty-client.go b/vendor/github.com/moul/gotty-client/gotty-client.go new file mode 100644 index 000000000..35e599d9b --- /dev/null +++ b/vendor/github.com/moul/gotty-client/gotty-client.go @@ -0,0 +1,469 @@ +package gottyclient + +import ( + "crypto/tls" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "os" + "regexp" + "strings" + "sync" + "time" + + "github.com/Sirupsen/logrus" + "github.com/creack/goselect" + "github.com/gorilla/websocket" + "golang.org/x/crypto/ssh/terminal" +) + +// GetAuthTokenURL transforms a GoTTY http URL to its AuthToken file URL +func GetAuthTokenURL(httpURL string) (*url.URL, *http.Header, error) { + header := http.Header{} + target, err := url.Parse(httpURL) + if err != nil { + return nil, nil, err + } + + target.Path = strings.TrimLeft(target.Path+"auth_token.js", "/") + + if target.User != nil { + header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(target.User.String()))) + target.User = nil + } + + return target, &header, nil +} + +// GetURLQuery returns url.query +func GetURLQuery(rawurl string) (url.Values, error) { + target, err := url.Parse(rawurl) + if err != nil { + return nil, err + } + return target.Query(), nil +} + +// GetWebsocketURL transforms a GoTTY http URL to its WebSocket URL +func GetWebsocketURL(httpURL string) (*url.URL, *http.Header, error) { + header := http.Header{} + target, err := url.Parse(httpURL) + if err != nil { + return nil, nil, err + } + + if target.Scheme == "https" { + target.Scheme = "wss" + } else { + target.Scheme = "ws" + } + + target.Path = strings.TrimLeft(target.Path+"ws", "/") + + if target.User != nil { + header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(target.User.String()))) + target.User = nil + } + + return target, &header, nil +} + +type Client struct { + Dialer *websocket.Dialer + Conn *websocket.Conn + URL string + WriteMutex *sync.Mutex + Output io.Writer + poison chan bool + SkipTLSVerify bool + UseProxyFromEnv bool + Connected bool +} + +type querySingleType struct { + AuthToken string `json:"AuthToken"` + Arguments string `json:"Arguments"` +} + +func (c *Client) write(data []byte) error { + c.WriteMutex.Lock() + defer c.WriteMutex.Unlock() + return c.Conn.WriteMessage(websocket.TextMessage, data) +} + +// GetAuthToken retrieves an Auth Token from dynamic auth_token.js file +func (c *Client) GetAuthToken() (string, error) { + target, header, err := GetAuthTokenURL(c.URL) + if err != nil { + return "", err + } + + logrus.Debugf("Fetching auth token auth-token: %q", target.String()) + req, err := http.NewRequest("GET", target.String(), nil) + req.Header = *header + tr := &http.Transport{} + if c.SkipTLSVerify { + conf := &tls.Config{InsecureSkipVerify: true} + tr.TLSClientConfig = conf + } + if c.UseProxyFromEnv { + tr.Proxy = http.ProxyFromEnvironment + } + client := &http.Client{Transport: tr} + resp, err := client.Do(req) + if err != nil { + return "", err + } + + switch resp.StatusCode { + case 200: + // Everything is OK + default: + return "", fmt.Errorf("unknown status code: %d (%s)", resp.StatusCode, http.StatusText(resp.StatusCode)) + } + + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return "", err + } + + re := regexp.MustCompile("var gotty_auth_token = '(.*)'") + output := re.FindStringSubmatch(string(body)) + if len(output) == 0 { + return "", fmt.Errorf("Cannot fetch GoTTY auth-token, please upgrade your GoTTY server.") + } + + return output[1], nil +} + +// Connect tries to dial a websocket server +func (c *Client) Connect() error { + // Retrieve AuthToken + authToken, err := c.GetAuthToken() + if err != nil { + return err + } + logrus.Debugf("Auth-token: %q", authToken) + + // Open WebSocket connection + target, header, err := GetWebsocketURL(c.URL) + if err != nil { + return err + } + logrus.Debugf("Connecting to websocket: %q", target.String()) + if c.SkipTLSVerify { + c.Dialer.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + } + if c.UseProxyFromEnv { + c.Dialer.Proxy = http.ProxyFromEnvironment + } + conn, _, err := c.Dialer.Dial(target.String(), *header) + if err != nil { + return err + } + c.Conn = conn + c.Connected = true + + // Pass arguments and auth-token + query, err := GetURLQuery(c.URL) + if err != nil { + return err + } + querySingle := querySingleType{ + Arguments: "?" + query.Encode(), + AuthToken: authToken, + } + json, err := json.Marshal(querySingle) + if err != nil { + logrus.Errorf("Failed to parse init message %v", err) + return err + } + // Send Json + logrus.Debugf("Sending arguments and auth-token") + err = c.write(json) + if err != nil { + return err + } + + go c.pingLoop() + + return nil +} + +func (c *Client) pingLoop() { + for { + logrus.Debugf("Sending ping") + c.write([]byte("1")) + time.Sleep(30 * time.Second) + } +} + +// Close will nicely close the dialer +func (c *Client) Close() { + c.Conn.Close() +} + +// ExitLoop will kill all goroutines launched by c.Loop() +// ExitLoop() -> wait Loop() -> Close() +func (c *Client) ExitLoop() { + fname := "ExitLoop" + openPoison(fname, c.poison) +} + +// Loop will look indefinitely for new messages +func (c *Client) Loop() error { + if !c.Connected { + err := c.Connect() + if err != nil { + return err + } + } + + wg := &sync.WaitGroup{} + + wg.Add(1) + go c.termsizeLoop(wg) + + wg.Add(1) + go c.readLoop(wg) + + wg.Add(1) + go c.writeLoop(wg) + + /* Wait for all of the above goroutines to finish */ + wg.Wait() + + logrus.Debug("Client.Loop() exiting") + return nil +} + +type winsize struct { + Rows uint16 `json:"rows"` + Columns uint16 `json:"columns"` + // unused + x uint16 + y uint16 +} + +type posionReason int + +const ( + committedSuicide = iota + killed +) + +func openPoison(fname string, poison chan bool) posionReason { + logrus.Debug(fname + " suicide") + + /* + * The close() may raise panic if multiple goroutines commit suicide at the + * same time. Prevent that panic from bubbling up. + */ + defer func() { + if r := recover(); r != nil { + logrus.Debug("Prevented panic() of simultaneous suicides", r) + } + }() + + /* Signal others to die */ + close(poison) + return committedSuicide +} + +func die(fname string, poison chan bool) posionReason { + logrus.Debug(fname + " died") + + wasOpen := <-poison + if wasOpen { + logrus.Error("ERROR: The channel was open when it wasn't suppoed to be") + } + + return killed +} + +func (c *Client) termsizeLoop(wg *sync.WaitGroup) posionReason { + + defer wg.Done() + fname := "termsizeLoop" + + ch := make(chan os.Signal, 1) + notifySignalSIGWINCH(ch) + defer resetSignalSIGWINCH() + + for { + if b, err := syscallTIOCGWINSZ(); err != nil { + logrus.Warn(err) + } else { + if err = c.write(append([]byte("2"), b...)); err != nil { + logrus.Warnf("ws.WriteMessage failed: %v", err) + } + } + select { + case <-c.poison: + /* Somebody poisoned the well; die */ + return die(fname, c.poison) + case <-ch: + } + } +} + +type exposeFd interface { + Fd() uintptr +} + +func (c *Client) writeLoop(wg *sync.WaitGroup) posionReason { + + defer wg.Done() + fname := "writeLoop" + + buff := make([]byte, 128) + oldState, err := terminal.MakeRaw(0) + if err == nil { + defer terminal.Restore(0, oldState) + } + + rdfs := &goselect.FDSet{} + reader := io.Reader(os.Stdin) + for { + select { + case <-c.poison: + /* Somebody poisoned the well; die */ + return die(fname, c.poison) + default: + } + + rdfs.Zero() + rdfs.Set(reader.(exposeFd).Fd()) + err := goselect.Select(1, rdfs, nil, nil, 50*time.Millisecond) + if err != nil { + return openPoison(fname, c.poison) + } + if rdfs.IsSet(reader.(exposeFd).Fd()) { + size, err := reader.Read(buff) + + if err != nil { + if err == io.EOF { + // Send EOF to GoTTY + + // Send 'Input' marker, as defined in GoTTY::client_context.go, + // followed by EOT (a translation of Ctrl-D for terminals) + err = c.write(append([]byte("0"), byte(4))) + + if err != nil { + return openPoison(fname, c.poison) + } + continue + } else { + return openPoison(fname, c.poison) + } + } + + if size <= 0 { + continue + } + + data := buff[:size] + err = c.write(append([]byte("0"), data...)) + if err != nil { + return openPoison(fname, c.poison) + } + } + } +} + +func (c *Client) readLoop(wg *sync.WaitGroup) posionReason { + + defer wg.Done() + fname := "readLoop" + + type MessageNonBlocking struct { + Data []byte + Err error + } + msgChan := make(chan MessageNonBlocking) + + for { + go func() { + _, data, err := c.Conn.ReadMessage() + msgChan <- MessageNonBlocking{Data: data, Err: err} + }() + + select { + case <-c.poison: + /* Somebody poisoned the well; die */ + return die(fname, c.poison) + case msg := <-msgChan: + if msg.Err != nil { + + if _, ok := msg.Err.(*websocket.CloseError); !ok { + logrus.Warnf("c.Conn.ReadMessage: %v", msg.Err) + } + return openPoison(fname, c.poison) + } + if len(msg.Data) == 0 { + + logrus.Warnf("An error has occured") + return openPoison(fname, c.poison) + } + switch msg.Data[0] { + case '0': // data + buf, err := base64.StdEncoding.DecodeString(string(msg.Data[1:])) + if err != nil { + logrus.Warnf("Invalid base64 content: %q", msg.Data[1:]) + break + } + c.Output.Write(buf) + case '1': // pong + case '2': // new title + newTitle := string(msg.Data[1:]) + fmt.Fprintf(c.Output, "\033]0;%s\007", newTitle) + case '3': // json prefs + logrus.Debugf("Unhandled protocol message: json pref: %s", string(msg.Data[1:])) + case '4': // autoreconnect + logrus.Debugf("Unhandled protocol message: autoreconnect: %s", string(msg.Data)) + default: + logrus.Warnf("Unhandled protocol message: %s", string(msg.Data)) + } + } + } +} + +// SetOutput changes the output stream +func (c *Client) SetOutput(w io.Writer) { + c.Output = w +} + +// ParseURL parses an URL which may be incomplete and tries to standardize it +func ParseURL(input string) (string, error) { + parsed, err := url.Parse(input) + if err != nil { + return "", err + } + switch parsed.Scheme { + case "http", "https": + // everything is ok + default: + return ParseURL(fmt.Sprintf("http://%s", input)) + } + return parsed.String(), nil +} + +// NewClient returns a GoTTY client object +func NewClient(inputURL string) (*Client, error) { + url, err := ParseURL(inputURL) + if err != nil { + return nil, err + } + return &Client{ + Dialer: &websocket.Dialer{}, + URL: url, + WriteMutex: &sync.Mutex{}, + Output: os.Stdout, + poison: make(chan bool), + }, nil +} diff --git a/vendor/github.com/renstrom/fuzzysearch/LICENSE b/vendor/github.com/renstrom/fuzzysearch/LICENSE new file mode 100644 index 000000000..9cc753370 --- /dev/null +++ b/vendor/github.com/renstrom/fuzzysearch/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Peter Renström + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/renstrom/fuzzysearch/fuzzy/fuzzy.go b/vendor/github.com/renstrom/fuzzysearch/fuzzy/fuzzy.go new file mode 100644 index 000000000..63277d51e --- /dev/null +++ b/vendor/github.com/renstrom/fuzzysearch/fuzzy/fuzzy.go @@ -0,0 +1,167 @@ +// Fuzzy searching allows for flexibly matching a string with partial input, +// useful for filtering data very quickly based on lightweight user input. +package fuzzy + +import ( + "unicode" + "unicode/utf8" +) + +var noop = func(r rune) rune { return r } + +// Match returns true if source matches target using a fuzzy-searching +// algorithm. Note that it doesn't implement Levenshtein distance (see +// RankMatch instead), but rather a simplified version where there's no +// approximation. The method will return true only if each character in the +// source can be found in the target and occurs after the preceding matches. +func Match(source, target string) bool { + return match(source, target, noop) +} + +// MatchFold is a case-insensitive version of Match. +func MatchFold(source, target string) bool { + return match(source, target, unicode.ToLower) +} + +func match(source, target string, fn func(rune) rune) bool { + lenDiff := len(target) - len(source) + + if lenDiff < 0 { + return false + } + + if lenDiff == 0 && source == target { + return true + } + +Outer: + for _, r1 := range source { + for i, r2 := range target { + if fn(r1) == fn(r2) { + target = target[i+utf8.RuneLen(r2):] + continue Outer + } + } + return false + } + + return true +} + +// Find will return a list of strings in targets that fuzzy matches source. +func Find(source string, targets []string) []string { + return find(source, targets, noop) +} + +// FindFold is a case-insensitive version of Find. +func FindFold(source string, targets []string) []string { + return find(source, targets, unicode.ToLower) +} + +func find(source string, targets []string, fn func(rune) rune) []string { + var matches []string + + for _, target := range targets { + if match(source, target, fn) { + matches = append(matches, target) + } + } + + return matches +} + +// RankMatch is similar to Match except it will measure the Levenshtein +// distance between the source and the target and return its result. If there +// was no match, it will return -1. +// Given the requirements of match, RankMatch only needs to perform a subset of +// the Levenshtein calculation, only deletions need be considered, required +// additions and substitutions would fail the match test. +func RankMatch(source, target string) int { + return rank(source, target, noop) +} + +// RankMatchFold is a case-insensitive version of RankMatch. +func RankMatchFold(source, target string) int { + return rank(source, target, unicode.ToLower) +} + +func rank(source, target string, fn func(rune) rune) int { + lenDiff := len(target) - len(source) + + if lenDiff < 0 { + return -1 + } + + if lenDiff == 0 && source == target { + return 0 + } + + runeDiff := 0 + +Outer: + for _, r1 := range source { + for i, r2 := range target { + if fn(r1) == fn(r2) { + target = target[i+utf8.RuneLen(r2):] + continue Outer + } else { + runeDiff++ + } + } + return -1 + } + + // Count up remaining char + for len(target) > 0 { + target = target[utf8.RuneLen(rune(target[0])):] + runeDiff++ + } + + return runeDiff +} + +// RankFind is similar to Find, except it will also rank all matches using +// Levenshtein distance. +func RankFind(source string, targets []string) Ranks { + var r Ranks + for _, target := range find(source, targets, noop) { + distance := LevenshteinDistance(source, target) + r = append(r, Rank{source, target, distance}) + } + return r +} + +// RankFindFold is a case-insensitive version of RankFind. +func RankFindFold(source string, targets []string) Ranks { + var r Ranks + for _, target := range find(source, targets, unicode.ToLower) { + distance := LevenshteinDistance(source, target) + r = append(r, Rank{source, target, distance}) + } + return r +} + +type Rank struct { + // Source is used as the source for matching. + Source string + + // Target is the word matched against. + Target string + + // Distance is the Levenshtein distance between Source and Target. + Distance int +} + +type Ranks []Rank + +func (r Ranks) Len() int { + return len(r) +} + +func (r Ranks) Swap(i, j int) { + r[i], r[j] = r[j], r[i] +} + +func (r Ranks) Less(i, j int) bool { + return r[i].Distance < r[j].Distance +} diff --git a/vendor/github.com/renstrom/fuzzysearch/fuzzy/levenshtein.go b/vendor/github.com/renstrom/fuzzysearch/fuzzy/levenshtein.go new file mode 100644 index 000000000..237923d34 --- /dev/null +++ b/vendor/github.com/renstrom/fuzzysearch/fuzzy/levenshtein.go @@ -0,0 +1,43 @@ +package fuzzy + +// LevenshteinDistance measures the difference between two strings. +// The Levenshtein distance between two words is the minimum number of +// single-character edits (i.e. insertions, deletions or substitutions) +// required to change one word into the other. +// +// This implemention is optimized to use O(min(m,n)) space and is based on the +// optimized C version found here: +// http://en.wikibooks.org/wiki/Algorithm_implementation/Strings/Levenshtein_distance#C +func LevenshteinDistance(s, t string) int { + r1, r2 := []rune(s), []rune(t) + column := make([]int, len(r1)+1) + + for y := 1; y <= len(r1); y++ { + column[y] = y + } + + for x := 1; x <= len(r2); x++ { + column[0] = x + + for y, lastDiag := 1, x-1; y <= len(r1); y++ { + oldDiag := column[y] + cost := 0 + if r1[y-1] != r2[x-1] { + cost = 1 + } + column[y] = min(column[y]+1, column[y-1]+1, lastDiag+cost) + lastDiag = oldDiag + } + } + + return column[len(r1)] +} + +func min(a, b, c int) int { + if a < b && a < c { + return a + } else if b < c { + return b + } + return c +} diff --git a/vendor/github.com/scaleway/scaleway-cli/LICENSE.md b/vendor/github.com/scaleway/scaleway-cli/LICENSE.md new file mode 100644 index 000000000..7503a16ca --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-cli/LICENSE.md @@ -0,0 +1,22 @@ +The MIT License +=============== + +Copyright (c) **2014-2016 Scaleway ([@scaleway](https://twitter.com/scaleway))** + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/scaleway/scaleway-cli/pkg/api/README.md b/vendor/github.com/scaleway/scaleway-cli/pkg/api/README.md new file mode 100644 index 000000000..559a7018d --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-cli/pkg/api/README.md @@ -0,0 +1,25 @@ +# Scaleway's API + +[![GoDoc](https://godoc.org/github.com/scaleway/scaleway-cli/pkg/api?status.svg)](https://godoc.org/github.com/scaleway/scaleway-cli/pkg/api) + +This package contains facilities to play with the Scaleway API, it includes the following features: + +- dedicated configuration file containing credentials to deal with the API +- caching to resolve UUIDs without contacting the API + +## Links + +- [API documentation](https://developer.scaleway.com) +- [Official Python SDK](https://github.com/scaleway/python-scaleway) +- Projects using this SDK + - https://github.com/scaleway/devhub + - https://github.com/scaleway/docker-machine-driver-scaleway + - https://github.com/scaleway-community/scaleway-ubuntu-coreos/blob/master/overlay/usr/local/update-firewall/scw-api/cache.go + - https://github.com/pulcy/quark + - https://github.com/hex-sh/terraform-provider-scaleway + - https://github.com/tscolari/bosh-scaleway-cpi +- Other **golang** clients + - https://github.com/lalyos/onlabs + - https://github.com/meatballhat/packer-builder-onlinelabs + - https://github.com/nlamirault/go-scaleway + - https://github.com/golang/build/blob/master/cmd/scaleway/scaleway.go diff --git a/vendor/github.com/scaleway/scaleway-cli/pkg/api/api.go b/vendor/github.com/scaleway/scaleway-cli/pkg/api/api.go new file mode 100644 index 000000000..5ce0157dc --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-cli/pkg/api/api.go @@ -0,0 +1,2754 @@ +// Copyright (C) 2015 Scaleway. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE.md file. + +// Interact with Scaleway API + +// Package api contains client and functions to interact with Scaleway API +package api + +import ( + "bytes" + "crypto/tls" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "net" + "net/http" + "net/http/httputil" + "net/url" + "os" + "sort" + "strconv" + "strings" + "text/tabwriter" + "text/template" + "time" + + "golang.org/x/sync/errgroup" +) + +// Default values +var ( + AccountAPI = "https://account.scaleway.com/" + MetadataAPI = "http://169.254.42.42/" + MarketplaceAPI = "https://api-marketplace.scaleway.com" + ComputeAPIPar1 = "https://cp-par1.scaleway.com/" + ComputeAPIAms1 = "https://cp-ams1.scaleway.com" + + URLPublicDNS = ".pub.cloud.scaleway.com" + URLPrivateDNS = ".priv.cloud.scaleway.com" +) + +func init() { + if url := os.Getenv("SCW_ACCOUNT_API"); url != "" { + AccountAPI = url + } + if url := os.Getenv("SCW_METADATA_API"); url != "" { + MetadataAPI = url + } + if url := os.Getenv("SCW_MARKETPLACE_API"); url != "" { + MarketplaceAPI = url + } +} + +const ( + perPage = 50 +) + +// ScalewayAPI is the interface used to communicate with the Scaleway API +type ScalewayAPI struct { + // Organization is the identifier of the Scaleway organization + Organization string + + // Token is the authentication token for the Scaleway organization + Token string + + // Password is the authentication password + password string + + userAgent string + + // Cache is used to quickly resolve identifiers from names + Cache *ScalewayCache + + client *http.Client + verbose bool + computeAPI string + + Region string + // + Logger +} + +// ScalewayAPIError represents a Scaleway API Error +type ScalewayAPIError struct { + // Message is a human-friendly error message + APIMessage string `json:"message,omitempty"` + + // Type is a string code that defines the kind of error + Type string `json:"type,omitempty"` + + // Fields contains detail about validation error + Fields map[string][]string `json:"fields,omitempty"` + + // StatusCode is the HTTP status code received + StatusCode int `json:"-"` + + // Message + Message string `json:"-"` +} + +// Error returns a string representing the error +func (e ScalewayAPIError) Error() string { + var b bytes.Buffer + + fmt.Fprintf(&b, "StatusCode: %v, ", e.StatusCode) + fmt.Fprintf(&b, "Type: %v, ", e.Type) + fmt.Fprintf(&b, "APIMessage: \x1b[31m%v\x1b[0m", e.APIMessage) + if len(e.Fields) > 0 { + fmt.Fprintf(&b, ", Details: %v", e.Fields) + } + return b.String() +} + +// HideAPICredentials removes API credentials from a string +func (s *ScalewayAPI) HideAPICredentials(input string) string { + output := input + if s.Token != "" { + output = strings.Replace(output, s.Token, "00000000-0000-4000-8000-000000000000", -1) + } + if s.Organization != "" { + output = strings.Replace(output, s.Organization, "00000000-0000-5000-9000-000000000000", -1) + } + if s.password != "" { + output = strings.Replace(output, s.password, "XX-XX-XX-XX", -1) + } + return output +} + +// ScalewayIPAddress represents a Scaleway IP address +type ScalewayIPAddress struct { + // Identifier is a unique identifier for the IP address + Identifier string `json:"id,omitempty"` + + // IP is an IPv4 address + IP string `json:"address,omitempty"` + + // Dynamic is a flag that defines an IP that change on each reboot + Dynamic *bool `json:"dynamic,omitempty"` +} + +// ScalewayVolume represents a Scaleway Volume +type ScalewayVolume struct { + // Identifier is a unique identifier for the volume + Identifier string `json:"id,omitempty"` + + // Size is the allocated size of the volume + Size uint64 `json:"size,omitempty"` + + // CreationDate is the creation date of the volume + CreationDate string `json:"creation_date,omitempty"` + + // ModificationDate is the date of the last modification of the volume + ModificationDate string `json:"modification_date,omitempty"` + + // Organization is the organization owning the volume + Organization string `json:"organization,omitempty"` + + // Name is the name of the volume + Name string `json:"name,omitempty"` + + // Server is the server using this image + Server *struct { + Identifier string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + } `json:"server,omitempty"` + + // VolumeType is a Scaleway identifier for the kind of volume (default: l_ssd) + VolumeType string `json:"volume_type,omitempty"` + + // ExportURI represents the url used by initrd/scripts to attach the volume + ExportURI string `json:"export_uri,omitempty"` +} + +// ScalewayOneVolume represents the response of a GET /volumes/UUID API call +type ScalewayOneVolume struct { + Volume ScalewayVolume `json:"volume,omitempty"` +} + +// ScalewayVolumes represents a group of Scaleway volumes +type ScalewayVolumes struct { + // Volumes holds scaleway volumes of the response + Volumes []ScalewayVolume `json:"volumes,omitempty"` +} + +// ScalewayVolumeDefinition represents a Scaleway volume definition +type ScalewayVolumeDefinition struct { + // Name is the user-defined name of the volume + Name string `json:"name"` + + // Image is the image used by the volume + Size uint64 `json:"size"` + + // Bootscript is the bootscript used by the volume + Type string `json:"volume_type"` + + // Organization is the owner of the volume + Organization string `json:"organization"` +} + +// ScalewayVolumePutDefinition represents a Scaleway volume with nullable fields (for PUT) +type ScalewayVolumePutDefinition struct { + Identifier *string `json:"id,omitempty"` + Size *uint64 `json:"size,omitempty"` + CreationDate *string `json:"creation_date,omitempty"` + ModificationDate *string `json:"modification_date,omitempty"` + Organization *string `json:"organization,omitempty"` + Name *string `json:"name,omitempty"` + Server struct { + Identifier *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + } `json:"server,omitempty"` + VolumeType *string `json:"volume_type,omitempty"` + ExportURI *string `json:"export_uri,omitempty"` +} + +// ScalewayImage represents a Scaleway Image +type ScalewayImage struct { + // Identifier is a unique identifier for the image + Identifier string `json:"id,omitempty"` + + // Name is a user-defined name for the image + Name string `json:"name,omitempty"` + + // CreationDate is the creation date of the image + CreationDate string `json:"creation_date,omitempty"` + + // ModificationDate is the date of the last modification of the image + ModificationDate string `json:"modification_date,omitempty"` + + // RootVolume is the root volume bound to the image + RootVolume ScalewayVolume `json:"root_volume,omitempty"` + + // Public is true for public images and false for user images + Public bool `json:"public,omitempty"` + + // Bootscript is the bootscript bound to the image + DefaultBootscript *ScalewayBootscript `json:"default_bootscript,omitempty"` + + // Organization is the owner of the image + Organization string `json:"organization,omitempty"` + + // Arch is the architecture target of the image + Arch string `json:"arch,omitempty"` + + // FIXME: extra_volumes +} + +// ScalewayImageIdentifier represents a Scaleway Image Identifier +type ScalewayImageIdentifier struct { + Identifier string + Arch string + Region string + Owner string +} + +// ScalewayOneImage represents the response of a GET /images/UUID API call +type ScalewayOneImage struct { + Image ScalewayImage `json:"image,omitempty"` +} + +// ScalewayImages represents a group of Scaleway images +type ScalewayImages struct { + // Images holds scaleway images of the response + Images []ScalewayImage `json:"images,omitempty"` +} + +// ScalewaySnapshot represents a Scaleway Snapshot +type ScalewaySnapshot struct { + // Identifier is a unique identifier for the snapshot + Identifier string `json:"id,omitempty"` + + // Name is a user-defined name for the snapshot + Name string `json:"name,omitempty"` + + // CreationDate is the creation date of the snapshot + CreationDate string `json:"creation_date,omitempty"` + + // ModificationDate is the date of the last modification of the snapshot + ModificationDate string `json:"modification_date,omitempty"` + + // Size is the allocated size of the volume + Size uint64 `json:"size,omitempty"` + + // Organization is the owner of the snapshot + Organization string `json:"organization"` + + // State is the current state of the snapshot + State string `json:"state"` + + // VolumeType is the kind of volume behind the snapshot + VolumeType string `json:"volume_type"` + + // BaseVolume is the volume from which the snapshot inherits + BaseVolume ScalewayVolume `json:"base_volume,omitempty"` +} + +// ScalewayOneSnapshot represents the response of a GET /snapshots/UUID API call +type ScalewayOneSnapshot struct { + Snapshot ScalewaySnapshot `json:"snapshot,omitempty"` +} + +// ScalewaySnapshots represents a group of Scaleway snapshots +type ScalewaySnapshots struct { + // Snapshots holds scaleway snapshots of the response + Snapshots []ScalewaySnapshot `json:"snapshots,omitempty"` +} + +// ScalewayBootscript represents a Scaleway Bootscript +type ScalewayBootscript struct { + Bootcmdargs string `json:"bootcmdargs,omitempty"` + Dtb string `json:"dtb,omitempty"` + Initrd string `json:"initrd,omitempty"` + Kernel string `json:"kernel,omitempty"` + + // Arch is the architecture target of the bootscript + Arch string `json:"architecture,omitempty"` + + // Identifier is a unique identifier for the bootscript + Identifier string `json:"id,omitempty"` + + // Organization is the owner of the bootscript + Organization string `json:"organization,omitempty"` + + // Name is a user-defined name for the bootscript + Title string `json:"title,omitempty"` + + // Public is true for public bootscripts and false for user bootscripts + Public bool `json:"public,omitempty"` + + Default bool `json:"default,omitempty"` +} + +// ScalewayOneBootscript represents the response of a GET /bootscripts/UUID API call +type ScalewayOneBootscript struct { + Bootscript ScalewayBootscript `json:"bootscript,omitempty"` +} + +// ScalewayBootscripts represents a group of Scaleway bootscripts +type ScalewayBootscripts struct { + // Bootscripts holds Scaleway bootscripts of the response + Bootscripts []ScalewayBootscript `json:"bootscripts,omitempty"` +} + +// ScalewayTask represents a Scaleway Task +type ScalewayTask struct { + // Identifier is a unique identifier for the task + Identifier string `json:"id,omitempty"` + + // StartDate is the start date of the task + StartDate string `json:"started_at,omitempty"` + + // TerminationDate is the termination date of the task + TerminationDate string `json:"terminated_at,omitempty"` + + HrefFrom string `json:"href_from,omitempty"` + + Description string `json:"description,omitempty"` + + Status string `json:"status,omitempty"` + + Progress int `json:"progress,omitempty"` +} + +// ScalewayOneTask represents the response of a GET /tasks/UUID API call +type ScalewayOneTask struct { + Task ScalewayTask `json:"task,omitempty"` +} + +// ScalewayTasks represents a group of Scaleway tasks +type ScalewayTasks struct { + // Tasks holds scaleway tasks of the response + Tasks []ScalewayTask `json:"tasks,omitempty"` +} + +// ScalewaySecurityGroupRule definition +type ScalewaySecurityGroupRule struct { + Direction string `json:"direction"` + Protocol string `json:"protocol"` + IPRange string `json:"ip_range"` + DestPortFrom int `json:"dest_port_from,omitempty"` + Action string `json:"action"` + Position int `json:"position"` + DestPortTo string `json:"dest_port_to"` + Editable bool `json:"editable"` + ID string `json:"id"` +} + +// ScalewayGetSecurityGroupRules represents the response of a GET /security_group/{groupID}/rules +type ScalewayGetSecurityGroupRules struct { + Rules []ScalewaySecurityGroupRule `json:"rules"` +} + +// ScalewayGetSecurityGroupRule represents the response of a GET /security_group/{groupID}/rules/{ruleID} +type ScalewayGetSecurityGroupRule struct { + Rules ScalewaySecurityGroupRule `json:"rule"` +} + +// ScalewayNewSecurityGroupRule definition POST/PUT request /security_group/{groupID} +type ScalewayNewSecurityGroupRule struct { + Action string `json:"action"` + Direction string `json:"direction"` + IPRange string `json:"ip_range"` + Protocol string `json:"protocol"` + DestPortFrom int `json:"dest_port_from,omitempty"` +} + +// ScalewaySecurityGroups definition +type ScalewaySecurityGroups struct { + Description string `json:"description"` + ID string `json:"id"` + Organization string `json:"organization"` + Name string `json:"name"` + Servers []ScalewaySecurityGroup `json:"servers"` + EnableDefaultSecurity bool `json:"enable_default_security"` + OrganizationDefault bool `json:"organization_default"` +} + +// ScalewayGetSecurityGroups represents the response of a GET /security_groups/ +type ScalewayGetSecurityGroups struct { + SecurityGroups []ScalewaySecurityGroups `json:"security_groups"` +} + +// ScalewayGetSecurityGroup represents the response of a GET /security_groups/{groupID} +type ScalewayGetSecurityGroup struct { + SecurityGroups ScalewaySecurityGroups `json:"security_group"` +} + +// ScalewayIPDefinition represents the IP's fields +type ScalewayIPDefinition struct { + Organization string `json:"organization"` + Reverse *string `json:"reverse"` + ID string `json:"id"` + Server *struct { + Identifier string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + } `json:"server"` + Address string `json:"address"` +} + +// ScalewayGetIPS represents the response of a GET /ips/ +type ScalewayGetIPS struct { + IPS []ScalewayIPDefinition `json:"ips"` +} + +// ScalewayGetIP represents the response of a GET /ips/{id_ip} +type ScalewayGetIP struct { + IP ScalewayIPDefinition `json:"ip"` +} + +// ScalewaySecurityGroup represents a Scaleway security group +type ScalewaySecurityGroup struct { + // Identifier is a unique identifier for the security group + Identifier string `json:"id,omitempty"` + + // Name is the user-defined name of the security group + Name string `json:"name,omitempty"` +} + +// ScalewayNewSecurityGroup definition POST request /security_groups +type ScalewayNewSecurityGroup struct { + Organization string `json:"organization"` + Name string `json:"name"` + Description string `json:"description"` +} + +// ScalewayUpdateSecurityGroup definition PUT request /security_groups +type ScalewayUpdateSecurityGroup struct { + Organization string `json:"organization"` + Name string `json:"name"` + Description string `json:"description"` + OrganizationDefault bool `json:"organization_default"` +} + +// ScalewayServer represents a Scaleway server +type ScalewayServer struct { + // Arch is the architecture target of the server + Arch string `json:"arch,omitempty"` + + // Identifier is a unique identifier for the server + Identifier string `json:"id,omitempty"` + + // Name is the user-defined name of the server + Name string `json:"name,omitempty"` + + // CreationDate is the creation date of the server + CreationDate string `json:"creation_date,omitempty"` + + // ModificationDate is the date of the last modification of the server + ModificationDate string `json:"modification_date,omitempty"` + + // Image is the image used by the server + Image ScalewayImage `json:"image,omitempty"` + + // DynamicIPRequired is a flag that defines a server with a dynamic ip address attached + DynamicIPRequired *bool `json:"dynamic_ip_required,omitempty"` + + // PublicIP is the public IP address bound to the server + PublicAddress ScalewayIPAddress `json:"public_ip,omitempty"` + + // State is the current status of the server + State string `json:"state,omitempty"` + + // StateDetail is the detailed status of the server + StateDetail string `json:"state_detail,omitempty"` + + // PrivateIP represents the private IPV4 attached to the server (changes on each boot) + PrivateIP string `json:"private_ip,omitempty"` + + // Bootscript is the unique identifier of the selected bootscript + Bootscript *ScalewayBootscript `json:"bootscript,omitempty"` + + // Hostname represents the ServerName in a format compatible with unix's hostname + Hostname string `json:"hostname,omitempty"` + + // Tags represents user-defined tags + Tags []string `json:"tags,omitempty"` + + // Volumes are the attached volumes + Volumes map[string]ScalewayVolume `json:"volumes,omitempty"` + + // SecurityGroup is the selected security group object + SecurityGroup ScalewaySecurityGroup `json:"security_group,omitempty"` + + // Organization is the owner of the server + Organization string `json:"organization,omitempty"` + + // CommercialType is the commercial type of the server (i.e: C1, C2[SML], VC1S) + CommercialType string `json:"commercial_type,omitempty"` + + // Location of the server + Location struct { + Platform string `json:"platform_id,omitempty"` + Chassis string `json:"chassis_id,omitempty"` + Cluster string `json:"cluster_id,omitempty"` + Hypervisor string `json:"hypervisor_id,omitempty"` + Blade string `json:"blade_id,omitempty"` + Node string `json:"node_id,omitempty"` + ZoneID string `json:"zone_id,omitempty"` + } `json:"location,omitempty"` + + IPV6 *ScalewayIPV6Definition `json:"ipv6,omitempty"` + + EnableIPV6 bool `json:"enable_ipv6,omitempty"` + + // This fields are not returned by the API, we generate it + DNSPublic string `json:"dns_public,omitempty"` + DNSPrivate string `json:"dns_private,omitempty"` +} + +// ScalewayIPV6Definition represents a Scaleway ipv6 +type ScalewayIPV6Definition struct { + Netmask string `json:"netmask"` + Gateway string `json:"gateway"` + Address string `json:"address"` +} + +// ScalewayServerPatchDefinition represents a Scaleway server with nullable fields (for PATCH) +type ScalewayServerPatchDefinition struct { + Arch *string `json:"arch,omitempty"` + Name *string `json:"name,omitempty"` + CreationDate *string `json:"creation_date,omitempty"` + ModificationDate *string `json:"modification_date,omitempty"` + Image *ScalewayImage `json:"image,omitempty"` + DynamicIPRequired *bool `json:"dynamic_ip_required,omitempty"` + PublicAddress *ScalewayIPAddress `json:"public_ip,omitempty"` + State *string `json:"state,omitempty"` + StateDetail *string `json:"state_detail,omitempty"` + PrivateIP *string `json:"private_ip,omitempty"` + Bootscript *string `json:"bootscript,omitempty"` + Hostname *string `json:"hostname,omitempty"` + Volumes *map[string]ScalewayVolume `json:"volumes,omitempty"` + SecurityGroup *ScalewaySecurityGroup `json:"security_group,omitempty"` + Organization *string `json:"organization,omitempty"` + Tags *[]string `json:"tags,omitempty"` + IPV6 *ScalewayIPV6Definition `json:"ipv6,omitempty"` + EnableIPV6 *bool `json:"enable_ipv6,omitempty"` +} + +// ScalewayServerDefinition represents a Scaleway server with image definition +type ScalewayServerDefinition struct { + // Name is the user-defined name of the server + Name string `json:"name"` + + // Image is the image used by the server + Image *string `json:"image,omitempty"` + + // Volumes are the attached volumes + Volumes map[string]string `json:"volumes,omitempty"` + + // DynamicIPRequired is a flag that defines a server with a dynamic ip address attached + DynamicIPRequired *bool `json:"dynamic_ip_required,omitempty"` + + // Bootscript is the bootscript used by the server + Bootscript *string `json:"bootscript"` + + // Tags are the metadata tags attached to the server + Tags []string `json:"tags,omitempty"` + + // Organization is the owner of the server + Organization string `json:"organization"` + + // CommercialType is the commercial type of the server (i.e: C1, C2[SML], VC1S) + CommercialType string `json:"commercial_type"` + + PublicIP string `json:"public_ip,omitempty"` + + EnableIPV6 bool `json:"enable_ipv6,omitempty"` + + SecurityGroup string `json:"security_group,omitempty"` +} + +// ScalewayOneServer represents the response of a GET /servers/UUID API call +type ScalewayOneServer struct { + Server ScalewayServer `json:"server,omitempty"` +} + +// ScalewayServers represents a group of Scaleway servers +type ScalewayServers struct { + // Servers holds scaleway servers of the response + Servers []ScalewayServer `json:"servers,omitempty"` +} + +// ScalewayServerAction represents an action to perform on a Scaleway server +type ScalewayServerAction struct { + // Action is the name of the action to trigger + Action string `json:"action,omitempty"` +} + +// ScalewaySnapshotDefinition represents a Scaleway snapshot definition +type ScalewaySnapshotDefinition struct { + VolumeIDentifier string `json:"volume_id"` + Name string `json:"name,omitempty"` + Organization string `json:"organization"` +} + +// ScalewayImageDefinition represents a Scaleway image definition +type ScalewayImageDefinition struct { + SnapshotIDentifier string `json:"root_volume"` + Name string `json:"name,omitempty"` + Organization string `json:"organization"` + Arch string `json:"arch"` + DefaultBootscript *string `json:"default_bootscript,omitempty"` +} + +// ScalewayRoleDefinition represents a Scaleway Token UserId Role +type ScalewayRoleDefinition struct { + Organization ScalewayOrganizationDefinition `json:"organization,omitempty"` + Role string `json:"role,omitempty"` +} + +// ScalewayTokenDefinition represents a Scaleway Token +type ScalewayTokenDefinition struct { + UserID string `json:"user_id"` + Description string `json:"description,omitempty"` + Roles ScalewayRoleDefinition `json:"roles"` + Expires string `json:"expires"` + InheritsUsersPerms bool `json:"inherits_user_perms"` + ID string `json:"id"` +} + +// ScalewayTokensDefinition represents a Scaleway Tokens +type ScalewayTokensDefinition struct { + Token ScalewayTokenDefinition `json:"token"` +} + +// ScalewayGetTokens represents a list of Scaleway Tokens +type ScalewayGetTokens struct { + Tokens []ScalewayTokenDefinition `json:"tokens"` +} + +// ScalewayContainerData represents a Scaleway container data (S3) +type ScalewayContainerData struct { + LastModified string `json:"last_modified"` + Name string `json:"name"` + Size string `json:"size"` +} + +// ScalewayGetContainerDatas represents a list of Scaleway containers data (S3) +type ScalewayGetContainerDatas struct { + Container []ScalewayContainerData `json:"container"` +} + +// ScalewayContainer represents a Scaleway container (S3) +type ScalewayContainer struct { + ScalewayOrganizationDefinition `json:"organization"` + Name string `json:"name"` + Size string `json:"size"` +} + +// ScalewayGetContainers represents a list of Scaleway containers (S3) +type ScalewayGetContainers struct { + Containers []ScalewayContainer `json:"containers"` +} + +// ScalewayConnectResponse represents the answer from POST /tokens +type ScalewayConnectResponse struct { + Token ScalewayTokenDefinition `json:"token"` +} + +// ScalewayConnect represents the data to connect +type ScalewayConnect struct { + Email string `json:"email"` + Password string `json:"password"` + Description string `json:"description"` + Expires bool `json:"expires"` +} + +// ScalewayOrganizationDefinition represents a Scaleway Organization +type ScalewayOrganizationDefinition struct { + ID string `json:"id"` + Name string `json:"name"` + Users []ScalewayUserDefinition `json:"users"` +} + +// ScalewayOrganizationsDefinition represents a Scaleway Organizations +type ScalewayOrganizationsDefinition struct { + Organizations []ScalewayOrganizationDefinition `json:"organizations"` +} + +// ScalewayUserDefinition represents a Scaleway User +type ScalewayUserDefinition struct { + Email string `json:"email"` + Firstname string `json:"firstname"` + Fullname string `json:"fullname"` + ID string `json:"id"` + Lastname string `json:"lastname"` + Organizations []ScalewayOrganizationDefinition `json:"organizations"` + Roles []ScalewayRoleDefinition `json:"roles"` + SSHPublicKeys []ScalewayKeyDefinition `json:"ssh_public_keys"` +} + +// ScalewayUsersDefinition represents the response of a GET /user +type ScalewayUsersDefinition struct { + User ScalewayUserDefinition `json:"user"` +} + +// ScalewayKeyDefinition represents a key +type ScalewayKeyDefinition struct { + Key string `json:"key"` + Fingerprint string `json:"fingerprint,omitempty"` +} + +// ScalewayUserPatchSSHKeyDefinition represents a User Patch +type ScalewayUserPatchSSHKeyDefinition struct { + SSHPublicKeys []ScalewayKeyDefinition `json:"ssh_public_keys"` +} + +// ScalewayDashboardResp represents a dashboard received from the API +type ScalewayDashboardResp struct { + Dashboard ScalewayDashboard +} + +// ScalewayDashboard represents a dashboard +type ScalewayDashboard struct { + VolumesCount int `json:"volumes_count"` + RunningServersCount int `json:"running_servers_count"` + ImagesCount int `json:"images_count"` + SnapshotsCount int `json:"snapshots_count"` + ServersCount int `json:"servers_count"` + IPsCount int `json:"ips_count"` +} + +// ScalewayPermissions represents the response of GET /permissions +type ScalewayPermissions map[string]ScalewayPermCategory + +// ScalewayPermCategory represents ScalewayPermissions's fields +type ScalewayPermCategory map[string][]string + +// ScalewayPermissionDefinition represents the permissions +type ScalewayPermissionDefinition struct { + Permissions ScalewayPermissions `json:"permissions"` +} + +// ScalewayUserdatas represents the response of a GET /user_data +type ScalewayUserdatas struct { + UserData []string `json:"user_data"` +} + +// ScalewayQuota represents a map of quota (name, value) +type ScalewayQuota map[string]int + +// ScalewayGetQuotas represents the response of GET /organizations/{orga_id}/quotas +type ScalewayGetQuotas struct { + Quotas ScalewayQuota `json:"quotas"` +} + +// ScalewayUserdata represents []byte +type ScalewayUserdata []byte + +// FuncMap used for json inspection +var FuncMap = template.FuncMap{ + "json": func(v interface{}) string { + a, _ := json.Marshal(v) + return string(a) + }, +} + +// MarketLocalImageDefinition represents localImage of marketplace version +type MarketLocalImageDefinition struct { + Arch string `json:"arch"` + ID string `json:"id"` + Zone string `json:"zone"` +} + +// MarketLocalImages represents an array of local images +type MarketLocalImages struct { + LocalImages []MarketLocalImageDefinition `json:"local_images"` +} + +// MarketLocalImage represents local image +type MarketLocalImage struct { + LocalImages MarketLocalImageDefinition `json:"local_image"` +} + +// MarketVersionDefinition represents version of marketplace image +type MarketVersionDefinition struct { + CreationDate string `json:"creation_date"` + ID string `json:"id"` + Image struct { + ID string `json:"id"` + Name string `json:"name"` + } `json:"image"` + ModificationDate string `json:"modification_date"` + Name string `json:"name"` + MarketLocalImages +} + +// MarketVersions represents an array of marketplace image versions +type MarketVersions struct { + Versions []MarketVersionDefinition `json:"versions"` +} + +// MarketVersion represents version of marketplace image +type MarketVersion struct { + Version MarketVersionDefinition `json:"version"` +} + +// MarketImage represents MarketPlace image +type MarketImage struct { + Categories []string `json:"categories"` + CreationDate string `json:"creation_date"` + CurrentPublicVersion string `json:"current_public_version"` + Description string `json:"description"` + ID string `json:"id"` + Logo string `json:"logo"` + ModificationDate string `json:"modification_date"` + Name string `json:"name"` + Organization struct { + ID string `json:"id"` + Name string `json:"name"` + } `json:"organization"` + Public bool `json:"-"` + MarketVersions +} + +// MarketImages represents MarketPlace images +type MarketImages struct { + Images []MarketImage `json:"images"` +} + +// NewScalewayAPI creates a ready-to-use ScalewayAPI client +func NewScalewayAPI(organization, token, userAgent, region string, options ...func(*ScalewayAPI)) (*ScalewayAPI, error) { + s := &ScalewayAPI{ + // exposed + Organization: organization, + Token: token, + Logger: NewDefaultLogger(), + + // internal + client: &http.Client{}, + verbose: os.Getenv("SCW_VERBOSE_API") != "", + password: "", + userAgent: userAgent, + } + for _, option := range options { + option(s) + } + cache, err := NewScalewayCache(func() { s.Logger.Debugf("Writing cache file to disk") }) + if err != nil { + return nil, err + } + s.Cache = cache + if os.Getenv("SCW_TLSVERIFY") == "0" { + s.client.Transport = &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + } + switch region { + case "par1", "": + s.computeAPI = ComputeAPIPar1 + case "ams1": + s.computeAPI = ComputeAPIAms1 + default: + return nil, fmt.Errorf("%s isn't a valid region", region) + } + s.Region = region + if url := os.Getenv("SCW_COMPUTE_API"); url != "" { + s.computeAPI = url + } + return s, nil +} + +// ClearCache clears the cache +func (s *ScalewayAPI) ClearCache() { + s.Cache.Clear() +} + +// Sync flushes out the cache to the disk +func (s *ScalewayAPI) Sync() { + s.Cache.Save() +} + +func (s *ScalewayAPI) response(method, uri string, content io.Reader) (resp *http.Response, err error) { + var ( + req *http.Request + ) + + req, err = http.NewRequest(method, uri, content) + if err != nil { + err = fmt.Errorf("response %s %s", method, uri) + return + } + req.Header.Set("X-Auth-Token", s.Token) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("User-Agent", s.userAgent) + s.LogHTTP(req) + if s.verbose { + dump, _ := httputil.DumpRequest(req, true) + s.Debugf("%v", string(dump)) + } else { + s.Debugf("[%s]: %v", method, uri) + } + resp, err = s.client.Do(req) + return +} + +// GetResponsePaginate fetchs all resources and returns an http.Response object for the requested resource +func (s *ScalewayAPI) GetResponsePaginate(apiURL, resource string, values url.Values) (*http.Response, error) { + resp, err := s.response("HEAD", fmt.Sprintf("%s/%s?%s", strings.TrimRight(apiURL, "/"), resource, values.Encode()), nil) + if err != nil { + return nil, err + } + + count := resp.Header.Get("X-Total-Count") + var maxElem int + if count == "" { + maxElem = 0 + } else { + maxElem, err = strconv.Atoi(count) + if err != nil { + return nil, err + } + } + + get := maxElem / perPage + if (float32(maxElem) / perPage) > float32(get) { + get++ + } + + if get <= 1 { // If there is 0 or 1 page of result, the response is not paginated + if len(values) == 0 { + return s.response("GET", fmt.Sprintf("%s/%s", strings.TrimRight(apiURL, "/"), resource), nil) + } + return s.response("GET", fmt.Sprintf("%s/%s?%s", strings.TrimRight(apiURL, "/"), resource, values.Encode()), nil) + } + + fetchAll := !(values.Get("per_page") != "" || values.Get("page") != "") + if fetchAll { + var g errgroup.Group + + ch := make(chan *http.Response, get) + for i := 1; i <= get; i++ { + i := i // closure tricks + g.Go(func() (err error) { + var resp *http.Response + + val := url.Values{} + val.Set("per_page", fmt.Sprintf("%v", perPage)) + val.Set("page", fmt.Sprintf("%v", i)) + resp, err = s.response("GET", fmt.Sprintf("%s/%s?%s", strings.TrimRight(apiURL, "/"), resource, val.Encode()), nil) + ch <- resp + return + }) + } + if err = g.Wait(); err != nil { + return nil, err + } + newBody := make(map[string][]json.RawMessage) + body := make(map[string][]json.RawMessage) + key := "" + for i := 0; i < get; i++ { + res := <-ch + if res.StatusCode != http.StatusOK { + return res, nil + } + content, err := ioutil.ReadAll(res.Body) + res.Body.Close() + if err != nil { + return nil, err + } + if err := json.Unmarshal(content, &body); err != nil { + return nil, err + } + + if i == 0 { + resp = res + for k := range body { + key = k + break + } + } + newBody[key] = append(newBody[key], body[key]...) + } + payload := new(bytes.Buffer) + if err := json.NewEncoder(payload).Encode(newBody); err != nil { + return nil, err + } + resp.Body = ioutil.NopCloser(payload) + } else { + resp, err = s.response("GET", fmt.Sprintf("%s/%s?%s", strings.TrimRight(apiURL, "/"), resource, values.Encode()), nil) + } + return resp, err +} + +// PostResponse returns an http.Response object for the updated resource +func (s *ScalewayAPI) PostResponse(apiURL, resource string, data interface{}) (*http.Response, error) { + payload := new(bytes.Buffer) + if err := json.NewEncoder(payload).Encode(data); err != nil { + return nil, err + } + return s.response("POST", fmt.Sprintf("%s/%s", strings.TrimRight(apiURL, "/"), resource), payload) +} + +// PatchResponse returns an http.Response object for the updated resource +func (s *ScalewayAPI) PatchResponse(apiURL, resource string, data interface{}) (*http.Response, error) { + payload := new(bytes.Buffer) + if err := json.NewEncoder(payload).Encode(data); err != nil { + return nil, err + } + return s.response("PATCH", fmt.Sprintf("%s/%s", strings.TrimRight(apiURL, "/"), resource), payload) +} + +// PutResponse returns an http.Response object for the updated resource +func (s *ScalewayAPI) PutResponse(apiURL, resource string, data interface{}) (*http.Response, error) { + payload := new(bytes.Buffer) + if err := json.NewEncoder(payload).Encode(data); err != nil { + return nil, err + } + return s.response("PUT", fmt.Sprintf("%s/%s", strings.TrimRight(apiURL, "/"), resource), payload) +} + +// DeleteResponse returns an http.Response object for the deleted resource +func (s *ScalewayAPI) DeleteResponse(apiURL, resource string) (*http.Response, error) { + return s.response("DELETE", fmt.Sprintf("%s/%s", strings.TrimRight(apiURL, "/"), resource), nil) +} + +// handleHTTPError checks the statusCode and displays the error +func (s *ScalewayAPI) handleHTTPError(goodStatusCode []int, resp *http.Response) ([]byte, error) { + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + if s.verbose { + resp.Body = ioutil.NopCloser(bytes.NewBuffer(body)) + dump, err := httputil.DumpResponse(resp, true) + if err == nil { + var js bytes.Buffer + + err = json.Indent(&js, body, "", " ") + if err != nil { + s.Debugf("[Response]: [%v]\n%v", resp.StatusCode, string(dump)) + } else { + s.Debugf("[Response]: [%v]\n%v", resp.StatusCode, js.String()) + } + } + } else { + s.Debugf("[Response]: [%v]\n%v", resp.StatusCode, string(body)) + } + + if resp.StatusCode >= http.StatusInternalServerError { + return nil, errors.New(string(body)) + } + good := false + for _, code := range goodStatusCode { + if code == resp.StatusCode { + good = true + } + } + if !good { + var scwError ScalewayAPIError + + if err := json.Unmarshal(body, &scwError); err != nil { + return nil, err + } + scwError.StatusCode = resp.StatusCode + s.Debugf("%s", scwError.Error()) + return nil, scwError + } + return body, nil +} + +func (s *ScalewayAPI) fetchServers(api string, query url.Values, out chan<- ScalewayServers) func() error { + return func() error { + resp, err := s.GetResponsePaginate(api, "servers", query) + if err != nil { + return err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return err + } + var servers ScalewayServers + + if err = json.Unmarshal(body, &servers); err != nil { + return err + } + out <- servers + return nil + } +} + +// GetServers gets the list of servers from the ScalewayAPI +func (s *ScalewayAPI) GetServers(all bool, limit int) (*[]ScalewayServer, error) { + query := url.Values{} + if !all { + query.Set("state", "running") + } + if limit > 0 { + // FIXME: wait for the API to be ready + // query.Set("per_page", strconv.Itoa(limit)) + panic("Not implemented yet") + } + if all && limit == 0 { + s.Cache.ClearServers() + } + var ( + g errgroup.Group + apis = []string{ + ComputeAPIPar1, + ComputeAPIAms1, + } + ) + + serverChan := make(chan ScalewayServers, 2) + for _, api := range apis { + g.Go(s.fetchServers(api, query, serverChan)) + } + + if err := g.Wait(); err != nil { + return nil, err + } + close(serverChan) + var servers ScalewayServers + + for server := range serverChan { + servers.Servers = append(servers.Servers, server.Servers...) + } + + for i, server := range servers.Servers { + servers.Servers[i].DNSPublic = server.Identifier + URLPublicDNS + servers.Servers[i].DNSPrivate = server.Identifier + URLPrivateDNS + s.Cache.InsertServer(server.Identifier, server.Location.ZoneID, server.Arch, server.Organization, server.Name) + } + return &servers.Servers, nil +} + +// ScalewaySortServers represents a wrapper to sort by CreationDate the servers +type ScalewaySortServers []ScalewayServer + +func (s ScalewaySortServers) Len() int { + return len(s) +} + +func (s ScalewaySortServers) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +func (s ScalewaySortServers) Less(i, j int) bool { + date1, _ := time.Parse("2006-01-02T15:04:05.000000+00:00", s[i].CreationDate) + date2, _ := time.Parse("2006-01-02T15:04:05.000000+00:00", s[j].CreationDate) + return date2.Before(date1) +} + +// GetServer gets a server from the ScalewayAPI +func (s *ScalewayAPI) GetServer(serverID string) (*ScalewayServer, error) { + resp, err := s.GetResponsePaginate(s.computeAPI, "servers/"+serverID, url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + + var oneServer ScalewayOneServer + + if err = json.Unmarshal(body, &oneServer); err != nil { + return nil, err + } + // FIXME arch, owner, title + oneServer.Server.DNSPublic = oneServer.Server.Identifier + URLPublicDNS + oneServer.Server.DNSPrivate = oneServer.Server.Identifier + URLPrivateDNS + s.Cache.InsertServer(oneServer.Server.Identifier, oneServer.Server.Location.ZoneID, oneServer.Server.Arch, oneServer.Server.Organization, oneServer.Server.Name) + return &oneServer.Server, nil +} + +// PostServerAction posts an action on a server +func (s *ScalewayAPI) PostServerAction(serverID, action string) error { + data := ScalewayServerAction{ + Action: action, + } + resp, err := s.PostResponse(s.computeAPI, fmt.Sprintf("servers/%s/action", serverID), data) + if err != nil { + return err + } + defer resp.Body.Close() + + _, err = s.handleHTTPError([]int{http.StatusAccepted}, resp) + return err +} + +// DeleteServer deletes a server +func (s *ScalewayAPI) DeleteServer(serverID string) error { + defer s.Cache.RemoveServer(serverID) + resp, err := s.DeleteResponse(s.computeAPI, fmt.Sprintf("servers/%s", serverID)) + if err != nil { + return err + } + defer resp.Body.Close() + + if _, err = s.handleHTTPError([]int{http.StatusNoContent}, resp); err != nil { + return err + } + return nil +} + +// PostServer creates a new server +func (s *ScalewayAPI) PostServer(definition ScalewayServerDefinition) (string, error) { + definition.Organization = s.Organization + + resp, err := s.PostResponse(s.computeAPI, "servers", definition) + if err != nil { + return "", err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusCreated}, resp) + if err != nil { + return "", err + } + var server ScalewayOneServer + + if err = json.Unmarshal(body, &server); err != nil { + return "", err + } + // FIXME arch, owner, title + s.Cache.InsertServer(server.Server.Identifier, server.Server.Location.ZoneID, server.Server.Arch, server.Server.Organization, server.Server.Name) + return server.Server.Identifier, nil +} + +// PatchUserSSHKey updates a user +func (s *ScalewayAPI) PatchUserSSHKey(UserID string, definition ScalewayUserPatchSSHKeyDefinition) error { + resp, err := s.PatchResponse(AccountAPI, fmt.Sprintf("users/%s", UserID), definition) + if err != nil { + return err + } + + defer resp.Body.Close() + + if _, err := s.handleHTTPError([]int{http.StatusOK}, resp); err != nil { + return err + } + return nil +} + +// PatchServer updates a server +func (s *ScalewayAPI) PatchServer(serverID string, definition ScalewayServerPatchDefinition) error { + resp, err := s.PatchResponse(s.computeAPI, fmt.Sprintf("servers/%s", serverID), definition) + if err != nil { + return err + } + defer resp.Body.Close() + + if _, err := s.handleHTTPError([]int{http.StatusOK}, resp); err != nil { + return err + } + return nil +} + +// PostSnapshot creates a new snapshot +func (s *ScalewayAPI) PostSnapshot(volumeID string, name string) (string, error) { + definition := ScalewaySnapshotDefinition{ + VolumeIDentifier: volumeID, + Name: name, + Organization: s.Organization, + } + resp, err := s.PostResponse(s.computeAPI, "snapshots", definition) + if err != nil { + return "", err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusCreated}, resp) + if err != nil { + return "", err + } + var snapshot ScalewayOneSnapshot + + if err = json.Unmarshal(body, &snapshot); err != nil { + return "", err + } + // FIXME arch, owner, title + s.Cache.InsertSnapshot(snapshot.Snapshot.Identifier, "", "", snapshot.Snapshot.Organization, snapshot.Snapshot.Name) + return snapshot.Snapshot.Identifier, nil +} + +// PostImage creates a new image +func (s *ScalewayAPI) PostImage(volumeID string, name string, bootscript string, arch string) (string, error) { + definition := ScalewayImageDefinition{ + SnapshotIDentifier: volumeID, + Name: name, + Organization: s.Organization, + Arch: arch, + } + if bootscript != "" { + definition.DefaultBootscript = &bootscript + } + + resp, err := s.PostResponse(s.computeAPI, "images", definition) + if err != nil { + return "", err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusCreated}, resp) + if err != nil { + return "", err + } + var image ScalewayOneImage + + if err = json.Unmarshal(body, &image); err != nil { + return "", err + } + // FIXME region, arch, owner, title + s.Cache.InsertImage(image.Image.Identifier, "", image.Image.Arch, image.Image.Organization, image.Image.Name, "") + return image.Image.Identifier, nil +} + +// PostVolume creates a new volume +func (s *ScalewayAPI) PostVolume(definition ScalewayVolumeDefinition) (string, error) { + definition.Organization = s.Organization + if definition.Type == "" { + definition.Type = "l_ssd" + } + + resp, err := s.PostResponse(s.computeAPI, "volumes", definition) + if err != nil { + return "", err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusCreated}, resp) + if err != nil { + return "", err + } + var volume ScalewayOneVolume + + if err = json.Unmarshal(body, &volume); err != nil { + return "", err + } + // FIXME: s.Cache.InsertVolume(volume.Volume.Identifier, volume.Volume.Name) + return volume.Volume.Identifier, nil +} + +// PutVolume updates a volume +func (s *ScalewayAPI) PutVolume(volumeID string, definition ScalewayVolumePutDefinition) error { + resp, err := s.PutResponse(s.computeAPI, fmt.Sprintf("volumes/%s", volumeID), definition) + if err != nil { + return err + } + defer resp.Body.Close() + + _, err = s.handleHTTPError([]int{http.StatusOK}, resp) + return err +} + +// ResolveServer attempts to find a matching Identifier for the input string +func (s *ScalewayAPI) ResolveServer(needle string) (ScalewayResolverResults, error) { + servers, err := s.Cache.LookUpServers(needle, true) + if err != nil { + return servers, err + } + if len(servers) == 0 { + if _, err = s.GetServers(true, 0); err != nil { + return nil, err + } + servers, err = s.Cache.LookUpServers(needle, true) + } + return servers, err +} + +// ResolveVolume attempts to find a matching Identifier for the input string +func (s *ScalewayAPI) ResolveVolume(needle string) (ScalewayResolverResults, error) { + volumes, err := s.Cache.LookUpVolumes(needle, true) + if err != nil { + return volumes, err + } + if len(volumes) == 0 { + if _, err = s.GetVolumes(); err != nil { + return nil, err + } + volumes, err = s.Cache.LookUpVolumes(needle, true) + } + return volumes, err +} + +// ResolveSnapshot attempts to find a matching Identifier for the input string +func (s *ScalewayAPI) ResolveSnapshot(needle string) (ScalewayResolverResults, error) { + snapshots, err := s.Cache.LookUpSnapshots(needle, true) + if err != nil { + return snapshots, err + } + if len(snapshots) == 0 { + if _, err = s.GetSnapshots(); err != nil { + return nil, err + } + snapshots, err = s.Cache.LookUpSnapshots(needle, true) + } + return snapshots, err +} + +// ResolveImage attempts to find a matching Identifier for the input string +func (s *ScalewayAPI) ResolveImage(needle string) (ScalewayResolverResults, error) { + images, err := s.Cache.LookUpImages(needle, true) + if err != nil { + return images, err + } + if len(images) == 0 { + if _, err = s.GetImages(); err != nil { + return nil, err + } + images, err = s.Cache.LookUpImages(needle, true) + } + return images, err +} + +// ResolveBootscript attempts to find a matching Identifier for the input string +func (s *ScalewayAPI) ResolveBootscript(needle string) (ScalewayResolverResults, error) { + bootscripts, err := s.Cache.LookUpBootscripts(needle, true) + if err != nil { + return bootscripts, err + } + if len(bootscripts) == 0 { + if _, err = s.GetBootscripts(); err != nil { + return nil, err + } + bootscripts, err = s.Cache.LookUpBootscripts(needle, true) + } + return bootscripts, err +} + +// GetImages gets the list of images from the ScalewayAPI +func (s *ScalewayAPI) GetImages() (*[]MarketImage, error) { + images, err := s.GetMarketPlaceImages("") + if err != nil { + return nil, err + } + s.Cache.ClearImages() + for i, image := range images.Images { + if image.CurrentPublicVersion != "" { + for _, version := range image.Versions { + if version.ID == image.CurrentPublicVersion { + for _, localImage := range version.LocalImages { + images.Images[i].Public = true + s.Cache.InsertImage(localImage.ID, localImage.Zone, localImage.Arch, image.Organization.ID, image.Name, image.CurrentPublicVersion) + } + } + } + } + } + values := url.Values{} + values.Set("organization", s.Organization) + resp, err := s.GetResponsePaginate(s.computeAPI, "images", values) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var OrgaImages ScalewayImages + + if err = json.Unmarshal(body, &OrgaImages); err != nil { + return nil, err + } + + for _, orgaImage := range OrgaImages.Images { + images.Images = append(images.Images, MarketImage{ + Categories: []string{"MyImages"}, + CreationDate: orgaImage.CreationDate, + CurrentPublicVersion: orgaImage.Identifier, + ModificationDate: orgaImage.ModificationDate, + Name: orgaImage.Name, + Public: false, + MarketVersions: MarketVersions{ + Versions: []MarketVersionDefinition{ + { + CreationDate: orgaImage.CreationDate, + ID: orgaImage.Identifier, + ModificationDate: orgaImage.ModificationDate, + MarketLocalImages: MarketLocalImages{ + LocalImages: []MarketLocalImageDefinition{ + { + Arch: orgaImage.Arch, + ID: orgaImage.Identifier, + // TODO: fecth images from ams1 and par1 + Zone: s.Region, + }, + }, + }, + }, + }, + }, + }) + s.Cache.InsertImage(orgaImage.Identifier, s.Region, orgaImage.Arch, orgaImage.Organization, orgaImage.Name, "") + } + return &images.Images, nil +} + +// GetImage gets an image from the ScalewayAPI +func (s *ScalewayAPI) GetImage(imageID string) (*ScalewayImage, error) { + resp, err := s.GetResponsePaginate(s.computeAPI, "images/"+imageID, url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var oneImage ScalewayOneImage + + if err = json.Unmarshal(body, &oneImage); err != nil { + return nil, err + } + // FIXME owner, title + s.Cache.InsertImage(oneImage.Image.Identifier, s.Region, oneImage.Image.Arch, oneImage.Image.Organization, oneImage.Image.Name, "") + return &oneImage.Image, nil +} + +// DeleteImage deletes a image +func (s *ScalewayAPI) DeleteImage(imageID string) error { + defer s.Cache.RemoveImage(imageID) + resp, err := s.DeleteResponse(s.computeAPI, fmt.Sprintf("images/%s", imageID)) + if err != nil { + return err + } + defer resp.Body.Close() + + if _, err := s.handleHTTPError([]int{http.StatusNoContent}, resp); err != nil { + return err + } + return nil +} + +// DeleteSnapshot deletes a snapshot +func (s *ScalewayAPI) DeleteSnapshot(snapshotID string) error { + defer s.Cache.RemoveSnapshot(snapshotID) + resp, err := s.DeleteResponse(s.computeAPI, fmt.Sprintf("snapshots/%s", snapshotID)) + if err != nil { + return err + } + defer resp.Body.Close() + + if _, err := s.handleHTTPError([]int{http.StatusNoContent}, resp); err != nil { + return err + } + return nil +} + +// DeleteVolume deletes a volume +func (s *ScalewayAPI) DeleteVolume(volumeID string) error { + defer s.Cache.RemoveVolume(volumeID) + resp, err := s.DeleteResponse(s.computeAPI, fmt.Sprintf("volumes/%s", volumeID)) + if err != nil { + return err + } + defer resp.Body.Close() + + if _, err := s.handleHTTPError([]int{http.StatusNoContent}, resp); err != nil { + return err + } + return nil +} + +// GetSnapshots gets the list of snapshots from the ScalewayAPI +func (s *ScalewayAPI) GetSnapshots() (*[]ScalewaySnapshot, error) { + query := url.Values{} + s.Cache.ClearSnapshots() + + resp, err := s.GetResponsePaginate(s.computeAPI, "snapshots", query) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var snapshots ScalewaySnapshots + + if err = json.Unmarshal(body, &snapshots); err != nil { + return nil, err + } + for _, snapshot := range snapshots.Snapshots { + // FIXME region, arch, owner, title + s.Cache.InsertSnapshot(snapshot.Identifier, "", "", snapshot.Organization, snapshot.Name) + } + return &snapshots.Snapshots, nil +} + +// GetSnapshot gets a snapshot from the ScalewayAPI +func (s *ScalewayAPI) GetSnapshot(snapshotID string) (*ScalewaySnapshot, error) { + resp, err := s.GetResponsePaginate(s.computeAPI, "snapshots/"+snapshotID, url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var oneSnapshot ScalewayOneSnapshot + + if err = json.Unmarshal(body, &oneSnapshot); err != nil { + return nil, err + } + // FIXME region, arch, owner, title + s.Cache.InsertSnapshot(oneSnapshot.Snapshot.Identifier, "", "", oneSnapshot.Snapshot.Organization, oneSnapshot.Snapshot.Name) + return &oneSnapshot.Snapshot, nil +} + +// GetVolumes gets the list of volumes from the ScalewayAPI +func (s *ScalewayAPI) GetVolumes() (*[]ScalewayVolume, error) { + query := url.Values{} + s.Cache.ClearVolumes() + + resp, err := s.GetResponsePaginate(s.computeAPI, "volumes", query) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + + var volumes ScalewayVolumes + + if err = json.Unmarshal(body, &volumes); err != nil { + return nil, err + } + for _, volume := range volumes.Volumes { + // FIXME region, arch, owner, title + s.Cache.InsertVolume(volume.Identifier, "", "", volume.Organization, volume.Name) + } + return &volumes.Volumes, nil +} + +// GetVolume gets a volume from the ScalewayAPI +func (s *ScalewayAPI) GetVolume(volumeID string) (*ScalewayVolume, error) { + resp, err := s.GetResponsePaginate(s.computeAPI, "volumes/"+volumeID, url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var oneVolume ScalewayOneVolume + + if err = json.Unmarshal(body, &oneVolume); err != nil { + return nil, err + } + // FIXME region, arch, owner, title + s.Cache.InsertVolume(oneVolume.Volume.Identifier, "", "", oneVolume.Volume.Organization, oneVolume.Volume.Name) + return &oneVolume.Volume, nil +} + +// GetBootscripts gets the list of bootscripts from the ScalewayAPI +func (s *ScalewayAPI) GetBootscripts() (*[]ScalewayBootscript, error) { + query := url.Values{} + + s.Cache.ClearBootscripts() + resp, err := s.GetResponsePaginate(s.computeAPI, "bootscripts", query) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var bootscripts ScalewayBootscripts + + if err = json.Unmarshal(body, &bootscripts); err != nil { + return nil, err + } + for _, bootscript := range bootscripts.Bootscripts { + // FIXME region, arch, owner, title + s.Cache.InsertBootscript(bootscript.Identifier, "", bootscript.Arch, bootscript.Organization, bootscript.Title) + } + return &bootscripts.Bootscripts, nil +} + +// GetBootscript gets a bootscript from the ScalewayAPI +func (s *ScalewayAPI) GetBootscript(bootscriptID string) (*ScalewayBootscript, error) { + resp, err := s.GetResponsePaginate(s.computeAPI, "bootscripts/"+bootscriptID, url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var oneBootscript ScalewayOneBootscript + + if err = json.Unmarshal(body, &oneBootscript); err != nil { + return nil, err + } + // FIXME region, arch, owner, title + s.Cache.InsertBootscript(oneBootscript.Bootscript.Identifier, "", oneBootscript.Bootscript.Arch, oneBootscript.Bootscript.Organization, oneBootscript.Bootscript.Title) + return &oneBootscript.Bootscript, nil +} + +// GetUserdatas gets list of userdata for a server +func (s *ScalewayAPI) GetUserdatas(serverID string, metadata bool) (*ScalewayUserdatas, error) { + var uri, endpoint string + + endpoint = s.computeAPI + if metadata { + uri = "/user_data" + endpoint = MetadataAPI + } else { + uri = fmt.Sprintf("servers/%s/user_data", serverID) + } + + resp, err := s.GetResponsePaginate(endpoint, uri, url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var userdatas ScalewayUserdatas + + if err = json.Unmarshal(body, &userdatas); err != nil { + return nil, err + } + return &userdatas, nil +} + +func (s *ScalewayUserdata) String() string { + return string(*s) +} + +// GetUserdata gets a specific userdata for a server +func (s *ScalewayAPI) GetUserdata(serverID, key string, metadata bool) (*ScalewayUserdata, error) { + var uri, endpoint string + + endpoint = s.computeAPI + if metadata { + uri = fmt.Sprintf("/user_data/%s", key) + endpoint = MetadataAPI + } else { + uri = fmt.Sprintf("servers/%s/user_data/%s", serverID, key) + } + + var err error + resp, err := s.GetResponsePaginate(endpoint, uri, url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("no such user_data %q (%d)", key, resp.StatusCode) + } + var data ScalewayUserdata + data, err = ioutil.ReadAll(resp.Body) + return &data, err +} + +// PatchUserdata sets a user data +func (s *ScalewayAPI) PatchUserdata(serverID, key string, value []byte, metadata bool) error { + var resource, endpoint string + + endpoint = s.computeAPI + if metadata { + resource = fmt.Sprintf("/user_data/%s", key) + endpoint = MetadataAPI + } else { + resource = fmt.Sprintf("servers/%s/user_data/%s", serverID, key) + } + + uri := fmt.Sprintf("%s/%s", strings.TrimRight(endpoint, "/"), resource) + payload := new(bytes.Buffer) + payload.Write(value) + + req, err := http.NewRequest("PATCH", uri, payload) + if err != nil { + return err + } + + req.Header.Set("X-Auth-Token", s.Token) + req.Header.Set("Content-Type", "text/plain") + req.Header.Set("User-Agent", s.userAgent) + + s.LogHTTP(req) + + resp, err := s.client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode == http.StatusNoContent { + return nil + } + + return fmt.Errorf("cannot set user_data (%d)", resp.StatusCode) +} + +// DeleteUserdata deletes a server user_data +func (s *ScalewayAPI) DeleteUserdata(serverID, key string, metadata bool) error { + var url, endpoint string + + endpoint = s.computeAPI + if metadata { + url = fmt.Sprintf("/user_data/%s", key) + endpoint = MetadataAPI + } else { + url = fmt.Sprintf("servers/%s/user_data/%s", serverID, key) + } + + resp, err := s.DeleteResponse(endpoint, url) + if err != nil { + return err + } + defer resp.Body.Close() + + _, err = s.handleHTTPError([]int{http.StatusNoContent}, resp) + return err +} + +// GetTasks get the list of tasks from the ScalewayAPI +func (s *ScalewayAPI) GetTasks() (*[]ScalewayTask, error) { + query := url.Values{} + resp, err := s.GetResponsePaginate(s.computeAPI, "tasks", query) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var tasks ScalewayTasks + + if err = json.Unmarshal(body, &tasks); err != nil { + return nil, err + } + return &tasks.Tasks, nil +} + +// CheckCredentials performs a dummy check to ensure we can contact the API +func (s *ScalewayAPI) CheckCredentials() error { + query := url.Values{} + + resp, err := s.GetResponsePaginate(AccountAPI, "tokens", query) + if err != nil { + return err + } + defer resp.Body.Close() + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return err + } + found := false + var tokens ScalewayGetTokens + + if err = json.Unmarshal(body, &tokens); err != nil { + return err + } + for _, token := range tokens.Tokens { + if token.ID == s.Token { + found = true + break + } + } + if !found { + return fmt.Errorf("Invalid token %v", s.Token) + } + return nil +} + +// GetUserID returns the userID +func (s *ScalewayAPI) GetUserID() (string, error) { + resp, err := s.GetResponsePaginate(AccountAPI, fmt.Sprintf("tokens/%s", s.Token), url.Values{}) + if err != nil { + return "", err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return "", err + } + var token ScalewayTokensDefinition + + if err = json.Unmarshal(body, &token); err != nil { + return "", err + } + return token.Token.UserID, nil +} + +// GetOrganization returns Organization +func (s *ScalewayAPI) GetOrganization() (*ScalewayOrganizationsDefinition, error) { + resp, err := s.GetResponsePaginate(AccountAPI, "organizations", url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var data ScalewayOrganizationsDefinition + + if err = json.Unmarshal(body, &data); err != nil { + return nil, err + } + return &data, nil +} + +// GetUser returns the user +func (s *ScalewayAPI) GetUser() (*ScalewayUserDefinition, error) { + userID, err := s.GetUserID() + if err != nil { + return nil, err + } + resp, err := s.GetResponsePaginate(AccountAPI, fmt.Sprintf("users/%s", userID), url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var user ScalewayUsersDefinition + + if err = json.Unmarshal(body, &user); err != nil { + return nil, err + } + return &user.User, nil +} + +// GetPermissions returns the permissions +func (s *ScalewayAPI) GetPermissions() (*ScalewayPermissionDefinition, error) { + resp, err := s.GetResponsePaginate(AccountAPI, fmt.Sprintf("tokens/%s/permissions", s.Token), url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var permissions ScalewayPermissionDefinition + + if err = json.Unmarshal(body, &permissions); err != nil { + return nil, err + } + return &permissions, nil +} + +// GetDashboard returns the dashboard +func (s *ScalewayAPI) GetDashboard() (*ScalewayDashboard, error) { + resp, err := s.GetResponsePaginate(s.computeAPI, "dashboard", url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var dashboard ScalewayDashboardResp + + if err = json.Unmarshal(body, &dashboard); err != nil { + return nil, err + } + return &dashboard.Dashboard, nil +} + +// GetServerID returns exactly one server matching +func (s *ScalewayAPI) GetServerID(needle string) (string, error) { + // Parses optional type prefix, i.e: "server:name" -> "name" + _, needle = parseNeedle(needle) + + servers, err := s.ResolveServer(needle) + if err != nil { + return "", fmt.Errorf("Unable to resolve server %s: %s", needle, err) + } + if len(servers) == 1 { + return servers[0].Identifier, nil + } + if len(servers) == 0 { + return "", fmt.Errorf("No such server: %s", needle) + } + return "", showResolverResults(needle, servers) +} + +func showResolverResults(needle string, results ScalewayResolverResults) error { + w := tabwriter.NewWriter(os.Stderr, 20, 1, 3, ' ', 0) + defer w.Flush() + sort.Sort(results) + fmt.Fprintf(w, " IMAGEID\tFROM\tNAME\tZONE\tARCH\n") + for _, result := range results { + if result.Arch == "" { + result.Arch = "n/a" + } + fmt.Fprintf(w, "- %s\t%s\t%s\t%s\t%s\n", result.TruncIdentifier(), result.CodeName(), result.Name, result.Region, result.Arch) + } + return fmt.Errorf("Too many candidates for %s (%d)", needle, len(results)) +} + +// GetVolumeID returns exactly one volume matching +func (s *ScalewayAPI) GetVolumeID(needle string) (string, error) { + // Parses optional type prefix, i.e: "volume:name" -> "name" + _, needle = parseNeedle(needle) + + volumes, err := s.ResolveVolume(needle) + if err != nil { + return "", fmt.Errorf("Unable to resolve volume %s: %s", needle, err) + } + if len(volumes) == 1 { + return volumes[0].Identifier, nil + } + if len(volumes) == 0 { + return "", fmt.Errorf("No such volume: %s", needle) + } + return "", showResolverResults(needle, volumes) +} + +// GetSnapshotID returns exactly one snapshot matching +func (s *ScalewayAPI) GetSnapshotID(needle string) (string, error) { + // Parses optional type prefix, i.e: "snapshot:name" -> "name" + _, needle = parseNeedle(needle) + + snapshots, err := s.ResolveSnapshot(needle) + if err != nil { + return "", fmt.Errorf("Unable to resolve snapshot %s: %s", needle, err) + } + if len(snapshots) == 1 { + return snapshots[0].Identifier, nil + } + if len(snapshots) == 0 { + return "", fmt.Errorf("No such snapshot: %s", needle) + } + return "", showResolverResults(needle, snapshots) +} + +// FilterImagesByArch removes entry that doesn't match with architecture +func FilterImagesByArch(res ScalewayResolverResults, arch string) (ret ScalewayResolverResults) { + if arch == "*" { + return res + } + for _, result := range res { + if result.Arch == arch { + ret = append(ret, result) + } + } + return +} + +// FilterImagesByRegion removes entry that doesn't match with region +func FilterImagesByRegion(res ScalewayResolverResults, region string) (ret ScalewayResolverResults) { + if region == "*" { + return res + } + for _, result := range res { + if result.Region == region { + ret = append(ret, result) + } + } + return +} + +// GetImageID returns exactly one image matching +func (s *ScalewayAPI) GetImageID(needle, arch string) (*ScalewayImageIdentifier, error) { + // Parses optional type prefix, i.e: "image:name" -> "name" + _, needle = parseNeedle(needle) + + images, err := s.ResolveImage(needle) + if err != nil { + return nil, fmt.Errorf("Unable to resolve image %s: %s", needle, err) + } + images = FilterImagesByArch(images, arch) + images = FilterImagesByRegion(images, s.Region) + if len(images) == 1 { + return &ScalewayImageIdentifier{ + Identifier: images[0].Identifier, + Arch: images[0].Arch, + // FIXME region, owner hardcoded + Region: images[0].Region, + Owner: "", + }, nil + } + if len(images) == 0 { + return nil, fmt.Errorf("No such image (zone %s, arch %s) : %s", s.Region, arch, needle) + } + return nil, showResolverResults(needle, images) +} + +// GetSecurityGroups returns a ScalewaySecurityGroups +func (s *ScalewayAPI) GetSecurityGroups() (*ScalewayGetSecurityGroups, error) { + resp, err := s.GetResponsePaginate(s.computeAPI, "security_groups", url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var securityGroups ScalewayGetSecurityGroups + + if err = json.Unmarshal(body, &securityGroups); err != nil { + return nil, err + } + return &securityGroups, nil +} + +// GetSecurityGroupRules returns a ScalewaySecurityGroupRules +func (s *ScalewayAPI) GetSecurityGroupRules(groupID string) (*ScalewayGetSecurityGroupRules, error) { + resp, err := s.GetResponsePaginate(s.computeAPI, fmt.Sprintf("security_groups/%s/rules", groupID), url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var securityGroupRules ScalewayGetSecurityGroupRules + + if err = json.Unmarshal(body, &securityGroupRules); err != nil { + return nil, err + } + return &securityGroupRules, nil +} + +// GetASecurityGroupRule returns a ScalewaySecurityGroupRule +func (s *ScalewayAPI) GetASecurityGroupRule(groupID string, rulesID string) (*ScalewayGetSecurityGroupRule, error) { + resp, err := s.GetResponsePaginate(s.computeAPI, fmt.Sprintf("security_groups/%s/rules/%s", groupID, rulesID), url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var securityGroupRules ScalewayGetSecurityGroupRule + + if err = json.Unmarshal(body, &securityGroupRules); err != nil { + return nil, err + } + return &securityGroupRules, nil +} + +// GetASecurityGroup returns a ScalewaySecurityGroup +func (s *ScalewayAPI) GetASecurityGroup(groupsID string) (*ScalewayGetSecurityGroup, error) { + resp, err := s.GetResponsePaginate(s.computeAPI, fmt.Sprintf("security_groups/%s", groupsID), url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var securityGroups ScalewayGetSecurityGroup + + if err = json.Unmarshal(body, &securityGroups); err != nil { + return nil, err + } + return &securityGroups, nil +} + +// PostSecurityGroup posts a group on a server +func (s *ScalewayAPI) PostSecurityGroup(group ScalewayNewSecurityGroup) error { + resp, err := s.PostResponse(s.computeAPI, "security_groups", group) + if err != nil { + return err + } + defer resp.Body.Close() + + _, err = s.handleHTTPError([]int{http.StatusCreated}, resp) + return err +} + +// PostSecurityGroupRule posts a rule on a server +func (s *ScalewayAPI) PostSecurityGroupRule(SecurityGroupID string, rules ScalewayNewSecurityGroupRule) error { + resp, err := s.PostResponse(s.computeAPI, fmt.Sprintf("security_groups/%s/rules", SecurityGroupID), rules) + if err != nil { + return err + } + defer resp.Body.Close() + + _, err = s.handleHTTPError([]int{http.StatusCreated}, resp) + return err +} + +// DeleteSecurityGroup deletes a SecurityGroup +func (s *ScalewayAPI) DeleteSecurityGroup(securityGroupID string) error { + resp, err := s.DeleteResponse(s.computeAPI, fmt.Sprintf("security_groups/%s", securityGroupID)) + if err != nil { + return err + } + defer resp.Body.Close() + + _, err = s.handleHTTPError([]int{http.StatusNoContent}, resp) + return err +} + +// PutSecurityGroup updates a SecurityGroup +func (s *ScalewayAPI) PutSecurityGroup(group ScalewayUpdateSecurityGroup, securityGroupID string) error { + resp, err := s.PutResponse(s.computeAPI, fmt.Sprintf("security_groups/%s", securityGroupID), group) + if err != nil { + return err + } + defer resp.Body.Close() + + _, err = s.handleHTTPError([]int{http.StatusOK}, resp) + return err +} + +// PutSecurityGroupRule updates a SecurityGroupRule +func (s *ScalewayAPI) PutSecurityGroupRule(rules ScalewayNewSecurityGroupRule, securityGroupID, RuleID string) error { + resp, err := s.PutResponse(s.computeAPI, fmt.Sprintf("security_groups/%s/rules/%s", securityGroupID, RuleID), rules) + if err != nil { + return err + } + defer resp.Body.Close() + + _, err = s.handleHTTPError([]int{http.StatusOK}, resp) + return err +} + +// DeleteSecurityGroupRule deletes a SecurityGroupRule +func (s *ScalewayAPI) DeleteSecurityGroupRule(SecurityGroupID, RuleID string) error { + resp, err := s.DeleteResponse(s.computeAPI, fmt.Sprintf("security_groups/%s/rules/%s", SecurityGroupID, RuleID)) + if err != nil { + return err + } + defer resp.Body.Close() + + _, err = s.handleHTTPError([]int{http.StatusNoContent}, resp) + return err +} + +// GetContainers returns a ScalewayGetContainers +func (s *ScalewayAPI) GetContainers() (*ScalewayGetContainers, error) { + resp, err := s.GetResponsePaginate(s.computeAPI, "containers", url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var containers ScalewayGetContainers + + if err = json.Unmarshal(body, &containers); err != nil { + return nil, err + } + return &containers, nil +} + +// GetContainerDatas returns a ScalewayGetContainerDatas +func (s *ScalewayAPI) GetContainerDatas(container string) (*ScalewayGetContainerDatas, error) { + resp, err := s.GetResponsePaginate(s.computeAPI, fmt.Sprintf("containers/%s", container), url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var datas ScalewayGetContainerDatas + + if err = json.Unmarshal(body, &datas); err != nil { + return nil, err + } + return &datas, nil +} + +// GetIPS returns a ScalewayGetIPS +func (s *ScalewayAPI) GetIPS() (*ScalewayGetIPS, error) { + resp, err := s.GetResponsePaginate(s.computeAPI, "ips", url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var ips ScalewayGetIPS + + if err = json.Unmarshal(body, &ips); err != nil { + return nil, err + } + return &ips, nil +} + +// NewIP returns a new IP +func (s *ScalewayAPI) NewIP() (*ScalewayGetIP, error) { + var orga struct { + Organization string `json:"organization"` + } + orga.Organization = s.Organization + resp, err := s.PostResponse(s.computeAPI, "ips", orga) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusCreated}, resp) + if err != nil { + return nil, err + } + var ip ScalewayGetIP + + if err = json.Unmarshal(body, &ip); err != nil { + return nil, err + } + return &ip, nil +} + +// AttachIP attachs an IP to a server +func (s *ScalewayAPI) AttachIP(ipID, serverID string) error { + var update struct { + Address string `json:"address"` + ID string `json:"id"` + Reverse *string `json:"reverse"` + Organization string `json:"organization"` + Server string `json:"server"` + } + + ip, err := s.GetIP(ipID) + if err != nil { + return err + } + update.Address = ip.IP.Address + update.ID = ip.IP.ID + update.Organization = ip.IP.Organization + update.Server = serverID + resp, err := s.PutResponse(s.computeAPI, fmt.Sprintf("ips/%s", ipID), update) + if err != nil { + return err + } + _, err = s.handleHTTPError([]int{http.StatusOK}, resp) + return err +} + +// DetachIP detaches an IP from a server +func (s *ScalewayAPI) DetachIP(ipID string) error { + ip, err := s.GetIP(ipID) + if err != nil { + return err + } + ip.IP.Server = nil + resp, err := s.PutResponse(s.computeAPI, fmt.Sprintf("ips/%s", ipID), ip.IP) + if err != nil { + return err + } + defer resp.Body.Close() + _, err = s.handleHTTPError([]int{http.StatusOK}, resp) + return err +} + +// DeleteIP deletes an IP +func (s *ScalewayAPI) DeleteIP(ipID string) error { + resp, err := s.DeleteResponse(s.computeAPI, fmt.Sprintf("ips/%s", ipID)) + if err != nil { + return err + } + defer resp.Body.Close() + _, err = s.handleHTTPError([]int{http.StatusNoContent}, resp) + return err +} + +// GetIP returns a ScalewayGetIP +func (s *ScalewayAPI) GetIP(ipID string) (*ScalewayGetIP, error) { + resp, err := s.GetResponsePaginate(s.computeAPI, fmt.Sprintf("ips/%s", ipID), url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var ip ScalewayGetIP + + if err = json.Unmarshal(body, &ip); err != nil { + return nil, err + } + return &ip, nil +} + +// GetQuotas returns a ScalewayGetQuotas +func (s *ScalewayAPI) GetQuotas() (*ScalewayGetQuotas, error) { + resp, err := s.GetResponsePaginate(AccountAPI, fmt.Sprintf("organizations/%s/quotas", s.Organization), url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var quotas ScalewayGetQuotas + + if err = json.Unmarshal(body, "as); err != nil { + return nil, err + } + return "as, nil +} + +// GetBootscriptID returns exactly one bootscript matching +func (s *ScalewayAPI) GetBootscriptID(needle, arch string) (string, error) { + // Parses optional type prefix, i.e: "bootscript:name" -> "name" + _, needle = parseNeedle(needle) + + bootscripts, err := s.ResolveBootscript(needle) + if err != nil { + return "", fmt.Errorf("Unable to resolve bootscript %s: %s", needle, err) + } + bootscripts.FilterByArch(arch) + if len(bootscripts) == 1 { + return bootscripts[0].Identifier, nil + } + if len(bootscripts) == 0 { + return "", fmt.Errorf("No such bootscript: %s", needle) + } + return "", showResolverResults(needle, bootscripts) +} + +func rootNetDial(network, addr string) (net.Conn, error) { + dialer := net.Dialer{ + Timeout: 10 * time.Second, + KeepAlive: 10 * time.Second, + } + + // bruteforce privileged ports + var localAddr net.Addr + var err error + for port := 1; port <= 1024; port++ { + localAddr, err = net.ResolveTCPAddr("tcp", fmt.Sprintf(":%d", port)) + + // this should never happen + if err != nil { + return nil, err + } + + dialer.LocalAddr = localAddr + + conn, err := dialer.Dial(network, addr) + + // if err is nil, dialer.Dial succeed, so let's go + // else, err != nil, but we don't care + if err == nil { + return conn, nil + } + } + // if here, all privileged ports were tried without success + return nil, fmt.Errorf("bind: permission denied, are you root ?") +} + +// SetPassword register the password +func (s *ScalewayAPI) SetPassword(password string) { + s.password = password +} + +// GetMarketPlaceImages returns images from marketplace +func (s *ScalewayAPI) GetMarketPlaceImages(uuidImage string) (*MarketImages, error) { + resp, err := s.GetResponsePaginate(MarketplaceAPI, fmt.Sprintf("images/%s", uuidImage), url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var ret MarketImages + + if uuidImage != "" { + ret.Images = make([]MarketImage, 1) + + var img MarketImage + + if err = json.Unmarshal(body, &img); err != nil { + return nil, err + } + ret.Images[0] = img + } else { + if err = json.Unmarshal(body, &ret); err != nil { + return nil, err + } + } + return &ret, nil +} + +// GetMarketPlaceImageVersions returns image version +func (s *ScalewayAPI) GetMarketPlaceImageVersions(uuidImage, uuidVersion string) (*MarketVersions, error) { + resp, err := s.GetResponsePaginate(MarketplaceAPI, fmt.Sprintf("images/%v/versions/%s", uuidImage, uuidVersion), url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var ret MarketVersions + + if uuidImage != "" { + var version MarketVersion + ret.Versions = make([]MarketVersionDefinition, 1) + + if err = json.Unmarshal(body, &version); err != nil { + return nil, err + } + ret.Versions[0] = version.Version + } else { + if err = json.Unmarshal(body, &ret); err != nil { + return nil, err + } + } + return &ret, nil +} + +// GetMarketPlaceImageCurrentVersion return the image current version +func (s *ScalewayAPI) GetMarketPlaceImageCurrentVersion(uuidImage string) (*MarketVersion, error) { + resp, err := s.GetResponsePaginate(MarketplaceAPI, fmt.Sprintf("images/%v/versions/current", uuidImage), url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var ret MarketVersion + + if err = json.Unmarshal(body, &ret); err != nil { + return nil, err + } + return &ret, nil +} + +// GetMarketPlaceLocalImages returns images from local region +func (s *ScalewayAPI) GetMarketPlaceLocalImages(uuidImage, uuidVersion, uuidLocalImage string) (*MarketLocalImages, error) { + resp, err := s.GetResponsePaginate(MarketplaceAPI, fmt.Sprintf("images/%v/versions/%s/local_images/%s", uuidImage, uuidVersion, uuidLocalImage), url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var ret MarketLocalImages + if uuidLocalImage != "" { + var localImage MarketLocalImage + ret.LocalImages = make([]MarketLocalImageDefinition, 1) + + if err = json.Unmarshal(body, &localImage); err != nil { + return nil, err + } + ret.LocalImages[0] = localImage.LocalImages + } else { + if err = json.Unmarshal(body, &ret); err != nil { + return nil, err + } + } + return &ret, nil +} + +// PostMarketPlaceImage adds new image +func (s *ScalewayAPI) PostMarketPlaceImage(images MarketImage) error { + resp, err := s.PostResponse(MarketplaceAPI, "images/", images) + if err != nil { + return err + } + defer resp.Body.Close() + _, err = s.handleHTTPError([]int{http.StatusAccepted}, resp) + return err +} + +// PostMarketPlaceImageVersion adds new image version +func (s *ScalewayAPI) PostMarketPlaceImageVersion(uuidImage string, version MarketVersion) error { + resp, err := s.PostResponse(MarketplaceAPI, fmt.Sprintf("images/%v/versions", uuidImage), version) + if err != nil { + return err + } + defer resp.Body.Close() + _, err = s.handleHTTPError([]int{http.StatusAccepted}, resp) + return err +} + +// PostMarketPlaceLocalImage adds new local image +func (s *ScalewayAPI) PostMarketPlaceLocalImage(uuidImage, uuidVersion, uuidLocalImage string, local MarketLocalImage) error { + resp, err := s.PostResponse(MarketplaceAPI, fmt.Sprintf("images/%v/versions/%s/local_images/%v", uuidImage, uuidVersion, uuidLocalImage), local) + if err != nil { + return err + } + defer resp.Body.Close() + _, err = s.handleHTTPError([]int{http.StatusAccepted}, resp) + return err +} + +// PutMarketPlaceImage updates image +func (s *ScalewayAPI) PutMarketPlaceImage(uudiImage string, images MarketImage) error { + resp, err := s.PutResponse(MarketplaceAPI, fmt.Sprintf("images/%v", uudiImage), images) + if err != nil { + return err + } + defer resp.Body.Close() + _, err = s.handleHTTPError([]int{http.StatusOK}, resp) + return err +} + +// PutMarketPlaceImageVersion updates image version +func (s *ScalewayAPI) PutMarketPlaceImageVersion(uuidImage, uuidVersion string, version MarketVersion) error { + resp, err := s.PutResponse(MarketplaceAPI, fmt.Sprintf("images/%v/versions/%v", uuidImage, uuidVersion), version) + if err != nil { + return err + } + defer resp.Body.Close() + _, err = s.handleHTTPError([]int{http.StatusOK}, resp) + return err +} + +// PutMarketPlaceLocalImage updates local image +func (s *ScalewayAPI) PutMarketPlaceLocalImage(uuidImage, uuidVersion, uuidLocalImage string, local MarketLocalImage) error { + resp, err := s.PostResponse(MarketplaceAPI, fmt.Sprintf("images/%v/versions/%s/local_images/%v", uuidImage, uuidVersion, uuidLocalImage), local) + if err != nil { + return err + } + defer resp.Body.Close() + _, err = s.handleHTTPError([]int{http.StatusOK}, resp) + return err +} + +// DeleteMarketPlaceImage deletes image +func (s *ScalewayAPI) DeleteMarketPlaceImage(uudImage string) error { + resp, err := s.DeleteResponse(MarketplaceAPI, fmt.Sprintf("images/%v", uudImage)) + if err != nil { + return err + } + defer resp.Body.Close() + _, err = s.handleHTTPError([]int{http.StatusNoContent}, resp) + return err +} + +// DeleteMarketPlaceImageVersion delete image version +func (s *ScalewayAPI) DeleteMarketPlaceImageVersion(uuidImage, uuidVersion string) error { + resp, err := s.DeleteResponse(MarketplaceAPI, fmt.Sprintf("images/%v/versions/%v", uuidImage, uuidVersion)) + if err != nil { + return err + } + defer resp.Body.Close() + _, err = s.handleHTTPError([]int{http.StatusNoContent}, resp) + return err +} + +// DeleteMarketPlaceLocalImage deletes local image +func (s *ScalewayAPI) DeleteMarketPlaceLocalImage(uuidImage, uuidVersion, uuidLocalImage string) error { + resp, err := s.DeleteResponse(MarketplaceAPI, fmt.Sprintf("images/%v/versions/%s/local_images/%v", uuidImage, uuidVersion, uuidLocalImage)) + if err != nil { + return err + } + defer resp.Body.Close() + _, err = s.handleHTTPError([]int{http.StatusNoContent}, resp) + return err +} + +// ResolveTTYUrl return an URL to get a tty +func (s *ScalewayAPI) ResolveTTYUrl() string { + switch s.Region { + case "par1", "": + return "https://tty-par1.scaleway.com/v2/" + case "ams1": + return "https://tty-ams1.scaleway.com" + } + return "" +} diff --git a/vendor/github.com/scaleway/scaleway-cli/pkg/api/cache.go b/vendor/github.com/scaleway/scaleway-cli/pkg/api/cache.go new file mode 100644 index 000000000..1d9771a0e --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-cli/pkg/api/cache.go @@ -0,0 +1,814 @@ +// Copyright (C) 2015 Scaleway. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE.md file. + +package api + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strings" + "sync" + + "github.com/moul/anonuuid" + "github.com/renstrom/fuzzysearch/fuzzy" +) + +const ( + // CacheRegion permits to access at the region field + CacheRegion = iota + // CacheArch permits to access at the arch field + CacheArch + // CacheOwner permits to access at the owner field + CacheOwner + // CacheTitle permits to access at the title field + CacheTitle + // CacheMarketPlaceUUID is used to determine the UUID of local images + CacheMarketPlaceUUID + // CacheMaxfield is used to determine the size of array + CacheMaxfield +) + +// ScalewayCache is used not to query the API to resolve full identifiers +type ScalewayCache struct { + // Images contains names of Scaleway images indexed by identifier + Images map[string][CacheMaxfield]string `json:"images"` + + // Snapshots contains names of Scaleway snapshots indexed by identifier + Snapshots map[string][CacheMaxfield]string `json:"snapshots"` + + // Volumes contains names of Scaleway volumes indexed by identifier + Volumes map[string][CacheMaxfield]string `json:"volumes"` + + // Bootscripts contains names of Scaleway bootscripts indexed by identifier + Bootscripts map[string][CacheMaxfield]string `json:"bootscripts"` + + // Servers contains names of Scaleway servers indexed by identifier + Servers map[string][CacheMaxfield]string `json:"servers"` + + // Path is the path to the cache file + Path string `json:"-"` + + // Modified tells if the cache needs to be overwritten or not + Modified bool `json:"-"` + + // Lock allows ScalewayCache to be used concurrently + Lock sync.Mutex `json:"-"` + + hookSave func() +} + +const ( + // IdentifierUnknown is used when we don't know explicitly the type key of the object (used for nil comparison) + IdentifierUnknown = 1 << iota + // IdentifierServer is the type key of cached server objects + IdentifierServer + // IdentifierImage is the type key of cached image objects + IdentifierImage + // IdentifierSnapshot is the type key of cached snapshot objects + IdentifierSnapshot + // IdentifierBootscript is the type key of cached bootscript objects + IdentifierBootscript + // IdentifierVolume is the type key of cached volume objects + IdentifierVolume +) + +// ScalewayResolverResult is a structure containing human-readable information +// about resolver results. This structure is used to display the user choices. +type ScalewayResolverResult struct { + Identifier string + Type int + Name string + Arch string + Needle string + RankMatch int + Region string +} + +// ScalewayResolverResults is a list of `ScalewayResolverResult` +type ScalewayResolverResults []ScalewayResolverResult + +// NewScalewayResolverResult returns a new ScalewayResolverResult +func NewScalewayResolverResult(Identifier, Name, Arch, Region string, Type int) (ScalewayResolverResult, error) { + if err := anonuuid.IsUUID(Identifier); err != nil { + return ScalewayResolverResult{}, err + } + return ScalewayResolverResult{ + Identifier: Identifier, + Type: Type, + Name: Name, + Arch: Arch, + Region: Region, + }, nil +} + +func (s ScalewayResolverResults) Len() int { + return len(s) +} + +func (s ScalewayResolverResults) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +func (s ScalewayResolverResults) Less(i, j int) bool { + return s[i].RankMatch < s[j].RankMatch +} + +// TruncIdentifier returns first 8 characters of an Identifier (UUID) +func (s *ScalewayResolverResult) TruncIdentifier() string { + return s.Identifier[:8] +} + +func identifierTypeName(kind int) string { + switch kind { + case IdentifierServer: + return "Server" + case IdentifierImage: + return "Image" + case IdentifierSnapshot: + return "Snapshot" + case IdentifierVolume: + return "Volume" + case IdentifierBootscript: + return "Bootscript" + } + return "" +} + +// CodeName returns a full resource name with typed prefix +func (s *ScalewayResolverResult) CodeName() string { + name := strings.ToLower(s.Name) + name = regexp.MustCompile(`[^a-z0-9-]`).ReplaceAllString(name, "-") + name = regexp.MustCompile(`--+`).ReplaceAllString(name, "-") + name = strings.Trim(name, "-") + + return fmt.Sprintf("%s:%s", strings.ToLower(identifierTypeName(s.Type)), name) +} + +// FilterByArch deletes the elements which not match with arch +func (s *ScalewayResolverResults) FilterByArch(arch string) { +REDO: + for i := range *s { + if (*s)[i].Arch != arch { + (*s)[i] = (*s)[len(*s)-1] + *s = (*s)[:len(*s)-1] + goto REDO + } + } +} + +// NewScalewayCache loads a per-user cache +func NewScalewayCache(hookSave func()) (*ScalewayCache, error) { + var cache ScalewayCache + + cache.hookSave = hookSave + homeDir := os.Getenv("HOME") // *nix + if homeDir == "" { // Windows + homeDir = os.Getenv("USERPROFILE") + } + if homeDir == "" { + homeDir = "/tmp" + } + cachePath := filepath.Join(homeDir, ".scw-cache.db") + cache.Path = cachePath + _, err := os.Stat(cachePath) + if os.IsNotExist(err) { + cache.Clear() + return &cache, nil + } else if err != nil { + return nil, err + } + file, err := ioutil.ReadFile(cachePath) + if err != nil { + return nil, err + } + err = json.Unmarshal(file, &cache) + if err != nil { + // fix compatibility with older version + if err = os.Remove(cachePath); err != nil { + return nil, err + } + cache.Clear() + return &cache, nil + } + if cache.Images == nil { + cache.Images = make(map[string][CacheMaxfield]string) + } + if cache.Snapshots == nil { + cache.Snapshots = make(map[string][CacheMaxfield]string) + } + if cache.Volumes == nil { + cache.Volumes = make(map[string][CacheMaxfield]string) + } + if cache.Servers == nil { + cache.Servers = make(map[string][CacheMaxfield]string) + } + if cache.Bootscripts == nil { + cache.Bootscripts = make(map[string][CacheMaxfield]string) + } + return &cache, nil +} + +// Clear removes all information from the cache +func (c *ScalewayCache) Clear() { + c.Images = make(map[string][CacheMaxfield]string) + c.Snapshots = make(map[string][CacheMaxfield]string) + c.Volumes = make(map[string][CacheMaxfield]string) + c.Bootscripts = make(map[string][CacheMaxfield]string) + c.Servers = make(map[string][CacheMaxfield]string) + c.Modified = true +} + +// Flush flushes the cache database +func (c *ScalewayCache) Flush() error { + return os.Remove(c.Path) +} + +// Save atomically overwrites the current cache database +func (c *ScalewayCache) Save() error { + c.Lock.Lock() + defer c.Lock.Unlock() + + c.hookSave() + if c.Modified { + file, err := ioutil.TempFile(filepath.Dir(c.Path), filepath.Base(c.Path)) + if err != nil { + return err + } + defer file.Close() + if err := json.NewEncoder(file).Encode(c); err != nil { + os.Remove(file.Name()) + return err + } + + if err := os.Rename(file.Name(), c.Path); err != nil { + os.Remove(file.Name()) + return err + } + } + return nil +} + +// ComputeRankMatch fills `ScalewayResolverResult.RankMatch` with its `fuzzy` score +func (s *ScalewayResolverResult) ComputeRankMatch(needle string) { + s.Needle = needle + s.RankMatch = fuzzy.RankMatch(needle, s.Name) +} + +// LookUpImages attempts to return identifiers matching a pattern +func (c *ScalewayCache) LookUpImages(needle string, acceptUUID bool) (ScalewayResolverResults, error) { + c.Lock.Lock() + defer c.Lock.Unlock() + + var res ScalewayResolverResults + var exactMatches ScalewayResolverResults + + if acceptUUID && anonuuid.IsUUID(needle) == nil { + if fields, ok := c.Images[needle]; ok { + entry, err := NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierImage) + if err != nil { + return ScalewayResolverResults{}, err + } + entry.ComputeRankMatch(needle) + res = append(res, entry) + } + } + + needle = regexp.MustCompile(`^user/`).ReplaceAllString(needle, "") + // FIXME: if 'user/' is in needle, only watch for a user image + nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*")) + for identifier, fields := range c.Images { + if fields[CacheTitle] == needle { + entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierImage) + if err != nil { + return ScalewayResolverResults{}, err + } + entry.ComputeRankMatch(needle) + exactMatches = append(exactMatches, entry) + } + if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) { + entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierImage) + if err != nil { + return ScalewayResolverResults{}, err + } + entry.ComputeRankMatch(needle) + res = append(res, entry) + } else if strings.HasPrefix(fields[CacheMarketPlaceUUID], needle) || nameRegex.MatchString(fields[CacheMarketPlaceUUID]) { + entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierImage) + if err != nil { + return ScalewayResolverResults{}, err + } + entry.ComputeRankMatch(needle) + res = append(res, entry) + } + } + + if len(exactMatches) == 1 { + return exactMatches, nil + } + + return removeDuplicatesResults(res), nil +} + +// LookUpSnapshots attempts to return identifiers matching a pattern +func (c *ScalewayCache) LookUpSnapshots(needle string, acceptUUID bool) (ScalewayResolverResults, error) { + c.Lock.Lock() + defer c.Lock.Unlock() + + var res ScalewayResolverResults + var exactMatches ScalewayResolverResults + + if acceptUUID && anonuuid.IsUUID(needle) == nil { + if fields, ok := c.Snapshots[needle]; ok { + entry, err := NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierSnapshot) + if err != nil { + return ScalewayResolverResults{}, err + } + entry.ComputeRankMatch(needle) + res = append(res, entry) + } + } + + needle = regexp.MustCompile(`^user/`).ReplaceAllString(needle, "") + nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*")) + for identifier, fields := range c.Snapshots { + if fields[CacheTitle] == needle { + entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierSnapshot) + if err != nil { + return ScalewayResolverResults{}, err + } + entry.ComputeRankMatch(needle) + exactMatches = append(exactMatches, entry) + } + if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) { + entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierSnapshot) + if err != nil { + return ScalewayResolverResults{}, err + } + entry.ComputeRankMatch(needle) + res = append(res, entry) + } + } + + if len(exactMatches) == 1 { + return exactMatches, nil + } + + return removeDuplicatesResults(res), nil +} + +// LookUpVolumes attempts to return identifiers matching a pattern +func (c *ScalewayCache) LookUpVolumes(needle string, acceptUUID bool) (ScalewayResolverResults, error) { + c.Lock.Lock() + defer c.Lock.Unlock() + + var res ScalewayResolverResults + var exactMatches ScalewayResolverResults + + if acceptUUID && anonuuid.IsUUID(needle) == nil { + if fields, ok := c.Volumes[needle]; ok { + entry, err := NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierVolume) + if err != nil { + return ScalewayResolverResults{}, err + } + entry.ComputeRankMatch(needle) + res = append(res, entry) + } + } + + nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*")) + for identifier, fields := range c.Volumes { + if fields[CacheTitle] == needle { + entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierVolume) + if err != nil { + return ScalewayResolverResults{}, err + } + entry.ComputeRankMatch(needle) + exactMatches = append(exactMatches, entry) + } + if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) { + entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierVolume) + if err != nil { + return ScalewayResolverResults{}, err + } + entry.ComputeRankMatch(needle) + res = append(res, entry) + } + } + + if len(exactMatches) == 1 { + return exactMatches, nil + } + + return removeDuplicatesResults(res), nil +} + +// LookUpBootscripts attempts to return identifiers matching a pattern +func (c *ScalewayCache) LookUpBootscripts(needle string, acceptUUID bool) (ScalewayResolverResults, error) { + c.Lock.Lock() + defer c.Lock.Unlock() + + var res ScalewayResolverResults + var exactMatches ScalewayResolverResults + + if acceptUUID && anonuuid.IsUUID(needle) == nil { + if fields, ok := c.Bootscripts[needle]; ok { + entry, err := NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierBootscript) + if err != nil { + return ScalewayResolverResults{}, err + } + entry.ComputeRankMatch(needle) + res = append(res, entry) + } + } + + nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*")) + for identifier, fields := range c.Bootscripts { + if fields[CacheTitle] == needle { + entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierBootscript) + if err != nil { + return ScalewayResolverResults{}, err + } + entry.ComputeRankMatch(needle) + exactMatches = append(exactMatches, entry) + } + if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) { + entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierBootscript) + if err != nil { + return ScalewayResolverResults{}, err + } + entry.ComputeRankMatch(needle) + res = append(res, entry) + } + } + + if len(exactMatches) == 1 { + return exactMatches, nil + } + + return removeDuplicatesResults(res), nil +} + +// LookUpServers attempts to return identifiers matching a pattern +func (c *ScalewayCache) LookUpServers(needle string, acceptUUID bool) (ScalewayResolverResults, error) { + c.Lock.Lock() + defer c.Lock.Unlock() + + var res ScalewayResolverResults + var exactMatches ScalewayResolverResults + + if acceptUUID && anonuuid.IsUUID(needle) == nil { + if fields, ok := c.Servers[needle]; ok { + entry, err := NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierServer) + if err != nil { + return ScalewayResolverResults{}, err + } + entry.ComputeRankMatch(needle) + res = append(res, entry) + } + } + + nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*")) + for identifier, fields := range c.Servers { + if fields[CacheTitle] == needle { + entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierServer) + if err != nil { + return ScalewayResolverResults{}, err + } + entry.ComputeRankMatch(needle) + exactMatches = append(exactMatches, entry) + } + if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) { + entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierServer) + if err != nil { + return ScalewayResolverResults{}, err + } + entry.ComputeRankMatch(needle) + res = append(res, entry) + } + } + + if len(exactMatches) == 1 { + return exactMatches, nil + } + + return removeDuplicatesResults(res), nil +} + +// removeDuplicatesResults transforms an array into a unique array +func removeDuplicatesResults(elements ScalewayResolverResults) ScalewayResolverResults { + encountered := map[string]ScalewayResolverResult{} + + // Create a map of all unique elements. + for v := range elements { + encountered[elements[v].Identifier] = elements[v] + } + + // Place all keys from the map into a slice. + results := ScalewayResolverResults{} + for _, result := range encountered { + results = append(results, result) + } + return results +} + +// parseNeedle parses a user needle and try to extract a forced object type +// i.e: +// - server:blah-blah -> kind=server, needle=blah-blah +// - blah-blah -> kind="", needle=blah-blah +// - not-existing-type:blah-blah +func parseNeedle(input string) (identifierType int, needle string) { + parts := strings.Split(input, ":") + if len(parts) == 2 { + switch parts[0] { + case "server": + return IdentifierServer, parts[1] + case "image": + return IdentifierImage, parts[1] + case "snapshot": + return IdentifierSnapshot, parts[1] + case "bootscript": + return IdentifierBootscript, parts[1] + case "volume": + return IdentifierVolume, parts[1] + } + } + return IdentifierUnknown, input +} + +// LookUpIdentifiers attempts to return identifiers matching a pattern +func (c *ScalewayCache) LookUpIdentifiers(needle string) (ScalewayResolverResults, error) { + results := ScalewayResolverResults{} + + identifierType, needle := parseNeedle(needle) + + if identifierType&(IdentifierUnknown|IdentifierServer) > 0 { + servers, err := c.LookUpServers(needle, false) + if err != nil { + return ScalewayResolverResults{}, err + } + for _, result := range servers { + entry, err := NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, result.Region, IdentifierServer) + if err != nil { + return ScalewayResolverResults{}, err + } + entry.ComputeRankMatch(needle) + results = append(results, entry) + } + } + + if identifierType&(IdentifierUnknown|IdentifierImage) > 0 { + images, err := c.LookUpImages(needle, false) + if err != nil { + return ScalewayResolverResults{}, err + } + for _, result := range images { + entry, err := NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, result.Region, IdentifierImage) + if err != nil { + return ScalewayResolverResults{}, err + } + entry.ComputeRankMatch(needle) + results = append(results, entry) + } + } + + if identifierType&(IdentifierUnknown|IdentifierSnapshot) > 0 { + snapshots, err := c.LookUpSnapshots(needle, false) + if err != nil { + return ScalewayResolverResults{}, err + } + for _, result := range snapshots { + entry, err := NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, result.Region, IdentifierSnapshot) + if err != nil { + return ScalewayResolverResults{}, err + } + entry.ComputeRankMatch(needle) + results = append(results, entry) + } + } + + if identifierType&(IdentifierUnknown|IdentifierVolume) > 0 { + volumes, err := c.LookUpVolumes(needle, false) + if err != nil { + return ScalewayResolverResults{}, err + } + for _, result := range volumes { + entry, err := NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, result.Region, IdentifierVolume) + if err != nil { + return ScalewayResolverResults{}, err + } + entry.ComputeRankMatch(needle) + results = append(results, entry) + } + } + + if identifierType&(IdentifierUnknown|IdentifierBootscript) > 0 { + bootscripts, err := c.LookUpBootscripts(needle, false) + if err != nil { + return ScalewayResolverResults{}, err + } + for _, result := range bootscripts { + entry, err := NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, result.Region, IdentifierBootscript) + if err != nil { + return ScalewayResolverResults{}, err + } + entry.ComputeRankMatch(needle) + results = append(results, entry) + } + } + return results, nil +} + +// InsertServer registers a server in the cache +func (c *ScalewayCache) InsertServer(identifier, region, arch, owner, name string) { + c.Lock.Lock() + defer c.Lock.Unlock() + + fields, exists := c.Servers[identifier] + if !exists || fields[CacheTitle] != name { + c.Servers[identifier] = [CacheMaxfield]string{region, arch, owner, name} + c.Modified = true + } +} + +// RemoveServer removes a server from the cache +func (c *ScalewayCache) RemoveServer(identifier string) { + c.Lock.Lock() + defer c.Lock.Unlock() + + delete(c.Servers, identifier) + c.Modified = true +} + +// ClearServers removes all servers from the cache +func (c *ScalewayCache) ClearServers() { + c.Lock.Lock() + defer c.Lock.Unlock() + + c.Servers = make(map[string][CacheMaxfield]string) + c.Modified = true +} + +// InsertImage registers an image in the cache +func (c *ScalewayCache) InsertImage(identifier, region, arch, owner, name, marketPlaceUUID string) { + c.Lock.Lock() + defer c.Lock.Unlock() + + fields, exists := c.Images[identifier] + if !exists || fields[CacheTitle] != name { + c.Images[identifier] = [CacheMaxfield]string{region, arch, owner, name, marketPlaceUUID} + c.Modified = true + } +} + +// RemoveImage removes a server from the cache +func (c *ScalewayCache) RemoveImage(identifier string) { + c.Lock.Lock() + defer c.Lock.Unlock() + + delete(c.Images, identifier) + c.Modified = true +} + +// ClearImages removes all images from the cache +func (c *ScalewayCache) ClearImages() { + c.Lock.Lock() + defer c.Lock.Unlock() + + c.Images = make(map[string][CacheMaxfield]string) + c.Modified = true +} + +// InsertSnapshot registers an snapshot in the cache +func (c *ScalewayCache) InsertSnapshot(identifier, region, arch, owner, name string) { + c.Lock.Lock() + defer c.Lock.Unlock() + + fields, exists := c.Snapshots[identifier] + if !exists || fields[CacheTitle] != name { + c.Snapshots[identifier] = [CacheMaxfield]string{region, arch, owner, name} + c.Modified = true + } +} + +// RemoveSnapshot removes a server from the cache +func (c *ScalewayCache) RemoveSnapshot(identifier string) { + c.Lock.Lock() + defer c.Lock.Unlock() + + delete(c.Snapshots, identifier) + c.Modified = true +} + +// ClearSnapshots removes all snapshots from the cache +func (c *ScalewayCache) ClearSnapshots() { + c.Lock.Lock() + defer c.Lock.Unlock() + + c.Snapshots = make(map[string][CacheMaxfield]string) + c.Modified = true +} + +// InsertVolume registers an volume in the cache +func (c *ScalewayCache) InsertVolume(identifier, region, arch, owner, name string) { + c.Lock.Lock() + defer c.Lock.Unlock() + + fields, exists := c.Volumes[identifier] + if !exists || fields[CacheTitle] != name { + c.Volumes[identifier] = [CacheMaxfield]string{region, arch, owner, name} + c.Modified = true + } +} + +// RemoveVolume removes a server from the cache +func (c *ScalewayCache) RemoveVolume(identifier string) { + c.Lock.Lock() + defer c.Lock.Unlock() + + delete(c.Volumes, identifier) + c.Modified = true +} + +// ClearVolumes removes all volumes from the cache +func (c *ScalewayCache) ClearVolumes() { + c.Lock.Lock() + defer c.Lock.Unlock() + + c.Volumes = make(map[string][CacheMaxfield]string) + c.Modified = true +} + +// InsertBootscript registers an bootscript in the cache +func (c *ScalewayCache) InsertBootscript(identifier, region, arch, owner, name string) { + c.Lock.Lock() + defer c.Lock.Unlock() + + fields, exists := c.Bootscripts[identifier] + if !exists || fields[CacheTitle] != name { + c.Bootscripts[identifier] = [CacheMaxfield]string{region, arch, owner, name} + c.Modified = true + } +} + +// RemoveBootscript removes a bootscript from the cache +func (c *ScalewayCache) RemoveBootscript(identifier string) { + c.Lock.Lock() + defer c.Lock.Unlock() + + delete(c.Bootscripts, identifier) + c.Modified = true +} + +// ClearBootscripts removes all bootscripts from the cache +func (c *ScalewayCache) ClearBootscripts() { + c.Lock.Lock() + defer c.Lock.Unlock() + + c.Bootscripts = make(map[string][CacheMaxfield]string) + c.Modified = true +} + +// GetNbServers returns the number of servers in the cache +func (c *ScalewayCache) GetNbServers() int { + c.Lock.Lock() + defer c.Lock.Unlock() + + return len(c.Servers) +} + +// GetNbImages returns the number of images in the cache +func (c *ScalewayCache) GetNbImages() int { + c.Lock.Lock() + defer c.Lock.Unlock() + + return len(c.Images) +} + +// GetNbSnapshots returns the number of snapshots in the cache +func (c *ScalewayCache) GetNbSnapshots() int { + c.Lock.Lock() + defer c.Lock.Unlock() + + return len(c.Snapshots) +} + +// GetNbVolumes returns the number of volumes in the cache +func (c *ScalewayCache) GetNbVolumes() int { + c.Lock.Lock() + defer c.Lock.Unlock() + + return len(c.Volumes) +} + +// GetNbBootscripts returns the number of bootscripts in the cache +func (c *ScalewayCache) GetNbBootscripts() int { + c.Lock.Lock() + defer c.Lock.Unlock() + + return len(c.Bootscripts) +} diff --git a/vendor/github.com/scaleway/scaleway-cli/pkg/api/helpers.go b/vendor/github.com/scaleway/scaleway-cli/pkg/api/helpers.go new file mode 100644 index 000000000..3a59e465d --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-cli/pkg/api/helpers.go @@ -0,0 +1,685 @@ +// Copyright (C) 2015 Scaleway. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE.md file. + +package api + +import ( + "errors" + "fmt" + "os" + "sort" + "strings" + "sync" + "time" + + "github.com/Sirupsen/logrus" + log "github.com/Sirupsen/logrus" + "github.com/docker/docker/pkg/namesgenerator" + "github.com/dustin/go-humanize" + "github.com/moul/anonuuid" + "github.com/scaleway/scaleway-cli/pkg/utils" +) + +// ScalewayResolvedIdentifier represents a list of matching identifier for a specifier pattern +type ScalewayResolvedIdentifier struct { + // Identifiers holds matching identifiers + Identifiers ScalewayResolverResults + + // Needle is the criteria used to lookup identifiers + Needle string +} + +// ScalewayImageInterface is an interface to multiple Scaleway items +type ScalewayImageInterface struct { + CreationDate time.Time + Identifier string + Name string + Tag string + VirtualSize uint64 + Public bool + Type string + Organization string + Archs []string + Region []string +} + +// ResolveGateway tries to resolve a server public ip address, else returns the input string, i.e. IPv4, hostname +func ResolveGateway(api *ScalewayAPI, gateway string) (string, error) { + if gateway == "" { + return "", nil + } + + // Parses optional type prefix, i.e: "server:name" -> "name" + _, gateway = parseNeedle(gateway) + + servers, err := api.ResolveServer(gateway) + if err != nil { + return "", err + } + + if len(servers) == 0 { + return gateway, nil + } + + if len(servers) > 1 { + return "", showResolverResults(gateway, servers) + } + + // if len(servers) == 1 { + server, err := api.GetServer(servers[0].Identifier) + if err != nil { + return "", err + } + return server.PublicAddress.IP, nil +} + +// CreateVolumeFromHumanSize creates a volume on the API with a human readable size +func CreateVolumeFromHumanSize(api *ScalewayAPI, size string) (*string, error) { + bytes, err := humanize.ParseBytes(size) + if err != nil { + return nil, err + } + + var newVolume ScalewayVolumeDefinition + newVolume.Name = size + newVolume.Size = bytes + newVolume.Type = "l_ssd" + + volumeID, err := api.PostVolume(newVolume) + if err != nil { + return nil, err + } + + return &volumeID, nil +} + +// fillIdentifierCache fills the cache by fetching from the API +func fillIdentifierCache(api *ScalewayAPI, identifierType int) { + log.Debugf("Filling the cache") + var wg sync.WaitGroup + wg.Add(5) + go func() { + if identifierType&(IdentifierUnknown|IdentifierServer) > 0 { + api.GetServers(true, 0) + } + wg.Done() + }() + go func() { + if identifierType&(IdentifierUnknown|IdentifierImage) > 0 { + api.GetImages() + } + wg.Done() + }() + go func() { + if identifierType&(IdentifierUnknown|IdentifierSnapshot) > 0 { + api.GetSnapshots() + } + wg.Done() + }() + go func() { + if identifierType&(IdentifierUnknown|IdentifierVolume) > 0 { + api.GetVolumes() + } + wg.Done() + }() + go func() { + if identifierType&(IdentifierUnknown|IdentifierBootscript) > 0 { + api.GetBootscripts() + } + wg.Done() + }() + wg.Wait() +} + +// GetIdentifier returns a an identifier if the resolved needles only match one element, else, it exists the program +func GetIdentifier(api *ScalewayAPI, needle string) (*ScalewayResolverResult, error) { + idents, err := ResolveIdentifier(api, needle) + if err != nil { + return nil, err + } + + if len(idents) == 1 { + return &idents[0], nil + } + if len(idents) == 0 { + return nil, fmt.Errorf("No such identifier: %s", needle) + } + + sort.Sort(idents) + for _, identifier := range idents { + // FIXME: also print the name + fmt.Fprintf(os.Stderr, "- %s\n", identifier.Identifier) + } + return nil, fmt.Errorf("Too many candidates for %s (%d)", needle, len(idents)) +} + +// ResolveIdentifier resolves needle provided by the user +func ResolveIdentifier(api *ScalewayAPI, needle string) (ScalewayResolverResults, error) { + idents, err := api.Cache.LookUpIdentifiers(needle) + if err != nil { + return idents, err + } + if len(idents) > 0 { + return idents, nil + } + + identifierType, _ := parseNeedle(needle) + fillIdentifierCache(api, identifierType) + + return api.Cache.LookUpIdentifiers(needle) +} + +// ResolveIdentifiers resolves needles provided by the user +func ResolveIdentifiers(api *ScalewayAPI, needles []string, out chan ScalewayResolvedIdentifier) { + // first attempt, only lookup from the cache + var unresolved []string + for _, needle := range needles { + idents, err := api.Cache.LookUpIdentifiers(needle) + if err != nil { + api.Logger.Fatalf("%s", err) + } + if len(idents) == 0 { + unresolved = append(unresolved, needle) + } else { + out <- ScalewayResolvedIdentifier{ + Identifiers: idents, + Needle: needle, + } + } + } + // fill the cache by fetching from the API and resolve missing identifiers + if len(unresolved) > 0 { + // compute identifierType: + // if identifierType is the same for every unresolved needle, + // we use it directly, else, we choose IdentifierUnknown to + // fulfill every types of cache + identifierType, _ := parseNeedle(unresolved[0]) + for _, needle := range unresolved { + newIdentifierType, _ := parseNeedle(needle) + if identifierType != newIdentifierType { + identifierType = IdentifierUnknown + break + } + } + + // fill all the cache + fillIdentifierCache(api, identifierType) + + // lookup again in the cache + for _, needle := range unresolved { + idents, err := api.Cache.LookUpIdentifiers(needle) + if err != nil { + api.Logger.Fatalf("%s", err) + } + out <- ScalewayResolvedIdentifier{ + Identifiers: idents, + Needle: needle, + } + } + } + + close(out) +} + +// InspectIdentifierResult is returned by `InspectIdentifiers` and contains the inspected `Object` with its `Type` +type InspectIdentifierResult struct { + Type int + Object interface{} +} + +// InspectIdentifiers inspects identifiers concurrently +func InspectIdentifiers(api *ScalewayAPI, ci chan ScalewayResolvedIdentifier, cj chan InspectIdentifierResult, arch string) { + var wg sync.WaitGroup + for { + idents, ok := <-ci + if !ok { + break + } + idents.Identifiers = FilterImagesByArch(idents.Identifiers, arch) + idents.Identifiers = FilterImagesByRegion(idents.Identifiers, api.Region) + if len(idents.Identifiers) != 1 { + if len(idents.Identifiers) == 0 { + log.Errorf("Unable to resolve identifier %s", idents.Needle) + } else { + logrus.Fatal(showResolverResults(idents.Needle, idents.Identifiers)) + } + } else { + ident := idents.Identifiers[0] + wg.Add(1) + go func() { + var obj interface{} + var err error + + switch ident.Type { + case IdentifierServer: + obj, err = api.GetServer(ident.Identifier) + case IdentifierImage: + obj, err = api.GetImage(ident.Identifier) + case IdentifierSnapshot: + obj, err = api.GetSnapshot(ident.Identifier) + case IdentifierVolume: + obj, err = api.GetVolume(ident.Identifier) + case IdentifierBootscript: + obj, err = api.GetBootscript(ident.Identifier) + } + if err == nil && obj != nil { + cj <- InspectIdentifierResult{ + Type: ident.Type, + Object: obj, + } + } + wg.Done() + }() + } + } + wg.Wait() + close(cj) +} + +// ConfigCreateServer represents the options sent to CreateServer and defining a server +type ConfigCreateServer struct { + ImageName string + Name string + Bootscript string + Env string + AdditionalVolumes string + IP string + CommercialType string + DynamicIPRequired bool + EnableIPV6 bool +} + +// CreateServer creates a server using API based on typical server fields +func CreateServer(api *ScalewayAPI, c *ConfigCreateServer) (string, error) { + commercialType := os.Getenv("SCW_COMMERCIAL_TYPE") + if commercialType == "" { + commercialType = c.CommercialType + } + if len(commercialType) < 2 { + return "", errors.New("Invalid commercial type") + } + + if c.Name == "" { + c.Name = strings.Replace(namesgenerator.GetRandomName(0), "_", "-", -1) + } + + var server ScalewayServerDefinition + + server.CommercialType = commercialType + server.Volumes = make(map[string]string) + server.DynamicIPRequired = &c.DynamicIPRequired + server.EnableIPV6 = c.EnableIPV6 + if commercialType == "" { + return "", errors.New("You need to specify a commercial-type") + } + if c.IP != "" { + if anonuuid.IsUUID(c.IP) == nil { + server.PublicIP = c.IP + } else { + ips, err := api.GetIPS() + if err != nil { + return "", err + } + for _, ip := range ips.IPS { + if ip.Address == c.IP { + server.PublicIP = ip.ID + break + } + } + if server.PublicIP == "" { + return "", fmt.Errorf("IP address %v not found", c.IP) + } + } + } + server.Tags = []string{} + if c.Env != "" { + server.Tags = strings.Split(c.Env, " ") + } + switch c.CommercialType { + case "VC1M", "X64-4GB": + if c.AdditionalVolumes == "" { + c.AdditionalVolumes = "50G" + log.Debugf("This server needs a least 50G") + } + case "VC1L", "X64-8GB", "X64-15GB": + if c.AdditionalVolumes == "" { + c.AdditionalVolumes = "150G" + log.Debugf("This server needs a least 150G") + } + case "X64-30GB": + if c.AdditionalVolumes == "" { + c.AdditionalVolumes = "100G 150G" + log.Debugf("This server needs a least 300G") + } + case "X64-60GB": + if c.AdditionalVolumes == "" { + c.AdditionalVolumes = "50G 150G 150G" + log.Debugf("This server needs a least 400G") + } + case "X64-120GB": + if c.AdditionalVolumes == "" { + c.AdditionalVolumes = "150G 150G 150G" + log.Debugf("This server needs a least 500G") + } + } + if c.AdditionalVolumes != "" { + volumes := strings.Split(c.AdditionalVolumes, " ") + for i := range volumes { + volumeID, err := CreateVolumeFromHumanSize(api, volumes[i]) + if err != nil { + return "", err + } + + volumeIDx := fmt.Sprintf("%d", i+1) + server.Volumes[volumeIDx] = *volumeID + } + } + arch := os.Getenv("SCW_TARGET_ARCH") + if arch == "" { + server.CommercialType = strings.ToUpper(server.CommercialType) + switch server.CommercialType[:2] { + case "C1": + arch = "arm" + case "C2", "VC", "X6": + arch = "x86_64" + default: + return "", fmt.Errorf("%s wrong commercial type", server.CommercialType) + } + } + imageIdentifier := &ScalewayImageIdentifier{ + Arch: arch, + } + server.Name = c.Name + inheritingVolume := false + _, err := humanize.ParseBytes(c.ImageName) + if err == nil { + // Create a new root volume + volumeID, errCreateVol := CreateVolumeFromHumanSize(api, c.ImageName) + if errCreateVol != nil { + return "", errCreateVol + } + server.Volumes["0"] = *volumeID + } else { + // Use an existing image + inheritingVolume = true + if anonuuid.IsUUID(c.ImageName) == nil { + server.Image = &c.ImageName + } else { + imageIdentifier, err = api.GetImageID(c.ImageName, arch) + if err != nil { + return "", err + } + if imageIdentifier.Identifier != "" { + server.Image = &imageIdentifier.Identifier + } else { + snapshotID, errGetSnapID := api.GetSnapshotID(c.ImageName) + if errGetSnapID != nil { + return "", errGetSnapID + } + snapshot, errGetSnap := api.GetSnapshot(snapshotID) + if errGetSnap != nil { + return "", errGetSnap + } + if snapshot.BaseVolume.Identifier == "" { + return "", fmt.Errorf("snapshot %v does not have base volume", snapshot.Name) + } + server.Volumes["0"] = snapshot.BaseVolume.Identifier + } + } + } + + if c.Bootscript != "" { + bootscript := "" + + if anonuuid.IsUUID(c.Bootscript) == nil { + bootscript = c.Bootscript + } else { + var errGetBootScript error + + bootscript, errGetBootScript = api.GetBootscriptID(c.Bootscript, imageIdentifier.Arch) + if errGetBootScript != nil { + return "", errGetBootScript + } + } + server.Bootscript = &bootscript + } + serverID, err := api.PostServer(server) + if err != nil { + return "", err + } + + // For inherited volumes, we prefix the name with server hostname + if inheritingVolume { + createdServer, err := api.GetServer(serverID) + if err != nil { + return "", err + } + currentVolume := createdServer.Volumes["0"] + + var volumePayload ScalewayVolumePutDefinition + newName := fmt.Sprintf("%s-%s", createdServer.Hostname, currentVolume.Name) + volumePayload.Name = &newName + volumePayload.CreationDate = ¤tVolume.CreationDate + volumePayload.Organization = ¤tVolume.Organization + volumePayload.Server.Identifier = ¤tVolume.Server.Identifier + volumePayload.Server.Name = ¤tVolume.Server.Name + volumePayload.Identifier = ¤tVolume.Identifier + volumePayload.Size = ¤tVolume.Size + volumePayload.ModificationDate = ¤tVolume.ModificationDate + volumePayload.ExportURI = ¤tVolume.ExportURI + volumePayload.VolumeType = ¤tVolume.VolumeType + + err = api.PutVolume(currentVolume.Identifier, volumePayload) + if err != nil { + return "", err + } + } + + return serverID, nil +} + +// WaitForServerState asks API in a loop until a server matches a wanted state +func WaitForServerState(api *ScalewayAPI, serverID string, targetState string) (*ScalewayServer, error) { + var server *ScalewayServer + var err error + + var currentState string + + for { + server, err = api.GetServer(serverID) + if err != nil { + return nil, err + } + if currentState != server.State { + log.Infof("Server changed state to '%s'", server.State) + currentState = server.State + } + if server.State == targetState { + break + } + time.Sleep(1 * time.Second) + } + + return server, nil +} + +// WaitForServerReady wait for a server state to be running, then wait for the SSH port to be available +func WaitForServerReady(api *ScalewayAPI, serverID, gateway string) (*ScalewayServer, error) { + promise := make(chan bool) + var server *ScalewayServer + var err error + var currentState string + + go func() { + defer close(promise) + + for { + server, err = api.GetServer(serverID) + if err != nil { + promise <- false + return + } + if currentState != server.State { + log.Infof("Server changed state to '%s'", server.State) + currentState = server.State + } + if server.State == "running" { + break + } + if server.State == "stopped" { + err = fmt.Errorf("The server has been stopped") + promise <- false + return + } + time.Sleep(1 * time.Second) + } + + if gateway == "" { + dest := fmt.Sprintf("%s:22", server.PublicAddress.IP) + log.Debugf("Waiting for server SSH port %s", dest) + err = utils.WaitForTCPPortOpen(dest) + if err != nil { + promise <- false + return + } + } else { + dest := fmt.Sprintf("%s:22", gateway) + log.Debugf("Waiting for server SSH port %s", dest) + err = utils.WaitForTCPPortOpen(dest) + if err != nil { + promise <- false + return + } + log.Debugf("Check for SSH port through the gateway: %s", server.PrivateIP) + timeout := time.Tick(120 * time.Second) + for { + select { + case <-timeout: + err = fmt.Errorf("Timeout: unable to ping %s", server.PrivateIP) + goto OUT + default: + if utils.SSHExec("", server.PrivateIP, "root", 22, []string{ + "nc", + "-z", + "-w", + "1", + server.PrivateIP, + "22", + }, false, gateway, false) == nil { + goto OUT + } + time.Sleep(2 * time.Second) + } + } + OUT: + if err != nil { + logrus.Info(err) + err = nil + } + } + promise <- true + }() + + loop := 0 + for { + select { + case done := <-promise: + utils.LogQuiet("\r \r") + if !done { + return nil, err + } + return server, nil + case <-time.After(time.Millisecond * 100): + utils.LogQuiet(fmt.Sprintf("\r%c\r", "-\\|/"[loop%4])) + loop = loop + 1 + if loop == 5 { + loop = 0 + } + } + } +} + +// WaitForServerStopped wait for a server state to be stopped +func WaitForServerStopped(api *ScalewayAPI, serverID string) (*ScalewayServer, error) { + server, err := WaitForServerState(api, serverID, "stopped") + if err != nil { + return nil, err + } + return server, nil +} + +// ByCreationDate sorts images by CreationDate field +type ByCreationDate []ScalewayImageInterface + +func (a ByCreationDate) Len() int { return len(a) } +func (a ByCreationDate) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a ByCreationDate) Less(i, j int) bool { return a[j].CreationDate.Before(a[i].CreationDate) } + +// StartServer start a server based on its needle, can optionaly block while server is booting +func StartServer(api *ScalewayAPI, needle string, wait bool) error { + server, err := api.GetServerID(needle) + if err != nil { + return err + } + + if err = api.PostServerAction(server, "poweron"); err != nil { + return err + } + + if wait { + _, err = WaitForServerReady(api, server, "") + if err != nil { + return fmt.Errorf("failed to wait for server %s to be ready, %v", needle, err) + } + } + return nil +} + +// StartServerOnce wraps StartServer for golang channel +func StartServerOnce(api *ScalewayAPI, needle string, wait bool, successChan chan string, errChan chan error) { + err := StartServer(api, needle, wait) + + if err != nil { + errChan <- err + return + } + successChan <- needle +} + +// DeleteServerForce tries to delete a server using multiple ways +func (a *ScalewayAPI) DeleteServerForce(serverID string) error { + // FIXME: also delete attached volumes and ip address + // FIXME: call delete and stop -t in parallel to speed up process + err := a.DeleteServer(serverID) + if err == nil { + logrus.Infof("Server '%s' successfully deleted", serverID) + return nil + } + + err = a.PostServerAction(serverID, "terminate") + if err == nil { + logrus.Infof("Server '%s' successfully terminated", serverID) + return nil + } + + // FIXME: retry in a loop until timeout or Control+C + logrus.Errorf("Failed to delete server %s", serverID) + logrus.Errorf("Try to run 'scw rm -f %s' later", serverID) + return err +} + +// GetSSHFingerprintFromServer returns an array which containts ssh-host-fingerprints +func (a *ScalewayAPI) GetSSHFingerprintFromServer(serverID string) []string { + ret := []string{} + + if value, err := a.GetUserdata(serverID, "ssh-host-fingerprints", false); err == nil { + PublicKeys := strings.Split(string(*value), "\n") + for i := range PublicKeys { + if fingerprint, err := utils.SSHGetFingerprint([]byte(PublicKeys[i])); err == nil { + ret = append(ret, fingerprint) + } + } + } + return ret +} diff --git a/vendor/github.com/scaleway/scaleway-cli/pkg/api/logger.go b/vendor/github.com/scaleway/scaleway-cli/pkg/api/logger.go new file mode 100644 index 000000000..58ad93716 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-cli/pkg/api/logger.go @@ -0,0 +1,77 @@ +// Copyright (C) 2015 Scaleway. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE.md file. + +package api + +import ( + "fmt" + "log" + "net/http" + "os" +) + +// Logger handles logging concerns for the Scaleway API SDK +type Logger interface { + LogHTTP(*http.Request) + Fatalf(format string, v ...interface{}) + Debugf(format string, v ...interface{}) + Infof(format string, v ...interface{}) + Warnf(format string, v ...interface{}) +} + +// NewDefaultLogger returns a logger which is configured for stdout +func NewDefaultLogger() Logger { + return &defaultLogger{ + Logger: log.New(os.Stdout, "", log.LstdFlags), + } +} + +type defaultLogger struct { + *log.Logger +} + +func (l *defaultLogger) LogHTTP(r *http.Request) { + l.Printf("%s %s\n", r.Method, r.URL.RawPath) +} + +func (l *defaultLogger) Fatalf(format string, v ...interface{}) { + l.Printf("[FATAL] %s\n", fmt.Sprintf(format, v)) + os.Exit(1) +} + +func (l *defaultLogger) Debugf(format string, v ...interface{}) { + l.Printf("[DEBUG] %s\n", fmt.Sprintf(format, v)) +} + +func (l *defaultLogger) Infof(format string, v ...interface{}) { + l.Printf("[INFO ] %s\n", fmt.Sprintf(format, v)) +} + +func (l *defaultLogger) Warnf(format string, v ...interface{}) { + l.Printf("[WARN ] %s\n", fmt.Sprintf(format, v)) +} + +type disableLogger struct { +} + +// NewDisableLogger returns a logger which is configured to do nothing +func NewDisableLogger() Logger { + return &disableLogger{} +} + +func (d *disableLogger) LogHTTP(r *http.Request) { +} + +func (d *disableLogger) Fatalf(format string, v ...interface{}) { + panic(fmt.Sprintf(format, v)) +} + +func (d *disableLogger) Debugf(format string, v ...interface{}) { +} + +func (d *disableLogger) Infof(format string, v ...interface{}) { +} + +func (d *disableLogger) Warnf(format string, v ...interface{}) { +} diff --git a/vendor/github.com/scaleway/scaleway-cli/pkg/sshcommand/sshcommand.go b/vendor/github.com/scaleway/scaleway-cli/pkg/sshcommand/sshcommand.go new file mode 100644 index 000000000..676b2f976 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-cli/pkg/sshcommand/sshcommand.go @@ -0,0 +1,124 @@ +package sshcommand + +import ( + "fmt" + "runtime" + "strings" +) + +// Command contains settings to build a ssh command +type Command struct { + Host string + User string + Port int + SSHOptions []string + Gateway *Command + Command []string + Debug bool + NoEscapeCommand bool + SkipHostKeyChecking bool + Quiet bool + AllocateTTY bool + EnableSSHKeyForwarding bool + + isGateway bool +} + +// New returns a minimal Command +func New(host string) *Command { + return &Command{ + Host: host, + } +} + +func (c *Command) applyDefaults() { + if strings.Contains(c.Host, "@") { + parts := strings.Split(c.Host, "@") + c.User = parts[0] + c.Host = parts[1] + } + + if c.Port == 0 { + c.Port = 22 + } + + if c.isGateway { + c.SSHOptions = []string{"-W", "%h:%p"} + } +} + +// Slice returns an execve compatible slice of arguments +func (c *Command) Slice() []string { + c.applyDefaults() + + slice := []string{} + + slice = append(slice, "ssh") + + if c.EnableSSHKeyForwarding { + slice = append(slice, "-A") + } + + if c.Quiet { + slice = append(slice, "-q") + } + + if c.SkipHostKeyChecking { + slice = append(slice, "-o", "UserKnownHostsFile=/dev/null", "-o", "StrictHostKeyChecking=no") + } + + if len(c.SSHOptions) > 0 { + slice = append(slice, c.SSHOptions...) + } + + if c.Gateway != nil { + c.Gateway.isGateway = true + slice = append(slice, "-o", "ProxyCommand="+c.Gateway.String()) + } + + if c.User != "" { + slice = append(slice, "-l", c.User) + } + + slice = append(slice, c.Host) + + if c.AllocateTTY { + slice = append(slice, "-t", "-t") + } + + slice = append(slice, "-p", fmt.Sprintf("%d", c.Port)) + if len(c.Command) > 0 { + slice = append(slice, "--", "/bin/sh", "-e") + if c.Debug { + slice = append(slice, "-x") + } + slice = append(slice, "-c") + + var escapedCommand []string + if c.NoEscapeCommand { + escapedCommand = c.Command + } else { + escapedCommand = []string{} + for _, part := range c.Command { + escapedCommand = append(escapedCommand, fmt.Sprintf("%q", part)) + } + } + slice = append(slice, fmt.Sprintf("%q", strings.Join(escapedCommand, " "))) + } + if runtime.GOOS == "windows" { + slice[len(slice)-1] = slice[len(slice)-1] + " " // Why ? + } + return slice +} + +// String returns a copy-pasteable command, useful for debugging +func (c *Command) String() string { + slice := c.Slice() + for i := range slice { + quoted := fmt.Sprintf("%q", slice[i]) + if strings.Contains(slice[i], " ") || len(quoted) != len(slice[i])+2 { + slice[i] = quoted + } + } + return strings.Join(slice, " ") +} diff --git a/vendor/github.com/scaleway/scaleway-cli/pkg/utils/quiet.go b/vendor/github.com/scaleway/scaleway-cli/pkg/utils/quiet.go new file mode 100644 index 000000000..775918d8d --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-cli/pkg/utils/quiet.go @@ -0,0 +1,30 @@ +// Copyright (C) 2015 Scaleway. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE.md file. + +// Package utils contains logquiet +package utils + +import ( + "fmt" + "os" +) + +// LogQuietStruct is a struct to store information about quiet state +type LogQuietStruct struct { + quiet bool +} + +var instanceQuiet LogQuietStruct + +// Quiet enable or disable quiet +func Quiet(option bool) { + instanceQuiet.quiet = option +} + +// LogQuiet Displays info if quiet is activated +func LogQuiet(str string) { + if !instanceQuiet.quiet { + fmt.Fprintf(os.Stderr, "%s", str) + } +} diff --git a/vendor/github.com/scaleway/scaleway-cli/pkg/utils/utils.go b/vendor/github.com/scaleway/scaleway-cli/pkg/utils/utils.go new file mode 100644 index 000000000..6f11f4869 --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-cli/pkg/utils/utils.go @@ -0,0 +1,253 @@ +// Copyright (C) 2015 Scaleway. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE.md file. + +// scw helpers + +// Package utils contains helpers +package utils + +import ( + "crypto/md5" + "errors" + "fmt" + "io" + "net" + "os" + "os/exec" + "path" + "path/filepath" + "reflect" + "regexp" + "strings" + "time" + + "golang.org/x/crypto/ssh" + + "github.com/Sirupsen/logrus" + log "github.com/Sirupsen/logrus" + "github.com/mattn/go-isatty" + "github.com/moul/gotty-client" + "github.com/scaleway/scaleway-cli/pkg/sshcommand" +) + +// SpawnRedirection is used to redirects the fluxes +type SpawnRedirection struct { + Stdin io.Reader + Stdout io.Writer + Stderr io.Writer +} + +// SSHExec executes a command over SSH and redirects file-descriptors +func SSHExec(publicIPAddress, privateIPAddress, user string, port int, command []string, checkConnection bool, gateway string, enableSSHKeyForwarding bool) error { + gatewayUser := "root" + gatewayIPAddress := gateway + if strings.Contains(gateway, "@") { + parts := strings.Split(gatewayIPAddress, "@") + if len(parts) != 2 { + return fmt.Errorf("gateway: must be like root@IP") + } + gatewayUser = parts[0] + gatewayIPAddress = parts[1] + gateway = gatewayUser + "@" + gatewayIPAddress + } + + if publicIPAddress == "" && gatewayIPAddress == "" { + return errors.New("server does not have public IP") + } + if privateIPAddress == "" && gatewayIPAddress != "" { + return errors.New("server does not have private IP") + } + + if checkConnection { + useGateway := gatewayIPAddress != "" + if useGateway && !IsTCPPortOpen(fmt.Sprintf("%s:22", gatewayIPAddress)) { + return errors.New("gateway is not available, try again later") + } + if !useGateway && !IsTCPPortOpen(fmt.Sprintf("%s:%d", publicIPAddress, port)) { + return errors.New("server is not ready, try again later") + } + } + + sshCommand := NewSSHExecCmd(publicIPAddress, privateIPAddress, user, port, isatty.IsTerminal(os.Stdin.Fd()), command, gateway, enableSSHKeyForwarding) + + log.Debugf("Executing: %s", sshCommand) + + spawn := exec.Command("ssh", sshCommand.Slice()[1:]...) + spawn.Stdout = os.Stdout + spawn.Stdin = os.Stdin + spawn.Stderr = os.Stderr + return spawn.Run() +} + +// NewSSHExecCmd computes execve compatible arguments to run a command via ssh +func NewSSHExecCmd(publicIPAddress, privateIPAddress, user string, port int, allocateTTY bool, command []string, gatewayIPAddress string, enableSSHKeyForwarding bool) *sshcommand.Command { + quiet := os.Getenv("DEBUG") != "1" + secureExec := os.Getenv("SCW_SECURE_EXEC") == "1" + sshCommand := &sshcommand.Command{ + AllocateTTY: allocateTTY, + Command: command, + Host: publicIPAddress, + Quiet: quiet, + SkipHostKeyChecking: !secureExec, + User: user, + NoEscapeCommand: true, + Port: port, + EnableSSHKeyForwarding: enableSSHKeyForwarding, + } + if gatewayIPAddress != "" { + sshCommand.Host = privateIPAddress + sshCommand.Gateway = &sshcommand.Command{ + Host: gatewayIPAddress, + SkipHostKeyChecking: !secureExec, + AllocateTTY: allocateTTY, + Quiet: quiet, + User: user, + Port: port, + } + } + + return sshCommand +} + +// GeneratingAnSSHKey generates an SSH key +func GeneratingAnSSHKey(cfg SpawnRedirection, path string, name string) (string, error) { + args := []string{ + "-t", + "rsa", + "-b", + "4096", + "-f", + filepath.Join(path, name), + "-N", + "", + "-C", + "", + } + log.Infof("Executing commands %v", args) + spawn := exec.Command("ssh-keygen", args...) + spawn.Stdout = cfg.Stdout + spawn.Stdin = cfg.Stdin + spawn.Stderr = cfg.Stderr + return args[5], spawn.Run() +} + +// WaitForTCPPortOpen calls IsTCPPortOpen in a loop +func WaitForTCPPortOpen(dest string) error { + for { + if IsTCPPortOpen(dest) { + break + } + time.Sleep(1 * time.Second) + } + return nil +} + +// IsTCPPortOpen returns true if a TCP communication with "host:port" can be initialized +func IsTCPPortOpen(dest string) bool { + conn, err := net.DialTimeout("tcp", dest, time.Duration(2000)*time.Millisecond) + if err == nil { + defer conn.Close() + } + return err == nil +} + +// TruncIf ensures the input string does not exceed max size if cond is met +func TruncIf(str string, max int, cond bool) string { + if cond && len(str) > max { + return str[:max] + } + return str +} + +// Wordify convert complex name to a single word without special shell characters +func Wordify(str string) string { + str = regexp.MustCompile(`[^a-zA-Z0-9-]`).ReplaceAllString(str, "_") + str = regexp.MustCompile(`__+`).ReplaceAllString(str, "_") + str = strings.Trim(str, "_") + return str +} + +// PathToTARPathparts returns the two parts of a unix path +func PathToTARPathparts(fullPath string) (string, string) { + fullPath = strings.TrimRight(fullPath, "/") + return path.Dir(fullPath), path.Base(fullPath) +} + +// RemoveDuplicates transforms an array into a unique array +func RemoveDuplicates(elements []string) []string { + encountered := map[string]bool{} + + // Create a map of all unique elements. + for v := range elements { + encountered[elements[v]] = true + } + + // Place all keys from the map into a slice. + result := []string{} + for key := range encountered { + result = append(result, key) + } + return result +} + +// AttachToSerial tries to connect to server serial using 'gotty-client' and fallback with a help message +func AttachToSerial(serverID, apiToken, url string) (*gottyclient.Client, chan bool, error) { + gottyURL := os.Getenv("SCW_GOTTY_URL") + if gottyURL == "" { + gottyURL = url + } + URL := fmt.Sprintf("%s?arg=%s&arg=%s", gottyURL, apiToken, serverID) + + logrus.Debug("Connection to ", URL) + gottycli, err := gottyclient.NewClient(URL) + if err != nil { + return nil, nil, err + } + + if os.Getenv("SCW_TLSVERIFY") == "0" { + gottycli.SkipTLSVerify = true + } + + gottycli.UseProxyFromEnv = true + + if err = gottycli.Connect(); err != nil { + return nil, nil, err + } + done := make(chan bool) + + fmt.Println("You are connected, type 'Ctrl+q' to quit.") + go func() { + gottycli.Loop() + gottycli.Close() + done <- true + }() + return gottycli, done, nil +} + +func rfc4716hex(data []byte) string { + fingerprint := "" + + for i := 0; i < len(data); i++ { + fingerprint = fmt.Sprintf("%s%0.2x", fingerprint, data[i]) + if i != len(data)-1 { + fingerprint = fingerprint + ":" + } + } + return fingerprint +} + +// SSHGetFingerprint returns the fingerprint of an SSH key +func SSHGetFingerprint(key []byte) (string, error) { + publicKey, comment, _, _, err := ssh.ParseAuthorizedKey(key) + if err != nil { + return "", err + } + switch reflect.TypeOf(publicKey).String() { + case "*ssh.rsaPublicKey", "*ssh.dsaPublicKey", "*ssh.ecdsaPublicKey": + md5sum := md5.Sum(publicKey.Marshal()) + return publicKey.Type() + " " + rfc4716hex(md5sum[:]) + " " + comment, nil + default: + return "", errors.New("Can't handle this key") + } +} diff --git a/vendor/golang.org/x/crypto/ssh/terminal/terminal.go b/vendor/golang.org/x/crypto/ssh/terminal/terminal.go new file mode 100644 index 000000000..18379a935 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/terminal/terminal.go @@ -0,0 +1,951 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package terminal + +import ( + "bytes" + "io" + "sync" + "unicode/utf8" +) + +// EscapeCodes contains escape sequences that can be written to the terminal in +// order to achieve different styles of text. +type EscapeCodes struct { + // Foreground colors + Black, Red, Green, Yellow, Blue, Magenta, Cyan, White []byte + + // Reset all attributes + Reset []byte +} + +var vt100EscapeCodes = EscapeCodes{ + Black: []byte{keyEscape, '[', '3', '0', 'm'}, + Red: []byte{keyEscape, '[', '3', '1', 'm'}, + Green: []byte{keyEscape, '[', '3', '2', 'm'}, + Yellow: []byte{keyEscape, '[', '3', '3', 'm'}, + Blue: []byte{keyEscape, '[', '3', '4', 'm'}, + Magenta: []byte{keyEscape, '[', '3', '5', 'm'}, + Cyan: []byte{keyEscape, '[', '3', '6', 'm'}, + White: []byte{keyEscape, '[', '3', '7', 'm'}, + + Reset: []byte{keyEscape, '[', '0', 'm'}, +} + +// Terminal contains the state for running a VT100 terminal that is capable of +// reading lines of input. +type Terminal struct { + // AutoCompleteCallback, if non-null, is called for each keypress with + // the full input line and the current position of the cursor (in + // bytes, as an index into |line|). If it returns ok=false, the key + // press is processed normally. Otherwise it returns a replacement line + // and the new cursor position. + AutoCompleteCallback func(line string, pos int, key rune) (newLine string, newPos int, ok bool) + + // Escape contains a pointer to the escape codes for this terminal. + // It's always a valid pointer, although the escape codes themselves + // may be empty if the terminal doesn't support them. + Escape *EscapeCodes + + // lock protects the terminal and the state in this object from + // concurrent processing of a key press and a Write() call. + lock sync.Mutex + + c io.ReadWriter + prompt []rune + + // line is the current line being entered. + line []rune + // pos is the logical position of the cursor in line + pos int + // echo is true if local echo is enabled + echo bool + // pasteActive is true iff there is a bracketed paste operation in + // progress. + pasteActive bool + + // cursorX contains the current X value of the cursor where the left + // edge is 0. cursorY contains the row number where the first row of + // the current line is 0. + cursorX, cursorY int + // maxLine is the greatest value of cursorY so far. + maxLine int + + termWidth, termHeight int + + // outBuf contains the terminal data to be sent. + outBuf []byte + // remainder contains the remainder of any partial key sequences after + // a read. It aliases into inBuf. + remainder []byte + inBuf [256]byte + + // history contains previously entered commands so that they can be + // accessed with the up and down keys. + history stRingBuffer + // historyIndex stores the currently accessed history entry, where zero + // means the immediately previous entry. + historyIndex int + // When navigating up and down the history it's possible to return to + // the incomplete, initial line. That value is stored in + // historyPending. + historyPending string +} + +// NewTerminal runs a VT100 terminal on the given ReadWriter. If the ReadWriter is +// a local terminal, that terminal must first have been put into raw mode. +// prompt is a string that is written at the start of each input line (i.e. +// "> "). +func NewTerminal(c io.ReadWriter, prompt string) *Terminal { + return &Terminal{ + Escape: &vt100EscapeCodes, + c: c, + prompt: []rune(prompt), + termWidth: 80, + termHeight: 24, + echo: true, + historyIndex: -1, + } +} + +const ( + keyCtrlD = 4 + keyCtrlU = 21 + keyEnter = '\r' + keyEscape = 27 + keyBackspace = 127 + keyUnknown = 0xd800 /* UTF-16 surrogate area */ + iota + keyUp + keyDown + keyLeft + keyRight + keyAltLeft + keyAltRight + keyHome + keyEnd + keyDeleteWord + keyDeleteLine + keyClearScreen + keyPasteStart + keyPasteEnd +) + +var ( + crlf = []byte{'\r', '\n'} + pasteStart = []byte{keyEscape, '[', '2', '0', '0', '~'} + pasteEnd = []byte{keyEscape, '[', '2', '0', '1', '~'} +) + +// bytesToKey tries to parse a key sequence from b. If successful, it returns +// the key and the remainder of the input. Otherwise it returns utf8.RuneError. +func bytesToKey(b []byte, pasteActive bool) (rune, []byte) { + if len(b) == 0 { + return utf8.RuneError, nil + } + + if !pasteActive { + switch b[0] { + case 1: // ^A + return keyHome, b[1:] + case 5: // ^E + return keyEnd, b[1:] + case 8: // ^H + return keyBackspace, b[1:] + case 11: // ^K + return keyDeleteLine, b[1:] + case 12: // ^L + return keyClearScreen, b[1:] + case 23: // ^W + return keyDeleteWord, b[1:] + } + } + + if b[0] != keyEscape { + if !utf8.FullRune(b) { + return utf8.RuneError, b + } + r, l := utf8.DecodeRune(b) + return r, b[l:] + } + + if !pasteActive && len(b) >= 3 && b[0] == keyEscape && b[1] == '[' { + switch b[2] { + case 'A': + return keyUp, b[3:] + case 'B': + return keyDown, b[3:] + case 'C': + return keyRight, b[3:] + case 'D': + return keyLeft, b[3:] + case 'H': + return keyHome, b[3:] + case 'F': + return keyEnd, b[3:] + } + } + + if !pasteActive && len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' { + switch b[5] { + case 'C': + return keyAltRight, b[6:] + case 'D': + return keyAltLeft, b[6:] + } + } + + if !pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteStart) { + return keyPasteStart, b[6:] + } + + if pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteEnd) { + return keyPasteEnd, b[6:] + } + + // If we get here then we have a key that we don't recognise, or a + // partial sequence. It's not clear how one should find the end of a + // sequence without knowing them all, but it seems that [a-zA-Z~] only + // appears at the end of a sequence. + for i, c := range b[0:] { + if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '~' { + return keyUnknown, b[i+1:] + } + } + + return utf8.RuneError, b +} + +// queue appends data to the end of t.outBuf +func (t *Terminal) queue(data []rune) { + t.outBuf = append(t.outBuf, []byte(string(data))...) +} + +var eraseUnderCursor = []rune{' ', keyEscape, '[', 'D'} +var space = []rune{' '} + +func isPrintable(key rune) bool { + isInSurrogateArea := key >= 0xd800 && key <= 0xdbff + return key >= 32 && !isInSurrogateArea +} + +// moveCursorToPos appends data to t.outBuf which will move the cursor to the +// given, logical position in the text. +func (t *Terminal) moveCursorToPos(pos int) { + if !t.echo { + return + } + + x := visualLength(t.prompt) + pos + y := x / t.termWidth + x = x % t.termWidth + + up := 0 + if y < t.cursorY { + up = t.cursorY - y + } + + down := 0 + if y > t.cursorY { + down = y - t.cursorY + } + + left := 0 + if x < t.cursorX { + left = t.cursorX - x + } + + right := 0 + if x > t.cursorX { + right = x - t.cursorX + } + + t.cursorX = x + t.cursorY = y + t.move(up, down, left, right) +} + +func (t *Terminal) move(up, down, left, right int) { + movement := make([]rune, 3*(up+down+left+right)) + m := movement + for i := 0; i < up; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'A' + m = m[3:] + } + for i := 0; i < down; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'B' + m = m[3:] + } + for i := 0; i < left; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'D' + m = m[3:] + } + for i := 0; i < right; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'C' + m = m[3:] + } + + t.queue(movement) +} + +func (t *Terminal) clearLineToRight() { + op := []rune{keyEscape, '[', 'K'} + t.queue(op) +} + +const maxLineLength = 4096 + +func (t *Terminal) setLine(newLine []rune, newPos int) { + if t.echo { + t.moveCursorToPos(0) + t.writeLine(newLine) + for i := len(newLine); i < len(t.line); i++ { + t.writeLine(space) + } + t.moveCursorToPos(newPos) + } + t.line = newLine + t.pos = newPos +} + +func (t *Terminal) advanceCursor(places int) { + t.cursorX += places + t.cursorY += t.cursorX / t.termWidth + if t.cursorY > t.maxLine { + t.maxLine = t.cursorY + } + t.cursorX = t.cursorX % t.termWidth + + if places > 0 && t.cursorX == 0 { + // Normally terminals will advance the current position + // when writing a character. But that doesn't happen + // for the last character in a line. However, when + // writing a character (except a new line) that causes + // a line wrap, the position will be advanced two + // places. + // + // So, if we are stopping at the end of a line, we + // need to write a newline so that our cursor can be + // advanced to the next line. + t.outBuf = append(t.outBuf, '\r', '\n') + } +} + +func (t *Terminal) eraseNPreviousChars(n int) { + if n == 0 { + return + } + + if t.pos < n { + n = t.pos + } + t.pos -= n + t.moveCursorToPos(t.pos) + + copy(t.line[t.pos:], t.line[n+t.pos:]) + t.line = t.line[:len(t.line)-n] + if t.echo { + t.writeLine(t.line[t.pos:]) + for i := 0; i < n; i++ { + t.queue(space) + } + t.advanceCursor(n) + t.moveCursorToPos(t.pos) + } +} + +// countToLeftWord returns then number of characters from the cursor to the +// start of the previous word. +func (t *Terminal) countToLeftWord() int { + if t.pos == 0 { + return 0 + } + + pos := t.pos - 1 + for pos > 0 { + if t.line[pos] != ' ' { + break + } + pos-- + } + for pos > 0 { + if t.line[pos] == ' ' { + pos++ + break + } + pos-- + } + + return t.pos - pos +} + +// countToRightWord returns then number of characters from the cursor to the +// start of the next word. +func (t *Terminal) countToRightWord() int { + pos := t.pos + for pos < len(t.line) { + if t.line[pos] == ' ' { + break + } + pos++ + } + for pos < len(t.line) { + if t.line[pos] != ' ' { + break + } + pos++ + } + return pos - t.pos +} + +// visualLength returns the number of visible glyphs in s. +func visualLength(runes []rune) int { + inEscapeSeq := false + length := 0 + + for _, r := range runes { + switch { + case inEscapeSeq: + if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') { + inEscapeSeq = false + } + case r == '\x1b': + inEscapeSeq = true + default: + length++ + } + } + + return length +} + +// handleKey processes the given key and, optionally, returns a line of text +// that the user has entered. +func (t *Terminal) handleKey(key rune) (line string, ok bool) { + if t.pasteActive && key != keyEnter { + t.addKeyToLine(key) + return + } + + switch key { + case keyBackspace: + if t.pos == 0 { + return + } + t.eraseNPreviousChars(1) + case keyAltLeft: + // move left by a word. + t.pos -= t.countToLeftWord() + t.moveCursorToPos(t.pos) + case keyAltRight: + // move right by a word. + t.pos += t.countToRightWord() + t.moveCursorToPos(t.pos) + case keyLeft: + if t.pos == 0 { + return + } + t.pos-- + t.moveCursorToPos(t.pos) + case keyRight: + if t.pos == len(t.line) { + return + } + t.pos++ + t.moveCursorToPos(t.pos) + case keyHome: + if t.pos == 0 { + return + } + t.pos = 0 + t.moveCursorToPos(t.pos) + case keyEnd: + if t.pos == len(t.line) { + return + } + t.pos = len(t.line) + t.moveCursorToPos(t.pos) + case keyUp: + entry, ok := t.history.NthPreviousEntry(t.historyIndex + 1) + if !ok { + return "", false + } + if t.historyIndex == -1 { + t.historyPending = string(t.line) + } + t.historyIndex++ + runes := []rune(entry) + t.setLine(runes, len(runes)) + case keyDown: + switch t.historyIndex { + case -1: + return + case 0: + runes := []rune(t.historyPending) + t.setLine(runes, len(runes)) + t.historyIndex-- + default: + entry, ok := t.history.NthPreviousEntry(t.historyIndex - 1) + if ok { + t.historyIndex-- + runes := []rune(entry) + t.setLine(runes, len(runes)) + } + } + case keyEnter: + t.moveCursorToPos(len(t.line)) + t.queue([]rune("\r\n")) + line = string(t.line) + ok = true + t.line = t.line[:0] + t.pos = 0 + t.cursorX = 0 + t.cursorY = 0 + t.maxLine = 0 + case keyDeleteWord: + // Delete zero or more spaces and then one or more characters. + t.eraseNPreviousChars(t.countToLeftWord()) + case keyDeleteLine: + // Delete everything from the current cursor position to the + // end of line. + for i := t.pos; i < len(t.line); i++ { + t.queue(space) + t.advanceCursor(1) + } + t.line = t.line[:t.pos] + t.moveCursorToPos(t.pos) + case keyCtrlD: + // Erase the character under the current position. + // The EOF case when the line is empty is handled in + // readLine(). + if t.pos < len(t.line) { + t.pos++ + t.eraseNPreviousChars(1) + } + case keyCtrlU: + t.eraseNPreviousChars(t.pos) + case keyClearScreen: + // Erases the screen and moves the cursor to the home position. + t.queue([]rune("\x1b[2J\x1b[H")) + t.queue(t.prompt) + t.cursorX, t.cursorY = 0, 0 + t.advanceCursor(visualLength(t.prompt)) + t.setLine(t.line, t.pos) + default: + if t.AutoCompleteCallback != nil { + prefix := string(t.line[:t.pos]) + suffix := string(t.line[t.pos:]) + + t.lock.Unlock() + newLine, newPos, completeOk := t.AutoCompleteCallback(prefix+suffix, len(prefix), key) + t.lock.Lock() + + if completeOk { + t.setLine([]rune(newLine), utf8.RuneCount([]byte(newLine)[:newPos])) + return + } + } + if !isPrintable(key) { + return + } + if len(t.line) == maxLineLength { + return + } + t.addKeyToLine(key) + } + return +} + +// addKeyToLine inserts the given key at the current position in the current +// line. +func (t *Terminal) addKeyToLine(key rune) { + if len(t.line) == cap(t.line) { + newLine := make([]rune, len(t.line), 2*(1+len(t.line))) + copy(newLine, t.line) + t.line = newLine + } + t.line = t.line[:len(t.line)+1] + copy(t.line[t.pos+1:], t.line[t.pos:]) + t.line[t.pos] = key + if t.echo { + t.writeLine(t.line[t.pos:]) + } + t.pos++ + t.moveCursorToPos(t.pos) +} + +func (t *Terminal) writeLine(line []rune) { + for len(line) != 0 { + remainingOnLine := t.termWidth - t.cursorX + todo := len(line) + if todo > remainingOnLine { + todo = remainingOnLine + } + t.queue(line[:todo]) + t.advanceCursor(visualLength(line[:todo])) + line = line[todo:] + } +} + +// writeWithCRLF writes buf to w but replaces all occurrences of \n with \r\n. +func writeWithCRLF(w io.Writer, buf []byte) (n int, err error) { + for len(buf) > 0 { + i := bytes.IndexByte(buf, '\n') + todo := len(buf) + if i >= 0 { + todo = i + } + + var nn int + nn, err = w.Write(buf[:todo]) + n += nn + if err != nil { + return n, err + } + buf = buf[todo:] + + if i >= 0 { + if _, err = w.Write(crlf); err != nil { + return n, err + } + n += 1 + buf = buf[1:] + } + } + + return n, nil +} + +func (t *Terminal) Write(buf []byte) (n int, err error) { + t.lock.Lock() + defer t.lock.Unlock() + + if t.cursorX == 0 && t.cursorY == 0 { + // This is the easy case: there's nothing on the screen that we + // have to move out of the way. + return writeWithCRLF(t.c, buf) + } + + // We have a prompt and possibly user input on the screen. We + // have to clear it first. + t.move(0 /* up */, 0 /* down */, t.cursorX /* left */, 0 /* right */) + t.cursorX = 0 + t.clearLineToRight() + + for t.cursorY > 0 { + t.move(1 /* up */, 0, 0, 0) + t.cursorY-- + t.clearLineToRight() + } + + if _, err = t.c.Write(t.outBuf); err != nil { + return + } + t.outBuf = t.outBuf[:0] + + if n, err = writeWithCRLF(t.c, buf); err != nil { + return + } + + t.writeLine(t.prompt) + if t.echo { + t.writeLine(t.line) + } + + t.moveCursorToPos(t.pos) + + if _, err = t.c.Write(t.outBuf); err != nil { + return + } + t.outBuf = t.outBuf[:0] + return +} + +// ReadPassword temporarily changes the prompt and reads a password, without +// echo, from the terminal. +func (t *Terminal) ReadPassword(prompt string) (line string, err error) { + t.lock.Lock() + defer t.lock.Unlock() + + oldPrompt := t.prompt + t.prompt = []rune(prompt) + t.echo = false + + line, err = t.readLine() + + t.prompt = oldPrompt + t.echo = true + + return +} + +// ReadLine returns a line of input from the terminal. +func (t *Terminal) ReadLine() (line string, err error) { + t.lock.Lock() + defer t.lock.Unlock() + + return t.readLine() +} + +func (t *Terminal) readLine() (line string, err error) { + // t.lock must be held at this point + + if t.cursorX == 0 && t.cursorY == 0 { + t.writeLine(t.prompt) + t.c.Write(t.outBuf) + t.outBuf = t.outBuf[:0] + } + + lineIsPasted := t.pasteActive + + for { + rest := t.remainder + lineOk := false + for !lineOk { + var key rune + key, rest = bytesToKey(rest, t.pasteActive) + if key == utf8.RuneError { + break + } + if !t.pasteActive { + if key == keyCtrlD { + if len(t.line) == 0 { + return "", io.EOF + } + } + if key == keyPasteStart { + t.pasteActive = true + if len(t.line) == 0 { + lineIsPasted = true + } + continue + } + } else if key == keyPasteEnd { + t.pasteActive = false + continue + } + if !t.pasteActive { + lineIsPasted = false + } + line, lineOk = t.handleKey(key) + } + if len(rest) > 0 { + n := copy(t.inBuf[:], rest) + t.remainder = t.inBuf[:n] + } else { + t.remainder = nil + } + t.c.Write(t.outBuf) + t.outBuf = t.outBuf[:0] + if lineOk { + if t.echo { + t.historyIndex = -1 + t.history.Add(line) + } + if lineIsPasted { + err = ErrPasteIndicator + } + return + } + + // t.remainder is a slice at the beginning of t.inBuf + // containing a partial key sequence + readBuf := t.inBuf[len(t.remainder):] + var n int + + t.lock.Unlock() + n, err = t.c.Read(readBuf) + t.lock.Lock() + + if err != nil { + return + } + + t.remainder = t.inBuf[:n+len(t.remainder)] + } +} + +// SetPrompt sets the prompt to be used when reading subsequent lines. +func (t *Terminal) SetPrompt(prompt string) { + t.lock.Lock() + defer t.lock.Unlock() + + t.prompt = []rune(prompt) +} + +func (t *Terminal) clearAndRepaintLinePlusNPrevious(numPrevLines int) { + // Move cursor to column zero at the start of the line. + t.move(t.cursorY, 0, t.cursorX, 0) + t.cursorX, t.cursorY = 0, 0 + t.clearLineToRight() + for t.cursorY < numPrevLines { + // Move down a line + t.move(0, 1, 0, 0) + t.cursorY++ + t.clearLineToRight() + } + // Move back to beginning. + t.move(t.cursorY, 0, 0, 0) + t.cursorX, t.cursorY = 0, 0 + + t.queue(t.prompt) + t.advanceCursor(visualLength(t.prompt)) + t.writeLine(t.line) + t.moveCursorToPos(t.pos) +} + +func (t *Terminal) SetSize(width, height int) error { + t.lock.Lock() + defer t.lock.Unlock() + + if width == 0 { + width = 1 + } + + oldWidth := t.termWidth + t.termWidth, t.termHeight = width, height + + switch { + case width == oldWidth: + // If the width didn't change then nothing else needs to be + // done. + return nil + case len(t.line) == 0 && t.cursorX == 0 && t.cursorY == 0: + // If there is nothing on current line and no prompt printed, + // just do nothing + return nil + case width < oldWidth: + // Some terminals (e.g. xterm) will truncate lines that were + // too long when shinking. Others, (e.g. gnome-terminal) will + // attempt to wrap them. For the former, repainting t.maxLine + // works great, but that behaviour goes badly wrong in the case + // of the latter because they have doubled every full line. + + // We assume that we are working on a terminal that wraps lines + // and adjust the cursor position based on every previous line + // wrapping and turning into two. This causes the prompt on + // xterms to move upwards, which isn't great, but it avoids a + // huge mess with gnome-terminal. + if t.cursorX >= t.termWidth { + t.cursorX = t.termWidth - 1 + } + t.cursorY *= 2 + t.clearAndRepaintLinePlusNPrevious(t.maxLine * 2) + case width > oldWidth: + // If the terminal expands then our position calculations will + // be wrong in the future because we think the cursor is + // |t.pos| chars into the string, but there will be a gap at + // the end of any wrapped line. + // + // But the position will actually be correct until we move, so + // we can move back to the beginning and repaint everything. + t.clearAndRepaintLinePlusNPrevious(t.maxLine) + } + + _, err := t.c.Write(t.outBuf) + t.outBuf = t.outBuf[:0] + return err +} + +type pasteIndicatorError struct{} + +func (pasteIndicatorError) Error() string { + return "terminal: ErrPasteIndicator not correctly handled" +} + +// ErrPasteIndicator may be returned from ReadLine as the error, in addition +// to valid line data. It indicates that bracketed paste mode is enabled and +// that the returned line consists only of pasted data. Programs may wish to +// interpret pasted data more literally than typed data. +var ErrPasteIndicator = pasteIndicatorError{} + +// SetBracketedPasteMode requests that the terminal bracket paste operations +// with markers. Not all terminals support this but, if it is supported, then +// enabling this mode will stop any autocomplete callback from running due to +// pastes. Additionally, any lines that are completely pasted will be returned +// from ReadLine with the error set to ErrPasteIndicator. +func (t *Terminal) SetBracketedPasteMode(on bool) { + if on { + io.WriteString(t.c, "\x1b[?2004h") + } else { + io.WriteString(t.c, "\x1b[?2004l") + } +} + +// stRingBuffer is a ring buffer of strings. +type stRingBuffer struct { + // entries contains max elements. + entries []string + max int + // head contains the index of the element most recently added to the ring. + head int + // size contains the number of elements in the ring. + size int +} + +func (s *stRingBuffer) Add(a string) { + if s.entries == nil { + const defaultNumEntries = 100 + s.entries = make([]string, defaultNumEntries) + s.max = defaultNumEntries + } + + s.head = (s.head + 1) % s.max + s.entries[s.head] = a + if s.size < s.max { + s.size++ + } +} + +// NthPreviousEntry returns the value passed to the nth previous call to Add. +// If n is zero then the immediately prior value is returned, if one, then the +// next most recent, and so on. If such an element doesn't exist then ok is +// false. +func (s *stRingBuffer) NthPreviousEntry(n int) (value string, ok bool) { + if n >= s.size { + return "", false + } + index := s.head - n + if index < 0 { + index += s.max + } + return s.entries[index], true +} + +// readPasswordLine reads from reader until it finds \n or io.EOF. +// The slice returned does not include the \n. +// readPasswordLine also ignores any \r it finds. +func readPasswordLine(reader io.Reader) ([]byte, error) { + var buf [1]byte + var ret []byte + + for { + n, err := reader.Read(buf[:]) + if n > 0 { + switch buf[0] { + case '\n': + return ret, nil + case '\r': + // remove \r from passwords on Windows + default: + ret = append(ret, buf[0]) + } + continue + } + if err != nil { + if err == io.EOF && len(ret) > 0 { + return ret, nil + } + return ret, err + } + } +} diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util.go b/vendor/golang.org/x/crypto/ssh/terminal/util.go new file mode 100644 index 000000000..d01919614 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/terminal/util.go @@ -0,0 +1,119 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd linux,!appengine netbsd openbsd + +// Package terminal provides support functions for dealing with terminals, as +// commonly found on UNIX systems. +// +// Putting a terminal into raw mode is the most common requirement: +// +// oldState, err := terminal.MakeRaw(0) +// if err != nil { +// panic(err) +// } +// defer terminal.Restore(0, oldState) +package terminal // import "golang.org/x/crypto/ssh/terminal" + +import ( + "syscall" + "unsafe" +) + +// State contains the state of a terminal. +type State struct { + termios syscall.Termios +} + +// IsTerminal returns true if the given file descriptor is a terminal. +func IsTerminal(fd int) bool { + var termios syscall.Termios + _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) + return err == 0 +} + +// MakeRaw put the terminal connected to the given file descriptor into raw +// mode and returns the previous state of the terminal so that it can be +// restored. +func MakeRaw(fd int) (*State, error) { + var oldState State + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 { + return nil, err + } + + newState := oldState.termios + // This attempts to replicate the behaviour documented for cfmakeraw in + // the termios(3) manpage. + newState.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON + newState.Oflag &^= syscall.OPOST + newState.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN + newState.Cflag &^= syscall.CSIZE | syscall.PARENB + newState.Cflag |= syscall.CS8 + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 { + return nil, err + } + + return &oldState, nil +} + +// GetState returns the current state of a terminal which may be useful to +// restore the terminal after a signal. +func GetState(fd int) (*State, error) { + var oldState State + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 { + return nil, err + } + + return &oldState, nil +} + +// Restore restores the terminal connected to the given file descriptor to a +// previous state. +func Restore(fd int, state *State) error { + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0); err != 0 { + return err + } + return nil +} + +// GetSize returns the dimensions of the given terminal. +func GetSize(fd int) (width, height int, err error) { + var dimensions [4]uint16 + + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&dimensions)), 0, 0, 0); err != 0 { + return -1, -1, err + } + return int(dimensions[1]), int(dimensions[0]), nil +} + +// passwordReader is an io.Reader that reads from a specific file descriptor. +type passwordReader int + +func (r passwordReader) Read(buf []byte) (int, error) { + return syscall.Read(int(r), buf) +} + +// ReadPassword reads a line of input from a terminal without local echo. This +// is commonly used for inputting passwords and other sensitive data. The slice +// returned does not include the \n. +func ReadPassword(fd int) ([]byte, error) { + var oldState syscall.Termios + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0); err != 0 { + return nil, err + } + + newState := oldState + newState.Lflag &^= syscall.ECHO + newState.Lflag |= syscall.ICANON | syscall.ISIG + newState.Iflag |= syscall.ICRNL + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 { + return nil, err + } + + defer func() { + syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0) + }() + + return readPasswordLine(passwordReader(fd)) +} diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_bsd.go b/vendor/golang.org/x/crypto/ssh/terminal/util_bsd.go new file mode 100644 index 000000000..9c1ffd145 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/terminal/util_bsd.go @@ -0,0 +1,12 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd netbsd openbsd + +package terminal + +import "syscall" + +const ioctlReadTermios = syscall.TIOCGETA +const ioctlWriteTermios = syscall.TIOCSETA diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_linux.go b/vendor/golang.org/x/crypto/ssh/terminal/util_linux.go new file mode 100644 index 000000000..5883b22d7 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/terminal/util_linux.go @@ -0,0 +1,11 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package terminal + +// These constants are declared here, rather than importing +// them from the syscall package as some syscall packages, even +// on linux, for example gccgo, do not declare them. +const ioctlReadTermios = 0x5401 // syscall.TCGETS +const ioctlWriteTermios = 0x5402 // syscall.TCSETS diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go b/vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go new file mode 100644 index 000000000..799f049f0 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go @@ -0,0 +1,58 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package terminal provides support functions for dealing with terminals, as +// commonly found on UNIX systems. +// +// Putting a terminal into raw mode is the most common requirement: +// +// oldState, err := terminal.MakeRaw(0) +// if err != nil { +// panic(err) +// } +// defer terminal.Restore(0, oldState) +package terminal + +import ( + "fmt" + "runtime" +) + +type State struct{} + +// IsTerminal returns true if the given file descriptor is a terminal. +func IsTerminal(fd int) bool { + return false +} + +// MakeRaw put the terminal connected to the given file descriptor into raw +// mode and returns the previous state of the terminal so that it can be +// restored. +func MakeRaw(fd int) (*State, error) { + return nil, fmt.Errorf("terminal: MakeRaw not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) +} + +// GetState returns the current state of a terminal which may be useful to +// restore the terminal after a signal. +func GetState(fd int) (*State, error) { + return nil, fmt.Errorf("terminal: GetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) +} + +// Restore restores the terminal connected to the given file descriptor to a +// previous state. +func Restore(fd int, state *State) error { + return fmt.Errorf("terminal: Restore not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) +} + +// GetSize returns the dimensions of the given terminal. +func GetSize(fd int) (width, height int, err error) { + return 0, 0, fmt.Errorf("terminal: GetSize not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) +} + +// ReadPassword reads a line of input from a terminal without local echo. This +// is commonly used for inputting passwords and other sensitive data. The slice +// returned does not include the \n. +func ReadPassword(fd int) ([]byte, error) { + return nil, fmt.Errorf("terminal: ReadPassword not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) +} diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go b/vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go new file mode 100644 index 000000000..07eb5edd7 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go @@ -0,0 +1,73 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build solaris + +package terminal // import "golang.org/x/crypto/ssh/terminal" + +import ( + "golang.org/x/sys/unix" + "io" + "syscall" +) + +// State contains the state of a terminal. +type State struct { + termios syscall.Termios +} + +// IsTerminal returns true if the given file descriptor is a terminal. +func IsTerminal(fd int) bool { + // see: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c + var termio unix.Termio + err := unix.IoctlSetTermio(fd, unix.TCGETA, &termio) + return err == nil +} + +// ReadPassword reads a line of input from a terminal without local echo. This +// is commonly used for inputting passwords and other sensitive data. The slice +// returned does not include the \n. +func ReadPassword(fd int) ([]byte, error) { + // see also: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libast/common/uwin/getpass.c + val, err := unix.IoctlGetTermios(fd, unix.TCGETS) + if err != nil { + return nil, err + } + oldState := *val + + newState := oldState + newState.Lflag &^= syscall.ECHO + newState.Lflag |= syscall.ICANON | syscall.ISIG + newState.Iflag |= syscall.ICRNL + err = unix.IoctlSetTermios(fd, unix.TCSETS, &newState) + if err != nil { + return nil, err + } + + defer unix.IoctlSetTermios(fd, unix.TCSETS, &oldState) + + var buf [16]byte + var ret []byte + for { + n, err := syscall.Read(fd, buf[:]) + if err != nil { + return nil, err + } + if n == 0 { + if len(ret) == 0 { + return nil, io.EOF + } + break + } + if buf[n-1] == '\n' { + n-- + } + ret = append(ret, buf[:n]...) + if n < len(buf) { + break + } + } + + return ret, nil +} diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_windows.go b/vendor/golang.org/x/crypto/ssh/terminal/util_windows.go new file mode 100644 index 000000000..e0a1f36ce --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/terminal/util_windows.go @@ -0,0 +1,155 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +// Package terminal provides support functions for dealing with terminals, as +// commonly found on UNIX systems. +// +// Putting a terminal into raw mode is the most common requirement: +// +// oldState, err := terminal.MakeRaw(0) +// if err != nil { +// panic(err) +// } +// defer terminal.Restore(0, oldState) +package terminal + +import ( + "syscall" + "unsafe" +) + +const ( + enableLineInput = 2 + enableEchoInput = 4 + enableProcessedInput = 1 + enableWindowInput = 8 + enableMouseInput = 16 + enableInsertMode = 32 + enableQuickEditMode = 64 + enableExtendedFlags = 128 + enableAutoPosition = 256 + enableProcessedOutput = 1 + enableWrapAtEolOutput = 2 +) + +var kernel32 = syscall.NewLazyDLL("kernel32.dll") + +var ( + procGetConsoleMode = kernel32.NewProc("GetConsoleMode") + procSetConsoleMode = kernel32.NewProc("SetConsoleMode") + procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") +) + +type ( + short int16 + word uint16 + + coord struct { + x short + y short + } + smallRect struct { + left short + top short + right short + bottom short + } + consoleScreenBufferInfo struct { + size coord + cursorPosition coord + attributes word + window smallRect + maximumWindowSize coord + } +) + +type State struct { + mode uint32 +} + +// IsTerminal returns true if the given file descriptor is a terminal. +func IsTerminal(fd int) bool { + var st uint32 + r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) + return r != 0 && e == 0 +} + +// MakeRaw put the terminal connected to the given file descriptor into raw +// mode and returns the previous state of the terminal so that it can be +// restored. +func MakeRaw(fd int) (*State, error) { + var st uint32 + _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) + if e != 0 { + return nil, error(e) + } + raw := st &^ (enableEchoInput | enableProcessedInput | enableLineInput | enableProcessedOutput) + _, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(raw), 0) + if e != 0 { + return nil, error(e) + } + return &State{st}, nil +} + +// GetState returns the current state of a terminal which may be useful to +// restore the terminal after a signal. +func GetState(fd int) (*State, error) { + var st uint32 + _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) + if e != 0 { + return nil, error(e) + } + return &State{st}, nil +} + +// Restore restores the terminal connected to the given file descriptor to a +// previous state. +func Restore(fd int, state *State) error { + _, _, err := syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(state.mode), 0) + return err +} + +// GetSize returns the dimensions of the given terminal. +func GetSize(fd int) (width, height int, err error) { + var info consoleScreenBufferInfo + _, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&info)), 0) + if e != 0 { + return 0, 0, error(e) + } + return int(info.size.x), int(info.size.y), nil +} + +// passwordReader is an io.Reader that reads from a specific Windows HANDLE. +type passwordReader int + +func (r passwordReader) Read(buf []byte) (int, error) { + return syscall.Read(syscall.Handle(r), buf) +} + +// ReadPassword reads a line of input from a terminal without local echo. This +// is commonly used for inputting passwords and other sensitive data. The slice +// returned does not include the \n. +func ReadPassword(fd int) ([]byte, error) { + var st uint32 + _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) + if e != 0 { + return nil, error(e) + } + old := st + + st &^= (enableEchoInput) + st |= (enableProcessedInput | enableLineInput | enableProcessedOutput) + _, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(st), 0) + if e != 0 { + return nil, error(e) + } + + defer func() { + syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(old), 0) + }() + + return readPasswordLine(passwordReader(fd)) +} diff --git a/vendor/golang.org/x/sync/LICENSE b/vendor/golang.org/x/sync/LICENSE new file mode 100644 index 000000000..6a66aea5e --- /dev/null +++ b/vendor/golang.org/x/sync/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/sync/PATENTS b/vendor/golang.org/x/sync/PATENTS new file mode 100644 index 000000000..733099041 --- /dev/null +++ b/vendor/golang.org/x/sync/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google 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, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/sync/errgroup/errgroup.go b/vendor/golang.org/x/sync/errgroup/errgroup.go new file mode 100644 index 000000000..533438d91 --- /dev/null +++ b/vendor/golang.org/x/sync/errgroup/errgroup.go @@ -0,0 +1,67 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package errgroup provides synchronization, error propagation, and Context +// cancelation for groups of goroutines working on subtasks of a common task. +package errgroup + +import ( + "sync" + + "golang.org/x/net/context" +) + +// A Group is a collection of goroutines working on subtasks that are part of +// the same overall task. +// +// A zero Group is valid and does not cancel on error. +type Group struct { + cancel func() + + wg sync.WaitGroup + + errOnce sync.Once + err error +} + +// WithContext returns a new Group and an associated Context derived from ctx. +// +// The derived Context is canceled the first time a function passed to Go +// returns a non-nil error or the first time Wait returns, whichever occurs +// first. +func WithContext(ctx context.Context) (*Group, context.Context) { + ctx, cancel := context.WithCancel(ctx) + return &Group{cancel: cancel}, ctx +} + +// Wait blocks until all function calls from the Go method have returned, then +// returns the first non-nil error (if any) from them. +func (g *Group) Wait() error { + g.wg.Wait() + if g.cancel != nil { + g.cancel() + } + return g.err +} + +// Go calls the given function in a new goroutine. +// +// The first call to return a non-nil error cancels the group; its error will be +// returned by Wait. +func (g *Group) Go(f func() error) { + g.wg.Add(1) + + go func() { + defer g.wg.Done() + + if err := f(); err != nil { + g.errOnce.Do(func() { + g.err = err + if g.cancel != nil { + g.cancel() + } + }) + } + }() +} From 4c7a467e9e7ac5a69754a6d616cd5a0c98ce0f13 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 8 Feb 2018 11:53:05 -0800 Subject: [PATCH 0491/1216] format scaleway docs --- website/source/docs/builders/scaleway.html.md | 59 ++++++++++--------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/website/source/docs/builders/scaleway.html.md b/website/source/docs/builders/scaleway.html.md index efa26dbee..b1864bdb0 100644 --- a/website/source/docs/builders/scaleway.html.md +++ b/website/source/docs/builders/scaleway.html.md @@ -4,10 +4,10 @@ sidebar_current: docs-builders-scaleway page_title: Scaleway - Builders description: |- The Scaleway Packer builder is able to create new images for use with - Scaleway BareMetal and Virtual cloud server. The builder takes a source image, runs any provisioning - necessary on the image after launching it, then snapshots it into a reusable - image. This reusable image can then be used as the foundation of new servers - that are launched within Scaleway. + Scaleway BareMetal and Virtual cloud server. The builder takes a source + image, runs any provisioning necessary on the image after launching it, then + snapshots it into a reusable image. This reusable image can then be used as + the foundation of new servers that are launched within Scaleway. --- @@ -36,36 +36,40 @@ builder. ### Required: -- `api_access_key` (string) - The api_access_key to use to access your account. - It can also be specified via - environment variable `SCALEWAY_API_ACCESS_KEY`. - Your access key is available in the ["Credentials" section](https://cloud.scaleway.com/#/credentials) of the control panel. +- `api_access_key` (string) - The api\_access\_key to use to access your + account. It can also be specified via environment variable + `SCALEWAY_API_ACCESS_KEY`. Your access key is available in the + ["Credentials" section](https://cloud.scaleway.com/#/credentials) of the + control panel. -- `api_token` (string) - The organization TOKEN to use to access your account. - It can also be specified via - environment variable `SCALEWAY_API_TOKEN`. - Your tokens are available in the ["Credentials" section](https://cloud.scaleway.com/#/credentials) of the control panel. +- `api_token` (string) - The organization TOKEN to use to access your + account. It can also be specified via environment variable + `SCALEWAY_API_TOKEN`. Your tokens are available in the ["Credentials" + section](https://cloud.scaleway.com/#/credentials) of the control panel. -- `image` (string) - The UUID of the base image to use. This is the - image that will be used to launch a new server and provision it. See - [https://api-marketplace.scaleway.com/images](https://api-marketplace.scaleway.com/images) - get the complete list of the accepted image UUID. +- `image` (string) - The UUID of the base image to use. This is the image + that will be used to launch a new server and provision it. See + get the complete list of the + accepted image UUID. -- `region` (string) - The name of the region to launch the - server in (`par1` or `ams1`). Consequently, this is the region where the snapshot will - be available. +- `region` (string) - The name of the region to launch the server in (`par1` + or `ams1`). Consequently, this is the region where the snapshot will be + available. -- `commercial_type` (string) - The name of the server commercial type: `C1`, `C2S`, `C2M`, - `C2L`, `X64-2GB`, `X64-4GB`, `X64-8GB`, `X64-15GB`, `X64-30GB`, `X64-60GB`, `X64-120GB`, `ARM64-2GB`, `ARM64-4GB`, `ARM64-8GB`, `ARM64-16GB`, `ARM64-32GB`, `ARM64-64GB`, `ARM64-128GB` +- `commercial_type` (string) - The name of the server commercial type: `C1`, + `C2S`, `C2M`, `C2L`, `X64-2GB`, `X64-4GB`, `X64-8GB`, `X64-15GB`, + `X64-30GB`, `X64-60GB`, `X64-120GB`, `ARM64-2GB`, `ARM64-4GB`, `ARM64-8GB`, + `ARM64-16GB`, `ARM64-32GB`, `ARM64-64GB`, `ARM64-128GB` ### Optional: -- `server_name` (string) - The name assigned to the server. Default `packer-UUID` +- `server_name` (string) - The name assigned to the server. Default + `packer-UUID` -- `image_name` (string) - The name of the resulting image that will - appear in your account. Default `packer-TIMESTAMP` +- `image_name` (string) - The name of the resulting image that will appear in + your account. Default `packer-TIMESTAMP` -- `snapshot_name` (string) - The name of the resulting snapshot that will +- `snapshot_name` (string) - The name of the resulting snapshot that will appear in your account. Default `packer-TIMESTAMP` ## Basic Example @@ -86,5 +90,6 @@ access tokens: } ``` -When you do not specified the `ssh_private_key_file`, a temporarily SSH keypair is generated to connect the server. -This key will only allow the `root` user to connect the server. +When you do not specified the `ssh_private_key_file`, a temporarily SSH keypair +is generated to connect the server. This key will only allow the `root` user to +connect the server. From e5ab2062769b8d8232ed622f3c774d560af07647 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 8 Feb 2018 12:33:51 -0800 Subject: [PATCH 0492/1216] add our 3 new builders to changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8c3105e8..ada8d8e5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ ### IMPROVEMENTS: +* **New builder:** `scaleway` - The Scaleway Packer builder is able to create new images for use with Scaleway BareMetal and Virtual cloud server. [GH-4770] +* **New builder:** `ncloud` for building server images using the NAVER Cloud Platform. [GH-5791] +* **New builder:** `oci-classic` for building new custom images for use with Oracle Cloud Infrastructure Classic Compute. [GH-5819] * builder/docker: Remove credentials from being shown in the log. [GH-5666] * builder/triton: Triton RBAC is now supported. [GH-5741] * provisioner/ansible: Improve user retrieval. [GH-5758] From ad2e5f1f08c97058d135cb19c0ee484fc6c29399 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 8 Feb 2018 12:52:44 -0800 Subject: [PATCH 0493/1216] fail in oracle classic builder if user tries winrm since it doesn't work yet, and add attributes and attributes_file fields to oracle builder --- builder/oracle/classic/config.go | 16 ++++++++++ .../oracle/classic/step_create_instance.go | 30 +++++++++++++++++++ builder/oracle/classic/step_list_images.go | 30 ++++++++++++++++--- .../docs/builders/oracle-classic.html.md | 13 +++++++- 4 files changed, 84 insertions(+), 5 deletions(-) diff --git a/builder/oracle/classic/config.go b/builder/oracle/classic/config.go index 4cd7c42a7..52a08cbe9 100644 --- a/builder/oracle/classic/config.go +++ b/builder/oracle/classic/config.go @@ -3,6 +3,7 @@ package classic import ( "fmt" "net/url" + "os" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -27,6 +28,9 @@ type Config struct { Shape string `mapstructure:"shape"` SourceImageList string `mapstructure:"source_image_list"` DestImageList string `mapstructure:"dest_image_list"` + // Attributes and Atributes file are both optional and mutually exclusive. + Attributes string `mapstructure:"attributes"` + AttributesFile string `mapstructure:"attributes_file"` // Optional; if you don't enter anything, the image list description // will read "Packer-built image list" DestImageListDescription string `mapstructure:"image_description"` @@ -81,9 +85,21 @@ func NewConfig(raws ...interface{}) (*Config, error) { } } + if c.Attributes != "" && c.AttributesFile != "" { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("Only one of user_data or user_data_file can be specified.")) + } else if c.AttributesFile != "" { + if _, err := os.Stat(c.AttributesFile); err != nil { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("attributes_file not found: %s", c.AttributesFile)) + } + } + if es := c.Comm.Prepare(&c.ctx); len(es) > 0 { errs = packer.MultiErrorAppend(errs, es...) } + if c.Comm.Type == "winrm" { + err = fmt.Errorf("winRM is not supported with the oracle-classic builder yet.") + errs = packer.MultiErrorAppend(errs, err) + } if errs != nil && len(errs.Errors) > 0 { return nil, errs diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go index 041889ab1..cf4b043fe 100644 --- a/builder/oracle/classic/step_create_instance.go +++ b/builder/oracle/classic/step_create_instance.go @@ -2,7 +2,9 @@ package classic import ( "context" + "encoding/json" "fmt" + "io/ioutil" "github.com/hashicorp/go-oracle-terraform/compute" "github.com/hashicorp/packer/helper/multistep" @@ -30,6 +32,33 @@ func (s *stepCreateInstance) Run(_ context.Context, state multistep.StateBag) mu // get instances client instanceClient := client.Instances() + var data map[string]interface{} + + if config.Attributes != "" { + err := json.Unmarshal([]byte(config.Attributes), &data) + if err != nil { + err = fmt.Errorf("Problem parsing json from attributes: %s", err) + ui.Error(err.Error()) + state.Put("error", err) + return multistep.ActionHalt + } + } else if config.AttributesFile != "" { + fidata, err := ioutil.ReadFile(config.AttributesFile) + if err != nil { + err = fmt.Errorf("Problem reading attributes_file: %s", err) + ui.Error(err.Error()) + state.Put("error", err) + return multistep.ActionHalt + } + err = json.Unmarshal(fidata, &data) + if err != nil { + err = fmt.Errorf("Problem parsing json from attrinutes_file: %s", err) + ui.Error(err.Error()) + state.Put("error", err) + return multistep.ActionHalt + } + } + // Instances Input input := &compute.CreateInstanceInput{ Name: config.ImageName, @@ -37,6 +66,7 @@ func (s *stepCreateInstance) Run(_ context.Context, state multistep.StateBag) mu ImageList: config.SourceImageList, SSHKeys: []string{keyName}, Networking: map[string]compute.NetworkingInfo{"eth0": netInfo}, + Attributes: data, } instanceInfo, err := instanceClient.CreateInstance(input) diff --git a/builder/oracle/classic/step_list_images.go b/builder/oracle/classic/step_list_images.go index a99612c14..247b96146 100644 --- a/builder/oracle/classic/step_list_images.go +++ b/builder/oracle/classic/step_list_images.go @@ -11,11 +11,10 @@ import ( type stepListImages struct{} -func (s *stepListImages) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { - // get variables from state - ui := state.Get("ui").(packer.Ui) - config := state.Get("config").(*Config) +func (s *stepListImages) listForSSH(state multistep.StateBag) multistep.StepAction { client := state.Get("client").(*compute.ComputeClient) + config := state.Get("config").(*Config) + ui := state.Get("ui").(packer.Ui) ui.Say("Adding image to image list...") imageListClient := client.ImageList() @@ -98,6 +97,29 @@ func (s *stepListImages) Run(_ context.Context, state multistep.StateBag) multis return multistep.ActionContinue } +func (s *stepListImages) listForWinRM(state multistep.StateBag) multistep.StepAction { + // This is a placeholder function; we will never reach this because we already + // return an error when winRM is set when validating the Packer config. + ui := state.Get("ui").(packer.Ui) + err := fmt.Errorf("The Oracle Classic builder does not currently support winRM.") + ui.Error(err.Error()) + state.Put("error", err) + return multistep.ActionHalt +} + +func (s *stepListImages) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { + // get variables from state + config := state.Get("config").(*Config) + + var action multistep.StepAction + if config.Comm.Type == "winrm" { + action = s.listForWinRM(state) + } else if config.Comm.Type == "ssh" { + action = s.listForSSH(state) + } + return action +} + func (s *stepListImages) Cleanup(state multistep.StateBag) { // Nothing to do return diff --git a/website/source/docs/builders/oracle-classic.html.md b/website/source/docs/builders/oracle-classic.html.md index f8991b968..98db5f2aa 100644 --- a/website/source/docs/builders/oracle-classic.html.md +++ b/website/source/docs/builders/oracle-classic.html.md @@ -63,6 +63,16 @@ This builder currently only works with the SSH communicator. ### Optional + - `attributes` (string) - (string) - Attributes to apply when launching the + instance. Note that you need to be careful about escaping characters due to + the templates being JSON. It is often more convenient to use + `attributes_file`, instead. You may only define either `attributes` or + `attributes_file`, not both. + + - `attributes_file` (string) - Path to a json file that will be used for the + attributes when launching the instance. You may only define either + `attributes` or `attributes_file`, not both. + - `image_description` (string) - a description for your destination image list. If you don't provide one, Packer will provide a generic description. @@ -91,7 +101,8 @@ obfuscated; you will need to add a working `username`, `password`, "api_endpoint": "https://api-###.compute.###.oraclecloud.com/", "source_image_list": "/oracle/public/OL_7.2_UEKR4_x86_64", "shape": "oc3", - "image_name": "Packer_Builder_Test_{{timestamp}}" + "image_name": "Packer_Builder_Test_{{timestamp}}", + "attributes": "{\"userdata\": {\"pre-bootstrap\": {\"script\": [\"...\"]}}}", "dest_image_list": "Packer_Builder_Test_List" } ], From ff717c57844bdfb2a934f1a41f7b538596070a2e Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 8 Feb 2018 13:21:21 -0800 Subject: [PATCH 0494/1216] wrong place for differentiation between ssh and winrm --- builder/oracle/classic/step_list_images.go | 30 +++------------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/builder/oracle/classic/step_list_images.go b/builder/oracle/classic/step_list_images.go index 247b96146..a99612c14 100644 --- a/builder/oracle/classic/step_list_images.go +++ b/builder/oracle/classic/step_list_images.go @@ -11,10 +11,11 @@ import ( type stepListImages struct{} -func (s *stepListImages) listForSSH(state multistep.StateBag) multistep.StepAction { - client := state.Get("client").(*compute.ComputeClient) - config := state.Get("config").(*Config) +func (s *stepListImages) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { + // get variables from state ui := state.Get("ui").(packer.Ui) + config := state.Get("config").(*Config) + client := state.Get("client").(*compute.ComputeClient) ui.Say("Adding image to image list...") imageListClient := client.ImageList() @@ -97,29 +98,6 @@ func (s *stepListImages) listForSSH(state multistep.StateBag) multistep.StepActi return multistep.ActionContinue } -func (s *stepListImages) listForWinRM(state multistep.StateBag) multistep.StepAction { - // This is a placeholder function; we will never reach this because we already - // return an error when winRM is set when validating the Packer config. - ui := state.Get("ui").(packer.Ui) - err := fmt.Errorf("The Oracle Classic builder does not currently support winRM.") - ui.Error(err.Error()) - state.Put("error", err) - return multistep.ActionHalt -} - -func (s *stepListImages) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { - // get variables from state - config := state.Get("config").(*Config) - - var action multistep.StepAction - if config.Comm.Type == "winrm" { - action = s.listForWinRM(state) - } else if config.Comm.Type == "ssh" { - action = s.listForSSH(state) - } - return action -} - func (s *stepListImages) Cleanup(state multistep.StateBag) { // Nothing to do return From 7f631fcb772616af9d8da80d66f4a4133acdaa7f Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 8 Feb 2018 14:12:39 -0800 Subject: [PATCH 0495/1216] unpack attributes in oracle-classic builder earlier so that we error fast if there's an issue --- builder/oracle/classic/config.go | 28 +++++++++++++++++ .../oracle/classic/step_create_instance.go | 31 +------------------ 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/builder/oracle/classic/config.go b/builder/oracle/classic/config.go index 52a08cbe9..233a3a0d1 100644 --- a/builder/oracle/classic/config.go +++ b/builder/oracle/classic/config.go @@ -1,7 +1,9 @@ package classic import ( + "encoding/json" "fmt" + "io/ioutil" "net/url" "os" @@ -15,6 +17,7 @@ import ( type Config struct { common.PackerConfig `mapstructure:",squash"` Comm communicator.Config `mapstructure:",squash"` + attribs map[string]interface{} // Access config overrides Username string `mapstructure:"username"` @@ -105,5 +108,30 @@ func NewConfig(raws ...interface{}) (*Config, error) { return nil, errs } + // unpack attributes from json into config + var data map[string]interface{} + + if c.Attributes != "" { + err := json.Unmarshal([]byte(c.Attributes), &data) + if err != nil { + err = fmt.Errorf("Problem parsing json from attributes: %s", err) + packer.MultiErrorAppend(errs, err) + } + c.attribs = data + } else if c.AttributesFile != "" { + fidata, err := ioutil.ReadFile(c.AttributesFile) + if err != nil { + err = fmt.Errorf("Problem reading attributes_file: %s", err) + packer.MultiErrorAppend(errs, err) + } + err = json.Unmarshal(fidata, &data) + c.attribs = data + if err != nil { + err = fmt.Errorf("Problem parsing json from attrinutes_file: %s", err) + packer.MultiErrorAppend(errs, err) + } + c.attribs = data + } + return c, nil } diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go index cf4b043fe..cda67122c 100644 --- a/builder/oracle/classic/step_create_instance.go +++ b/builder/oracle/classic/step_create_instance.go @@ -2,9 +2,7 @@ package classic import ( "context" - "encoding/json" "fmt" - "io/ioutil" "github.com/hashicorp/go-oracle-terraform/compute" "github.com/hashicorp/packer/helper/multistep" @@ -32,33 +30,6 @@ func (s *stepCreateInstance) Run(_ context.Context, state multistep.StateBag) mu // get instances client instanceClient := client.Instances() - var data map[string]interface{} - - if config.Attributes != "" { - err := json.Unmarshal([]byte(config.Attributes), &data) - if err != nil { - err = fmt.Errorf("Problem parsing json from attributes: %s", err) - ui.Error(err.Error()) - state.Put("error", err) - return multistep.ActionHalt - } - } else if config.AttributesFile != "" { - fidata, err := ioutil.ReadFile(config.AttributesFile) - if err != nil { - err = fmt.Errorf("Problem reading attributes_file: %s", err) - ui.Error(err.Error()) - state.Put("error", err) - return multistep.ActionHalt - } - err = json.Unmarshal(fidata, &data) - if err != nil { - err = fmt.Errorf("Problem parsing json from attrinutes_file: %s", err) - ui.Error(err.Error()) - state.Put("error", err) - return multistep.ActionHalt - } - } - // Instances Input input := &compute.CreateInstanceInput{ Name: config.ImageName, @@ -66,7 +37,7 @@ func (s *stepCreateInstance) Run(_ context.Context, state multistep.StateBag) mu ImageList: config.SourceImageList, SSHKeys: []string{keyName}, Networking: map[string]compute.NetworkingInfo{"eth0": netInfo}, - Attributes: data, + Attributes: config.attribs, } instanceInfo, err := instanceClient.CreateInstance(input) From a9b48309c90ae67598af90b4398bbd16958a0abd Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 8 Feb 2018 14:48:04 -0800 Subject: [PATCH 0496/1216] update amazon authentication docs Correct docs to reflect new behavior. This is largely borrowed from terraform. --- website/source/docs/builders/amazon.html.md | 105 ++++++++++++++------ 1 file changed, 73 insertions(+), 32 deletions(-) diff --git a/website/source/docs/builders/amazon.html.md b/website/source/docs/builders/amazon.html.md index e77e4bf79..7e7b763c9 100644 --- a/website/source/docs/builders/amazon.html.md +++ b/website/source/docs/builders/amazon.html.md @@ -49,49 +49,90 @@ filesystem and data. -## Specifying Amazon Credentials +## Authentication -When you use any of the amazon builders, you must provide credentials to the API -in the form of an access key id and secret. These look like: +The AWS provider offers a flexible means of providing credentials for +authentication. The following methods are supported, in this order, and +explained below: - access key id: AKIAIOSFODNN7EXAMPLE - secret access key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY +- Static credentials +- Environment variables +- Shared credentials file +- EC2 Role -If you use other AWS tools you may already have these configured. If so, packer -will try to use them, *unless* they are specified in your packer template. -Credentials are resolved in the following order: +### Static Credentials -1. Values hard-coded in the packer template are always authoritative. -2. *Variables* in the packer template may be resolved from command-line flags - or from environment variables. Please read about [User - Variables](https://www.packer.io/docs/templates/user-variables.html) - for details. -3. If no credentials are found, packer falls back to automatic lookup. +Static credentials can be provided in the form of an access key id and secret. +These look like: -### Automatic Lookup +```json +{ + "access_key": "AKIAIOSFODNN7EXAMPLE", + "secret_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + "region": "us-east-1", + "type": "amazon-ebs" +} +``` -Packer depends on the [AWS -SDK](https://aws.amazon.com/documentation/sdk-for-go/) to perform automatic -lookup using *credential chains*. In short, the SDK looks for credentials in -the following order: +### Environment variables -1. Environment variables. -2. Shared credentials file. -3. If your application is running on an Amazon EC2 instance, IAM role for Amazon EC2. +You can provide your credentials via the `AWS_ACCESS_KEY_ID` and +`AWS_SECRET_ACCESS_KEY`, environment variables, representing your AWS Access +Key and AWS Secret Key, respectively. Note that setting your AWS credentials +using either these environment variables will override the use of +`AWS_SHARED_CREDENTIALS_FILE` and `AWS_PROFILE`. The `AWS_DEFAULT_REGION` and +`AWS_SESSION_TOKEN` environment variables are also used, if applicable: -Please refer to the SDK's documentation on [specifying -credentials](https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials) -for more information. -## Using an IAM Task or Instance Role +Usage: -If AWS keys are not specified in the template, a -[shared credentials file](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html#cli-config-files) -or through environment variables Packer will use credentials provided by -the task's or instance's IAM role, if it has one. +``` +$ export AWS_ACCESS_KEY_ID="anaccesskey" +$ export AWS_SECRET_ACCESS_KEY="asecretkey" +$ export AWS_DEFAULT_REGION="us-west-2" +$ packer build packer.json +``` -The following policy document provides the minimal set permissions necessary for -Packer to work: +### Shared Credentials file + +You can use an AWS credentials file to specify your credentials. The default +location is $HOME/.aws/credentials on Linux and OS X, or +"%USERPROFILE%.aws\credentials" for Windows users. If we fail to detect +credentials inline, or in the environment, Packer will check this location. You +can optionally specify a different location in the configuration by setting the +environment with the `AWS_SHARED_CREDENTIALS_FILE` variable. + +You may also configure the profile to use by setting the `profile` +configuration option, or setting the `AWS_PROFILE` environment variable: + +```json +{ + "profile": "customprofile", + "region": "us-east-1", + "type": "amazon-ebs" +} +``` + + +### IAM Task or Instance Role + +Finally, Packer will use credentials provided by the task's or instance's IAM +role, if it has one. + +This is a preferred approach over any other when running in EC2 as you can +avoid hard coding credentials. Instead these are leased on-the-fly by Packer, +which reduces the chance of leakage. + +The default deadline for the EC2 metadata API endpoint is 100 milliseconds, +which can be overidden by setting the `AWS_METADATA_TIMEOUT` environment +variable. The variable expects a positive golang Time.Duration string, which is +a sequence of decimal numbers and a unit suffix; valid suffixes are `ns` +(nanoseconds), `us` (microseconds), `ms` (milliseconds), `s` (seconds), `m` +(minutes), and `h` (hours). Examples of valid inputs: `100ms`, `250ms`, `1s`, +`2.5s`, `2.5m`, `1m30s`. + +The following policy document provides the minimal set permissions necessary +for Packer to work: ``` json { From c11627fba7a0e88a921e7510e5203af3a1596b63 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 8 Feb 2018 14:52:51 -0800 Subject: [PATCH 0497/1216] document new skip_metadata_api_check option --- website/source/docs/builders/amazon-chroot.html.md | 6 ++++++ website/source/docs/builders/amazon-ebs.html.md | 6 ++++++ website/source/docs/builders/amazon-ebssurrogate.html.md | 6 ++++++ website/source/docs/builders/amazon-ebsvolume.html.md | 6 ++++++ website/source/docs/builders/amazon-instance.html.md | 6 ++++++ 5 files changed, 30 insertions(+) diff --git a/website/source/docs/builders/amazon-chroot.html.md b/website/source/docs/builders/amazon-chroot.html.md index a983305c2..1757b06bf 100644 --- a/website/source/docs/builders/amazon-chroot.html.md +++ b/website/source/docs/builders/amazon-chroot.html.md @@ -243,6 +243,12 @@ each category, the available configuration keys are alphabetized. of the `source_ami` unless `from_scratch` is `true`, in which case this field must be defined. +- `skip_metadata_api_check` - (boolean) Skip the AWS Metadata API check. + Useful for AWS API implementations that do not have a metadata API + endpoint. Setting to `true` prevents Packer from authenticating via the + Metadata API. You may need to use other authentication methods like static + credentials, configuration variables, or environment variables. + - `skip_region_validation` (boolean) - Set to true if you want to skip validation of the `ami_regions` configuration option. Default `false`. diff --git a/website/source/docs/builders/amazon-ebs.html.md b/website/source/docs/builders/amazon-ebs.html.md index 6b48621b4..c83698569 100644 --- a/website/source/docs/builders/amazon-ebs.html.md +++ b/website/source/docs/builders/amazon-ebs.html.md @@ -244,6 +244,12 @@ builder. in case Packer exits ungracefully. Possible values are "stop" and "terminate", default is `stop`. +- `skip_metadata_api_check` - (boolean) Skip the AWS Metadata API check. + Useful for AWS API implementations that do not have a metadata API + endpoint. Setting to `true` prevents Packer from authenticating via the + Metadata API. You may need to use other authentication methods like static + credentials, configuration variables, or environment variables. + - `skip_region_validation` (boolean) - Set to true if you want to skip validation of the region configuration option. Default `false`. diff --git a/website/source/docs/builders/amazon-ebssurrogate.html.md b/website/source/docs/builders/amazon-ebssurrogate.html.md index 7b995fa87..aeffe2f09 100644 --- a/website/source/docs/builders/amazon-ebssurrogate.html.md +++ b/website/source/docs/builders/amazon-ebssurrogate.html.md @@ -237,6 +237,12 @@ builder. incase packer exits ungracefully. Possible values are "stop" and "terminate", default is `stop`. +- `skip_metadata_api_check` - (boolean) Skip the AWS Metadata API check. + Useful for AWS API implementations that do not have a metadata API + endpoint. Setting to `true` prevents Packer from authenticating via the + Metadata API. You may need to use other authentication methods like static + credentials, configuration variables, or environment variables. + - `skip_region_validation` (boolean) - Set to true if you want to skip validation of the region configuration option. Default `false`. diff --git a/website/source/docs/builders/amazon-ebsvolume.html.md b/website/source/docs/builders/amazon-ebsvolume.html.md index b500aa2bd..174673ed0 100644 --- a/website/source/docs/builders/amazon-ebsvolume.html.md +++ b/website/source/docs/builders/amazon-ebsvolume.html.md @@ -156,6 +156,12 @@ builder. in case Packer exits ungracefully. Possible values are `stop` and `terminate`. Defaults to `stop`. +- `skip_metadata_api_check` - (boolean) Skip the AWS Metadata API check. + Useful for AWS API implementations that do not have a metadata API + endpoint. Setting to `true` prevents Packer from authenticating via the + Metadata API. You may need to use other authentication methods like static + credentials, configuration variables, or environment variables. + - `skip_region_validation` (boolean) - Set to `true` if you want to skip validation of the region configuration option. Defaults to `false`. diff --git a/website/source/docs/builders/amazon-instance.html.md b/website/source/docs/builders/amazon-instance.html.md index f8f72708a..299222837 100644 --- a/website/source/docs/builders/amazon-instance.html.md +++ b/website/source/docs/builders/amazon-instance.html.md @@ -248,6 +248,12 @@ builder. The default is `0.0.0.0/0` (ie, allow any IPv4 source). This is only used when `security_group_id` or `security_group_ids` is not specified. +- `skip_metadata_api_check` - (boolean) Skip the AWS Metadata API check. + Useful for AWS API implementations that do not have a metadata API + endpoint. Setting to `true` prevents Packer from authenticating via the + Metadata API. You may need to use other authentication methods like static + credentials, configuration variables, or environment variables. + - `skip_region_validation` (boolean) - Set to true if you want to skip validation of the region configuration option. Defaults to `false`. From 5af42ee9e285501c0ae8fdc1f2c2086ed1bf2fd1 Mon Sep 17 00:00:00 2001 From: SwampDragons Date: Thu, 8 Feb 2018 15:10:53 -0800 Subject: [PATCH 0498/1216] Revert "Add `winrm_no_proxy` option." --- helper/communicator/config.go | 9 +++---- helper/communicator/step_connect_test.go | 24 ----------------- helper/communicator/step_connect_winrm.go | 26 ------------------- .../docs/templates/communicator.html.md | 15 ++++------- 4 files changed, 9 insertions(+), 65 deletions(-) diff --git a/helper/communicator/config.go b/helper/communicator/config.go index 52b81a3a6..f2664b192 100644 --- a/helper/communicator/config.go +++ b/helper/communicator/config.go @@ -41,15 +41,14 @@ type Config struct { SSHReadWriteTimeout time.Duration `mapstructure:"ssh_read_write_timeout"` // WinRM - WinRMHost string `mapstructure:"winrm_host"` - WinRMInsecure bool `mapstructure:"winrm_insecure"` - WinRMNoProxy bool `mapstructure:"winrm_no_proxy"` + WinRMUser string `mapstructure:"winrm_username"` WinRMPassword string `mapstructure:"winrm_password"` + WinRMHost string `mapstructure:"winrm_host"` WinRMPort int `mapstructure:"winrm_port"` WinRMTimeout time.Duration `mapstructure:"winrm_timeout"` - WinRMUseNTLM bool `mapstructure:"winrm_use_ntlm"` WinRMUseSSL bool `mapstructure:"winrm_use_ssl"` - WinRMUser string `mapstructure:"winrm_username"` + WinRMInsecure bool `mapstructure:"winrm_insecure"` + WinRMUseNTLM bool `mapstructure:"winrm_use_ntlm"` WinRMTransportDecorator func() winrm.Transporter } diff --git a/helper/communicator/step_connect_test.go b/helper/communicator/step_connect_test.go index 6db32fba5..fb61f6463 100644 --- a/helper/communicator/step_connect_test.go +++ b/helper/communicator/step_connect_test.go @@ -3,12 +3,10 @@ package communicator import ( "bytes" "context" - "os" "testing" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" - "github.com/stretchr/testify/assert" ) func TestStepConnect_impl(t *testing.T) { @@ -31,28 +29,6 @@ func TestStepConnect_none(t *testing.T) { } } -var noProxyTests = []struct { - current string - expected string -}{ - {"", "foo:1"}, - {"foo:1", "foo:1"}, - {"foo:1,bar:2", "foo:1,bar:2"}, - {"bar:2", "bar:2,foo:1"}, -} - -func TestStepConnect_setNoProxy(t *testing.T) { - key := "NO_PROXY" - for _, tt := range noProxyTests { - if value := os.Getenv(key); value != "" { - os.Unsetenv(key) - defer func() { os.Setenv(key, value) }() - os.Setenv(key, tt.current) - assert.Equal(t, tt.expected, os.Getenv(key), "env not set correctly.") - } - } -} - func testState(t *testing.T) multistep.StateBag { state := new(multistep.BasicStateBag) state.Put("hook", &packer.MockHook{}) diff --git a/helper/communicator/step_connect_winrm.go b/helper/communicator/step_connect_winrm.go index cdda73a5a..06e6236f8 100644 --- a/helper/communicator/step_connect_winrm.go +++ b/helper/communicator/step_connect_winrm.go @@ -7,7 +7,6 @@ import ( "fmt" "io" "log" - "os" "strings" "time" @@ -129,12 +128,6 @@ func (s *StepConnectWinRM) waitForWinRM(state multistep.StateBag, cancel <-chan } } - if s.Config.WinRMNoProxy { - if err := setNoProxy(host, port); err != nil { - return nil, fmt.Errorf("Error setting no_proxy: %s", err) - } - } - log.Println("[INFO] Attempting WinRM connection...") comm, err = winrm.New(&winrm.Config{ Host: host, @@ -189,22 +182,3 @@ func (s *StepConnectWinRM) waitForWinRM(state multistep.StateBag, cancel <-chan return comm, nil } - -// setNoProxy configures the $NO_PROXY env var -func setNoProxy(host string, port int) error { - current := os.Getenv("NO_PROXY") - p := fmt.Sprintf("%s:%d", host, port) - // not set - // set - // is set and not contains - // set - // is set and contains - if current == "" { - return os.Setenv("NO_PROXY", p) - } - if !strings.Contains(current, p) { - return os.Setenv("NO_PROXY", strings.Join([]string{current, p}, ",")) - } - return nil - -} diff --git a/website/source/docs/templates/communicator.html.md b/website/source/docs/templates/communicator.html.md index db768a90a..0165ab5a1 100644 --- a/website/source/docs/templates/communicator.html.md +++ b/website/source/docs/templates/communicator.html.md @@ -139,21 +139,16 @@ The WinRM communicator has the following options. - `winrm_password` (string) - The password to use to connect to WinRM. -- `winrm_insecure` (boolean) - If true, do not check server certificate - chain and host name - -* `winrm_no_proxy` (boolean) - Setting this to `true` adds the remote - `host:post` to the `NO_PROXY` environment variable. This has the effect of - bypassing any configured proxies when connecting to the remote host. - Default to `false`. - - `winrm_timeout` (string) - The amount of time to wait for WinRM to become available. This defaults to "30m" since setting up a Windows machine generally takes a long time. +- `winrm_use_ssl` (boolean) - If true, use HTTPS for WinRM + +- `winrm_insecure` (boolean) - If true, do not check server certificate + chain and host name + - `winrm_use_ntlm` (boolean) - If true, NTLM authentication will be used for WinRM, rather than default (basic authentication), removing the requirement for basic authentication to be enabled within the target guest. Further reading for remote connection authentication can be found [here](https://msdn.microsoft.com/en-us/library/aa384295(v=vs.85).aspx). - -- `winrm_use_ssl` (boolean) - If true, use HTTPS for WinRM From 49f9d318377591bbba2de56395e6b8fa94020903 Mon Sep 17 00:00:00 2001 From: DanHam Date: Thu, 8 Feb 2018 23:15:15 +0000 Subject: [PATCH 0499/1216] Update docs to explain new auto escape of chars special to PowerShell --- .../docs/provisioners/powershell.html.md | 100 +++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/website/source/docs/provisioners/powershell.html.md b/website/source/docs/provisioners/powershell.html.md index ae897489f..a9c218638 100644 --- a/website/source/docs/provisioners/powershell.html.md +++ b/website/source/docs/provisioners/powershell.html.md @@ -56,7 +56,7 @@ Optional parameters: files, and Packer should therefore not convert Windows line endings to Unix line endings (if there are any). By default this is false. -- `elevated_execute_command` (string) - The command to use to execute the +- `elevated_execute_command` (string) - The command to use to execute the elevated script. By default this is as follows: ``` powershell @@ -125,3 +125,101 @@ commonly useful environmental variables: download large files over http. This may be useful if you're experiencing slower speeds using the default file provisioner. A file provisioner using the `winrm` communicator may experience these types of difficulties. + +## Packer's Handling of Characters Special to PowerShell + +The escape character in PowerShell is the `backtick`, also sometimes +referred to as the `grave accent`. When, and when not, to escape characters +special to PowerShell is probably best demonstrated with a series of examples. + +### When To Escape... + +Users need to deal with escaping characters special to PowerShell when they +appear *directly* in commands used in the `inline` PowerShell provisioner and +when they appear *directly* in the users own scripts. +Note that where double quotes appear within double quotes, the addition of +a backslash escape is required for the JSON template to be parsed correctly. + +``` json + "provisioners": [ + { + "type": "powershell", + "inline": [ + "Write-Host \"A literal dollar `$ must be escaped\"", + "Write-Host \"A literal backtick `` must be escaped\"", + "Write-Host \"Here `\"double quotes`\" must be escaped\"", + "Write-Host \"Here `'single quotes`' don`'t really need to be\"", + "Write-Host \"escaped... but it doesn`'t hurt to do so.\"", + ] + }, +``` + +The above snippet should result in the following output on the Packer console: + +``` +==> amazon-ebs: Provisioning with Powershell... +==> amazon-ebs: Provisioning with powershell script: /var/folders/15/d0f7gdg13rnd1cxp7tgmr55c0000gn/T/packer-powershell-provisioner508190439 + amazon-ebs: A literal dollar $ must be escaped + amazon-ebs: A literal backtick ` must be escaped + amazon-ebs: Here "double quotes" must be escaped + amazon-ebs: Here 'single quotes' don't really need to be + amazon-ebs: escaped... but it doesn't hurt to do so. +``` + +### When Not To Escape... + +Special characters appearing in user environment variable values and in the +`elevated_user` and `elevated_password` fields will be automatically +dealt with for the user. There is no need to use escapes in these instances. + +``` json +{ + "variables": { + "psvar": "My$tring" + }, + ... + "provisioners": [ + { + "type": "powershell", + "elevated_user": "Administrator", + "elevated_password": "Super$3cr3t!", + "inline": "Write-Output \"The dollar in the elevated_password is interpreted correctly\"" + }, + { + "type": "powershell", + "environment_vars": [ + "VAR1=A$Dollar", + "VAR2=A`Backtick", + "VAR3=A'SingleQuote", + "VAR4=A\"DoubleQuote", + "VAR5={{user `psvar`}}" + ], + "inline": [ + "Write-Output \"In the following examples the special character is interpreted correctly:\"", + "Write-Output \"The dollar in VAR1: $Env:VAR1\"", + "Write-Output \"The backtick in VAR2: $Env:VAR2\"", + "Write-Output \"The single quote in VAR3: $Env:VAR3\"", + "Write-Output \"The double quote in VAR4: $Env:VAR4\"", + "Write-Output \"The dollar in VAR5 (expanded from a user var): $Env:VAR5\"" + ] + } + ] + ... +} +``` + +The above snippet should result in the following output on the Packer console: + +``` +==> amazon-ebs: Provisioning with Powershell... +==> amazon-ebs: Provisioning with powershell script: /var/folders/15/d0f7gdg13rnd1cxp7tgmr55c0000gn/T/packer-powershell-provisioner961728919 + amazon-ebs: The dollar in the elevated_password is interpreted correctly +==> amazon-ebs: Provisioning with Powershell... +==> amazon-ebs: Provisioning with powershell script: /var/folders/15/d0f7gdg13rnd1cxp7tgmr55c0000gn/T/packer-powershell-provisioner142826554 + amazon-ebs: In the following examples the special character is interpreted correctly: + amazon-ebs: The dollar in VAR1: A$Dollar + amazon-ebs: The backtick in VAR2: A`Backtick + amazon-ebs: The single quote in VAR3: A'SingleQuote + amazon-ebs: The double quote in VAR4: A"DoubleQuote + amazon-ebs: The dollar in VAR5 (expanded from a user var): My$tring +``` From 19a89a101ec0a38fec88885eba4c0ba880a3bedc Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 8 Feb 2018 16:47:17 -0800 Subject: [PATCH 0500/1216] builder/amazon: remove ssh_private_ip ssh_private_ip should now be set through ssh_interface. Adds fixer to automatically fix existing json files --- builder/amazon/common/run_config.go | 9 --- fix/fixer.go | 2 + fix/fixer_amazon_enhanced_networking.go | 15 +++++ fix/fixer_amazon_enhanced_networking_test.go | 8 +-- fix/fixer_amazon_private_ip.go | 70 ++++++++++++++++++++ fix/fixer_amazon_private_ip_test.go | 64 ++++++++++++++++++ fix/fixer_amazon_shutdown_behavior.go | 4 +- 7 files changed, 158 insertions(+), 14 deletions(-) create mode 100644 fix/fixer_amazon_private_ip.go create mode 100644 fix/fixer_amazon_private_ip_test.go diff --git a/builder/amazon/common/run_config.go b/builder/amazon/common/run_config.go index 1b50b4842..289a461b9 100644 --- a/builder/amazon/common/run_config.go +++ b/builder/amazon/common/run_config.go @@ -53,7 +53,6 @@ type RunConfig struct { // Communicator settings Comm communicator.Config `mapstructure:",squash"` SSHKeyPairName string `mapstructure:"ssh_keypair_name"` - SSHPrivateIp bool `mapstructure:"ssh_private_ip"` SSHInterface string `mapstructure:"ssh_interface"` } @@ -78,14 +77,6 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { // Validation errs := c.Comm.Prepare(ctx) - if c.SSHPrivateIp && c.SSHInterface != "" { - errs = append(errs, errors.New("ssh_interface and ssh_private_ip should not both be specified")) - } - - // Legacy configurable - if c.SSHPrivateIp { - c.SSHInterface = "private_ip" - } // Valadating ssh_interface if c.SSHInterface != "public_ip" && diff --git a/fix/fixer.go b/fix/fixer.go index 5ca0b3a18..9f6247ee9 100644 --- a/fix/fixer.go +++ b/fix/fixer.go @@ -33,6 +33,7 @@ func init() { "manifest-filename": new(FixerManifestFilename), "amazon-shutdown_behavior": new(FixerAmazonShutdownBehavior), "amazon-enhanced-networking": new(FixerAmazonEnhancedNetworking), + "amazon-private-ip": new(FixerAmazonPrivateIP), "docker-email": new(FixerDockerEmail), } @@ -50,6 +51,7 @@ func init() { "manifest-filename", "amazon-shutdown_behavior", "amazon-enhanced-networking", + "amazon-private-ip", "docker-email", } } diff --git a/fix/fixer_amazon_enhanced_networking.go b/fix/fixer_amazon_enhanced_networking.go index 4c9330ebe..7188da7f7 100644 --- a/fix/fixer_amazon_enhanced_networking.go +++ b/fix/fixer_amazon_enhanced_networking.go @@ -1,6 +1,8 @@ package fix import ( + "strings" + "github.com/mitchellh/mapstructure" ) @@ -22,6 +24,19 @@ func (FixerAmazonEnhancedNetworking) Fix(input map[string]interface{}) (map[stri // Go through each builder and replace the enhanced_networking if we can for _, builder := range tpl.Builders { + builderTypeRaw, ok := builder["type"] + if !ok { + continue + } + + builderType, ok := builderTypeRaw.(string) + if !ok { + continue + } + + if !strings.HasPrefix(builderType, "amazon-") { + continue + } enhancedNetworkingRaw, ok := builder["enhanced_networking"] if !ok { continue diff --git a/fix/fixer_amazon_enhanced_networking_test.go b/fix/fixer_amazon_enhanced_networking_test.go index f8b5be178..78b58d582 100644 --- a/fix/fixer_amazon_enhanced_networking_test.go +++ b/fix/fixer_amazon_enhanced_networking_test.go @@ -17,12 +17,12 @@ func TestFixerAmazonEnhancedNetworking(t *testing.T) { // Attach field == false { Input: map[string]interface{}{ - "type": "ebs", + "type": "amazon-ebs", "enhanced_networking": false, }, Expected: map[string]interface{}{ - "type": "ebs", + "type": "amazon-ebs", "ena_support": false, }, }, @@ -30,12 +30,12 @@ func TestFixerAmazonEnhancedNetworking(t *testing.T) { // Attach field == true { Input: map[string]interface{}{ - "type": "ebs", + "type": "amazon-ebs", "enhanced_networking": true, }, Expected: map[string]interface{}{ - "type": "ebs", + "type": "amazon-ebs", "ena_support": true, }, }, diff --git a/fix/fixer_amazon_private_ip.go b/fix/fixer_amazon_private_ip.go new file mode 100644 index 000000000..7bfce1291 --- /dev/null +++ b/fix/fixer_amazon_private_ip.go @@ -0,0 +1,70 @@ +package fix + +import ( + "log" + "strings" + + "github.com/mitchellh/mapstructure" +) + +// FixerAmazonPrivateIP is a Fixer that replaces instances of `"private_ip": +// true` with `"ssh_interface": "private_ip"` +type FixerAmazonPrivateIP struct{} + +func (FixerAmazonPrivateIP) Fix(input map[string]interface{}) (map[string]interface{}, error) { + type template struct { + Builders []map[string]interface{} + } + + // Decode the input into our structure, if we can + var tpl template + if err := mapstructure.Decode(input, &tpl); err != nil { + return nil, err + } + + // Go through each builder and replace the enhanced_networking if we can + for _, builder := range tpl.Builders { + builderTypeRaw, ok := builder["type"] + if !ok { + continue + } + + builderType, ok := builderTypeRaw.(string) + if !ok { + continue + } + + if !strings.HasPrefix(builderType, "amazon-") { + continue + } + + // if ssh_interface already set, do nothing + if _, ok := builder["ssh_interface"]; ok { + continue + } + + privateIPi, ok := builder["ssh_private_ip"] + if !ok { + continue + } + privateIP, ok := privateIPi.(bool) + if !ok { + log.Fatalf("Wrong type for ssh_private_ip") + continue + } + + delete(builder, "ssh_private_ip") + if privateIP { + builder["ssh_interface"] = "private_ip" + } else { + builder["ssh_interface"] = "public_ip" + } + } + + input["builders"] = tpl.Builders + return input, nil +} + +func (FixerAmazonPrivateIP) Synopsis() string { + return "Replaces `\"ssh_private_ip\": true` in amazon builders with `\"ssh_interface\": \"private_ip\"`" +} diff --git a/fix/fixer_amazon_private_ip_test.go b/fix/fixer_amazon_private_ip_test.go new file mode 100644 index 000000000..554584ded --- /dev/null +++ b/fix/fixer_amazon_private_ip_test.go @@ -0,0 +1,64 @@ +package fix + +import ( + "reflect" + "testing" +) + +func TestFixerAmazonPrivateIP_Impl(t *testing.T) { + var _ Fixer = new(FixerAmazonPrivateIP) +} + +func TestFixerAmazonPrivateIP(t *testing.T) { + cases := []struct { + Input map[string]interface{} + Expected map[string]interface{} + }{ + // Attach field == false + { + Input: map[string]interface{}{ + "type": "amazon-ebs", + "ssh_private_ip": false, + }, + + Expected: map[string]interface{}{ + "type": "amazon-ebs", + "ssh_interface": "public_ip", + }, + }, + + // Attach field == true + { + Input: map[string]interface{}{ + "type": "amazon-ebs", + "ssh_private_ip": true, + }, + + Expected: map[string]interface{}{ + "type": "amazon-ebs", + "ssh_interface": "private_ip", + }, + }, + } + + for _, tc := range cases { + var f FixerAmazonPrivateIP + + input := map[string]interface{}{ + "builders": []map[string]interface{}{tc.Input}, + } + + expected := map[string]interface{}{ + "builders": []map[string]interface{}{tc.Expected}, + } + + output, err := f.Fix(input) + if err != nil { + t.Fatalf("err: %s", err) + } + + if !reflect.DeepEqual(output, expected) { + t.Fatalf("unexpected: %#v\nexpected: %#v\n", output, expected) + } + } +} diff --git a/fix/fixer_amazon_shutdown_behavior.go b/fix/fixer_amazon_shutdown_behavior.go index 2e47c5592..686ef5119 100644 --- a/fix/fixer_amazon_shutdown_behavior.go +++ b/fix/fixer_amazon_shutdown_behavior.go @@ -1,6 +1,8 @@ package fix import ( + "strings" + "github.com/mitchellh/mapstructure" ) @@ -31,7 +33,7 @@ func (FixerAmazonShutdownBehavior) Fix(input map[string]interface{}) (map[string continue } - if builderType != "amazon-ebs" && builderType != "amazon-ebsvolume" && builderType != "amazon-instance" && builderType != "amazon-chroot" { + if !strings.HasPrefix(builderType, "amazon-") { continue } From 87f8a2e8fe47f41d20b1c1b155772712737fee0e Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 9 Feb 2018 10:08:59 -0800 Subject: [PATCH 0501/1216] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ada8d8e5e..4b4fc6b10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * 3rd party plugins: We have moved internal dependencies, meaning your 3rd party plugins will no longer compile (however existing builds will still work fine); the work to fix them is minimal and documented in GH-5810. [GH-5810] * provisioner/file: We've made destination semantics more consistent across the various communicators. In general, if the destination is a directory, files will be uploaded into the directory instead of failing. This mirrors the behavior of `rsync`. There's a chance some users might be depending on the previous buggy behavior, so it's worth ensuring your configuration is correct. [GH-5426] * builder/openstack: Extension support has been removed. To use OpenStack builder with the OpenStack Newton (Oct 2016) or earlier, we recommend you use Packer v1.1.2 or earlier version. +* builder/amazon: The `ssh_private_ip` option has been removed. Instead, please use `"ssh_interface": "private"`. A fixer has been written for this, which can be invoked with `packer fix`. [GH-5876] ### IMPROVEMENTS: From b863b7ab9ea3c6b13fd2f8031bf81deeddcbb13d Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 9 Feb 2018 14:28:49 -0800 Subject: [PATCH 0502/1216] fix outgoing links --- .../assets/images/Adobe_PDF_file_icon_24x24.png | Bin 1288 -> 0 bytes website/source/docs/builders/vmware-iso.html.md | 8 ++++++-- 2 files changed, 6 insertions(+), 2 deletions(-) delete mode 100644 website/source/assets/images/Adobe_PDF_file_icon_24x24.png diff --git a/website/source/assets/images/Adobe_PDF_file_icon_24x24.png b/website/source/assets/images/Adobe_PDF_file_icon_24x24.png deleted file mode 100644 index a722e5b334140fcb2a527b44465dbf4ca6e05cd4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1288 zcmV+j1^4=iP)5)P}~C2Cc=S;&*9j zNffFDZ6Bl*O8Znwp|lTus1LpfT3TAc4+vN&eGoJ+l82T;tCmzu;tY<)iN>)rCNpzy z?#DT2@70HM?|8@Qh%VUd^Rf3@>%Z3jzt53NSL0Cy0PoSI+=?H3(~W=j@O6MQGXp?C zL{L#w6$1oA8%?3s403tvALrISi1YLFGTpPVuz)aQ=Qn#0BNnYLiTeQ2x`hY=>Rc7a zKvfX|)Zy|GHXN{i@#61#TU)<2v-Nhnt&K(_0?4HW`J1SMfR%=AMl0rA&3)<6VfsCh zMibOy{Y`KVZM7>n-Xj;+2;ch=@qwSj!8_+t@yT=R>+APzY;0WacDqhQf{*HMbged& z1v=lx-}V*U(i!CKW&Dw^QJ#Jk20i?BUndTSl&}09VuUP(^FxXoR=^3(dc8;*&2Opd z)7@_O`~7~um!_#`QI=u2xfurU_CmSvg`hwEZHV7^FqGHdg#7$bikt7Hxc(O8hreKy zwHTT(ECa(lFcK&;K@7&@JP9F~nH^nQTYEOo^Uw7AebZpLD7dJ&n2#Lep)1dyb5WJS zA09(H9jIpz0#Q6Mltdt6L<5TvRVAxs#2BT~Xp9dWIB<8CWe;_`U9>$KEU|& zOKd*=1Z5GZ9s4dN5M0W5=?wi7f29;hVNgWK4FaG7S(=fiDQTK&qtRe67|a3q(x{kI zgVEAyij56o3x=S&MqVKNU^$_^ndv)+!X?%*xTu&WlcM-)A2^{_G1bt0h*JhzO#B#E3SUkfi9Kj|ep9uf+9wNRm`KnK<4K z@UJn;6TnqC;qPQ zP(Jxb;;Dbb$_n=Wd&s2jyI8cR(+Nzoh-7svA5lK@1mWyyl4JKHJ|lnfPsqOggg2IH zJ^p*7-9bXwfx_>FKvhu@%%{Y~GlUmUP@Z^>+HW4geeUz zE{QS5k884c@4a(QcQDQE*|OOrZQ{%A3b44iNC<%o7cPuXo;>-N@p#;sCi+P&#u)4M ydTVuc^*_7*DrT$(w1Fm675Ee;KnZLD{r>^{G#E%4x}j_U0000 Virtual Disk Manager User's Guide for desktop VMware clients. + + Virtual Disk Manager User's Guide for desktop VMware clients. For ESXi, refer to the proper ESXi documentation. - `cdrom_adapter_type` (string) - The adapter type (or bus) that will be used @@ -216,7 +218,9 @@ builder. - `network_adapter_type` (string) - This is the ethernet adapter type the the virtual machine will be created with. By default the "e1000" network adapter type will be used by Packer. For more information, please consult the - Choosing a network adapter for your virtual machine for desktop VMware + + Choosing a network adapter for your virtual machine for desktop VMware clients. For ESXi, refer to the proper ESXi documentation. - `output_directory` (string) - This is the path to the directory where the From 593577c139bb57a468c414cd5c7b143469cf8a78 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 9 Feb 2018 15:01:44 -0800 Subject: [PATCH 0503/1216] populate missing lines from changelog --- CHANGELOG.md | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b4fc6b10..00abba155 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ### BACKWARDS INCOMPATIBILITIES: * core: Affects Windows guests: User variables containing Powershell special characters no longer need to be escaped.[GH-5376] -* provisioner/powershell: Regression from v1.1.1 forcing extra escaping of environment variables in the non-elevated provisioner has been fixed. [GH-5515] +* provisioner/powershell: Regression from v1.1.1 forcing extra escaping of environment variables in the non-elevated provisioner has been fixed. [GH-5515] [GH-5872] * 3rd party plugins: We have moved internal dependencies, meaning your 3rd party plugins will no longer compile (however existing builds will still work fine); the work to fix them is minimal and documented in GH-5810. [GH-5810] * provisioner/file: We've made destination semantics more consistent across the various communicators. In general, if the destination is a directory, files will be uploaded into the directory instead of failing. This mirrors the behavior of `rsync`. There's a chance some users might be depending on the previous buggy behavior, so it's worth ensuring your configuration is correct. [GH-5426] * builder/openstack: Extension support has been removed. To use OpenStack builder with the OpenStack Newton (Oct 2016) or earlier, we recommend you use Packer v1.1.2 or earlier version. @@ -33,17 +33,39 @@ * builder/amazon: Add Paris region (eu-west-3) [GH-5718] * builder/azure: Add validation for incorrect VHD URLs [GH-5695] * builder/amazon: Remove Session Token (STS) from being shown in the log. [GH-5665] +* post-processor/vsphere-template: Now accepts artifacts from the vSphere post-processor. [GH-5380] +* builder/vmware-iso: Add support for usb/serial/parallel ports. [GH-3417] +* builder/vmware-iso: Add support for virtual soundcards. [GH-3417] +* builder/vmware-iso: Add support for setting network type and network adapter type. [GH-3417] +* builder/vmware-iso: Add support for cdrom and disk adapter types. [GH-3417] +* builder/vmware-iso: More reliably retrieve the guest networking configuration. [GH-3417] +* builder/amazon: Timeout early if metadata service can't be reached. [GH-5764] +* builder/amazon: Report which authentication provider we're using. [GH-5764] +* builder/amazon: Give better error messages if we have trouble during authentication. [GH-5764] +* builder/amazon: Add `skip_metadata_api_check` option to skip consulting the amazon metadata service. [GH-5764] +* core: Improved support for downloading and validating a uri containing a Windows UNC path or a relative file:// scheme. [GH-2906] +* builder/google: Support specifying licenses for images. [GH-5842] +* post-processor/google-export: Synchronize credential semantics with the Google builder. [GH-4148] +* builder/vmware: Add support for "super" key in `boot_command`. [GH-5681] +* core: Gracefully clean up resources on SIGTERM. [GH-5318] +* builder/hyper-v: Allow MAC address specification. [GH-5709] +* communicator/ssh: Detect dead connections. [GH-4709] ### BUG FIXES: +* provisioner/ansible-remote: Fixes an error where Packer's private key can be overridden by inherited `ansible_ssh_private_key` options. [GH-5869] * builder/alicloud-ecs: Attach keypair before starting instance in alicloud builder [GH-5739] * builder/vmware: Fixed file handle leak that may have caused race conditions in vmware builder [GH-5767] -* provisioner/powershell: Regression from v1.1.1 forcing extra escaping of environment variables in the non-elevated provisioner has been fixed. [GH-5515] +* provisioner/powershell: Regression from v1.1.1 forcing extra escaping of environment variables in the non-elevated provisioner has been fixed. [GH-5515] [GH-5872] * provisioner/ansible: The "default extra variables" feature added in Packer v1.0.1 caused the ansible-local provisioner to fail when an --extra-vars argument was specified in the extra_arguments configuration option; this has been fixed. [GH-5335] * communicator/ssh: Add deadline to SSH connection to prevent Packer hangs after script provisioner reboots vm [GH-4684] * builder/virtualbox: Fix regression affecting users running Packer on a Windows host that kept Packer from finding Virtualbox guest additions if Packer ran on a different drive from the one where the guest additions were stored. [GH-5761] * builder/virtualbox: Fix interpolation ordering so that edge cases around guest_additions_url are handled correctly [GH-5757] * builder/amazon: NewSession now inherits MaxRetries and other settings. [GH-5719] +* builder/amazon: Fix tagging support when building in us-gov/china. [GH-5841] +* builder/vmware: Fix case where artifacts might not be cleaned up correctly. [GH-5835] +* provisioner/ansible-local: Fix support for `--extra-vars` in `extra_arguments`. [GH-5703] +* communicator/winrm: Fix issue copying empty directories. [GH-5763] ## 1.1.3 (December 8, 2017) From 034e722650794a98efd4dec48050fe4e175289c8 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 9 Feb 2018 15:02:32 -0800 Subject: [PATCH 0504/1216] format changelog --- CHANGELOG.md | 154 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 104 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00abba155..c9e38a60b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,71 +1,125 @@ ## (UNRELEASED) ### BACKWARDS INCOMPATIBILITIES: -* core: Affects Windows guests: User variables containing Powershell special characters no longer need to be escaped.[GH-5376] -* provisioner/powershell: Regression from v1.1.1 forcing extra escaping of environment variables in the non-elevated provisioner has been fixed. [GH-5515] [GH-5872] -* 3rd party plugins: We have moved internal dependencies, meaning your 3rd party plugins will no longer compile (however existing builds will still work fine); the work to fix them is minimal and documented in GH-5810. [GH-5810] -* provisioner/file: We've made destination semantics more consistent across the various communicators. In general, if the destination is a directory, files will be uploaded into the directory instead of failing. This mirrors the behavior of `rsync`. There's a chance some users might be depending on the previous buggy behavior, so it's worth ensuring your configuration is correct. [GH-5426] -* builder/openstack: Extension support has been removed. To use OpenStack builder with the OpenStack Newton (Oct 2016) or earlier, we recommend you use Packer v1.1.2 or earlier version. -* builder/amazon: The `ssh_private_ip` option has been removed. Instead, please use `"ssh_interface": "private"`. A fixer has been written for this, which can be invoked with `packer fix`. [GH-5876] +* 3rd party plugins: We have moved internal dependencies, meaning your 3rd + party plugins will no longer compile (however existing builds will still + work fine); the work to fix them is minimal and documented in GH-5810. + [GH-5810] +* builder/amazon: The `ssh_private_ip` option has been removed. Instead, please + use `"ssh_interface": "private"`. A fixer has been written for this, which + can be invoked with `packer fix`. [GH-5876] +* builder/openstack: Extension support has been removed. To use OpenStack + builder with the OpenStack Newton (Oct 2016) or earlier, we recommend you + use Packer v1.1.2 or earlier version. +* core: Affects Windows guests: User variables containing Powershell special + characters no longer need to be escaped.[GH-5376] +* provisioner/file: We've made destination semantics more consistent across the + various communicators. In general, if the destination is a directory, files + will be uploaded into the directory instead of failing. This mirrors the + behavior of `rsync`. There's a chance some users might be depending on the + previous buggy behavior, so it's worth ensuring your configuration is + correct. [GH-5426] +* provisioner/powershell: Regression from v1.1.1 forcing extra escaping of + environment variables in the non-elevated provisioner has been fixed. + [GH-5515] [GH-5872] ### IMPROVEMENTS: -* **New builder:** `scaleway` - The Scaleway Packer builder is able to create new images for use with Scaleway BareMetal and Virtual cloud server. [GH-4770] -* **New builder:** `ncloud` for building server images using the NAVER Cloud Platform. [GH-5791] -* **New builder:** `oci-classic` for building new custom images for use with Oracle Cloud Infrastructure Classic Compute. [GH-5819] -* builder/docker: Remove credentials from being shown in the log. [GH-5666] -* builder/triton: Triton RBAC is now supported. [GH-5741] -* provisioner/ansible: Improve user retrieval. [GH-5758] -* post-processor/docker: Remove credentials from being shown in the log. [GH-5666] -* builder/amazon: Warn during prepare if we didn't get both an access key and a secret key when we were expecting one. [GH-5762] -* builder/amazon: Replace `InstanceStatusOK` check with `InstanceReady`. This reduces build times universally while still working for all instance types. [GH-5678] +* **New builder:** `ncloud` for building server images using the NAVER Cloud + Platform. [GH-5791] +* **New builder:** `oci-classic` for building new custom images for use with + Oracle Cloud Infrastructure Classic Compute. [GH-5819] +* **New builder:** `scaleway` - The Scaleway Packer builder is able to create + new images for use with Scaleway BareMetal and Virtual cloud server. + [GH-4770] * builder/amazon: Add `kms_key_id` option to block device mappings. [GH-5774] -* builder/hyper-v: New option to use differential disks and Inline disk creation to improve build time and reduce disk usage [GH-5631] -* post-processor/vagrant: Add vagrant post-processor support for Google [GH-5732] -* provisioner/chef: Added Policyfile support to chef-client provisioner. [GH-5831] -* builder/qemu: Add Intel HAXM support to QEMU builder [GH-5738] -* communicator/ssh: Add session-level keep-alives [GH-5830] -* post-processor/amazon-import: Allow user to specify role name in amazon-import [GH-5817] -* provisioner/chef: Add support for 'trusted_certs_dir' chef-client configuration option [GH-5790] -* builder/triton: Updated triton-go dependencies, allowing better error handling. [GH-5795] -* core: Improved error logging in floppy file handling. [GH-5802] -* provisioner/amazon: Use Amazon SDK's InstanceRunning waiter instead of InstanceStatusOK waiter [GH-5773] +* builder/amazon: Add `skip_metadata_api_check` option to skip consulting the + amazon metadata service. [GH-5764] * builder/amazon: Add Paris region (eu-west-3) [GH-5718] +* builder/amazon: Give better error messages if we have trouble during + authentication. [GH-5764] +* builder/amazon: Remove Session Token (STS) from being shown in the log. + [GH-5665] +* builder/amazon: Replace `InstanceStatusOK` check with `InstanceReady`. This + reduces build times universally while still working for all instance types. + [GH-5678] +* builder/amazon: Report which authentication provider we're using. [GH-5764] +* builder/amazon: Timeout early if metadata service can't be reached. [GH-5764] +* builder/amazon: Warn during prepare if we didn't get both an access key and a + secret key when we were expecting one. [GH-5762] * builder/azure: Add validation for incorrect VHD URLs [GH-5695] -* builder/amazon: Remove Session Token (STS) from being shown in the log. [GH-5665] -* post-processor/vsphere-template: Now accepts artifacts from the vSphere post-processor. [GH-5380] +* builder/docker: Remove credentials from being shown in the log. [GH-5666] +* builder/google: Support specifying licenses for images. [GH-5842] +* builder/hyper-v: Allow MAC address specification. [GH-5709] +* builder/hyper-v: New option to use differential disks and Inline disk + creation to improve build time and reduce disk usage [GH-5631] +* builder/qemu: Add Intel HAXM support to QEMU builder [GH-5738] +* builder/triton: Triton RBAC is now supported. [GH-5741] +* builder/triton: Updated triton-go dependencies, allowing better error + handling. [GH-5795] +* builder/vmware-iso: Add support for cdrom and disk adapter types. [GH-3417] +* builder/vmware-iso: Add support for setting network type and network adapter + type. [GH-3417] * builder/vmware-iso: Add support for usb/serial/parallel ports. [GH-3417] * builder/vmware-iso: Add support for virtual soundcards. [GH-3417] -* builder/vmware-iso: Add support for setting network type and network adapter type. [GH-3417] -* builder/vmware-iso: Add support for cdrom and disk adapter types. [GH-3417] -* builder/vmware-iso: More reliably retrieve the guest networking configuration. [GH-3417] -* builder/amazon: Timeout early if metadata service can't be reached. [GH-5764] -* builder/amazon: Report which authentication provider we're using. [GH-5764] -* builder/amazon: Give better error messages if we have trouble during authentication. [GH-5764] -* builder/amazon: Add `skip_metadata_api_check` option to skip consulting the amazon metadata service. [GH-5764] -* core: Improved support for downloading and validating a uri containing a Windows UNC path or a relative file:// scheme. [GH-2906] -* builder/google: Support specifying licenses for images. [GH-5842] -* post-processor/google-export: Synchronize credential semantics with the Google builder. [GH-4148] +* builder/vmware-iso: More reliably retrieve the guest networking + configuration. [GH-3417] * builder/vmware: Add support for "super" key in `boot_command`. [GH-5681] -* core: Gracefully clean up resources on SIGTERM. [GH-5318] -* builder/hyper-v: Allow MAC address specification. [GH-5709] +* communicator/ssh: Add session-level keep-alives [GH-5830] * communicator/ssh: Detect dead connections. [GH-4709] +* core: Gracefully clean up resources on SIGTERM. [GH-5318] +* core: Improved error logging in floppy file handling. [GH-5802] +* core: Improved support for downloading and validating a uri containing a + Windows UNC path or a relative file:// scheme. [GH-2906] +* post-processor/amazon-import: Allow user to specify role name in amazon- + import [GH-5817] +* post-processor/docker: Remove credentials from being shown in the log. + [GH-5666] +* post-processor/google-export: Synchronize credential semantics with the + Google builder. [GH-4148] +* post-processor/vagrant: Add vagrant post-processor support for Google + [GH-5732] +* post-processor/vsphere-template: Now accepts artifacts from the vSphere post- + processor. [GH-5380] +* provisioner/amazon: Use Amazon SDK's InstanceRunning waiter instead of + InstanceStatusOK waiter [GH-5773] +* provisioner/ansible: Improve user retrieval. [GH-5758] +* provisioner/chef: Add support for 'trusted_certs_dir' chef-client + configuration option [GH-5790] +* provisioner/chef: Added Policyfile support to chef-client provisioner. + [GH-5831] ### BUG FIXES: -* provisioner/ansible-remote: Fixes an error where Packer's private key can be overridden by inherited `ansible_ssh_private_key` options. [GH-5869] -* builder/alicloud-ecs: Attach keypair before starting instance in alicloud builder [GH-5739] -* builder/vmware: Fixed file handle leak that may have caused race conditions in vmware builder [GH-5767] -* provisioner/powershell: Regression from v1.1.1 forcing extra escaping of environment variables in the non-elevated provisioner has been fixed. [GH-5515] [GH-5872] -* provisioner/ansible: The "default extra variables" feature added in Packer v1.0.1 caused the ansible-local provisioner to fail when an --extra-vars argument was specified in the extra_arguments configuration option; this has been fixed. [GH-5335] -* communicator/ssh: Add deadline to SSH connection to prevent Packer hangs after script provisioner reboots vm [GH-4684] -* builder/virtualbox: Fix regression affecting users running Packer on a Windows host that kept Packer from finding Virtualbox guest additions if Packer ran on a different drive from the one where the guest additions were stored. [GH-5761] -* builder/virtualbox: Fix interpolation ordering so that edge cases around guest_additions_url are handled correctly [GH-5757] -* builder/amazon: NewSession now inherits MaxRetries and other settings. [GH-5719] +* builder/alicloud-ecs: Attach keypair before starting instance in alicloud + builder [GH-5739] * builder/amazon: Fix tagging support when building in us-gov/china. [GH-5841] -* builder/vmware: Fix case where artifacts might not be cleaned up correctly. [GH-5835] -* provisioner/ansible-local: Fix support for `--extra-vars` in `extra_arguments`. [GH-5703] +* builder/amazon: NewSession now inherits MaxRetries and other settings. + [GH-5719] +* builder/virtualbox: Fix interpolation ordering so that edge cases around + guest_additions_url are handled correctly [GH-5757] +* builder/virtualbox: Fix regression affecting users running Packer on a + Windows host that kept Packer from finding Virtualbox guest additions if + Packer ran on a different drive from the one where the guest additions were + stored. [GH-5761] +* builder/vmware: Fix case where artifacts might not be cleaned up correctly. + [GH-5835] +* builder/vmware: Fixed file handle leak that may have caused race conditions + in vmware builder [GH-5767] +* communicator/ssh: Add deadline to SSH connection to prevent Packer hangs + after script provisioner reboots vm [GH-4684] * communicator/winrm: Fix issue copying empty directories. [GH-5763] +* provisioner/ansible-local: Fix support for `--extra-vars` in + `extra_arguments`. [GH-5703] +* provisioner/ansible-remote: Fixes an error where Packer's private key can be + overridden by inherited `ansible_ssh_private_key` options. [GH-5869] +* provisioner/ansible: The "default extra variables" feature added in Packer + v1.0.1 caused the ansible-local provisioner to fail when an --extra-vars + argument was specified in the extra_arguments configuration option; this + has been fixed. [GH-5335] +* provisioner/powershell: Regression from v1.1.1 forcing extra escaping of + environment variables in the non-elevated provisioner has been fixed. + [GH-5515] [GH-5872] ## 1.1.3 (December 8, 2017) From 0093f48614379d11248f2b971fa7cb86de59bd0a Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 9 Feb 2018 15:07:10 -0800 Subject: [PATCH 0505/1216] prep for 1.2.0 --- CHANGELOG.md | 2 +- version/version.go | 4 ++-- website/config.rb | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9e38a60b..622d01f9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## (UNRELEASED) +## 1.2.0 (February 9, 2018) ### BACKWARDS INCOMPATIBILITIES: * 3rd party plugins: We have moved internal dependencies, meaning your 3rd diff --git a/version/version.go b/version/version.go index 03ee6f644..5538683af 100644 --- a/version/version.go +++ b/version/version.go @@ -9,12 +9,12 @@ import ( var GitCommit string // The main version number that is being run at the moment. -const Version = "1.1.4" +const Version = "1.2.0" // A pre-release marker for the version. If this is "" (empty string) // then it means that it is a final release. Otherwise, this is a pre-release // such as "dev" (in development), "beta", "rc1", etc. -const VersionPrerelease = "dev" +const VersionPrerelease = "" func FormattedVersion() string { var versionString bytes.Buffer diff --git a/website/config.rb b/website/config.rb index fc5caf4e5..e6d530fdc 100644 --- a/website/config.rb +++ b/website/config.rb @@ -2,7 +2,7 @@ set :base_url, "https://www.packer.io/" activate :hashicorp do |h| h.name = "packer" - h.version = "1.1.3" + h.version = "1.2.0" h.github_slug = "hashicorp/packer" h.website_root = "website" end From f2a94a97b7714ae210230435e81d6f137585a183 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 9 Feb 2018 16:11:32 -0800 Subject: [PATCH 0507/1216] next version is 1.2.1 --- version/version.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version/version.go b/version/version.go index 5538683af..93fffef3e 100644 --- a/version/version.go +++ b/version/version.go @@ -9,12 +9,12 @@ import ( var GitCommit string // The main version number that is being run at the moment. -const Version = "1.2.0" +const Version = "1.2.1" // A pre-release marker for the version. If this is "" (empty string) // then it means that it is a final release. Otherwise, this is a pre-release // such as "dev" (in development), "beta", "rc1", etc. -const VersionPrerelease = "" +const VersionPrerelease = "dev" func FormattedVersion() string { var versionString bytes.Buffer From 9e636f01ed51bda8e1325885d938587d078a9468 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 9 Feb 2018 16:18:08 -0800 Subject: [PATCH 0508/1216] prep changelog for next release --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 622d01f9f..d8878587c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## UNRELEASED + + ## 1.2.0 (February 9, 2018) ### BACKWARDS INCOMPATIBILITIES: From 21313f4cf3740e8d1f7f7fe8efb8c2f0f4d23b07 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Sat, 10 Feb 2018 13:59:28 -0800 Subject: [PATCH 0509/1216] fix link --- website/source/docs/builders/oracle.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/builders/oracle.html.md b/website/source/docs/builders/oracle.html.md index 70a1917d0..7184b0a59 100644 --- a/website/source/docs/builders/oracle.html.md +++ b/website/source/docs/builders/oracle.html.md @@ -17,6 +17,6 @@ designed to support both platforms. Please choose the one that's right for you: instance and creating an image list from a snapshot of it after provisioning. -- [oracle-oci](/docs/builders/amazon-instance.html) - Create custom images in +- [oracle-oci](/docs/builders/oracle-oci.html) - Create custom images in Oracle Cloud Infrastructure (OCI) by launching a base instance and creating an image from it after provisioning. From 2747562708efe81983f70445c058f9ee7413b82d Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Sun, 11 Feb 2018 13:57:25 -0800 Subject: [PATCH 0510/1216] remove ssh_private_ip from docs --- website/source/docs/builders/amazon-ebs.html.md | 4 ++-- website/source/docs/builders/amazon-ebssurrogate.html.md | 4 ++-- website/source/docs/builders/amazon-ebsvolume.html.md | 4 ++-- website/source/docs/builders/amazon-instance.html.md | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/website/source/docs/builders/amazon-ebs.html.md b/website/source/docs/builders/amazon-ebs.html.md index 892029d26..4f2a44498 100644 --- a/website/source/docs/builders/amazon-ebs.html.md +++ b/website/source/docs/builders/amazon-ebs.html.md @@ -337,8 +337,8 @@ builder. in AWS with the source instance, set the `ssh_keypair_name` field to the name of the key pair. -- `ssh_private_ip` (boolean) - *Deprecated* use `ssh_interface` instead. If `true`, - then SSH will always use the private IP if available. Also works for WinRM. +- `ssh_private_ip` (boolean) - No longer supported. See + [`ssh_interface`](#ssh_interface). A fixer exists to migrate. - `ssh_interface` (string) - One of `public_ip`, `private_ip`, `public_dns` or `private_dns`. If set, either the public IP address, diff --git a/website/source/docs/builders/amazon-ebssurrogate.html.md b/website/source/docs/builders/amazon-ebssurrogate.html.md index 42aaa8664..c01c3c493 100644 --- a/website/source/docs/builders/amazon-ebssurrogate.html.md +++ b/website/source/docs/builders/amazon-ebssurrogate.html.md @@ -330,8 +330,8 @@ builder. in AWS with the source instance, set the `ssh_keypair_name` field to the name of the key pair. -- `ssh_private_ip` (boolean) - *Deprecated* use `ssh_interface` instead. If `true`, - then SSH will always use the private IP if available. Also works for WinRM. +- `ssh_private_ip` (boolean) - No longer supported. See + [`ssh_interface`](#ssh_interface). A fixer exists to migrate. - `ssh_interface` (string) - One of `public_ip`, `private_ip`, `public_dns` or `private_dns`. If set, either the public IP address, diff --git a/website/source/docs/builders/amazon-ebsvolume.html.md b/website/source/docs/builders/amazon-ebsvolume.html.md index 5580897f2..186295b87 100644 --- a/website/source/docs/builders/amazon-ebsvolume.html.md +++ b/website/source/docs/builders/amazon-ebsvolume.html.md @@ -224,8 +224,8 @@ builder. [`ssh_private_key_file`](/docs/templates/communicator.html#ssh_private_key_file) must be specified with this. -- `ssh_private_ip` (boolean) - *Deprecated* use `ssh_interface` instead. If `true`, - then SSH will always use the private IP if available. Also works for WinRM. +- `ssh_private_ip` (boolean) - No longer supported. See + [`ssh_interface`](#ssh_interface). A fixer exists to migrate. - `ssh_interface` (string) - One of `public_ip`, `private_ip`, `public_dns` or `private_dns`. If set, either the public IP address, diff --git a/website/source/docs/builders/amazon-instance.html.md b/website/source/docs/builders/amazon-instance.html.md index 624c5d6e0..924e174dd 100644 --- a/website/source/docs/builders/amazon-instance.html.md +++ b/website/source/docs/builders/amazon-instance.html.md @@ -338,8 +338,8 @@ builder. in AWS with the source instance, set the `ssh_keypair_name` field to the name of the key pair. -- `ssh_private_ip` (boolean) - *Deprecated* use `ssh_interface` instead. If `true`, - then SSH will always use the private IP if available. Also works for WinRM. +- `ssh_private_ip` (boolean) - No longer supported. See + [`ssh_interface`](#ssh_interface). A fixer exists to migrate. - `ssh_interface` (string) - One of `public_ip`, `private_ip`, `public_dns` or `private_dns`. If set, either the public IP address, From a4123eabe35e3d29e3aa6d50efa107800aa87cb6 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Mon, 12 Feb 2018 10:45:53 -0800 Subject: [PATCH 0511/1216] prevent 0-value ticker during ssh keepalive --- communicator/ssh/communicator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/communicator/ssh/communicator.go b/communicator/ssh/communicator.go index 5c2cfac9d..b5b67633c 100644 --- a/communicator/ssh/communicator.go +++ b/communicator/ssh/communicator.go @@ -112,7 +112,7 @@ func (c *comm) Start(cmd *packer.RemoteCmd) (err error) { } go func() { - if c.config.KeepAliveInterval < 0 { + if c.config.KeepAliveInterval <= 0 { return } c := time.NewTicker(c.config.KeepAliveInterval) From 7d9a86becb0d4790ce4cd10ecd6eb7c3f059db89 Mon Sep 17 00:00:00 2001 From: SwampDragons Date: Mon, 12 Feb 2018 11:03:19 -0800 Subject: [PATCH 0512/1216] Revert "Fix #5335" --- provisioner/ansible-local/provisioner.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provisioner/ansible-local/provisioner.go b/provisioner/ansible-local/provisioner.go index e007bfb85..ec1eec041 100644 --- a/provisioner/ansible-local/provisioner.go +++ b/provisioner/ansible-local/provisioner.go @@ -304,7 +304,7 @@ func (p *Provisioner) executeAnsible(ui packer.Ui, comm packer.Communicator) err playbook := filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(p.config.PlaybookFile))) inventory := filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(p.config.InventoryFile))) - extraArgs := fmt.Sprintf(" --extra-vars \\\"packer_build_name=%s packer_builder_type=%s packer_http_addr=%s\\\" ", + extraArgs := fmt.Sprintf(" --extra-vars \"packer_build_name=%s packer_builder_type=%s packer_http_addr=%s\" ", p.config.PackerBuildName, p.config.PackerBuilderType, common.GetHTTPAddr()) if len(p.config.ExtraArguments) > 0 { extraArgs = extraArgs + strings.Join(p.config.ExtraArguments, " ") From f3ac1af5dfbb37cb6de1e247be13cc45d4c65f6c Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Mon, 12 Feb 2018 11:18:50 -0800 Subject: [PATCH 0513/1216] add scaleway codeowners --- CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/CODEOWNERS b/CODEOWNERS index 050f4d084..216cce578 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -14,6 +14,7 @@ /builder/profitbricks/ @jasmingacic /builder/triton/ @jen20 @sean- /builder/ncloud/ @YuSungDuk +/builder/scaleway/ @dimtion @edouardb # provisioners From 7966e202b7f4de1d45e316e3e0fb5ca5f344cd05 Mon Sep 17 00:00:00 2001 From: Robert Neumayer Date: Tue, 13 Feb 2018 13:25:48 +0100 Subject: [PATCH 0514/1216] Fix typo --- builder/oracle/oci/config_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/builder/oracle/oci/config_test.go b/builder/oracle/oci/config_test.go index 9a6bd6486..84002e937 100644 --- a/builder/oracle/oci/config_test.go +++ b/builder/oracle/oci/config_test.go @@ -24,7 +24,8 @@ func testConfig(accessConfFile *os.File) map[string]interface{} { "subnet_ocid": "ocd1...", // Comm - "ssh_username": "opc", + "ssh_username": "opc", + "use_private_ip": false, } } @@ -167,7 +168,7 @@ func TestConfig(t *testing.T) { }) // Test that AccessCfgFile properties are overridden by their - // corosponding template keys. + // corresponding template keys. accessOverrides := map[string]string{ "user_ocid": "User", "tenancy_ocid": "Tenancy", From 20b93e753d134888b5efa9dc6822fabfb19136b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B2=E1=84=89=E1=85=A5=E1=86=BC=E1=84=83?= =?UTF-8?q?=E1=85=A5=E1=86=A8?= Date: Tue, 13 Feb 2018 21:52:23 +0900 Subject: [PATCH 0515/1216] Fix `naver` of build docs to `NAVER Cloud` --- website/source/layouts/docs.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/layouts/docs.erb b/website/source/layouts/docs.erb index 5f089e041..552b154ab 100644 --- a/website/source/layouts/docs.erb +++ b/website/source/layouts/docs.erb @@ -123,7 +123,7 @@ LXD > - Naver + NAVER Cloud > Null From 30fa1494d54021db786ce0732e7acc3559addbdf Mon Sep 17 00:00:00 2001 From: Robert Neumayer Date: Tue, 13 Feb 2018 14:20:26 +0100 Subject: [PATCH 0516/1216] Add option to use prive ip for oci builder --- builder/oracle/oci/config.go | 13 ++++---- builder/oracle/oci/config_test.go | 3 +- builder/oracle/oci/driver_mock.go | 7 +++- builder/oracle/oci/driver_oci.go | 5 ++- builder/oracle/oci/step_instance_info_test.go | 32 +++++++++++++++++++ builder/oracle/oci/step_test.go | 8 +++-- 6 files changed, 56 insertions(+), 12 deletions(-) diff --git a/builder/oracle/oci/config.go b/builder/oracle/oci/config.go index 36f8bb8e9..881ed1e35 100644 --- a/builder/oracle/oci/config.go +++ b/builder/oracle/oci/config.go @@ -26,12 +26,13 @@ type Config struct { AccessCfgFileAccount string `mapstructure:"access_cfg_file_account"` // Access config overrides - UserID string `mapstructure:"user_ocid"` - TenancyID string `mapstructure:"tenancy_ocid"` - Region string `mapstructure:"region"` - Fingerprint string `mapstructure:"fingerprint"` - KeyFile string `mapstructure:"key_file"` - PassPhrase string `mapstructure:"pass_phrase"` + UserID string `mapstructure:"user_ocid"` + TenancyID string `mapstructure:"tenancy_ocid"` + Region string `mapstructure:"region"` + Fingerprint string `mapstructure:"fingerprint"` + KeyFile string `mapstructure:"key_file"` + PassPhrase string `mapstructure:"pass_phrase"` + UsePrivateIP bool `mapstructure:"use_private_ip"` AvailabilityDomain string `mapstructure:"availability_domain"` CompartmentID string `mapstructure:"compartment_ocid"` diff --git a/builder/oracle/oci/config_test.go b/builder/oracle/oci/config_test.go index 9a6bd6486..7953e7354 100644 --- a/builder/oracle/oci/config_test.go +++ b/builder/oracle/oci/config_test.go @@ -24,7 +24,8 @@ func testConfig(accessConfFile *os.File) map[string]interface{} { "subnet_ocid": "ocd1...", // Comm - "ssh_username": "opc", + "ssh_username": "opc", + "use_private_ip": false, } } diff --git a/builder/oracle/oci/driver_mock.go b/builder/oracle/oci/driver_mock.go index 6173760fc..f8bd9f920 100644 --- a/builder/oracle/oci/driver_mock.go +++ b/builder/oracle/oci/driver_mock.go @@ -24,6 +24,8 @@ type driverMock struct { WaitForImageCreationErr error WaitForInstanceStateErr error + + cfg *Config } // CreateInstance creates a new compute instance. @@ -57,11 +59,14 @@ func (d *driverMock) DeleteImage(id string) error { return nil } -// GetInstanceIP returns the public IP corresponding to the given instance id. +// GetInstanceIP returns the public or private IP corresponding to the given instance id. func (d *driverMock) GetInstanceIP(id string) (string, error) { if d.GetInstanceIPErr != nil { return "", d.GetInstanceIPErr } + if d.cfg.UsePrivateIP { + return "private_ip", nil + } return "ip", nil } diff --git a/builder/oracle/oci/driver_oci.go b/builder/oracle/oci/driver_oci.go index 1d6f943c0..ead05a12d 100644 --- a/builder/oracle/oci/driver_oci.go +++ b/builder/oracle/oci/driver_oci.go @@ -63,7 +63,7 @@ func (d *driverOCI) DeleteImage(id string) error { return d.client.Compute.Images.Delete(&client.DeleteImageParams{ID: id}) } -// GetInstanceIP returns the public IP corresponding to the given instance id. +// GetInstanceIP returns the public or private IP corresponding to the given instance id. func (d *driverOCI) GetInstanceIP(id string) (string, error) { // get nvic and cross ref to find pub ip address vnics, err := d.client.Compute.VNICAttachments.List( @@ -85,6 +85,9 @@ func (d *driverOCI) GetInstanceIP(id string) (string, error) { return "", fmt.Errorf("Error getting VNIC details: %s", err) } + if d.cfg.UsePrivateIP { + return vnic.PrivateIP, nil + } return vnic.PublicIP, nil } diff --git a/builder/oracle/oci/step_instance_info_test.go b/builder/oracle/oci/step_instance_info_test.go index 7117ec44a..d4e18dd44 100644 --- a/builder/oracle/oci/step_instance_info_test.go +++ b/builder/oracle/oci/step_instance_info_test.go @@ -1,11 +1,13 @@ package oci import ( + "bytes" "context" "errors" "testing" "github.com/hashicorp/packer/helper/multistep" + "github.com/hashicorp/packer/packer" ) func TestInstanceInfo(t *testing.T) { @@ -29,6 +31,36 @@ func TestInstanceInfo(t *testing.T) { } } +func TestInstanceInfoPrivateIP(t *testing.T) { + baseTestConfig := baseTestConfig() + baseTestConfig.UsePrivateIP = true + state := new(multistep.BasicStateBag) + state.Put("config", baseTestConfig) + state.Put("driver", &driverMock{cfg: baseTestConfig}) + state.Put("hook", &packer.MockHook{}) + state.Put("ui", &packer.BasicUi{ + Reader: new(bytes.Buffer), + Writer: new(bytes.Buffer), + }) + state.Put("instance_id", "ocid1...") + + step := new(stepInstanceInfo) + defer step.Cleanup(state) + + if action := step.Run(context.Background(), state); action != multistep.ActionContinue { + t.Fatalf("bad action: %#v", action) + } + + instanceIPRaw, ok := state.GetOk("instance_ip") + if !ok { + t.Fatalf("should have instance_ip") + } + + if instanceIPRaw.(string) != "private_ip" { + t.Fatalf("should've got ip ('%s' != 'private_ip')", instanceIPRaw.(string)) + } +} + func TestInstanceInfo_GetInstanceIPErr(t *testing.T) { state := testState() state.Put("instance_id", "ocid1...") diff --git a/builder/oracle/oci/step_test.go b/builder/oracle/oci/step_test.go index f46ffa1e4..cca58666c 100644 --- a/builder/oracle/oci/step_test.go +++ b/builder/oracle/oci/step_test.go @@ -36,7 +36,8 @@ func baseTestConfig() *Config { "key_file": keyFile.Name(), // Comm - "ssh_username": "opc", + "ssh_username": "opc", + "use_private_ip": false, }) // Once we have a config object they key file isn't re-read so we can @@ -50,9 +51,10 @@ func baseTestConfig() *Config { } func testState() multistep.StateBag { + baseTestConfig := baseTestConfig() state := new(multistep.BasicStateBag) - state.Put("config", baseTestConfig()) - state.Put("driver", &driverMock{}) + state.Put("config", baseTestConfig) + state.Put("driver", &driverMock{cfg: baseTestConfig}) state.Put("hook", &packer.MockHook{}) state.Put("ui", &packer.BasicUi{ Reader: new(bytes.Buffer), From ff30b3b3f706ab952e0a6962fa06107bd503e013 Mon Sep 17 00:00:00 2001 From: Robert Neumayer Date: Tue, 13 Feb 2018 14:23:19 +0100 Subject: [PATCH 0517/1216] Remove unrelated changes --- builder/oracle/oci/config_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/builder/oracle/oci/config_test.go b/builder/oracle/oci/config_test.go index 84002e937..f1352aafa 100644 --- a/builder/oracle/oci/config_test.go +++ b/builder/oracle/oci/config_test.go @@ -24,8 +24,7 @@ func testConfig(accessConfFile *os.File) map[string]interface{} { "subnet_ocid": "ocd1...", // Comm - "ssh_username": "opc", - "use_private_ip": false, + "ssh_username": "opc", } } From 0534e3420c151adc10d0cd652cf43463ae8a5c98 Mon Sep 17 00:00:00 2001 From: Robert Neumayer Date: Tue, 13 Feb 2018 14:41:51 +0100 Subject: [PATCH 0518/1216] Update docs --- website/source/docs/builders/oracle-oci.html.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/website/source/docs/builders/oracle-oci.html.md b/website/source/docs/builders/oracle-oci.html.md index 6271c5830..ea4b08404 100644 --- a/website/source/docs/builders/oracle-oci.html.md +++ b/website/source/docs/builders/oracle-oci.html.md @@ -123,6 +123,8 @@ builder. value provided by the [OCI config file](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/sdkconfig.htm) if present. + - `use_private_ip` (boolean) - Use private ip addresses to connect to the instance via ssh. + ## Basic Example From d6e5342ecef48411afb3a85cfd4af5b492cc1bd1 Mon Sep 17 00:00:00 2001 From: Ali Rizvi-Santiago Date: Tue, 13 Feb 2018 17:27:12 -0600 Subject: [PATCH 0519/1216] Fixed a type-o in the VMWare builder when locating the dhcp configuration file on Linux. Closes issue #5882. --- builder/vmware/common/driver_workstation_unix.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/vmware/common/driver_workstation_unix.go b/builder/vmware/common/driver_workstation_unix.go index 45e94d21e..8b6a7f60b 100644 --- a/builder/vmware/common/driver_workstation_unix.go +++ b/builder/vmware/common/driver_workstation_unix.go @@ -59,7 +59,7 @@ func workstationDhcpConfPath(device string) string { log.Printf("Error finding VMware root: %s", err) return "" } - return filepath.Join(base, device, "dhcp/dhcpd.conf") + return filepath.Join(base, device, "dhcp/dhcp.conf") } func workstationVmnetnatConfPath(device string) string { From 3e3c16d6276440844190215be3d16339e769f747 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Tue, 13 Feb 2018 16:48:39 -0800 Subject: [PATCH 0520/1216] update docs --- website/source/docs/builders/amazon-chroot.html.md | 6 ++++++ website/source/docs/builders/amazon-ebs.html.md | 6 ++++++ website/source/docs/builders/amazon-ebssurrogate.html.md | 6 ++++++ website/source/docs/builders/amazon-ebsvolume.html.md | 6 ++++++ website/source/docs/builders/amazon-instance.html.md | 6 ++++++ 5 files changed, 30 insertions(+) diff --git a/website/source/docs/builders/amazon-chroot.html.md b/website/source/docs/builders/amazon-chroot.html.md index 7bb0adcec..53c21ff30 100644 --- a/website/source/docs/builders/amazon-chroot.html.md +++ b/website/source/docs/builders/amazon-chroot.html.md @@ -300,6 +300,12 @@ each category, the available configuration keys are alphabetized. - `most_recent` (boolean) - Selects the newest created image when true. This is most useful for selecting a daily distro build. + You may set this in place of `source_ami` or in conjunction with it. If you + set this in conjunction with `source_ami`, the `source_ami` will be added to + the filter. The provided `source_ami` must meet all of the filtering criteria + provided in `source_ami_filter`; this pins the AMI returned by the filter, + but will cause Packer to fail if the `source_ami` does not exist. + - `sriov_support` (boolean) - Enable enhanced networking (SriovNetSupport but not ENA) on HVM-compatible AMIs. If true, add `ec2:ModifyInstanceAttribute` to your AWS IAM policy. Note: you must make sure enhanced networking is enabled on your instance. See [Amazon's diff --git a/website/source/docs/builders/amazon-ebs.html.md b/website/source/docs/builders/amazon-ebs.html.md index 4f2a44498..effa6e5ef 100644 --- a/website/source/docs/builders/amazon-ebs.html.md +++ b/website/source/docs/builders/amazon-ebs.html.md @@ -302,6 +302,12 @@ builder. - `most_recent` (boolean) - Selects the newest created image when true. This is most useful for selecting a daily distro build. + You may set this in place of `source_ami` or in conjunction with it. If you + set this in conjunction with `source_ami`, the `source_ami` will be added to + the filter. The provided `source_ami` must meet all of the filtering criteria + provided in `source_ami_filter`; this pins the AMI returned by the filter, + but will cause Packer to fail if the `source_ami` does not exist. + - `spot_price` (string) - The maximum hourly price to pay for a spot instance to create the AMI. Spot instances are a type of instance that EC2 starts when the current spot price is less than the maximum price you specify. Spot diff --git a/website/source/docs/builders/amazon-ebssurrogate.html.md b/website/source/docs/builders/amazon-ebssurrogate.html.md index c01c3c493..757ae1229 100644 --- a/website/source/docs/builders/amazon-ebssurrogate.html.md +++ b/website/source/docs/builders/amazon-ebssurrogate.html.md @@ -294,6 +294,12 @@ builder. - `most_recent` (boolean) - Selects the newest created image when true. This is most useful for selecting a daily distro build. + + You may set this in place of `source_ami` or in conjunction with it. If you + set this in conjunction with `source_ami`, the `source_ami` will be added to + the filter. The provided `source_ami` must meet all of the filtering criteria + provided in `source_ami_filter`; this pins the AMI returned by the filter, + but will cause Packer to fail if the `source_ami` does not exist. - `spot_price` (string) - The maximum hourly price to pay for a spot instance to create the AMI. Spot instances are a type of instance that EC2 starts diff --git a/website/source/docs/builders/amazon-ebsvolume.html.md b/website/source/docs/builders/amazon-ebsvolume.html.md index 186295b87..c8604cc25 100644 --- a/website/source/docs/builders/amazon-ebsvolume.html.md +++ b/website/source/docs/builders/amazon-ebsvolume.html.md @@ -198,6 +198,12 @@ builder. - `most_recent` (boolean) - Selects the newest created image when true. This is most useful for selecting a daily distro build. + You may set this in place of `source_ami` or in conjunction with it. If you + set this in conjunction with `source_ami`, the `source_ami` will be added to + the filter. The provided `source_ami` must meet all of the filtering criteria + provided in `source_ami_filter`; this pins the AMI returned by the filter, + but will cause Packer to fail if the `source_ami` does not exist. + - `spot_price` (string) - The maximum hourly price to pay for a spot instance to create the AMI. Spot instances are a type of instance that EC2 starts when the current spot price is less than the maximum price you specify. Spot diff --git a/website/source/docs/builders/amazon-instance.html.md b/website/source/docs/builders/amazon-instance.html.md index 924e174dd..5773c8bdd 100644 --- a/website/source/docs/builders/amazon-instance.html.md +++ b/website/source/docs/builders/amazon-instance.html.md @@ -299,6 +299,12 @@ builder. - `most_recent` (boolean) - Selects the newest created image when true. This is most useful for selecting a daily distro build. + + You may set this in place of `source_ami` or in conjunction with it. If you + set this in conjunction with `source_ami`, the `source_ami` will be added to + the filter. The provided `source_ami` must meet all of the filtering criteria + provided in `source_ami_filter`; this pins the AMI returned by the filter, + but will cause Packer to fail if the `source_ami` does not exist. - `snapshot_tags` (object of key/value strings) - Tags to apply to snapshot. They will override AMI tags if already applied to snapshot. From 27ed479b0a42dd1e15c673ee421fceab01f306ab Mon Sep 17 00:00:00 2001 From: Ali Rizvi-Santiago Date: Wed, 14 Feb 2018 00:08:10 -0600 Subject: [PATCH 0521/1216] Reinforced the VMWare Workstation builder methodology for locating the dhcp.conf and dhcpd.leases files on Linux. It was reported that on WS14 on Linux, that the path may be different than stated in the documentation. This modifies `workstationDhcpConfPath` and `workstationDhcpLeasesPath` functions to walk through every permutation while attempting to find the correct file. This reinforces the fix for issue #5882. --- .../vmware/common/driver_workstation_unix.go | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/builder/vmware/common/driver_workstation_unix.go b/builder/vmware/common/driver_workstation_unix.go index 8b6a7f60b..5bbbc7d1d 100644 --- a/builder/vmware/common/driver_workstation_unix.go +++ b/builder/vmware/common/driver_workstation_unix.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "log" + "os" "os/exec" "path/filepath" "regexp" @@ -50,7 +51,24 @@ func workstationDhcpLeasesPath(device string) string { log.Printf("Error finding VMware root: %s", err) return "" } - return filepath.Join(base, device, "dhcpd/dhcpd.leases") + + // Build the base path to VMware configuration for specified device: `/etc/vmware/${device}` + devicebase := filepath.Join(base, device) + + // Walk through a list of paths searching for the correct permutation... + // ...as it appears that in >= WS14 and < WS14, the leases file may be labelled differently. + + // Docs say we should expect: dhcpd/dhcpd.leases + paths := []string{"dhcpd/dhcpd.leases", "dhcpd/dhcp.leases", "dhcp/dhcpd.leases", "dhcp/dhcp.leases"} + for _, p := range paths { + fp := filepath.Join(devicebase, p) + if _, err := os.Stat(fp); !os.IsNotExist(err) { + return fp + } + } + + log.Printf("Error finding VMWare DHCP Server Leases (dhcpd.leases) under device path: %s", devicebase) + return "" } func workstationDhcpConfPath(device string) string { @@ -59,7 +77,24 @@ func workstationDhcpConfPath(device string) string { log.Printf("Error finding VMware root: %s", err) return "" } - return filepath.Join(base, device, "dhcp/dhcp.conf") + + // Build the base path to VMware configuration for specified device: `/etc/vmware/${device}` + devicebase := filepath.Join(base, device) + + // Walk through a list of paths searching for the correct permutation... + // ...as it appears that in >= WS14 and < WS14, the dhcp config may be labelled differently. + + // Docs say we should expect: dhcp/dhcp.conf + paths := []string{"dhcp/dhcp.conf", "dhcp/dhcpd.conf", "dhcpd/dhcp.conf", "dhcpd/dhcpd.conf"} + for _, p := range paths { + fp := filepath.Join(devicebase, p) + if _, err := os.Stat(fp); !os.IsNotExist(err) { + return fp + } + } + + log.Printf("Error finding VMWare DHCP Server Configuration (dhcp.conf) under device path: %s", devicebase) + return "" } func workstationVmnetnatConfPath(device string) string { From d3848903e046dcd3473d2debe289bad1d8f79731 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Wed, 14 Feb 2018 19:07:40 -0800 Subject: [PATCH 0522/1216] Clarify behavior or launch_block_device_mappings. cf https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/creating-an-ami-ebs.html https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CreateImage.html https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CreateVolume.html --- .../source/docs/builders/amazon-ebs.html.md | 18 ++++++++++------- .../docs/builders/amazon-ebssurrogate.html.md | 20 +++++++++++-------- .../docs/builders/amazon-instance.html.md | 20 +++++++++++-------- 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/website/source/docs/builders/amazon-ebs.html.md b/website/source/docs/builders/amazon-ebs.html.md index effa6e5ef..4c57611f4 100644 --- a/website/source/docs/builders/amazon-ebs.html.md +++ b/website/source/docs/builders/amazon-ebs.html.md @@ -192,10 +192,14 @@ builder. profile](https://docs.aws.amazon.com/IAM/latest/UserGuide/instance-profiles.html) to launch the EC2 instance with. -- `launch_block_device_mappings` (array of block device mappings) - Add one or - more block devices before the Packer build starts. These are not necessarily - preserved when booting from the AMI built with Packer. See - `ami_block_device_mappings`, above, for details. +- `launch_block_device_mappings` (array of block device mappings) - Add one + or more block devices before the Packer build starts. If you add instance + store volumes or EBS volumes in addition to the root device volume, the + created AMI will contain block device mapping information for those + volumes. Amazon creates snapshots of the source instance's root volume and + any other EBS volumes described here. When you launch an instance from this + new AMI, the instance automatically launches with these additional volumes, + and will restore them from snapshots taken from the source instance. - `mfa_code` (string) - The MFA [TOTP](https://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm) code. This should probably be a user variable since it changes all the time. @@ -303,10 +307,10 @@ builder. This is most useful for selecting a daily distro build. You may set this in place of `source_ami` or in conjunction with it. If you - set this in conjunction with `source_ami`, the `source_ami` will be added to + set this in conjunction with `source_ami`, the `source_ami` will be added to the filter. The provided `source_ami` must meet all of the filtering criteria - provided in `source_ami_filter`; this pins the AMI returned by the filter, - but will cause Packer to fail if the `source_ami` does not exist. + provided in `source_ami_filter`; this pins the AMI returned by the filter, + but will cause Packer to fail if the `source_ami` does not exist. - `spot_price` (string) - The maximum hourly price to pay for a spot instance to create the AMI. Spot instances are a type of instance that EC2 starts diff --git a/website/source/docs/builders/amazon-ebssurrogate.html.md b/website/source/docs/builders/amazon-ebssurrogate.html.md index 757ae1229..d40ec04e1 100644 --- a/website/source/docs/builders/amazon-ebssurrogate.html.md +++ b/website/source/docs/builders/amazon-ebssurrogate.html.md @@ -185,10 +185,14 @@ builder. profile](https://docs.aws.amazon.com/IAM/latest/UserGuide/instance-profiles.html) to launch the EC2 instance with. -- `launch_block_device_mappings` (array of block device mappings) - Add one or - more block devices before the packer build starts. These are not necessarily - preserved when booting from the AMI built with packer. See - `ami_block_device_mappings`, above, for details. +- `launch_block_device_mappings` (array of block device mappings) - Add one + or more block devices before the Packer build starts. If you add instance + store volumes or EBS volumes in addition to the root device volume, the + created AMI will contain block device mapping information for those + volumes. Amazon creates snapshots of the source instance's root volume and + any other EBS volumes described here. When you launch an instance from this + new AMI, the instance automatically launches with these additional volumes, + and will restore them from snapshots taken from the source instance. - `mfa_code` (string) - The MFA [TOTP](https://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm) code. This should probably be a user variable since it changes all the time. @@ -294,12 +298,12 @@ builder. - `most_recent` (boolean) - Selects the newest created image when true. This is most useful for selecting a daily distro build. - + You may set this in place of `source_ami` or in conjunction with it. If you - set this in conjunction with `source_ami`, the `source_ami` will be added to + set this in conjunction with `source_ami`, the `source_ami` will be added to the filter. The provided `source_ami` must meet all of the filtering criteria - provided in `source_ami_filter`; this pins the AMI returned by the filter, - but will cause Packer to fail if the `source_ami` does not exist. + provided in `source_ami_filter`; this pins the AMI returned by the filter, + but will cause Packer to fail if the `source_ami` does not exist. - `spot_price` (string) - The maximum hourly price to pay for a spot instance to create the AMI. Spot instances are a type of instance that EC2 starts diff --git a/website/source/docs/builders/amazon-instance.html.md b/website/source/docs/builders/amazon-instance.html.md index 5773c8bdd..62417e1b7 100644 --- a/website/source/docs/builders/amazon-instance.html.md +++ b/website/source/docs/builders/amazon-instance.html.md @@ -207,10 +207,14 @@ builder. profile](https://docs.aws.amazon.com/IAM/latest/UserGuide/instance-profiles.html) to launch the EC2 instance with. -- `launch_block_device_mappings` (array of block device mappings) - Add one or - more block devices before the Packer build starts. These are not necessarily - preserved when booting from the AMI built with Packer. See - `ami_block_device_mappings`, above, for details. +- `launch_block_device_mappings` (array of block device mappings) - Add one + or more block devices before the Packer build starts. If you add instance + store volumes or EBS volumes in addition to the root device volume, the + created AMI will contain block device mapping information for those + volumes. Amazon creates snapshots of the source instance's root volume and + any other EBS volumes described here. When you launch an instance from this + new AMI, the instance automatically launches with these additional volumes, + and will restore them from snapshots taken from the source instance. - `mfa_code` (string) - The MFA [TOTP](https://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm) code. This should probably be a user variable since it changes all the time. @@ -299,12 +303,12 @@ builder. - `most_recent` (boolean) - Selects the newest created image when true. This is most useful for selecting a daily distro build. - + You may set this in place of `source_ami` or in conjunction with it. If you - set this in conjunction with `source_ami`, the `source_ami` will be added to + set this in conjunction with `source_ami`, the `source_ami` will be added to the filter. The provided `source_ami` must meet all of the filtering criteria - provided in `source_ami_filter`; this pins the AMI returned by the filter, - but will cause Packer to fail if the `source_ami` does not exist. + provided in `source_ami_filter`; this pins the AMI returned by the filter, + but will cause Packer to fail if the `source_ami` does not exist. - `snapshot_tags` (object of key/value strings) - Tags to apply to snapshot. They will override AMI tags if already applied to snapshot. From 7fd71c35ef076bff25d15f2f93b3898766c6fdc5 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 15 Feb 2018 13:38:28 -0800 Subject: [PATCH 0523/1216] update go-aws-sdk to v1.12.72 --- vendor/github.com/aws/aws-sdk-go/CHANGELOG.md | 24 + .../aws/aws-sdk-go/aws/endpoints/defaults.go | 1 + .../aws/aws-sdk-go/aws/session/env_config.go | 8 + .../aws/aws-sdk-go/aws/session/session.go | 14 +- .../github.com/aws/aws-sdk-go/aws/version.go | 2 +- .../aws/aws-sdk-go/service/ec2/api.go | 811 ------------------ .../aws/aws-sdk-go/service/ecr/api.go | 58 -- .../aws/aws-sdk-go/service/s3/api.go | 250 ------ .../aws/aws-sdk-go/service/sts/api.go | 17 - vendor/vendor.json | 290 +++---- 10 files changed, 185 insertions(+), 1290 deletions(-) diff --git a/vendor/github.com/aws/aws-sdk-go/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go/CHANGELOG.md index 0fc7f9c88..dd7465367 100644 --- a/vendor/github.com/aws/aws-sdk-go/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go/CHANGELOG.md @@ -1,3 +1,27 @@ +Release v1.12.72 (2018-02-07) +=== + +### Service Client Updates +* `aws/endpoints`: Updated Regions and Endpoints metadata. +* `service/glue`: Updates service API and documentation + * This new feature will now allow customers to add a customized json classifier. They can specify a json path to indicate the object, array or field of the json documents they'd like crawlers to inspect when they crawl json files. +* `service/servicecatalog`: Updates service API, documentation, and paginators + * This release of Service Catalog adds SearchProvisionedProducts API and ProvisionedProductPlan APIs. +* `service/servicediscovery`: Updates service API and documentation + * This release adds support for registering CNAME record types and creating Route 53 alias records that route traffic to Amazon Elastic Load Balancers using Amazon Route 53 Auto Naming APIs. +* `service/ssm`: Updates service API and documentation + * This Patch Manager release supports configuring Linux repos as part of patch baselines, controlling updates of non-OS security packages and also creating patch baselines for SUSE12 + +### SDK Enhancements +* `private/model/api`: Add validation to ensure there is no duplication of services in models/apis ([#1758](https://github.com/aws/aws-sdk-go/pull/1758)) + * Prevents the SDK from mistakenly generating code a single service multiple times with different model versions. +* `example/service/ec2/instancesbyRegion`: Fix typos in example ([#1762](https://github.com/aws/aws-sdk-go/pull/1762)) +* `private/model/api`: removing SDK API reference crosslinks from input/output shapes. (#1765) + +### SDK Bugs +* `aws/session`: Fix bug in session.New not supporting AWS_SDK_LOAD_CONFIG ([#1770](https://github.com/aws/aws-sdk-go/pull/1770)) + * Fixes a bug in the session.New function that was not correctly sourcing the shared configuration files' path. + * Fixes [#1771](https://github.com/aws/aws-sdk-go/pull/1771) Release v1.12.71 (2018-02-05) === diff --git a/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go b/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go index 689c380b4..9e728feee 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go @@ -1182,6 +1182,7 @@ var awsPartition = partition{ "eu-central-1": endpoint{}, "eu-west-1": endpoint{}, "us-east-1": endpoint{}, + "us-east-2": endpoint{}, "us-west-1": endpoint{}, "us-west-2": endpoint{}, }, diff --git a/vendor/github.com/aws/aws-sdk-go/aws/session/env_config.go b/vendor/github.com/aws/aws-sdk-go/aws/session/env_config.go index f1adcf481..12b452177 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/session/env_config.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/session/env_config.go @@ -5,6 +5,7 @@ import ( "strconv" "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/defaults" ) // EnvProviderName provides a name of the provider when config is loaded from environment. @@ -176,6 +177,13 @@ func envConfigLoad(enableSharedConfig bool) envConfig { setFromEnvVal(&cfg.SharedCredentialsFile, sharedCredsFileEnvKey) setFromEnvVal(&cfg.SharedConfigFile, sharedConfigFileEnvKey) + if len(cfg.SharedCredentialsFile) == 0 { + cfg.SharedCredentialsFile = defaults.SharedCredentialsFilename() + } + if len(cfg.SharedConfigFile) == 0 { + cfg.SharedConfigFile = defaults.SharedConfigFilename() + } + cfg.CustomCABundle = os.Getenv("AWS_CA_BUNDLE") return cfg diff --git a/vendor/github.com/aws/aws-sdk-go/aws/session/session.go b/vendor/github.com/aws/aws-sdk-go/aws/session/session.go index 9f75d5ac5..2fc789d6d 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/session/session.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/session/session.go @@ -58,7 +58,12 @@ func New(cfgs ...*aws.Config) *Session { envCfg := loadEnvConfig() if envCfg.EnableSharedConfig { - s, err := newSession(Options{}, envCfg, cfgs...) + var cfg aws.Config + cfg.MergeIn(cfgs...) + s, err := NewSessionWithOptions(Options{ + Config: cfg, + SharedConfigState: SharedConfigEnable, + }) if err != nil { // Old session.New expected all errors to be discovered when // a request is made, and would report the errors then. This @@ -243,13 +248,6 @@ func NewSessionWithOptions(opts Options) (*Session, error) { envCfg.EnableSharedConfig = true } - if len(envCfg.SharedCredentialsFile) == 0 { - envCfg.SharedCredentialsFile = defaults.SharedCredentialsFilename() - } - if len(envCfg.SharedConfigFile) == 0 { - envCfg.SharedConfigFile = defaults.SharedConfigFilename() - } - // Only use AWS_CA_BUNDLE if session option is not provided. if len(envCfg.CustomCABundle) != 0 && opts.CustomCABundle == nil { f, err := os.Open(envCfg.CustomCABundle) diff --git a/vendor/github.com/aws/aws-sdk-go/aws/version.go b/vendor/github.com/aws/aws-sdk-go/aws/version.go index b55286b31..02dfdd9e2 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/version.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/version.go @@ -5,4 +5,4 @@ package aws const SDKName = "aws-sdk-go" // SDKVersion is the version of this SDK -const SDKVersion = "1.12.71" +const SDKVersion = "1.12.72" diff --git a/vendor/github.com/aws/aws-sdk-go/service/ec2/api.go b/vendor/github.com/aws/aws-sdk-go/service/ec2/api.go index 1743b3449..3461bb96a 100644 --- a/vendor/github.com/aws/aws-sdk-go/service/ec2/api.go +++ b/vendor/github.com/aws/aws-sdk-go/service/ec2/api.go @@ -22266,7 +22266,6 @@ func (c *EC2) UpdateSecurityGroupRuleDescriptionsIngressWithContext(ctx aws.Cont } // Contains the parameters for accepting the quote. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AcceptReservedInstancesExchangeQuoteRequest type AcceptReservedInstancesExchangeQuoteInput struct { _ struct{} `type:"structure"` @@ -22339,7 +22338,6 @@ func (s *AcceptReservedInstancesExchangeQuoteInput) SetTargetConfigurations(v [] } // The result of the exchange and whether it was successful. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AcceptReservedInstancesExchangeQuoteResult type AcceptReservedInstancesExchangeQuoteOutput struct { _ struct{} `type:"structure"` @@ -22363,7 +22361,6 @@ func (s *AcceptReservedInstancesExchangeQuoteOutput) SetExchangeId(v string) *Ac return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AcceptVpcEndpointConnectionsRequest type AcceptVpcEndpointConnectionsInput struct { _ struct{} `type:"structure"` @@ -22428,7 +22425,6 @@ func (s *AcceptVpcEndpointConnectionsInput) SetVpcEndpointIds(v []*string) *Acce return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AcceptVpcEndpointConnectionsResult type AcceptVpcEndpointConnectionsOutput struct { _ struct{} `type:"structure"` @@ -22453,7 +22449,6 @@ func (s *AcceptVpcEndpointConnectionsOutput) SetUnsuccessful(v []*UnsuccessfulIt } // Contains the parameters for AcceptVpcPeeringConnection. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AcceptVpcPeeringConnectionRequest type AcceptVpcPeeringConnectionInput struct { _ struct{} `type:"structure"` @@ -22491,7 +22486,6 @@ func (s *AcceptVpcPeeringConnectionInput) SetVpcPeeringConnectionId(v string) *A } // Contains the output of AcceptVpcPeeringConnection. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AcceptVpcPeeringConnectionResult type AcceptVpcPeeringConnectionOutput struct { _ struct{} `type:"structure"` @@ -22516,7 +22510,6 @@ func (s *AcceptVpcPeeringConnectionOutput) SetVpcPeeringConnection(v *VpcPeering } // Describes an account attribute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AccountAttribute type AccountAttribute struct { _ struct{} `type:"structure"` @@ -22550,7 +22543,6 @@ func (s *AccountAttribute) SetAttributeValues(v []*AccountAttributeValue) *Accou } // Describes a value of an account attribute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AccountAttributeValue type AccountAttributeValue struct { _ struct{} `type:"structure"` @@ -22575,7 +22567,6 @@ func (s *AccountAttributeValue) SetAttributeValue(v string) *AccountAttributeVal } // Describes a running instance in a Spot Fleet. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ActiveInstance type ActiveInstance struct { _ struct{} `type:"structure"` @@ -22629,7 +22620,6 @@ func (s *ActiveInstance) SetSpotInstanceRequestId(v string) *ActiveInstance { } // Describes an Elastic IP address. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/Address type Address struct { _ struct{} `type:"structure"` @@ -22728,7 +22718,6 @@ func (s *Address) SetTags(v []*Tag) *Address { } // Contains the parameters for AllocateAddress. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AllocateAddressRequest type AllocateAddressInput struct { _ struct{} `type:"structure"` @@ -22776,7 +22765,6 @@ func (s *AllocateAddressInput) SetDryRun(v bool) *AllocateAddressInput { } // Contains the output of AllocateAddress. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AllocateAddressResult type AllocateAddressOutput struct { _ struct{} `type:"structure"` @@ -22821,7 +22809,6 @@ func (s *AllocateAddressOutput) SetPublicIp(v string) *AllocateAddressOutput { } // Contains the parameters for AllocateHosts. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AllocateHostsRequest type AllocateHostsInput struct { _ struct{} `type:"structure"` @@ -22916,7 +22903,6 @@ func (s *AllocateHostsInput) SetQuantity(v int64) *AllocateHostsInput { } // Contains the output of AllocateHosts. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AllocateHostsResult type AllocateHostsOutput struct { _ struct{} `type:"structure"` @@ -22942,7 +22928,6 @@ func (s *AllocateHostsOutput) SetHostIds(v []*string) *AllocateHostsOutput { } // Describes a principal. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AllowedPrincipal type AllowedPrincipal struct { _ struct{} `type:"structure"` @@ -22975,7 +22960,6 @@ func (s *AllowedPrincipal) SetPrincipalType(v string) *AllowedPrincipal { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AssignIpv6AddressesRequest type AssignIpv6AddressesInput struct { _ struct{} `type:"structure"` @@ -23035,7 +23019,6 @@ func (s *AssignIpv6AddressesInput) SetNetworkInterfaceId(v string) *AssignIpv6Ad return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AssignIpv6AddressesResult type AssignIpv6AddressesOutput struct { _ struct{} `type:"structure"` @@ -23069,7 +23052,6 @@ func (s *AssignIpv6AddressesOutput) SetNetworkInterfaceId(v string) *AssignIpv6A } // Contains the parameters for AssignPrivateIpAddresses. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AssignPrivateIpAddressesRequest type AssignPrivateIpAddressesInput struct { _ struct{} `type:"structure"` @@ -23142,7 +23124,6 @@ func (s *AssignPrivateIpAddressesInput) SetSecondaryPrivateIpAddressCount(v int6 return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AssignPrivateIpAddressesOutput type AssignPrivateIpAddressesOutput struct { _ struct{} `type:"structure"` } @@ -23158,7 +23139,6 @@ func (s AssignPrivateIpAddressesOutput) GoString() string { } // Contains the parameters for AssociateAddress. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AssociateAddressRequest type AssociateAddressInput struct { _ struct{} `type:"structure"` @@ -23251,7 +23231,6 @@ func (s *AssociateAddressInput) SetPublicIp(v string) *AssociateAddressInput { } // Contains the output of AssociateAddress. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AssociateAddressResult type AssociateAddressOutput struct { _ struct{} `type:"structure"` @@ -23277,7 +23256,6 @@ func (s *AssociateAddressOutput) SetAssociationId(v string) *AssociateAddressOut } // Contains the parameters for AssociateDhcpOptions. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AssociateDhcpOptionsRequest type AssociateDhcpOptionsInput struct { _ struct{} `type:"structure"` @@ -23343,7 +23321,6 @@ func (s *AssociateDhcpOptionsInput) SetVpcId(v string) *AssociateDhcpOptionsInpu return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AssociateDhcpOptionsOutput type AssociateDhcpOptionsOutput struct { _ struct{} `type:"structure"` } @@ -23358,7 +23335,6 @@ func (s AssociateDhcpOptionsOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AssociateIamInstanceProfileRequest type AssociateIamInstanceProfileInput struct { _ struct{} `type:"structure"` @@ -23411,7 +23387,6 @@ func (s *AssociateIamInstanceProfileInput) SetInstanceId(v string) *AssociateIam return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AssociateIamInstanceProfileResult type AssociateIamInstanceProfileOutput struct { _ struct{} `type:"structure"` @@ -23436,7 +23411,6 @@ func (s *AssociateIamInstanceProfileOutput) SetIamInstanceProfileAssociation(v * } // Contains the parameters for AssociateRouteTable. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AssociateRouteTableRequest type AssociateRouteTableInput struct { _ struct{} `type:"structure"` @@ -23502,7 +23476,6 @@ func (s *AssociateRouteTableInput) SetSubnetId(v string) *AssociateRouteTableInp } // Contains the output of AssociateRouteTable. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AssociateRouteTableResult type AssociateRouteTableOutput struct { _ struct{} `type:"structure"` @@ -23526,7 +23499,6 @@ func (s *AssociateRouteTableOutput) SetAssociationId(v string) *AssociateRouteTa return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AssociateSubnetCidrBlockRequest type AssociateSubnetCidrBlockInput struct { _ struct{} `type:"structure"` @@ -23579,7 +23551,6 @@ func (s *AssociateSubnetCidrBlockInput) SetSubnetId(v string) *AssociateSubnetCi return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AssociateSubnetCidrBlockResult type AssociateSubnetCidrBlockOutput struct { _ struct{} `type:"structure"` @@ -23612,7 +23583,6 @@ func (s *AssociateSubnetCidrBlockOutput) SetSubnetId(v string) *AssociateSubnetC return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AssociateVpcCidrBlockRequest type AssociateVpcCidrBlockInput struct { _ struct{} `type:"structure"` @@ -23671,7 +23641,6 @@ func (s *AssociateVpcCidrBlockInput) SetVpcId(v string) *AssociateVpcCidrBlockIn return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AssociateVpcCidrBlockResult type AssociateVpcCidrBlockOutput struct { _ struct{} `type:"structure"` @@ -23714,7 +23683,6 @@ func (s *AssociateVpcCidrBlockOutput) SetVpcId(v string) *AssociateVpcCidrBlockO } // Contains the parameters for AttachClassicLinkVpc. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AttachClassicLinkVpcRequest type AttachClassicLinkVpcInput struct { _ struct{} `type:"structure"` @@ -23795,7 +23763,6 @@ func (s *AttachClassicLinkVpcInput) SetVpcId(v string) *AttachClassicLinkVpcInpu } // Contains the output of AttachClassicLinkVpc. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AttachClassicLinkVpcResult type AttachClassicLinkVpcOutput struct { _ struct{} `type:"structure"` @@ -23820,7 +23787,6 @@ func (s *AttachClassicLinkVpcOutput) SetReturn(v bool) *AttachClassicLinkVpcOutp } // Contains the parameters for AttachInternetGateway. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AttachInternetGatewayRequest type AttachInternetGatewayInput struct { _ struct{} `type:"structure"` @@ -23885,7 +23851,6 @@ func (s *AttachInternetGatewayInput) SetVpcId(v string) *AttachInternetGatewayIn return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AttachInternetGatewayOutput type AttachInternetGatewayOutput struct { _ struct{} `type:"structure"` } @@ -23901,7 +23866,6 @@ func (s AttachInternetGatewayOutput) GoString() string { } // Contains the parameters for AttachNetworkInterface. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AttachNetworkInterfaceRequest type AttachNetworkInterfaceInput struct { _ struct{} `type:"structure"` @@ -23981,7 +23945,6 @@ func (s *AttachNetworkInterfaceInput) SetNetworkInterfaceId(v string) *AttachNet } // Contains the output of AttachNetworkInterface. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AttachNetworkInterfaceResult type AttachNetworkInterfaceOutput struct { _ struct{} `type:"structure"` @@ -24006,7 +23969,6 @@ func (s *AttachNetworkInterfaceOutput) SetAttachmentId(v string) *AttachNetworkI } // Contains the parameters for AttachVolume. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AttachVolumeRequest type AttachVolumeInput struct { _ struct{} `type:"structure"` @@ -24087,7 +24049,6 @@ func (s *AttachVolumeInput) SetVolumeId(v string) *AttachVolumeInput { } // Contains the parameters for AttachVpnGateway. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AttachVpnGatewayRequest type AttachVpnGatewayInput struct { _ struct{} `type:"structure"` @@ -24153,7 +24114,6 @@ func (s *AttachVpnGatewayInput) SetVpnGatewayId(v string) *AttachVpnGatewayInput } // Contains the output of AttachVpnGateway. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AttachVpnGatewayResult type AttachVpnGatewayOutput struct { _ struct{} `type:"structure"` @@ -24178,7 +24138,6 @@ func (s *AttachVpnGatewayOutput) SetVpcAttachment(v *VpcAttachment) *AttachVpnGa } // Describes a value for a resource attribute that is a Boolean value. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AttributeBooleanValue type AttributeBooleanValue struct { _ struct{} `type:"structure"` @@ -24203,7 +24162,6 @@ func (s *AttributeBooleanValue) SetValue(v bool) *AttributeBooleanValue { } // Describes a value for a resource attribute that is a String. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AttributeValue type AttributeValue struct { _ struct{} `type:"structure"` @@ -24228,7 +24186,6 @@ func (s *AttributeValue) SetValue(v string) *AttributeValue { } // Contains the parameters for AuthorizeSecurityGroupEgress. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AuthorizeSecurityGroupEgressRequest type AuthorizeSecurityGroupEgressInput struct { _ struct{} `type:"structure"` @@ -24346,7 +24303,6 @@ func (s *AuthorizeSecurityGroupEgressInput) SetToPort(v int64) *AuthorizeSecurit return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AuthorizeSecurityGroupEgressOutput type AuthorizeSecurityGroupEgressOutput struct { _ struct{} `type:"structure"` } @@ -24362,7 +24318,6 @@ func (s AuthorizeSecurityGroupEgressOutput) GoString() string { } // Contains the parameters for AuthorizeSecurityGroupIngress. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AuthorizeSecurityGroupIngressRequest type AuthorizeSecurityGroupIngressInput struct { _ struct{} `type:"structure"` @@ -24495,7 +24450,6 @@ func (s *AuthorizeSecurityGroupIngressInput) SetToPort(v int64) *AuthorizeSecuri return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AuthorizeSecurityGroupIngressOutput type AuthorizeSecurityGroupIngressOutput struct { _ struct{} `type:"structure"` } @@ -24511,7 +24465,6 @@ func (s AuthorizeSecurityGroupIngressOutput) GoString() string { } // Describes an Availability Zone. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AvailabilityZone type AvailabilityZone struct { _ struct{} `type:"structure"` @@ -24563,7 +24516,6 @@ func (s *AvailabilityZone) SetZoneName(v string) *AvailabilityZone { } // Describes a message about an Availability Zone. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AvailabilityZoneMessage type AvailabilityZoneMessage struct { _ struct{} `type:"structure"` @@ -24588,7 +24540,6 @@ func (s *AvailabilityZoneMessage) SetMessage(v string) *AvailabilityZoneMessage } // The capacity information for instances launched onto the Dedicated Host. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/AvailableCapacity type AvailableCapacity struct { _ struct{} `type:"structure"` @@ -24621,7 +24572,6 @@ func (s *AvailableCapacity) SetAvailableVCpus(v int64) *AvailableCapacity { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/BlobAttributeValue type BlobAttributeValue struct { _ struct{} `type:"structure"` @@ -24646,7 +24596,6 @@ func (s *BlobAttributeValue) SetValue(v []byte) *BlobAttributeValue { } // Describes a block device mapping. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/BlockDeviceMapping type BlockDeviceMapping struct { _ struct{} `type:"structure"` @@ -24709,7 +24658,6 @@ func (s *BlockDeviceMapping) SetVirtualName(v string) *BlockDeviceMapping { } // Contains the parameters for BundleInstance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/BundleInstanceRequest type BundleInstanceInput struct { _ struct{} `type:"structure"` @@ -24783,7 +24731,6 @@ func (s *BundleInstanceInput) SetStorage(v *Storage) *BundleInstanceInput { } // Contains the output of BundleInstance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/BundleInstanceResult type BundleInstanceOutput struct { _ struct{} `type:"structure"` @@ -24808,7 +24755,6 @@ func (s *BundleInstanceOutput) SetBundleTask(v *BundleTask) *BundleInstanceOutpu } // Describes a bundle task. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/BundleTask type BundleTask struct { _ struct{} `type:"structure"` @@ -24896,7 +24842,6 @@ func (s *BundleTask) SetUpdateTime(v time.Time) *BundleTask { } // Describes an error for BundleInstance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/BundleTaskError type BundleTaskError struct { _ struct{} `type:"structure"` @@ -24930,7 +24875,6 @@ func (s *BundleTaskError) SetMessage(v string) *BundleTaskError { } // Contains the parameters for CancelBundleTask. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CancelBundleTaskRequest type CancelBundleTaskInput struct { _ struct{} `type:"structure"` @@ -24982,7 +24926,6 @@ func (s *CancelBundleTaskInput) SetDryRun(v bool) *CancelBundleTaskInput { } // Contains the output of CancelBundleTask. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CancelBundleTaskResult type CancelBundleTaskOutput struct { _ struct{} `type:"structure"` @@ -25007,7 +24950,6 @@ func (s *CancelBundleTaskOutput) SetBundleTask(v *BundleTask) *CancelBundleTaskO } // Contains the parameters for CancelConversionTask. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CancelConversionRequest type CancelConversionTaskInput struct { _ struct{} `type:"structure"` @@ -25067,7 +25009,6 @@ func (s *CancelConversionTaskInput) SetReasonMessage(v string) *CancelConversion return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CancelConversionTaskOutput type CancelConversionTaskOutput struct { _ struct{} `type:"structure"` } @@ -25083,7 +25024,6 @@ func (s CancelConversionTaskOutput) GoString() string { } // Contains the parameters for CancelExportTask. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CancelExportTaskRequest type CancelExportTaskInput struct { _ struct{} `type:"structure"` @@ -25122,7 +25062,6 @@ func (s *CancelExportTaskInput) SetExportTaskId(v string) *CancelExportTaskInput return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CancelExportTaskOutput type CancelExportTaskOutput struct { _ struct{} `type:"structure"` } @@ -25138,7 +25077,6 @@ func (s CancelExportTaskOutput) GoString() string { } // Contains the parameters for CancelImportTask. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CancelImportTaskRequest type CancelImportTaskInput struct { _ struct{} `type:"structure"` @@ -25184,7 +25122,6 @@ func (s *CancelImportTaskInput) SetImportTaskId(v string) *CancelImportTaskInput } // Contains the output for CancelImportTask. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CancelImportTaskResult type CancelImportTaskOutput struct { _ struct{} `type:"structure"` @@ -25227,7 +25164,6 @@ func (s *CancelImportTaskOutput) SetState(v string) *CancelImportTaskOutput { } // Contains the parameters for CancelReservedInstancesListing. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CancelReservedInstancesListingRequest type CancelReservedInstancesListingInput struct { _ struct{} `type:"structure"` @@ -25267,7 +25203,6 @@ func (s *CancelReservedInstancesListingInput) SetReservedInstancesListingId(v st } // Contains the output of CancelReservedInstancesListing. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CancelReservedInstancesListingResult type CancelReservedInstancesListingOutput struct { _ struct{} `type:"structure"` @@ -25292,7 +25227,6 @@ func (s *CancelReservedInstancesListingOutput) SetReservedInstancesListings(v [] } // Describes a Spot Fleet error. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CancelSpotFleetRequestsError type CancelSpotFleetRequestsError struct { _ struct{} `type:"structure"` @@ -25330,7 +25264,6 @@ func (s *CancelSpotFleetRequestsError) SetMessage(v string) *CancelSpotFleetRequ } // Describes a Spot Fleet request that was not successfully canceled. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CancelSpotFleetRequestsErrorItem type CancelSpotFleetRequestsErrorItem struct { _ struct{} `type:"structure"` @@ -25368,7 +25301,6 @@ func (s *CancelSpotFleetRequestsErrorItem) SetSpotFleetRequestId(v string) *Canc } // Contains the parameters for CancelSpotFleetRequests. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CancelSpotFleetRequestsRequest type CancelSpotFleetRequestsInput struct { _ struct{} `type:"structure"` @@ -25435,7 +25367,6 @@ func (s *CancelSpotFleetRequestsInput) SetTerminateInstances(v bool) *CancelSpot } // Contains the output of CancelSpotFleetRequests. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CancelSpotFleetRequestsResponse type CancelSpotFleetRequestsOutput struct { _ struct{} `type:"structure"` @@ -25469,7 +25400,6 @@ func (s *CancelSpotFleetRequestsOutput) SetUnsuccessfulFleetRequests(v []*Cancel } // Describes a Spot Fleet request that was successfully canceled. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CancelSpotFleetRequestsSuccessItem type CancelSpotFleetRequestsSuccessItem struct { _ struct{} `type:"structure"` @@ -25518,7 +25448,6 @@ func (s *CancelSpotFleetRequestsSuccessItem) SetSpotFleetRequestId(v string) *Ca } // Contains the parameters for CancelSpotInstanceRequests. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CancelSpotInstanceRequestsRequest type CancelSpotInstanceRequestsInput struct { _ struct{} `type:"structure"` @@ -25570,7 +25499,6 @@ func (s *CancelSpotInstanceRequestsInput) SetSpotInstanceRequestIds(v []*string) } // Contains the output of CancelSpotInstanceRequests. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CancelSpotInstanceRequestsResult type CancelSpotInstanceRequestsOutput struct { _ struct{} `type:"structure"` @@ -25595,7 +25523,6 @@ func (s *CancelSpotInstanceRequestsOutput) SetCancelledSpotInstanceRequests(v [] } // Describes a request to cancel a Spot Instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CancelledSpotInstanceRequest type CancelledSpotInstanceRequest struct { _ struct{} `type:"structure"` @@ -25629,7 +25556,6 @@ func (s *CancelledSpotInstanceRequest) SetState(v string) *CancelledSpotInstance } // Describes an IPv4 CIDR block. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CidrBlock type CidrBlock struct { _ struct{} `type:"structure"` @@ -25654,7 +25580,6 @@ func (s *CidrBlock) SetCidrBlock(v string) *CidrBlock { } // Describes the ClassicLink DNS support status of a VPC. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ClassicLinkDnsSupport type ClassicLinkDnsSupport struct { _ struct{} `type:"structure"` @@ -25688,7 +25613,6 @@ func (s *ClassicLinkDnsSupport) SetVpcId(v string) *ClassicLinkDnsSupport { } // Describes a linked EC2-Classic instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ClassicLinkInstance type ClassicLinkInstance struct { _ struct{} `type:"structure"` @@ -25740,7 +25664,6 @@ func (s *ClassicLinkInstance) SetVpcId(v string) *ClassicLinkInstance { } // Describes a Classic Load Balancer. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ClassicLoadBalancer type ClassicLoadBalancer struct { _ struct{} `type:"structure"` @@ -25781,7 +25704,6 @@ func (s *ClassicLoadBalancer) SetName(v string) *ClassicLoadBalancer { // Describes the Classic Load Balancers to attach to a Spot Fleet. Spot Fleet // registers the running Spot Instances with these Classic Load Balancers. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ClassicLoadBalancersConfig type ClassicLoadBalancersConfig struct { _ struct{} `type:"structure"` @@ -25834,7 +25756,6 @@ func (s *ClassicLoadBalancersConfig) SetClassicLoadBalancers(v []*ClassicLoadBal } // Describes the client-specific data. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ClientData type ClientData struct { _ struct{} `type:"structure"` @@ -25886,7 +25807,6 @@ func (s *ClientData) SetUploadStart(v time.Time) *ClientData { } // Contains the parameters for ConfirmProductInstance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ConfirmProductInstanceRequest type ConfirmProductInstanceInput struct { _ struct{} `type:"structure"` @@ -25952,7 +25872,6 @@ func (s *ConfirmProductInstanceInput) SetProductCode(v string) *ConfirmProductIn } // Contains the output of ConfirmProductInstance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ConfirmProductInstanceResult type ConfirmProductInstanceOutput struct { _ struct{} `type:"structure"` @@ -25988,7 +25907,6 @@ func (s *ConfirmProductInstanceOutput) SetReturn(v bool) *ConfirmProductInstance } // Describes a connection notification for a VPC endpoint or VPC endpoint service. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ConnectionNotification type ConnectionNotification struct { _ struct{} `type:"structure"` @@ -26068,7 +25986,6 @@ func (s *ConnectionNotification) SetVpcEndpointId(v string) *ConnectionNotificat } // Describes a conversion task. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ConversionTask type ConversionTask struct { _ struct{} `type:"structure"` @@ -26153,7 +26070,6 @@ func (s *ConversionTask) SetTags(v []*Tag) *ConversionTask { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CopyFpgaImageRequest type CopyFpgaImageInput struct { _ struct{} `type:"structure"` @@ -26246,7 +26162,6 @@ func (s *CopyFpgaImageInput) SetSourceRegion(v string) *CopyFpgaImageInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CopyFpgaImageResult type CopyFpgaImageOutput struct { _ struct{} `type:"structure"` @@ -26271,7 +26186,6 @@ func (s *CopyFpgaImageOutput) SetFpgaImageId(v string) *CopyFpgaImageOutput { } // Contains the parameters for CopyImage. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CopyImageRequest type CopyImageInput struct { _ struct{} `type:"structure"` @@ -26400,7 +26314,6 @@ func (s *CopyImageInput) SetSourceRegion(v string) *CopyImageInput { } // Contains the output of CopyImage. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CopyImageResult type CopyImageOutput struct { _ struct{} `type:"structure"` @@ -26425,7 +26338,6 @@ func (s *CopyImageOutput) SetImageId(v string) *CopyImageOutput { } // Contains the parameters for CopySnapshot. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CopySnapshotRequest type CopySnapshotInput struct { _ struct{} `type:"structure"` @@ -26567,7 +26479,6 @@ func (s *CopySnapshotInput) SetSourceSnapshotId(v string) *CopySnapshotInput { } // Contains the output of CopySnapshot. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CopySnapshotResult type CopySnapshotOutput struct { _ struct{} `type:"structure"` @@ -26592,7 +26503,6 @@ func (s *CopySnapshotOutput) SetSnapshotId(v string) *CopySnapshotOutput { } // Contains the parameters for CreateCustomerGateway. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateCustomerGatewayRequest type CreateCustomerGatewayInput struct { _ struct{} `type:"structure"` @@ -26675,7 +26585,6 @@ func (s *CreateCustomerGatewayInput) SetType(v string) *CreateCustomerGatewayInp } // Contains the output of CreateCustomerGateway. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateCustomerGatewayResult type CreateCustomerGatewayOutput struct { _ struct{} `type:"structure"` @@ -26699,7 +26608,6 @@ func (s *CreateCustomerGatewayOutput) SetCustomerGateway(v *CustomerGateway) *Cr return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateDefaultSubnetRequest type CreateDefaultSubnetInput struct { _ struct{} `type:"structure"` @@ -26750,7 +26658,6 @@ func (s *CreateDefaultSubnetInput) SetDryRun(v bool) *CreateDefaultSubnetInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateDefaultSubnetResult type CreateDefaultSubnetOutput struct { _ struct{} `type:"structure"` @@ -26775,7 +26682,6 @@ func (s *CreateDefaultSubnetOutput) SetSubnet(v *Subnet) *CreateDefaultSubnetOut } // Contains the parameters for CreateDefaultVpc. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateDefaultVpcRequest type CreateDefaultVpcInput struct { _ struct{} `type:"structure"` @@ -26803,7 +26709,6 @@ func (s *CreateDefaultVpcInput) SetDryRun(v bool) *CreateDefaultVpcInput { } // Contains the output of CreateDefaultVpc. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateDefaultVpcResult type CreateDefaultVpcOutput struct { _ struct{} `type:"structure"` @@ -26828,7 +26733,6 @@ func (s *CreateDefaultVpcOutput) SetVpc(v *Vpc) *CreateDefaultVpcOutput { } // Contains the parameters for CreateDhcpOptions. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateDhcpOptionsRequest type CreateDhcpOptionsInput struct { _ struct{} `type:"structure"` @@ -26880,7 +26784,6 @@ func (s *CreateDhcpOptionsInput) SetDryRun(v bool) *CreateDhcpOptionsInput { } // Contains the output of CreateDhcpOptions. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateDhcpOptionsResult type CreateDhcpOptionsOutput struct { _ struct{} `type:"structure"` @@ -26904,7 +26807,6 @@ func (s *CreateDhcpOptionsOutput) SetDhcpOptions(v *DhcpOptions) *CreateDhcpOpti return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateEgressOnlyInternetGatewayRequest type CreateEgressOnlyInternetGatewayInput struct { _ struct{} `type:"structure"` @@ -26965,7 +26867,6 @@ func (s *CreateEgressOnlyInternetGatewayInput) SetVpcId(v string) *CreateEgressO return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateEgressOnlyInternetGatewayResult type CreateEgressOnlyInternetGatewayOutput struct { _ struct{} `type:"structure"` @@ -27000,7 +26901,6 @@ func (s *CreateEgressOnlyInternetGatewayOutput) SetEgressOnlyInternetGateway(v * } // Contains the parameters for CreateFlowLogs. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateFlowLogsRequest type CreateFlowLogsInput struct { _ struct{} `type:"structure"` @@ -27109,7 +27009,6 @@ func (s *CreateFlowLogsInput) SetTrafficType(v string) *CreateFlowLogsInput { } // Contains the output of CreateFlowLogs. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateFlowLogsResult type CreateFlowLogsOutput struct { _ struct{} `type:"structure"` @@ -27152,7 +27051,6 @@ func (s *CreateFlowLogsOutput) SetUnsuccessful(v []*UnsuccessfulItem) *CreateFlo return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateFpgaImageRequest type CreateFpgaImageInput struct { _ struct{} `type:"structure"` @@ -27241,7 +27139,6 @@ func (s *CreateFpgaImageInput) SetName(v string) *CreateFpgaImageInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateFpgaImageResult type CreateFpgaImageOutput struct { _ struct{} `type:"structure"` @@ -27275,7 +27172,6 @@ func (s *CreateFpgaImageOutput) SetFpgaImageId(v string) *CreateFpgaImageOutput } // Contains the parameters for CreateImage. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateImageRequest type CreateImageInput struct { _ struct{} `type:"structure"` @@ -27375,7 +27271,6 @@ func (s *CreateImageInput) SetNoReboot(v bool) *CreateImageInput { } // Contains the output of CreateImage. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateImageResult type CreateImageOutput struct { _ struct{} `type:"structure"` @@ -27400,7 +27295,6 @@ func (s *CreateImageOutput) SetImageId(v string) *CreateImageOutput { } // Contains the parameters for CreateInstanceExportTask. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateInstanceExportTaskRequest type CreateInstanceExportTaskInput struct { _ struct{} `type:"structure"` @@ -27468,7 +27362,6 @@ func (s *CreateInstanceExportTaskInput) SetTargetEnvironment(v string) *CreateIn } // Contains the output for CreateInstanceExportTask. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateInstanceExportTaskResult type CreateInstanceExportTaskOutput struct { _ struct{} `type:"structure"` @@ -27493,7 +27386,6 @@ func (s *CreateInstanceExportTaskOutput) SetExportTask(v *ExportTask) *CreateIns } // Contains the parameters for CreateInternetGateway. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateInternetGatewayRequest type CreateInternetGatewayInput struct { _ struct{} `type:"structure"` @@ -27521,7 +27413,6 @@ func (s *CreateInternetGatewayInput) SetDryRun(v bool) *CreateInternetGatewayInp } // Contains the output of CreateInternetGateway. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateInternetGatewayResult type CreateInternetGatewayOutput struct { _ struct{} `type:"structure"` @@ -27546,7 +27437,6 @@ func (s *CreateInternetGatewayOutput) SetInternetGateway(v *InternetGateway) *Cr } // Contains the parameters for CreateKeyPair. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateKeyPairRequest type CreateKeyPairInput struct { _ struct{} `type:"structure"` @@ -27600,7 +27490,6 @@ func (s *CreateKeyPairInput) SetKeyName(v string) *CreateKeyPairInput { } // Describes a key pair. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/KeyPair type CreateKeyPairOutput struct { _ struct{} `type:"structure"` @@ -27642,7 +27531,6 @@ func (s *CreateKeyPairOutput) SetKeyName(v string) *CreateKeyPairOutput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateLaunchTemplateRequest type CreateLaunchTemplateInput struct { _ struct{} `type:"structure"` @@ -27734,7 +27622,6 @@ func (s *CreateLaunchTemplateInput) SetVersionDescription(v string) *CreateLaunc return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateLaunchTemplateResult type CreateLaunchTemplateOutput struct { _ struct{} `type:"structure"` @@ -27758,7 +27645,6 @@ func (s *CreateLaunchTemplateOutput) SetLaunchTemplate(v *LaunchTemplate) *Creat return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateLaunchTemplateVersionRequest type CreateLaunchTemplateVersionInput struct { _ struct{} `type:"structure"` @@ -27867,7 +27753,6 @@ func (s *CreateLaunchTemplateVersionInput) SetVersionDescription(v string) *Crea return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateLaunchTemplateVersionResult type CreateLaunchTemplateVersionOutput struct { _ struct{} `type:"structure"` @@ -27892,7 +27777,6 @@ func (s *CreateLaunchTemplateVersionOutput) SetLaunchTemplateVersion(v *LaunchTe } // Contains the parameters for CreateNatGateway. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateNatGatewayRequest type CreateNatGatewayInput struct { _ struct{} `type:"structure"` @@ -27960,7 +27844,6 @@ func (s *CreateNatGatewayInput) SetSubnetId(v string) *CreateNatGatewayInput { } // Contains the output of CreateNatGateway. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateNatGatewayResult type CreateNatGatewayOutput struct { _ struct{} `type:"structure"` @@ -27995,7 +27878,6 @@ func (s *CreateNatGatewayOutput) SetNatGateway(v *NatGateway) *CreateNatGatewayO } // Contains the parameters for CreateNetworkAclEntry. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateNetworkAclEntryRequest type CreateNetworkAclEntryInput struct { _ struct{} `type:"structure"` @@ -28150,7 +28032,6 @@ func (s *CreateNetworkAclEntryInput) SetRuleNumber(v int64) *CreateNetworkAclEnt return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateNetworkAclEntryOutput type CreateNetworkAclEntryOutput struct { _ struct{} `type:"structure"` } @@ -28166,7 +28047,6 @@ func (s CreateNetworkAclEntryOutput) GoString() string { } // Contains the parameters for CreateNetworkAcl. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateNetworkAclRequest type CreateNetworkAclInput struct { _ struct{} `type:"structure"` @@ -28218,7 +28098,6 @@ func (s *CreateNetworkAclInput) SetVpcId(v string) *CreateNetworkAclInput { } // Contains the output of CreateNetworkAcl. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateNetworkAclResult type CreateNetworkAclOutput struct { _ struct{} `type:"structure"` @@ -28243,7 +28122,6 @@ func (s *CreateNetworkAclOutput) SetNetworkAcl(v *NetworkAcl) *CreateNetworkAclO } // Contains the parameters for CreateNetworkInterface. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateNetworkInterfaceRequest type CreateNetworkInterfaceInput struct { _ struct{} `type:"structure"` @@ -28385,7 +28263,6 @@ func (s *CreateNetworkInterfaceInput) SetSubnetId(v string) *CreateNetworkInterf } // Contains the output of CreateNetworkInterface. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateNetworkInterfaceResult type CreateNetworkInterfaceOutput struct { _ struct{} `type:"structure"` @@ -28410,7 +28287,6 @@ func (s *CreateNetworkInterfaceOutput) SetNetworkInterface(v *NetworkInterface) } // Contains the parameters for CreateNetworkInterfacePermission. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateNetworkInterfacePermissionRequest type CreateNetworkInterfacePermissionInput struct { _ struct{} `type:"structure"` @@ -28494,7 +28370,6 @@ func (s *CreateNetworkInterfacePermissionInput) SetPermission(v string) *CreateN } // Contains the output of CreateNetworkInterfacePermission. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateNetworkInterfacePermissionResult type CreateNetworkInterfacePermissionOutput struct { _ struct{} `type:"structure"` @@ -28519,7 +28394,6 @@ func (s *CreateNetworkInterfacePermissionOutput) SetInterfacePermission(v *Netwo } // Contains the parameters for CreatePlacementGroup. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreatePlacementGroupRequest type CreatePlacementGroupInput struct { _ struct{} `type:"structure"` @@ -28587,7 +28461,6 @@ func (s *CreatePlacementGroupInput) SetStrategy(v string) *CreatePlacementGroupI return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreatePlacementGroupOutput type CreatePlacementGroupOutput struct { _ struct{} `type:"structure"` } @@ -28603,7 +28476,6 @@ func (s CreatePlacementGroupOutput) GoString() string { } // Contains the parameters for CreateReservedInstancesListing. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateReservedInstancesListingRequest type CreateReservedInstancesListingInput struct { _ struct{} `type:"structure"` @@ -28691,7 +28563,6 @@ func (s *CreateReservedInstancesListingInput) SetReservedInstancesId(v string) * } // Contains the output of CreateReservedInstancesListing. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateReservedInstancesListingResult type CreateReservedInstancesListingOutput struct { _ struct{} `type:"structure"` @@ -28716,7 +28587,6 @@ func (s *CreateReservedInstancesListingOutput) SetReservedInstancesListings(v [] } // Contains the parameters for CreateRoute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateRouteRequest type CreateRouteInput struct { _ struct{} `type:"structure"` @@ -28844,7 +28714,6 @@ func (s *CreateRouteInput) SetVpcPeeringConnectionId(v string) *CreateRouteInput } // Contains the output of CreateRoute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateRouteResult type CreateRouteOutput struct { _ struct{} `type:"structure"` @@ -28869,7 +28738,6 @@ func (s *CreateRouteOutput) SetReturn(v bool) *CreateRouteOutput { } // Contains the parameters for CreateRouteTable. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateRouteTableRequest type CreateRouteTableInput struct { _ struct{} `type:"structure"` @@ -28921,7 +28789,6 @@ func (s *CreateRouteTableInput) SetVpcId(v string) *CreateRouteTableInput { } // Contains the output of CreateRouteTable. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateRouteTableResult type CreateRouteTableOutput struct { _ struct{} `type:"structure"` @@ -28946,7 +28813,6 @@ func (s *CreateRouteTableOutput) SetRouteTable(v *RouteTable) *CreateRouteTableO } // Contains the parameters for CreateSecurityGroup. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateSecurityGroupRequest type CreateSecurityGroupInput struct { _ struct{} `type:"structure"` @@ -29033,7 +28899,6 @@ func (s *CreateSecurityGroupInput) SetVpcId(v string) *CreateSecurityGroupInput } // Contains the output of CreateSecurityGroup. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateSecurityGroupResult type CreateSecurityGroupOutput struct { _ struct{} `type:"structure"` @@ -29058,7 +28923,6 @@ func (s *CreateSecurityGroupOutput) SetGroupId(v string) *CreateSecurityGroupOut } // Contains the parameters for CreateSnapshot. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateSnapshotRequest type CreateSnapshotInput struct { _ struct{} `type:"structure"` @@ -29119,7 +28983,6 @@ func (s *CreateSnapshotInput) SetVolumeId(v string) *CreateSnapshotInput { } // Contains the parameters for CreateSpotDatafeedSubscription. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateSpotDatafeedSubscriptionRequest type CreateSpotDatafeedSubscriptionInput struct { _ struct{} `type:"structure"` @@ -29180,7 +29043,6 @@ func (s *CreateSpotDatafeedSubscriptionInput) SetPrefix(v string) *CreateSpotDat } // Contains the output of CreateSpotDatafeedSubscription. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateSpotDatafeedSubscriptionResult type CreateSpotDatafeedSubscriptionOutput struct { _ struct{} `type:"structure"` @@ -29205,7 +29067,6 @@ func (s *CreateSpotDatafeedSubscriptionOutput) SetSpotDatafeedSubscription(v *Sp } // Contains the parameters for CreateSubnet. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateSubnetRequest type CreateSubnetInput struct { _ struct{} `type:"structure"` @@ -29293,7 +29154,6 @@ func (s *CreateSubnetInput) SetVpcId(v string) *CreateSubnetInput { } // Contains the output of CreateSubnet. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateSubnetResult type CreateSubnetOutput struct { _ struct{} `type:"structure"` @@ -29318,7 +29178,6 @@ func (s *CreateSubnetOutput) SetSubnet(v *Subnet) *CreateSubnetOutput { } // Contains the parameters for CreateTags. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateTagsRequest type CreateTagsInput struct { _ struct{} `type:"structure"` @@ -29385,7 +29244,6 @@ func (s *CreateTagsInput) SetTags(v []*Tag) *CreateTagsInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateTagsOutput type CreateTagsOutput struct { _ struct{} `type:"structure"` } @@ -29401,7 +29259,6 @@ func (s CreateTagsOutput) GoString() string { } // Contains the parameters for CreateVolume. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateVolumeRequest type CreateVolumeInput struct { _ struct{} `type:"structure"` @@ -29545,7 +29402,6 @@ func (s *CreateVolumeInput) SetVolumeType(v string) *CreateVolumeInput { // Describes the user or group to be added or removed from the permissions for // a volume. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateVolumePermission type CreateVolumePermission struct { _ struct{} `type:"structure"` @@ -29581,7 +29437,6 @@ func (s *CreateVolumePermission) SetUserId(v string) *CreateVolumePermission { } // Describes modifications to the permissions for a volume. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateVolumePermissionModifications type CreateVolumePermissionModifications struct { _ struct{} `type:"structure"` @@ -29616,7 +29471,6 @@ func (s *CreateVolumePermissionModifications) SetRemove(v []*CreateVolumePermiss return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateVpcEndpointConnectionNotificationRequest type CreateVpcEndpointConnectionNotificationInput struct { _ struct{} `type:"structure"` @@ -29710,7 +29564,6 @@ func (s *CreateVpcEndpointConnectionNotificationInput) SetVpcEndpointId(v string return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateVpcEndpointConnectionNotificationResult type CreateVpcEndpointConnectionNotificationOutput struct { _ struct{} `type:"structure"` @@ -29745,7 +29598,6 @@ func (s *CreateVpcEndpointConnectionNotificationOutput) SetConnectionNotificatio } // Contains the parameters for CreateVpcEndpoint. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateVpcEndpointRequest type CreateVpcEndpointInput struct { _ struct{} `type:"structure"` @@ -29895,7 +29747,6 @@ func (s *CreateVpcEndpointInput) SetVpcId(v string) *CreateVpcEndpointInput { } // Contains the output of CreateVpcEndpoint. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateVpcEndpointResult type CreateVpcEndpointOutput struct { _ struct{} `type:"structure"` @@ -29929,7 +29780,6 @@ func (s *CreateVpcEndpointOutput) SetVpcEndpoint(v *VpcEndpoint) *CreateVpcEndpo return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateVpcEndpointServiceConfigurationRequest type CreateVpcEndpointServiceConfigurationInput struct { _ struct{} `type:"structure"` @@ -30001,7 +29851,6 @@ func (s *CreateVpcEndpointServiceConfigurationInput) SetNetworkLoadBalancerArns( return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateVpcEndpointServiceConfigurationResult type CreateVpcEndpointServiceConfigurationOutput struct { _ struct{} `type:"structure"` @@ -30036,7 +29885,6 @@ func (s *CreateVpcEndpointServiceConfigurationOutput) SetServiceConfiguration(v } // Contains the parameters for CreateVpc. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateVpcRequest type CreateVpcInput struct { _ struct{} `type:"structure"` @@ -30117,7 +29965,6 @@ func (s *CreateVpcInput) SetInstanceTenancy(v string) *CreateVpcInput { } // Contains the output of CreateVpc. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateVpcResult type CreateVpcOutput struct { _ struct{} `type:"structure"` @@ -30142,7 +29989,6 @@ func (s *CreateVpcOutput) SetVpc(v *Vpc) *CreateVpcOutput { } // Contains the parameters for CreateVpcPeeringConnection. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateVpcPeeringConnectionRequest type CreateVpcPeeringConnectionInput struct { _ struct{} `type:"structure"` @@ -30212,7 +30058,6 @@ func (s *CreateVpcPeeringConnectionInput) SetVpcId(v string) *CreateVpcPeeringCo } // Contains the output of CreateVpcPeeringConnection. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateVpcPeeringConnectionResult type CreateVpcPeeringConnectionOutput struct { _ struct{} `type:"structure"` @@ -30237,7 +30082,6 @@ func (s *CreateVpcPeeringConnectionOutput) SetVpcPeeringConnection(v *VpcPeering } // Contains the parameters for CreateVpnConnection. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateVpnConnectionRequest type CreateVpnConnectionInput struct { _ struct{} `type:"structure"` @@ -30326,7 +30170,6 @@ func (s *CreateVpnConnectionInput) SetVpnGatewayId(v string) *CreateVpnConnectio } // Contains the output of CreateVpnConnection. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateVpnConnectionResult type CreateVpnConnectionOutput struct { _ struct{} `type:"structure"` @@ -30351,7 +30194,6 @@ func (s *CreateVpnConnectionOutput) SetVpnConnection(v *VpnConnection) *CreateVp } // Contains the parameters for CreateVpnConnectionRoute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateVpnConnectionRouteRequest type CreateVpnConnectionRouteInput struct { _ struct{} `type:"structure"` @@ -30404,7 +30246,6 @@ func (s *CreateVpnConnectionRouteInput) SetVpnConnectionId(v string) *CreateVpnC return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateVpnConnectionRouteOutput type CreateVpnConnectionRouteOutput struct { _ struct{} `type:"structure"` } @@ -30420,7 +30261,6 @@ func (s CreateVpnConnectionRouteOutput) GoString() string { } // Contains the parameters for CreateVpnGateway. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateVpnGatewayRequest type CreateVpnGatewayInput struct { _ struct{} `type:"structure"` @@ -30494,7 +30334,6 @@ func (s *CreateVpnGatewayInput) SetType(v string) *CreateVpnGatewayInput { } // Contains the output of CreateVpnGateway. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreateVpnGatewayResult type CreateVpnGatewayOutput struct { _ struct{} `type:"structure"` @@ -30519,7 +30358,6 @@ func (s *CreateVpnGatewayOutput) SetVpnGateway(v *VpnGateway) *CreateVpnGatewayO } // Describes the credit option for CPU usage of a T2 instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreditSpecification type CreditSpecification struct { _ struct{} `type:"structure"` @@ -30544,7 +30382,6 @@ func (s *CreditSpecification) SetCpuCredits(v string) *CreditSpecification { } // The credit option for CPU usage of a T2 instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CreditSpecificationRequest type CreditSpecificationRequest struct { _ struct{} `type:"structure"` @@ -30585,7 +30422,6 @@ func (s *CreditSpecificationRequest) SetCpuCredits(v string) *CreditSpecificatio } // Describes a customer gateway. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/CustomerGateway type CustomerGateway struct { _ struct{} `type:"structure"` @@ -30657,7 +30493,6 @@ func (s *CustomerGateway) SetType(v string) *CustomerGateway { } // Contains the parameters for DeleteCustomerGateway. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteCustomerGatewayRequest type DeleteCustomerGatewayInput struct { _ struct{} `type:"structure"` @@ -30708,7 +30543,6 @@ func (s *DeleteCustomerGatewayInput) SetDryRun(v bool) *DeleteCustomerGatewayInp return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteCustomerGatewayOutput type DeleteCustomerGatewayOutput struct { _ struct{} `type:"structure"` } @@ -30724,7 +30558,6 @@ func (s DeleteCustomerGatewayOutput) GoString() string { } // Contains the parameters for DeleteDhcpOptions. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteDhcpOptionsRequest type DeleteDhcpOptionsInput struct { _ struct{} `type:"structure"` @@ -30775,7 +30608,6 @@ func (s *DeleteDhcpOptionsInput) SetDryRun(v bool) *DeleteDhcpOptionsInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteDhcpOptionsOutput type DeleteDhcpOptionsOutput struct { _ struct{} `type:"structure"` } @@ -30790,7 +30622,6 @@ func (s DeleteDhcpOptionsOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteEgressOnlyInternetGatewayRequest type DeleteEgressOnlyInternetGatewayInput struct { _ struct{} `type:"structure"` @@ -30841,7 +30672,6 @@ func (s *DeleteEgressOnlyInternetGatewayInput) SetEgressOnlyInternetGatewayId(v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteEgressOnlyInternetGatewayResult type DeleteEgressOnlyInternetGatewayOutput struct { _ struct{} `type:"structure"` @@ -30866,7 +30696,6 @@ func (s *DeleteEgressOnlyInternetGatewayOutput) SetReturnCode(v bool) *DeleteEgr } // Contains the parameters for DeleteFlowLogs. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteFlowLogsRequest type DeleteFlowLogsInput struct { _ struct{} `type:"structure"` @@ -30906,7 +30735,6 @@ func (s *DeleteFlowLogsInput) SetFlowLogIds(v []*string) *DeleteFlowLogsInput { } // Contains the output of DeleteFlowLogs. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteFlowLogsResult type DeleteFlowLogsOutput struct { _ struct{} `type:"structure"` @@ -30930,7 +30758,6 @@ func (s *DeleteFlowLogsOutput) SetUnsuccessful(v []*UnsuccessfulItem) *DeleteFlo return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteFpgaImageRequest type DeleteFpgaImageInput struct { _ struct{} `type:"structure"` @@ -30981,7 +30808,6 @@ func (s *DeleteFpgaImageInput) SetFpgaImageId(v string) *DeleteFpgaImageInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteFpgaImageResult type DeleteFpgaImageOutput struct { _ struct{} `type:"structure"` @@ -31006,7 +30832,6 @@ func (s *DeleteFpgaImageOutput) SetReturn(v bool) *DeleteFpgaImageOutput { } // Contains the parameters for DeleteInternetGateway. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteInternetGatewayRequest type DeleteInternetGatewayInput struct { _ struct{} `type:"structure"` @@ -31057,7 +30882,6 @@ func (s *DeleteInternetGatewayInput) SetInternetGatewayId(v string) *DeleteInter return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteInternetGatewayOutput type DeleteInternetGatewayOutput struct { _ struct{} `type:"structure"` } @@ -31073,7 +30897,6 @@ func (s DeleteInternetGatewayOutput) GoString() string { } // Contains the parameters for DeleteKeyPair. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteKeyPairRequest type DeleteKeyPairInput struct { _ struct{} `type:"structure"` @@ -31124,7 +30947,6 @@ func (s *DeleteKeyPairInput) SetKeyName(v string) *DeleteKeyPairInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteKeyPairOutput type DeleteKeyPairOutput struct { _ struct{} `type:"structure"` } @@ -31139,7 +30961,6 @@ func (s DeleteKeyPairOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteLaunchTemplateRequest type DeleteLaunchTemplateInput struct { _ struct{} `type:"structure"` @@ -31199,7 +31020,6 @@ func (s *DeleteLaunchTemplateInput) SetLaunchTemplateName(v string) *DeleteLaunc return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteLaunchTemplateResult type DeleteLaunchTemplateOutput struct { _ struct{} `type:"structure"` @@ -31223,7 +31043,6 @@ func (s *DeleteLaunchTemplateOutput) SetLaunchTemplate(v *LaunchTemplate) *Delet return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteLaunchTemplateVersionsRequest type DeleteLaunchTemplateVersionsInput struct { _ struct{} `type:"structure"` @@ -31297,7 +31116,6 @@ func (s *DeleteLaunchTemplateVersionsInput) SetVersions(v []*string) *DeleteLaun return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteLaunchTemplateVersionsResult type DeleteLaunchTemplateVersionsOutput struct { _ struct{} `type:"structure"` @@ -31331,7 +31149,6 @@ func (s *DeleteLaunchTemplateVersionsOutput) SetUnsuccessfullyDeletedLaunchTempl } // Describes a launch template version that could not be deleted. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteLaunchTemplateVersionsResponseErrorItem type DeleteLaunchTemplateVersionsResponseErrorItem struct { _ struct{} `type:"structure"` @@ -31383,7 +31200,6 @@ func (s *DeleteLaunchTemplateVersionsResponseErrorItem) SetVersionNumber(v int64 } // Describes a launch template version that was successfully deleted. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteLaunchTemplateVersionsResponseSuccessItem type DeleteLaunchTemplateVersionsResponseSuccessItem struct { _ struct{} `type:"structure"` @@ -31426,7 +31242,6 @@ func (s *DeleteLaunchTemplateVersionsResponseSuccessItem) SetVersionNumber(v int } // Contains the parameters for DeleteNatGateway. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteNatGatewayRequest type DeleteNatGatewayInput struct { _ struct{} `type:"structure"` @@ -31466,7 +31281,6 @@ func (s *DeleteNatGatewayInput) SetNatGatewayId(v string) *DeleteNatGatewayInput } // Contains the output of DeleteNatGateway. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteNatGatewayResult type DeleteNatGatewayOutput struct { _ struct{} `type:"structure"` @@ -31491,7 +31305,6 @@ func (s *DeleteNatGatewayOutput) SetNatGatewayId(v string) *DeleteNatGatewayOutp } // Contains the parameters for DeleteNetworkAclEntry. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteNetworkAclEntryRequest type DeleteNetworkAclEntryInput struct { _ struct{} `type:"structure"` @@ -31570,7 +31383,6 @@ func (s *DeleteNetworkAclEntryInput) SetRuleNumber(v int64) *DeleteNetworkAclEnt return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteNetworkAclEntryOutput type DeleteNetworkAclEntryOutput struct { _ struct{} `type:"structure"` } @@ -31586,7 +31398,6 @@ func (s DeleteNetworkAclEntryOutput) GoString() string { } // Contains the parameters for DeleteNetworkAcl. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteNetworkAclRequest type DeleteNetworkAclInput struct { _ struct{} `type:"structure"` @@ -31637,7 +31448,6 @@ func (s *DeleteNetworkAclInput) SetNetworkAclId(v string) *DeleteNetworkAclInput return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteNetworkAclOutput type DeleteNetworkAclOutput struct { _ struct{} `type:"structure"` } @@ -31653,7 +31463,6 @@ func (s DeleteNetworkAclOutput) GoString() string { } // Contains the parameters for DeleteNetworkInterface. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteNetworkInterfaceRequest type DeleteNetworkInterfaceInput struct { _ struct{} `type:"structure"` @@ -31704,7 +31513,6 @@ func (s *DeleteNetworkInterfaceInput) SetNetworkInterfaceId(v string) *DeleteNet return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteNetworkInterfaceOutput type DeleteNetworkInterfaceOutput struct { _ struct{} `type:"structure"` } @@ -31720,7 +31528,6 @@ func (s DeleteNetworkInterfaceOutput) GoString() string { } // Contains the parameters for DeleteNetworkInterfacePermission. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteNetworkInterfacePermissionRequest type DeleteNetworkInterfacePermissionInput struct { _ struct{} `type:"structure"` @@ -31782,7 +31589,6 @@ func (s *DeleteNetworkInterfacePermissionInput) SetNetworkInterfacePermissionId( } // Contains the output for DeleteNetworkInterfacePermission. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteNetworkInterfacePermissionResult type DeleteNetworkInterfacePermissionOutput struct { _ struct{} `type:"structure"` @@ -31807,7 +31613,6 @@ func (s *DeleteNetworkInterfacePermissionOutput) SetReturn(v bool) *DeleteNetwor } // Contains the parameters for DeletePlacementGroup. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeletePlacementGroupRequest type DeletePlacementGroupInput struct { _ struct{} `type:"structure"` @@ -31858,7 +31663,6 @@ func (s *DeletePlacementGroupInput) SetGroupName(v string) *DeletePlacementGroup return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeletePlacementGroupOutput type DeletePlacementGroupOutput struct { _ struct{} `type:"structure"` } @@ -31874,7 +31678,6 @@ func (s DeletePlacementGroupOutput) GoString() string { } // Contains the parameters for DeleteRoute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteRouteRequest type DeleteRouteInput struct { _ struct{} `type:"structure"` @@ -31945,7 +31748,6 @@ func (s *DeleteRouteInput) SetRouteTableId(v string) *DeleteRouteInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteRouteOutput type DeleteRouteOutput struct { _ struct{} `type:"structure"` } @@ -31961,7 +31763,6 @@ func (s DeleteRouteOutput) GoString() string { } // Contains the parameters for DeleteRouteTable. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteRouteTableRequest type DeleteRouteTableInput struct { _ struct{} `type:"structure"` @@ -32012,7 +31813,6 @@ func (s *DeleteRouteTableInput) SetRouteTableId(v string) *DeleteRouteTableInput return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteRouteTableOutput type DeleteRouteTableOutput struct { _ struct{} `type:"structure"` } @@ -32028,7 +31828,6 @@ func (s DeleteRouteTableOutput) GoString() string { } // Contains the parameters for DeleteSecurityGroup. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteSecurityGroupRequest type DeleteSecurityGroupInput struct { _ struct{} `type:"structure"` @@ -32074,7 +31873,6 @@ func (s *DeleteSecurityGroupInput) SetGroupName(v string) *DeleteSecurityGroupIn return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteSecurityGroupOutput type DeleteSecurityGroupOutput struct { _ struct{} `type:"structure"` } @@ -32090,7 +31888,6 @@ func (s DeleteSecurityGroupOutput) GoString() string { } // Contains the parameters for DeleteSnapshot. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteSnapshotRequest type DeleteSnapshotInput struct { _ struct{} `type:"structure"` @@ -32141,7 +31938,6 @@ func (s *DeleteSnapshotInput) SetSnapshotId(v string) *DeleteSnapshotInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteSnapshotOutput type DeleteSnapshotOutput struct { _ struct{} `type:"structure"` } @@ -32157,7 +31953,6 @@ func (s DeleteSnapshotOutput) GoString() string { } // Contains the parameters for DeleteSpotDatafeedSubscription. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteSpotDatafeedSubscriptionRequest type DeleteSpotDatafeedSubscriptionInput struct { _ struct{} `type:"structure"` @@ -32184,7 +31979,6 @@ func (s *DeleteSpotDatafeedSubscriptionInput) SetDryRun(v bool) *DeleteSpotDataf return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteSpotDatafeedSubscriptionOutput type DeleteSpotDatafeedSubscriptionOutput struct { _ struct{} `type:"structure"` } @@ -32200,7 +31994,6 @@ func (s DeleteSpotDatafeedSubscriptionOutput) GoString() string { } // Contains the parameters for DeleteSubnet. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteSubnetRequest type DeleteSubnetInput struct { _ struct{} `type:"structure"` @@ -32251,7 +32044,6 @@ func (s *DeleteSubnetInput) SetSubnetId(v string) *DeleteSubnetInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteSubnetOutput type DeleteSubnetOutput struct { _ struct{} `type:"structure"` } @@ -32267,7 +32059,6 @@ func (s DeleteSubnetOutput) GoString() string { } // Contains the parameters for DeleteTags. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteTagsRequest type DeleteTagsInput struct { _ struct{} `type:"structure"` @@ -32332,7 +32123,6 @@ func (s *DeleteTagsInput) SetTags(v []*Tag) *DeleteTagsInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteTagsOutput type DeleteTagsOutput struct { _ struct{} `type:"structure"` } @@ -32348,7 +32138,6 @@ func (s DeleteTagsOutput) GoString() string { } // Contains the parameters for DeleteVolume. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteVolumeRequest type DeleteVolumeInput struct { _ struct{} `type:"structure"` @@ -32399,7 +32188,6 @@ func (s *DeleteVolumeInput) SetVolumeId(v string) *DeleteVolumeInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteVolumeOutput type DeleteVolumeOutput struct { _ struct{} `type:"structure"` } @@ -32414,7 +32202,6 @@ func (s DeleteVolumeOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteVpcEndpointConnectionNotificationsRequest type DeleteVpcEndpointConnectionNotificationsInput struct { _ struct{} `type:"structure"` @@ -32465,7 +32252,6 @@ func (s *DeleteVpcEndpointConnectionNotificationsInput) SetDryRun(v bool) *Delet return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteVpcEndpointConnectionNotificationsResult type DeleteVpcEndpointConnectionNotificationsOutput struct { _ struct{} `type:"structure"` @@ -32489,7 +32275,6 @@ func (s *DeleteVpcEndpointConnectionNotificationsOutput) SetUnsuccessful(v []*Un return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteVpcEndpointServiceConfigurationsRequest type DeleteVpcEndpointServiceConfigurationsInput struct { _ struct{} `type:"structure"` @@ -32540,7 +32325,6 @@ func (s *DeleteVpcEndpointServiceConfigurationsInput) SetServiceIds(v []*string) return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteVpcEndpointServiceConfigurationsResult type DeleteVpcEndpointServiceConfigurationsOutput struct { _ struct{} `type:"structure"` @@ -32565,7 +32349,6 @@ func (s *DeleteVpcEndpointServiceConfigurationsOutput) SetUnsuccessful(v []*Unsu } // Contains the parameters for DeleteVpcEndpoints. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteVpcEndpointsRequest type DeleteVpcEndpointsInput struct { _ struct{} `type:"structure"` @@ -32617,7 +32400,6 @@ func (s *DeleteVpcEndpointsInput) SetVpcEndpointIds(v []*string) *DeleteVpcEndpo } // Contains the output of DeleteVpcEndpoints. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteVpcEndpointsResult type DeleteVpcEndpointsOutput struct { _ struct{} `type:"structure"` @@ -32642,7 +32424,6 @@ func (s *DeleteVpcEndpointsOutput) SetUnsuccessful(v []*UnsuccessfulItem) *Delet } // Contains the parameters for DeleteVpc. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteVpcRequest type DeleteVpcInput struct { _ struct{} `type:"structure"` @@ -32693,7 +32474,6 @@ func (s *DeleteVpcInput) SetVpcId(v string) *DeleteVpcInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteVpcOutput type DeleteVpcOutput struct { _ struct{} `type:"structure"` } @@ -32709,7 +32489,6 @@ func (s DeleteVpcOutput) GoString() string { } // Contains the parameters for DeleteVpcPeeringConnection. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteVpcPeeringConnectionRequest type DeleteVpcPeeringConnectionInput struct { _ struct{} `type:"structure"` @@ -32761,7 +32540,6 @@ func (s *DeleteVpcPeeringConnectionInput) SetVpcPeeringConnectionId(v string) *D } // Contains the output of DeleteVpcPeeringConnection. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteVpcPeeringConnectionResult type DeleteVpcPeeringConnectionOutput struct { _ struct{} `type:"structure"` @@ -32786,7 +32564,6 @@ func (s *DeleteVpcPeeringConnectionOutput) SetReturn(v bool) *DeleteVpcPeeringCo } // Contains the parameters for DeleteVpnConnection. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteVpnConnectionRequest type DeleteVpnConnectionInput struct { _ struct{} `type:"structure"` @@ -32837,7 +32614,6 @@ func (s *DeleteVpnConnectionInput) SetVpnConnectionId(v string) *DeleteVpnConnec return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteVpnConnectionOutput type DeleteVpnConnectionOutput struct { _ struct{} `type:"structure"` } @@ -32853,7 +32629,6 @@ func (s DeleteVpnConnectionOutput) GoString() string { } // Contains the parameters for DeleteVpnConnectionRoute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteVpnConnectionRouteRequest type DeleteVpnConnectionRouteInput struct { _ struct{} `type:"structure"` @@ -32906,7 +32681,6 @@ func (s *DeleteVpnConnectionRouteInput) SetVpnConnectionId(v string) *DeleteVpnC return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteVpnConnectionRouteOutput type DeleteVpnConnectionRouteOutput struct { _ struct{} `type:"structure"` } @@ -32922,7 +32696,6 @@ func (s DeleteVpnConnectionRouteOutput) GoString() string { } // Contains the parameters for DeleteVpnGateway. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteVpnGatewayRequest type DeleteVpnGatewayInput struct { _ struct{} `type:"structure"` @@ -32973,7 +32746,6 @@ func (s *DeleteVpnGatewayInput) SetVpnGatewayId(v string) *DeleteVpnGatewayInput return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeleteVpnGatewayOutput type DeleteVpnGatewayOutput struct { _ struct{} `type:"structure"` } @@ -32989,7 +32761,6 @@ func (s DeleteVpnGatewayOutput) GoString() string { } // Contains the parameters for DeregisterImage. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeregisterImageRequest type DeregisterImageInput struct { _ struct{} `type:"structure"` @@ -33040,7 +32811,6 @@ func (s *DeregisterImageInput) SetImageId(v string) *DeregisterImageInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DeregisterImageOutput type DeregisterImageOutput struct { _ struct{} `type:"structure"` } @@ -33056,7 +32826,6 @@ func (s DeregisterImageOutput) GoString() string { } // Contains the parameters for DescribeAccountAttributes. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeAccountAttributesRequest type DescribeAccountAttributesInput struct { _ struct{} `type:"structure"` @@ -33093,7 +32862,6 @@ func (s *DescribeAccountAttributesInput) SetDryRun(v bool) *DescribeAccountAttri } // Contains the output of DescribeAccountAttributes. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeAccountAttributesResult type DescribeAccountAttributesOutput struct { _ struct{} `type:"structure"` @@ -33118,7 +32886,6 @@ func (s *DescribeAccountAttributesOutput) SetAccountAttributes(v []*AccountAttri } // Contains the parameters for DescribeAddresses. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeAddressesRequest type DescribeAddressesInput struct { _ struct{} `type:"structure"` @@ -33209,7 +32976,6 @@ func (s *DescribeAddressesInput) SetPublicIps(v []*string) *DescribeAddressesInp } // Contains the output of DescribeAddresses. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeAddressesResult type DescribeAddressesOutput struct { _ struct{} `type:"structure"` @@ -33234,7 +33000,6 @@ func (s *DescribeAddressesOutput) SetAddresses(v []*Address) *DescribeAddressesO } // Contains the parameters for DescribeAvailabilityZones. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeAvailabilityZonesRequest type DescribeAvailabilityZonesInput struct { _ struct{} `type:"structure"` @@ -33290,7 +33055,6 @@ func (s *DescribeAvailabilityZonesInput) SetZoneNames(v []*string) *DescribeAvai } // Contains the output of DescribeAvailabiltyZones. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeAvailabilityZonesResult type DescribeAvailabilityZonesOutput struct { _ struct{} `type:"structure"` @@ -33315,7 +33079,6 @@ func (s *DescribeAvailabilityZonesOutput) SetAvailabilityZones(v []*Availability } // Contains the parameters for DescribeBundleTasks. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeBundleTasksRequest type DescribeBundleTasksInput struct { _ struct{} `type:"structure"` @@ -33385,7 +33148,6 @@ func (s *DescribeBundleTasksInput) SetFilters(v []*Filter) *DescribeBundleTasksI } // Contains the output of DescribeBundleTasks. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeBundleTasksResult type DescribeBundleTasksOutput struct { _ struct{} `type:"structure"` @@ -33410,7 +33172,6 @@ func (s *DescribeBundleTasksOutput) SetBundleTasks(v []*BundleTask) *DescribeBun } // Contains the parameters for DescribeClassicLinkInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeClassicLinkInstancesRequest type DescribeClassicLinkInstancesInput struct { _ struct{} `type:"structure"` @@ -33501,7 +33262,6 @@ func (s *DescribeClassicLinkInstancesInput) SetNextToken(v string) *DescribeClas } // Contains the output of DescribeClassicLinkInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeClassicLinkInstancesResult type DescribeClassicLinkInstancesOutput struct { _ struct{} `type:"structure"` @@ -33536,7 +33296,6 @@ func (s *DescribeClassicLinkInstancesOutput) SetNextToken(v string) *DescribeCla } // Contains the parameters for DescribeConversionTasks. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeConversionTasksRequest type DescribeConversionTasksInput struct { _ struct{} `type:"structure"` @@ -33573,7 +33332,6 @@ func (s *DescribeConversionTasksInput) SetDryRun(v bool) *DescribeConversionTask } // Contains the output for DescribeConversionTasks. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeConversionTasksResult type DescribeConversionTasksOutput struct { _ struct{} `type:"structure"` @@ -33598,7 +33356,6 @@ func (s *DescribeConversionTasksOutput) SetConversionTasks(v []*ConversionTask) } // Contains the parameters for DescribeCustomerGateways. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeCustomerGatewaysRequest type DescribeCustomerGatewaysInput struct { _ struct{} `type:"structure"` @@ -33676,7 +33433,6 @@ func (s *DescribeCustomerGatewaysInput) SetFilters(v []*Filter) *DescribeCustome } // Contains the output of DescribeCustomerGateways. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeCustomerGatewaysResult type DescribeCustomerGatewaysOutput struct { _ struct{} `type:"structure"` @@ -33701,7 +33457,6 @@ func (s *DescribeCustomerGatewaysOutput) SetCustomerGateways(v []*CustomerGatewa } // Contains the parameters for DescribeDhcpOptions. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeDhcpOptionsRequest type DescribeDhcpOptionsInput struct { _ struct{} `type:"structure"` @@ -33771,7 +33526,6 @@ func (s *DescribeDhcpOptionsInput) SetFilters(v []*Filter) *DescribeDhcpOptionsI } // Contains the output of DescribeDhcpOptions. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeDhcpOptionsResult type DescribeDhcpOptionsOutput struct { _ struct{} `type:"structure"` @@ -33795,7 +33549,6 @@ func (s *DescribeDhcpOptionsOutput) SetDhcpOptions(v []*DhcpOptions) *DescribeDh return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeEgressOnlyInternetGatewaysRequest type DescribeEgressOnlyInternetGatewaysInput struct { _ struct{} `type:"structure"` @@ -33852,7 +33605,6 @@ func (s *DescribeEgressOnlyInternetGatewaysInput) SetNextToken(v string) *Descri return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeEgressOnlyInternetGatewaysResult type DescribeEgressOnlyInternetGatewaysOutput struct { _ struct{} `type:"structure"` @@ -33885,7 +33637,6 @@ func (s *DescribeEgressOnlyInternetGatewaysOutput) SetNextToken(v string) *Descr return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeElasticGpusRequest type DescribeElasticGpusInput struct { _ struct{} `type:"structure"` @@ -33960,7 +33711,6 @@ func (s *DescribeElasticGpusInput) SetNextToken(v string) *DescribeElasticGpusIn return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeElasticGpusResult type DescribeElasticGpusOutput struct { _ struct{} `type:"structure"` @@ -34006,7 +33756,6 @@ func (s *DescribeElasticGpusOutput) SetNextToken(v string) *DescribeElasticGpusO } // Contains the parameters for DescribeExportTasks. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeExportTasksRequest type DescribeExportTasksInput struct { _ struct{} `type:"structure"` @@ -34031,7 +33780,6 @@ func (s *DescribeExportTasksInput) SetExportTaskIds(v []*string) *DescribeExport } // Contains the output for DescribeExportTasks. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeExportTasksResult type DescribeExportTasksOutput struct { _ struct{} `type:"structure"` @@ -34056,7 +33804,6 @@ func (s *DescribeExportTasksOutput) SetExportTasks(v []*ExportTask) *DescribeExp } // Contains the parameters for DescribeFlowLogs. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeFlowLogsRequest type DescribeFlowLogsInput struct { _ struct{} `type:"structure"` @@ -34122,7 +33869,6 @@ func (s *DescribeFlowLogsInput) SetNextToken(v string) *DescribeFlowLogsInput { } // Contains the output of DescribeFlowLogs. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeFlowLogsResult type DescribeFlowLogsOutput struct { _ struct{} `type:"structure"` @@ -34156,7 +33902,6 @@ func (s *DescribeFlowLogsOutput) SetNextToken(v string) *DescribeFlowLogsOutput return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeFpgaImageAttributeRequest type DescribeFpgaImageAttributeInput struct { _ struct{} `type:"structure"` @@ -34221,7 +33966,6 @@ func (s *DescribeFpgaImageAttributeInput) SetFpgaImageId(v string) *DescribeFpga return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeFpgaImageAttributeResult type DescribeFpgaImageAttributeOutput struct { _ struct{} `type:"structure"` @@ -34245,7 +33989,6 @@ func (s *DescribeFpgaImageAttributeOutput) SetFpgaImageAttribute(v *FpgaImageAtt return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeFpgaImagesRequest type DescribeFpgaImagesInput struct { _ struct{} `type:"structure"` @@ -34369,7 +34112,6 @@ func (s *DescribeFpgaImagesInput) SetOwners(v []*string) *DescribeFpgaImagesInpu return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeFpgaImagesResult type DescribeFpgaImagesOutput struct { _ struct{} `type:"structure"` @@ -34403,7 +34145,6 @@ func (s *DescribeFpgaImagesOutput) SetNextToken(v string) *DescribeFpgaImagesOut return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeHostReservationOfferingsRequest type DescribeHostReservationOfferingsInput struct { _ struct{} `type:"structure"` @@ -34487,7 +34228,6 @@ func (s *DescribeHostReservationOfferingsInput) SetOfferingId(v string) *Describ return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeHostReservationOfferingsResult type DescribeHostReservationOfferingsOutput struct { _ struct{} `type:"structure"` @@ -34521,7 +34261,6 @@ func (s *DescribeHostReservationOfferingsOutput) SetOfferingSet(v []*HostOfferin return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeHostReservationsRequest type DescribeHostReservationsInput struct { _ struct{} `type:"structure"` @@ -34582,7 +34321,6 @@ func (s *DescribeHostReservationsInput) SetNextToken(v string) *DescribeHostRese return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeHostReservationsResult type DescribeHostReservationsOutput struct { _ struct{} `type:"structure"` @@ -34617,7 +34355,6 @@ func (s *DescribeHostReservationsOutput) SetNextToken(v string) *DescribeHostRes } // Contains the parameters for DescribeHosts. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeHostsRequest type DescribeHostsInput struct { _ struct{} `type:"structure"` @@ -34689,7 +34426,6 @@ func (s *DescribeHostsInput) SetNextToken(v string) *DescribeHostsInput { } // Contains the output of DescribeHosts. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeHostsResult type DescribeHostsOutput struct { _ struct{} `type:"structure"` @@ -34723,7 +34459,6 @@ func (s *DescribeHostsOutput) SetNextToken(v string) *DescribeHostsOutput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeIamInstanceProfileAssociationsRequest type DescribeIamInstanceProfileAssociationsInput struct { _ struct{} `type:"structure"` @@ -34796,7 +34531,6 @@ func (s *DescribeIamInstanceProfileAssociationsInput) SetNextToken(v string) *De return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeIamInstanceProfileAssociationsResult type DescribeIamInstanceProfileAssociationsOutput struct { _ struct{} `type:"structure"` @@ -34831,7 +34565,6 @@ func (s *DescribeIamInstanceProfileAssociationsOutput) SetNextToken(v string) *D } // Contains the parameters for DescribeIdFormat. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeIdFormatRequest type DescribeIdFormatInput struct { _ struct{} `type:"structure"` @@ -34856,7 +34589,6 @@ func (s *DescribeIdFormatInput) SetResource(v string) *DescribeIdFormatInput { } // Contains the output of DescribeIdFormat. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeIdFormatResult type DescribeIdFormatOutput struct { _ struct{} `type:"structure"` @@ -34881,7 +34613,6 @@ func (s *DescribeIdFormatOutput) SetStatuses(v []*IdFormat) *DescribeIdFormatOut } // Contains the parameters for DescribeIdentityIdFormat. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeIdentityIdFormatRequest type DescribeIdentityIdFormatInput struct { _ struct{} `type:"structure"` @@ -34931,7 +34662,6 @@ func (s *DescribeIdentityIdFormatInput) SetResource(v string) *DescribeIdentityI } // Contains the output of DescribeIdentityIdFormat. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeIdentityIdFormatResult type DescribeIdentityIdFormatOutput struct { _ struct{} `type:"structure"` @@ -34956,7 +34686,6 @@ func (s *DescribeIdentityIdFormatOutput) SetStatuses(v []*IdFormat) *DescribeIde } // Contains the parameters for DescribeImageAttribute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeImageAttributeRequest type DescribeImageAttributeInput struct { _ struct{} `type:"structure"` @@ -35026,7 +34755,6 @@ func (s *DescribeImageAttributeInput) SetImageId(v string) *DescribeImageAttribu } // Describes an image attribute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ImageAttribute type DescribeImageAttributeOutput struct { _ struct{} `type:"structure"` @@ -35115,7 +34843,6 @@ func (s *DescribeImageAttributeOutput) SetSriovNetSupport(v *AttributeValue) *De } // Contains the parameters for DescribeImages. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeImagesRequest type DescribeImagesInput struct { _ struct{} `type:"structure"` @@ -35267,7 +34994,6 @@ func (s *DescribeImagesInput) SetOwners(v []*string) *DescribeImagesInput { } // Contains the output of DescribeImages. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeImagesResult type DescribeImagesOutput struct { _ struct{} `type:"structure"` @@ -35292,7 +35018,6 @@ func (s *DescribeImagesOutput) SetImages(v []*Image) *DescribeImagesOutput { } // Contains the parameters for DescribeImportImageTasks. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeImportImageTasksRequest type DescribeImportImageTasksInput struct { _ struct{} `type:"structure"` @@ -35358,7 +35083,6 @@ func (s *DescribeImportImageTasksInput) SetNextToken(v string) *DescribeImportIm } // Contains the output for DescribeImportImageTasks. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeImportImageTasksResult type DescribeImportImageTasksOutput struct { _ struct{} `type:"structure"` @@ -35394,7 +35118,6 @@ func (s *DescribeImportImageTasksOutput) SetNextToken(v string) *DescribeImportI } // Contains the parameters for DescribeImportSnapshotTasks. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeImportSnapshotTasksRequest type DescribeImportSnapshotTasksInput struct { _ struct{} `type:"structure"` @@ -35459,7 +35182,6 @@ func (s *DescribeImportSnapshotTasksInput) SetNextToken(v string) *DescribeImpor } // Contains the output for DescribeImportSnapshotTasks. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeImportSnapshotTasksResult type DescribeImportSnapshotTasksOutput struct { _ struct{} `type:"structure"` @@ -35495,7 +35217,6 @@ func (s *DescribeImportSnapshotTasksOutput) SetNextToken(v string) *DescribeImpo } // Contains the parameters for DescribeInstanceAttribute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeInstanceAttributeRequest type DescribeInstanceAttributeInput struct { _ struct{} `type:"structure"` @@ -35563,7 +35284,6 @@ func (s *DescribeInstanceAttributeInput) SetInstanceId(v string) *DescribeInstan } // Describes an instance attribute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/InstanceAttribute type DescribeInstanceAttributeOutput struct { _ struct{} `type:"structure"` @@ -35718,7 +35438,6 @@ func (s *DescribeInstanceAttributeOutput) SetUserData(v *AttributeValue) *Descri return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeInstanceCreditSpecificationsRequest type DescribeInstanceCreditSpecificationsInput struct { _ struct{} `type:"structure"` @@ -35790,7 +35509,6 @@ func (s *DescribeInstanceCreditSpecificationsInput) SetNextToken(v string) *Desc return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeInstanceCreditSpecificationsResult type DescribeInstanceCreditSpecificationsOutput struct { _ struct{} `type:"structure"` @@ -35825,7 +35543,6 @@ func (s *DescribeInstanceCreditSpecificationsOutput) SetNextToken(v string) *Des } // Contains the parameters for DescribeInstanceStatus. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeInstanceStatusRequest type DescribeInstanceStatusInput struct { _ struct{} `type:"structure"` @@ -35942,7 +35659,6 @@ func (s *DescribeInstanceStatusInput) SetNextToken(v string) *DescribeInstanceSt } // Contains the output of DescribeInstanceStatus. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeInstanceStatusResult type DescribeInstanceStatusOutput struct { _ struct{} `type:"structure"` @@ -35977,7 +35693,6 @@ func (s *DescribeInstanceStatusOutput) SetNextToken(v string) *DescribeInstanceS } // Contains the parameters for DescribeInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeInstancesRequest type DescribeInstancesInput struct { _ struct{} `type:"structure"` @@ -36283,7 +35998,6 @@ func (s *DescribeInstancesInput) SetNextToken(v string) *DescribeInstancesInput } // Contains the output of DescribeInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeInstancesResult type DescribeInstancesOutput struct { _ struct{} `type:"structure"` @@ -36318,7 +36032,6 @@ func (s *DescribeInstancesOutput) SetReservations(v []*Reservation) *DescribeIns } // Contains the parameters for DescribeInternetGateways. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeInternetGatewaysRequest type DescribeInternetGatewaysInput struct { _ struct{} `type:"structure"` @@ -36389,7 +36102,6 @@ func (s *DescribeInternetGatewaysInput) SetInternetGatewayIds(v []*string) *Desc } // Contains the output of DescribeInternetGateways. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeInternetGatewaysResult type DescribeInternetGatewaysOutput struct { _ struct{} `type:"structure"` @@ -36414,7 +36126,6 @@ func (s *DescribeInternetGatewaysOutput) SetInternetGateways(v []*InternetGatewa } // Contains the parameters for DescribeKeyPairs. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeKeyPairsRequest type DescribeKeyPairsInput struct { _ struct{} `type:"structure"` @@ -36466,7 +36177,6 @@ func (s *DescribeKeyPairsInput) SetKeyNames(v []*string) *DescribeKeyPairsInput } // Contains the output of DescribeKeyPairs. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeKeyPairsResult type DescribeKeyPairsOutput struct { _ struct{} `type:"structure"` @@ -36490,7 +36200,6 @@ func (s *DescribeKeyPairsOutput) SetKeyPairs(v []*KeyPairInfo) *DescribeKeyPairs return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeLaunchTemplateVersionsRequest type DescribeLaunchTemplateVersionsInput struct { _ struct{} `type:"structure"` @@ -36624,7 +36333,6 @@ func (s *DescribeLaunchTemplateVersionsInput) SetVersions(v []*string) *Describe return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeLaunchTemplateVersionsResult type DescribeLaunchTemplateVersionsOutput struct { _ struct{} `type:"structure"` @@ -36658,7 +36366,6 @@ func (s *DescribeLaunchTemplateVersionsOutput) SetNextToken(v string) *DescribeL return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeLaunchTemplatesRequest type DescribeLaunchTemplatesInput struct { _ struct{} `type:"structure"` @@ -36748,7 +36455,6 @@ func (s *DescribeLaunchTemplatesInput) SetNextToken(v string) *DescribeLaunchTem return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeLaunchTemplatesResult type DescribeLaunchTemplatesOutput struct { _ struct{} `type:"structure"` @@ -36783,7 +36489,6 @@ func (s *DescribeLaunchTemplatesOutput) SetNextToken(v string) *DescribeLaunchTe } // Contains the parameters for DescribeMovingAddresses. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeMovingAddressesRequest type DescribeMovingAddressesInput struct { _ struct{} `type:"structure"` @@ -36855,7 +36560,6 @@ func (s *DescribeMovingAddressesInput) SetPublicIps(v []*string) *DescribeMoving } // Contains the output of DescribeMovingAddresses. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeMovingAddressesResult type DescribeMovingAddressesOutput struct { _ struct{} `type:"structure"` @@ -36890,7 +36594,6 @@ func (s *DescribeMovingAddressesOutput) SetNextToken(v string) *DescribeMovingAd } // Contains the parameters for DescribeNatGateways. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeNatGatewaysRequest type DescribeNatGatewaysInput struct { _ struct{} `type:"structure"` @@ -36972,7 +36675,6 @@ func (s *DescribeNatGatewaysInput) SetNextToken(v string) *DescribeNatGatewaysIn } // Contains the output of DescribeNatGateways. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeNatGatewaysResult type DescribeNatGatewaysOutput struct { _ struct{} `type:"structure"` @@ -37007,7 +36709,6 @@ func (s *DescribeNatGatewaysOutput) SetNextToken(v string) *DescribeNatGatewaysO } // Contains the parameters for DescribeNetworkAcls. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeNetworkAclsRequest type DescribeNetworkAclsInput struct { _ struct{} `type:"structure"` @@ -37109,7 +36810,6 @@ func (s *DescribeNetworkAclsInput) SetNetworkAclIds(v []*string) *DescribeNetwor } // Contains the output of DescribeNetworkAcls. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeNetworkAclsResult type DescribeNetworkAclsOutput struct { _ struct{} `type:"structure"` @@ -37134,7 +36834,6 @@ func (s *DescribeNetworkAclsOutput) SetNetworkAcls(v []*NetworkAcl) *DescribeNet } // Contains the parameters for DescribeNetworkInterfaceAttribute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeNetworkInterfaceAttributeRequest type DescribeNetworkInterfaceAttributeInput struct { _ struct{} `type:"structure"` @@ -37195,7 +36894,6 @@ func (s *DescribeNetworkInterfaceAttributeInput) SetNetworkInterfaceId(v string) } // Contains the output of DescribeNetworkInterfaceAttribute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeNetworkInterfaceAttributeResult type DescribeNetworkInterfaceAttributeOutput struct { _ struct{} `type:"structure"` @@ -37256,7 +36954,6 @@ func (s *DescribeNetworkInterfaceAttributeOutput) SetSourceDestCheck(v *Attribut } // Contains the parameters for DescribeNetworkInterfacePermissions. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeNetworkInterfacePermissionsRequest type DescribeNetworkInterfacePermissionsInput struct { _ struct{} `type:"structure"` @@ -37323,7 +37020,6 @@ func (s *DescribeNetworkInterfacePermissionsInput) SetNextToken(v string) *Descr } // Contains the output for DescribeNetworkInterfacePermissions. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeNetworkInterfacePermissionsResult type DescribeNetworkInterfacePermissionsOutput struct { _ struct{} `type:"structure"` @@ -37357,7 +37053,6 @@ func (s *DescribeNetworkInterfacePermissionsOutput) SetNextToken(v string) *Desc } // Contains the parameters for DescribeNetworkInterfaces. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeNetworkInterfacesRequest type DescribeNetworkInterfacesInput struct { _ struct{} `type:"structure"` @@ -37515,7 +37210,6 @@ func (s *DescribeNetworkInterfacesInput) SetNetworkInterfaceIds(v []*string) *De } // Contains the output of DescribeNetworkInterfaces. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeNetworkInterfacesResult type DescribeNetworkInterfacesOutput struct { _ struct{} `type:"structure"` @@ -37540,7 +37234,6 @@ func (s *DescribeNetworkInterfacesOutput) SetNetworkInterfaces(v []*NetworkInter } // Contains the parameters for DescribePlacementGroups. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribePlacementGroupsRequest type DescribePlacementGroupsInput struct { _ struct{} `type:"structure"` @@ -37595,7 +37288,6 @@ func (s *DescribePlacementGroupsInput) SetGroupNames(v []*string) *DescribePlace } // Contains the output of DescribePlacementGroups. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribePlacementGroupsResult type DescribePlacementGroupsOutput struct { _ struct{} `type:"structure"` @@ -37620,7 +37312,6 @@ func (s *DescribePlacementGroupsOutput) SetPlacementGroups(v []*PlacementGroup) } // Contains the parameters for DescribePrefixLists. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribePrefixListsRequest type DescribePrefixListsInput struct { _ struct{} `type:"structure"` @@ -37694,7 +37385,6 @@ func (s *DescribePrefixListsInput) SetPrefixListIds(v []*string) *DescribePrefix } // Contains the output of DescribePrefixLists. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribePrefixListsResult type DescribePrefixListsOutput struct { _ struct{} `type:"structure"` @@ -37729,7 +37419,6 @@ func (s *DescribePrefixListsOutput) SetPrefixLists(v []*PrefixList) *DescribePre } // Contains the parameters for DescribeRegions. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeRegionsRequest type DescribeRegionsInput struct { _ struct{} `type:"structure"` @@ -37779,7 +37468,6 @@ func (s *DescribeRegionsInput) SetRegionNames(v []*string) *DescribeRegionsInput } // Contains the output of DescribeRegions. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeRegionsResult type DescribeRegionsOutput struct { _ struct{} `type:"structure"` @@ -37804,7 +37492,6 @@ func (s *DescribeRegionsOutput) SetRegions(v []*Region) *DescribeRegionsOutput { } // Contains the parameters for DescribeReservedInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeReservedInstancesRequest type DescribeReservedInstancesInput struct { _ struct{} `type:"structure"` @@ -37924,7 +37611,6 @@ func (s *DescribeReservedInstancesInput) SetReservedInstancesIds(v []*string) *D } // Contains the parameters for DescribeReservedInstancesListings. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeReservedInstancesListingsRequest type DescribeReservedInstancesListingsInput struct { _ struct{} `type:"structure"` @@ -37976,7 +37662,6 @@ func (s *DescribeReservedInstancesListingsInput) SetReservedInstancesListingId(v } // Contains the output of DescribeReservedInstancesListings. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeReservedInstancesListingsResult type DescribeReservedInstancesListingsOutput struct { _ struct{} `type:"structure"` @@ -38001,7 +37686,6 @@ func (s *DescribeReservedInstancesListingsOutput) SetReservedInstancesListings(v } // Contains the parameters for DescribeReservedInstancesModifications. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeReservedInstancesModificationsRequest type DescribeReservedInstancesModificationsInput struct { _ struct{} `type:"structure"` @@ -38077,7 +37761,6 @@ func (s *DescribeReservedInstancesModificationsInput) SetReservedInstancesModifi } // Contains the output of DescribeReservedInstancesModifications. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeReservedInstancesModificationsResult type DescribeReservedInstancesModificationsOutput struct { _ struct{} `type:"structure"` @@ -38112,7 +37795,6 @@ func (s *DescribeReservedInstancesModificationsOutput) SetReservedInstancesModif } // Contains the parameters for DescribeReservedInstancesOfferings. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeReservedInstancesOfferingsRequest type DescribeReservedInstancesOfferingsInput struct { _ struct{} `type:"structure"` @@ -38322,7 +38004,6 @@ func (s *DescribeReservedInstancesOfferingsInput) SetReservedInstancesOfferingId } // Contains the output of DescribeReservedInstancesOfferings. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeReservedInstancesOfferingsResult type DescribeReservedInstancesOfferingsOutput struct { _ struct{} `type:"structure"` @@ -38357,7 +38038,6 @@ func (s *DescribeReservedInstancesOfferingsOutput) SetReservedInstancesOfferings } // Contains the output for DescribeReservedInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeReservedInstancesResult type DescribeReservedInstancesOutput struct { _ struct{} `type:"structure"` @@ -38382,7 +38062,6 @@ func (s *DescribeReservedInstancesOutput) SetReservedInstances(v []*ReservedInst } // Contains the parameters for DescribeRouteTables. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeRouteTablesRequest type DescribeRouteTablesInput struct { _ struct{} `type:"structure"` @@ -38495,7 +38174,6 @@ func (s *DescribeRouteTablesInput) SetRouteTableIds(v []*string) *DescribeRouteT } // Contains the output of DescribeRouteTables. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeRouteTablesResult type DescribeRouteTablesOutput struct { _ struct{} `type:"structure"` @@ -38520,7 +38198,6 @@ func (s *DescribeRouteTablesOutput) SetRouteTables(v []*RouteTable) *DescribeRou } // Contains the parameters for DescribeScheduledInstanceAvailability. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeScheduledInstanceAvailabilityRequest type DescribeScheduledInstanceAvailabilityInput struct { _ struct{} `type:"structure"` @@ -38650,7 +38327,6 @@ func (s *DescribeScheduledInstanceAvailabilityInput) SetRecurrence(v *ScheduledI } // Contains the output of DescribeScheduledInstanceAvailability. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeScheduledInstanceAvailabilityResult type DescribeScheduledInstanceAvailabilityOutput struct { _ struct{} `type:"structure"` @@ -38685,7 +38361,6 @@ func (s *DescribeScheduledInstanceAvailabilityOutput) SetScheduledInstanceAvaila } // Contains the parameters for DescribeScheduledInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeScheduledInstancesRequest type DescribeScheduledInstancesInput struct { _ struct{} `type:"structure"` @@ -38768,7 +38443,6 @@ func (s *DescribeScheduledInstancesInput) SetSlotStartTimeRange(v *SlotStartTime } // Contains the output of DescribeScheduledInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeScheduledInstancesResult type DescribeScheduledInstancesOutput struct { _ struct{} `type:"structure"` @@ -38802,7 +38476,6 @@ func (s *DescribeScheduledInstancesOutput) SetScheduledInstanceSet(v []*Schedule return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeSecurityGroupReferencesRequest type DescribeSecurityGroupReferencesInput struct { _ struct{} `type:"structure"` @@ -38853,7 +38526,6 @@ func (s *DescribeSecurityGroupReferencesInput) SetGroupId(v []*string) *Describe return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeSecurityGroupReferencesResult type DescribeSecurityGroupReferencesOutput struct { _ struct{} `type:"structure"` @@ -38878,7 +38550,6 @@ func (s *DescribeSecurityGroupReferencesOutput) SetSecurityGroupReferenceSet(v [ } // Contains the parameters for DescribeSecurityGroups. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeSecurityGroupsRequest type DescribeSecurityGroupsInput struct { _ struct{} `type:"structure"` @@ -39031,7 +38702,6 @@ func (s *DescribeSecurityGroupsInput) SetNextToken(v string) *DescribeSecurityGr } // Contains the output of DescribeSecurityGroups. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeSecurityGroupsResult type DescribeSecurityGroupsOutput struct { _ struct{} `type:"structure"` @@ -39066,7 +38736,6 @@ func (s *DescribeSecurityGroupsOutput) SetSecurityGroups(v []*SecurityGroup) *De } // Contains the parameters for DescribeSnapshotAttribute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeSnapshotAttributeRequest type DescribeSnapshotAttributeInput struct { _ struct{} `type:"structure"` @@ -39132,7 +38801,6 @@ func (s *DescribeSnapshotAttributeInput) SetSnapshotId(v string) *DescribeSnapsh } // Contains the output of DescribeSnapshotAttribute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeSnapshotAttributeResult type DescribeSnapshotAttributeOutput struct { _ struct{} `type:"structure"` @@ -39175,7 +38843,6 @@ func (s *DescribeSnapshotAttributeOutput) SetSnapshotId(v string) *DescribeSnaps } // Contains the parameters for DescribeSnapshots. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeSnapshotsRequest type DescribeSnapshotsInput struct { _ struct{} `type:"structure"` @@ -39309,7 +38976,6 @@ func (s *DescribeSnapshotsInput) SetSnapshotIds(v []*string) *DescribeSnapshotsI } // Contains the output of DescribeSnapshots. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeSnapshotsResult type DescribeSnapshotsOutput struct { _ struct{} `type:"structure"` @@ -39346,7 +39012,6 @@ func (s *DescribeSnapshotsOutput) SetSnapshots(v []*Snapshot) *DescribeSnapshots } // Contains the parameters for DescribeSpotDatafeedSubscription. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeSpotDatafeedSubscriptionRequest type DescribeSpotDatafeedSubscriptionInput struct { _ struct{} `type:"structure"` @@ -39374,7 +39039,6 @@ func (s *DescribeSpotDatafeedSubscriptionInput) SetDryRun(v bool) *DescribeSpotD } // Contains the output of DescribeSpotDatafeedSubscription. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeSpotDatafeedSubscriptionResult type DescribeSpotDatafeedSubscriptionOutput struct { _ struct{} `type:"structure"` @@ -39399,7 +39063,6 @@ func (s *DescribeSpotDatafeedSubscriptionOutput) SetSpotDatafeedSubscription(v * } // Contains the parameters for DescribeSpotFleetInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeSpotFleetInstancesRequest type DescribeSpotFleetInstancesInput struct { _ struct{} `type:"structure"` @@ -39471,7 +39134,6 @@ func (s *DescribeSpotFleetInstancesInput) SetSpotFleetRequestId(v string) *Descr } // Contains the output of DescribeSpotFleetInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeSpotFleetInstancesResponse type DescribeSpotFleetInstancesOutput struct { _ struct{} `type:"structure"` @@ -39520,7 +39182,6 @@ func (s *DescribeSpotFleetInstancesOutput) SetSpotFleetRequestId(v string) *Desc } // Contains the parameters for DescribeSpotFleetRequestHistory. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeSpotFleetRequestHistoryRequest type DescribeSpotFleetRequestHistoryInput struct { _ struct{} `type:"structure"` @@ -39615,7 +39276,6 @@ func (s *DescribeSpotFleetRequestHistoryInput) SetStartTime(v time.Time) *Descri } // Contains the output of DescribeSpotFleetRequestHistory. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeSpotFleetRequestHistoryResponse type DescribeSpotFleetRequestHistoryOutput struct { _ struct{} `type:"structure"` @@ -39688,7 +39348,6 @@ func (s *DescribeSpotFleetRequestHistoryOutput) SetStartTime(v time.Time) *Descr } // Contains the parameters for DescribeSpotFleetRequests. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeSpotFleetRequestsRequest type DescribeSpotFleetRequestsInput struct { _ struct{} `type:"structure"` @@ -39745,7 +39404,6 @@ func (s *DescribeSpotFleetRequestsInput) SetSpotFleetRequestIds(v []*string) *De } // Contains the output of DescribeSpotFleetRequests. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeSpotFleetRequestsResponse type DescribeSpotFleetRequestsOutput struct { _ struct{} `type:"structure"` @@ -39782,7 +39440,6 @@ func (s *DescribeSpotFleetRequestsOutput) SetSpotFleetRequestConfigs(v []*SpotFl } // Contains the parameters for DescribeSpotInstanceRequests. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeSpotInstanceRequestsRequest type DescribeSpotInstanceRequestsInput struct { _ struct{} `type:"structure"` @@ -39937,7 +39594,6 @@ func (s *DescribeSpotInstanceRequestsInput) SetSpotInstanceRequestIds(v []*strin } // Contains the output of DescribeSpotInstanceRequests. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeSpotInstanceRequestsResult type DescribeSpotInstanceRequestsOutput struct { _ struct{} `type:"structure"` @@ -39962,7 +39618,6 @@ func (s *DescribeSpotInstanceRequestsOutput) SetSpotInstanceRequests(v []*SpotIn } // Contains the parameters for DescribeSpotPriceHistory. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeSpotPriceHistoryRequest type DescribeSpotPriceHistoryInput struct { _ struct{} `type:"structure"` @@ -40082,7 +39737,6 @@ func (s *DescribeSpotPriceHistoryInput) SetStartTime(v time.Time) *DescribeSpotP } // Contains the output of DescribeSpotPriceHistory. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeSpotPriceHistoryResult type DescribeSpotPriceHistoryOutput struct { _ struct{} `type:"structure"` @@ -40116,7 +39770,6 @@ func (s *DescribeSpotPriceHistoryOutput) SetSpotPriceHistory(v []*SpotPrice) *De return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeStaleSecurityGroupsRequest type DescribeStaleSecurityGroupsInput struct { _ struct{} `type:"structure"` @@ -40194,7 +39847,6 @@ func (s *DescribeStaleSecurityGroupsInput) SetVpcId(v string) *DescribeStaleSecu return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeStaleSecurityGroupsResult type DescribeStaleSecurityGroupsOutput struct { _ struct{} `type:"structure"` @@ -40229,7 +39881,6 @@ func (s *DescribeStaleSecurityGroupsOutput) SetStaleSecurityGroupSet(v []*StaleS } // Contains the parameters for DescribeSubnets. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeSubnetsRequest type DescribeSubnetsInput struct { _ struct{} `type:"structure"` @@ -40321,7 +39972,6 @@ func (s *DescribeSubnetsInput) SetSubnetIds(v []*string) *DescribeSubnetsInput { } // Contains the output of DescribeSubnets. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeSubnetsResult type DescribeSubnetsOutput struct { _ struct{} `type:"structure"` @@ -40346,7 +39996,6 @@ func (s *DescribeSubnetsOutput) SetSubnets(v []*Subnet) *DescribeSubnetsOutput { } // Contains the parameters for DescribeTags. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeTagsRequest type DescribeTagsInput struct { _ struct{} `type:"structure"` @@ -40415,7 +40064,6 @@ func (s *DescribeTagsInput) SetNextToken(v string) *DescribeTagsInput { } // Contains the output of DescribeTags. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeTagsResult type DescribeTagsOutput struct { _ struct{} `type:"structure"` @@ -40450,7 +40098,6 @@ func (s *DescribeTagsOutput) SetTags(v []*TagDescription) *DescribeTagsOutput { } // Contains the parameters for DescribeVolumeAttribute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVolumeAttributeRequest type DescribeVolumeAttributeInput struct { _ struct{} `type:"structure"` @@ -40511,7 +40158,6 @@ func (s *DescribeVolumeAttributeInput) SetVolumeId(v string) *DescribeVolumeAttr } // Contains the output of DescribeVolumeAttribute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVolumeAttributeResult type DescribeVolumeAttributeOutput struct { _ struct{} `type:"structure"` @@ -40554,7 +40200,6 @@ func (s *DescribeVolumeAttributeOutput) SetVolumeId(v string) *DescribeVolumeAtt } // Contains the parameters for DescribeVolumeStatus. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVolumeStatusRequest type DescribeVolumeStatusInput struct { _ struct{} `type:"structure"` @@ -40660,7 +40305,6 @@ func (s *DescribeVolumeStatusInput) SetVolumeIds(v []*string) *DescribeVolumeSta } // Contains the output of DescribeVolumeStatus. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVolumeStatusResult type DescribeVolumeStatusOutput struct { _ struct{} `type:"structure"` @@ -40695,7 +40339,6 @@ func (s *DescribeVolumeStatusOutput) SetVolumeStatuses(v []*VolumeStatusItem) *D } // Contains the parameters for DescribeVolumes. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVolumesRequest type DescribeVolumesInput struct { _ struct{} `type:"structure"` @@ -40818,7 +40461,6 @@ func (s *DescribeVolumesInput) SetVolumeIds(v []*string) *DescribeVolumesInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVolumesModificationsRequest type DescribeVolumesModificationsInput struct { _ struct{} `type:"structure"` @@ -40884,7 +40526,6 @@ func (s *DescribeVolumesModificationsInput) SetVolumeIds(v []*string) *DescribeV return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVolumesModificationsResult type DescribeVolumesModificationsOutput struct { _ struct{} `type:"structure"` @@ -40918,7 +40559,6 @@ func (s *DescribeVolumesModificationsOutput) SetVolumesModifications(v []*Volume } // Contains the output of DescribeVolumes. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVolumesResult type DescribeVolumesOutput struct { _ struct{} `type:"structure"` @@ -40955,7 +40595,6 @@ func (s *DescribeVolumesOutput) SetVolumes(v []*Volume) *DescribeVolumesOutput { } // Contains the parameters for DescribeVpcAttribute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVpcAttributeRequest type DescribeVpcAttributeInput struct { _ struct{} `type:"structure"` @@ -41021,7 +40660,6 @@ func (s *DescribeVpcAttributeInput) SetVpcId(v string) *DescribeVpcAttributeInpu } // Contains the output of DescribeVpcAttribute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVpcAttributeResult type DescribeVpcAttributeOutput struct { _ struct{} `type:"structure"` @@ -41068,7 +40706,6 @@ func (s *DescribeVpcAttributeOutput) SetVpcId(v string) *DescribeVpcAttributeOut } // Contains the parameters for DescribeVpcClassicLinkDnsSupport. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVpcClassicLinkDnsSupportRequest type DescribeVpcClassicLinkDnsSupportInput struct { _ struct{} `type:"structure"` @@ -41130,7 +40767,6 @@ func (s *DescribeVpcClassicLinkDnsSupportInput) SetVpcIds(v []*string) *Describe } // Contains the output of DescribeVpcClassicLinkDnsSupport. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVpcClassicLinkDnsSupportResult type DescribeVpcClassicLinkDnsSupportOutput struct { _ struct{} `type:"structure"` @@ -41164,7 +40800,6 @@ func (s *DescribeVpcClassicLinkDnsSupportOutput) SetVpcs(v []*ClassicLinkDnsSupp } // Contains the parameters for DescribeVpcClassicLink. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVpcClassicLinkRequest type DescribeVpcClassicLinkInput struct { _ struct{} `type:"structure"` @@ -41229,7 +40864,6 @@ func (s *DescribeVpcClassicLinkInput) SetVpcIds(v []*string) *DescribeVpcClassic } // Contains the output of DescribeVpcClassicLink. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVpcClassicLinkResult type DescribeVpcClassicLinkOutput struct { _ struct{} `type:"structure"` @@ -41253,7 +40887,6 @@ func (s *DescribeVpcClassicLinkOutput) SetVpcs(v []*VpcClassicLink) *DescribeVpc return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVpcEndpointConnectionNotificationsRequest type DescribeVpcEndpointConnectionNotificationsInput struct { _ struct{} `type:"structure"` @@ -41330,7 +40963,6 @@ func (s *DescribeVpcEndpointConnectionNotificationsInput) SetNextToken(v string) return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVpcEndpointConnectionNotificationsResult type DescribeVpcEndpointConnectionNotificationsOutput struct { _ struct{} `type:"structure"` @@ -41364,7 +40996,6 @@ func (s *DescribeVpcEndpointConnectionNotificationsOutput) SetNextToken(v string return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVpcEndpointConnectionsRequest type DescribeVpcEndpointConnectionsInput struct { _ struct{} `type:"structure"` @@ -41431,7 +41062,6 @@ func (s *DescribeVpcEndpointConnectionsInput) SetNextToken(v string) *DescribeVp return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVpcEndpointConnectionsResult type DescribeVpcEndpointConnectionsOutput struct { _ struct{} `type:"structure"` @@ -41465,7 +41095,6 @@ func (s *DescribeVpcEndpointConnectionsOutput) SetVpcEndpointConnections(v []*Vp return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVpcEndpointServiceConfigurationsRequest type DescribeVpcEndpointServiceConfigurationsInput struct { _ struct{} `type:"structure"` @@ -41539,7 +41168,6 @@ func (s *DescribeVpcEndpointServiceConfigurationsInput) SetServiceIds(v []*strin return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVpcEndpointServiceConfigurationsResult type DescribeVpcEndpointServiceConfigurationsOutput struct { _ struct{} `type:"structure"` @@ -41573,7 +41201,6 @@ func (s *DescribeVpcEndpointServiceConfigurationsOutput) SetServiceConfiguration return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVpcEndpointServicePermissionsRequest type DescribeVpcEndpointServicePermissionsInput struct { _ struct{} `type:"structure"` @@ -41660,7 +41287,6 @@ func (s *DescribeVpcEndpointServicePermissionsInput) SetServiceId(v string) *Des return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVpcEndpointServicePermissionsResult type DescribeVpcEndpointServicePermissionsOutput struct { _ struct{} `type:"structure"` @@ -41695,7 +41321,6 @@ func (s *DescribeVpcEndpointServicePermissionsOutput) SetNextToken(v string) *De } // Contains the parameters for DescribeVpcEndpointServices. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVpcEndpointServicesRequest type DescribeVpcEndpointServicesInput struct { _ struct{} `type:"structure"` @@ -41766,7 +41391,6 @@ func (s *DescribeVpcEndpointServicesInput) SetServiceNames(v []*string) *Describ } // Contains the output of DescribeVpcEndpointServices. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVpcEndpointServicesResult type DescribeVpcEndpointServicesOutput struct { _ struct{} `type:"structure"` @@ -41810,7 +41434,6 @@ func (s *DescribeVpcEndpointServicesOutput) SetServiceNames(v []*string) *Descri } // Contains the parameters for DescribeVpcEndpoints. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVpcEndpointsRequest type DescribeVpcEndpointsInput struct { _ struct{} `type:"structure"` @@ -41888,7 +41511,6 @@ func (s *DescribeVpcEndpointsInput) SetVpcEndpointIds(v []*string) *DescribeVpcE } // Contains the output of DescribeVpcEndpoints. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVpcEndpointsResult type DescribeVpcEndpointsOutput struct { _ struct{} `type:"structure"` @@ -41923,7 +41545,6 @@ func (s *DescribeVpcEndpointsOutput) SetVpcEndpoints(v []*VpcEndpoint) *Describe } // Contains the parameters for DescribeVpcPeeringConnections. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVpcPeeringConnectionsRequest type DescribeVpcPeeringConnectionsInput struct { _ struct{} `type:"structure"` @@ -42012,7 +41633,6 @@ func (s *DescribeVpcPeeringConnectionsInput) SetVpcPeeringConnectionIds(v []*str } // Contains the output of DescribeVpcPeeringConnections. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVpcPeeringConnectionsResult type DescribeVpcPeeringConnectionsOutput struct { _ struct{} `type:"structure"` @@ -42037,7 +41657,6 @@ func (s *DescribeVpcPeeringConnectionsOutput) SetVpcPeeringConnections(v []*VpcP } // Contains the parameters for DescribeVpcs. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVpcsRequest type DescribeVpcsInput struct { _ struct{} `type:"structure"` @@ -42132,7 +41751,6 @@ func (s *DescribeVpcsInput) SetVpcIds(v []*string) *DescribeVpcsInput { } // Contains the output of DescribeVpcs. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVpcsResult type DescribeVpcsOutput struct { _ struct{} `type:"structure"` @@ -42157,7 +41775,6 @@ func (s *DescribeVpcsOutput) SetVpcs(v []*Vpc) *DescribeVpcsOutput { } // Contains the parameters for DescribeVpnConnections. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVpnConnectionsRequest type DescribeVpnConnectionsInput struct { _ struct{} `type:"structure"` @@ -42248,7 +41865,6 @@ func (s *DescribeVpnConnectionsInput) SetVpnConnectionIds(v []*string) *Describe } // Contains the output of DescribeVpnConnections. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVpnConnectionsResult type DescribeVpnConnectionsOutput struct { _ struct{} `type:"structure"` @@ -42273,7 +41889,6 @@ func (s *DescribeVpnConnectionsOutput) SetVpnConnections(v []*VpnConnection) *De } // Contains the parameters for DescribeVpnGateways. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVpnGatewaysRequest type DescribeVpnGatewaysInput struct { _ struct{} `type:"structure"` @@ -42356,7 +41971,6 @@ func (s *DescribeVpnGatewaysInput) SetVpnGatewayIds(v []*string) *DescribeVpnGat } // Contains the output of DescribeVpnGateways. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeVpnGatewaysResult type DescribeVpnGatewaysOutput struct { _ struct{} `type:"structure"` @@ -42381,7 +41995,6 @@ func (s *DescribeVpnGatewaysOutput) SetVpnGateways(v []*VpnGateway) *DescribeVpn } // Contains the parameters for DetachClassicLinkVpc. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DetachClassicLinkVpcRequest type DetachClassicLinkVpcInput struct { _ struct{} `type:"structure"` @@ -42447,7 +42060,6 @@ func (s *DetachClassicLinkVpcInput) SetVpcId(v string) *DetachClassicLinkVpcInpu } // Contains the output of DetachClassicLinkVpc. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DetachClassicLinkVpcResult type DetachClassicLinkVpcOutput struct { _ struct{} `type:"structure"` @@ -42472,7 +42084,6 @@ func (s *DetachClassicLinkVpcOutput) SetReturn(v bool) *DetachClassicLinkVpcOutp } // Contains the parameters for DetachInternetGateway. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DetachInternetGatewayRequest type DetachInternetGatewayInput struct { _ struct{} `type:"structure"` @@ -42537,7 +42148,6 @@ func (s *DetachInternetGatewayInput) SetVpcId(v string) *DetachInternetGatewayIn return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DetachInternetGatewayOutput type DetachInternetGatewayOutput struct { _ struct{} `type:"structure"` } @@ -42553,7 +42163,6 @@ func (s DetachInternetGatewayOutput) GoString() string { } // Contains the parameters for DetachNetworkInterface. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DetachNetworkInterfaceRequest type DetachNetworkInterfaceInput struct { _ struct{} `type:"structure"` @@ -42613,7 +42222,6 @@ func (s *DetachNetworkInterfaceInput) SetForce(v bool) *DetachNetworkInterfaceIn return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DetachNetworkInterfaceOutput type DetachNetworkInterfaceOutput struct { _ struct{} `type:"structure"` } @@ -42629,7 +42237,6 @@ func (s DetachNetworkInterfaceOutput) GoString() string { } // Contains the parameters for DetachVolume. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DetachVolumeRequest type DetachVolumeInput struct { _ struct{} `type:"structure"` @@ -42714,7 +42321,6 @@ func (s *DetachVolumeInput) SetVolumeId(v string) *DetachVolumeInput { } // Contains the parameters for DetachVpnGateway. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DetachVpnGatewayRequest type DetachVpnGatewayInput struct { _ struct{} `type:"structure"` @@ -42779,7 +42385,6 @@ func (s *DetachVpnGatewayInput) SetVpnGatewayId(v string) *DetachVpnGatewayInput return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DetachVpnGatewayOutput type DetachVpnGatewayOutput struct { _ struct{} `type:"structure"` } @@ -42795,7 +42400,6 @@ func (s DetachVpnGatewayOutput) GoString() string { } // Describes a DHCP configuration option. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DhcpConfiguration type DhcpConfiguration struct { _ struct{} `type:"structure"` @@ -42829,7 +42433,6 @@ func (s *DhcpConfiguration) SetValues(v []*AttributeValue) *DhcpConfiguration { } // Describes a set of DHCP options. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DhcpOptions type DhcpOptions struct { _ struct{} `type:"structure"` @@ -42872,7 +42475,6 @@ func (s *DhcpOptions) SetTags(v []*Tag) *DhcpOptions { } // Contains the parameters for DisableVgwRoutePropagation. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DisableVgwRoutePropagationRequest type DisableVgwRoutePropagationInput struct { _ struct{} `type:"structure"` @@ -42925,7 +42527,6 @@ func (s *DisableVgwRoutePropagationInput) SetRouteTableId(v string) *DisableVgwR return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DisableVgwRoutePropagationOutput type DisableVgwRoutePropagationOutput struct { _ struct{} `type:"structure"` } @@ -42941,7 +42542,6 @@ func (s DisableVgwRoutePropagationOutput) GoString() string { } // Contains the parameters for DisableVpcClassicLinkDnsSupport. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DisableVpcClassicLinkDnsSupportRequest type DisableVpcClassicLinkDnsSupportInput struct { _ struct{} `type:"structure"` @@ -42966,7 +42566,6 @@ func (s *DisableVpcClassicLinkDnsSupportInput) SetVpcId(v string) *DisableVpcCla } // Contains the output of DisableVpcClassicLinkDnsSupport. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DisableVpcClassicLinkDnsSupportResult type DisableVpcClassicLinkDnsSupportOutput struct { _ struct{} `type:"structure"` @@ -42991,7 +42590,6 @@ func (s *DisableVpcClassicLinkDnsSupportOutput) SetReturn(v bool) *DisableVpcCla } // Contains the parameters for DisableVpcClassicLink. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DisableVpcClassicLinkRequest type DisableVpcClassicLinkInput struct { _ struct{} `type:"structure"` @@ -43043,7 +42641,6 @@ func (s *DisableVpcClassicLinkInput) SetVpcId(v string) *DisableVpcClassicLinkIn } // Contains the output of DisableVpcClassicLink. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DisableVpcClassicLinkResult type DisableVpcClassicLinkOutput struct { _ struct{} `type:"structure"` @@ -43068,7 +42665,6 @@ func (s *DisableVpcClassicLinkOutput) SetReturn(v bool) *DisableVpcClassicLinkOu } // Contains the parameters for DisassociateAddress. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DisassociateAddressRequest type DisassociateAddressInput struct { _ struct{} `type:"structure"` @@ -43113,7 +42709,6 @@ func (s *DisassociateAddressInput) SetPublicIp(v string) *DisassociateAddressInp return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DisassociateAddressOutput type DisassociateAddressOutput struct { _ struct{} `type:"structure"` } @@ -43128,7 +42723,6 @@ func (s DisassociateAddressOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DisassociateIamInstanceProfileRequest type DisassociateIamInstanceProfileInput struct { _ struct{} `type:"structure"` @@ -43167,7 +42761,6 @@ func (s *DisassociateIamInstanceProfileInput) SetAssociationId(v string) *Disass return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DisassociateIamInstanceProfileResult type DisassociateIamInstanceProfileOutput struct { _ struct{} `type:"structure"` @@ -43192,7 +42785,6 @@ func (s *DisassociateIamInstanceProfileOutput) SetIamInstanceProfileAssociation( } // Contains the parameters for DisassociateRouteTable. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DisassociateRouteTableRequest type DisassociateRouteTableInput struct { _ struct{} `type:"structure"` @@ -43244,7 +42836,6 @@ func (s *DisassociateRouteTableInput) SetDryRun(v bool) *DisassociateRouteTableI return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DisassociateRouteTableOutput type DisassociateRouteTableOutput struct { _ struct{} `type:"structure"` } @@ -43259,7 +42850,6 @@ func (s DisassociateRouteTableOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DisassociateSubnetCidrBlockRequest type DisassociateSubnetCidrBlockInput struct { _ struct{} `type:"structure"` @@ -43298,7 +42888,6 @@ func (s *DisassociateSubnetCidrBlockInput) SetAssociationId(v string) *Disassoci return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DisassociateSubnetCidrBlockResult type DisassociateSubnetCidrBlockOutput struct { _ struct{} `type:"structure"` @@ -43331,7 +42920,6 @@ func (s *DisassociateSubnetCidrBlockOutput) SetSubnetId(v string) *DisassociateS return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DisassociateVpcCidrBlockRequest type DisassociateVpcCidrBlockInput struct { _ struct{} `type:"structure"` @@ -43370,7 +42958,6 @@ func (s *DisassociateVpcCidrBlockInput) SetAssociationId(v string) *Disassociate return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DisassociateVpcCidrBlockResult type DisassociateVpcCidrBlockOutput struct { _ struct{} `type:"structure"` @@ -43413,7 +43000,6 @@ func (s *DisassociateVpcCidrBlockOutput) SetVpcId(v string) *DisassociateVpcCidr } // Describes a disk image. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DiskImage type DiskImage struct { _ struct{} `type:"structure"` @@ -43476,7 +43062,6 @@ func (s *DiskImage) SetVolume(v *VolumeDetail) *DiskImage { } // Describes a disk image. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DiskImageDescription type DiskImageDescription struct { _ struct{} `type:"structure"` @@ -43541,7 +43126,6 @@ func (s *DiskImageDescription) SetSize(v int64) *DiskImageDescription { } // Describes a disk image. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DiskImageDetail type DiskImageDetail struct { _ struct{} `type:"structure"` @@ -43616,7 +43200,6 @@ func (s *DiskImageDetail) SetImportManifestUrl(v string) *DiskImageDetail { } // Describes a disk image volume. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DiskImageVolumeDescription type DiskImageVolumeDescription struct { _ struct{} `type:"structure"` @@ -43652,7 +43235,6 @@ func (s *DiskImageVolumeDescription) SetSize(v int64) *DiskImageVolumeDescriptio } // Describes a DNS entry. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DnsEntry type DnsEntry struct { _ struct{} `type:"structure"` @@ -43686,7 +43268,6 @@ func (s *DnsEntry) SetHostedZoneId(v string) *DnsEntry { } // Describes a block device for an EBS volume. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/EbsBlockDevice type EbsBlockDevice struct { _ struct{} `type:"structure"` @@ -43796,7 +43377,6 @@ func (s *EbsBlockDevice) SetVolumeType(v string) *EbsBlockDevice { } // Describes a parameter used to set up an EBS volume in a block device mapping. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/EbsInstanceBlockDevice type EbsInstanceBlockDevice struct { _ struct{} `type:"structure"` @@ -43849,7 +43429,6 @@ func (s *EbsInstanceBlockDevice) SetVolumeId(v string) *EbsInstanceBlockDevice { // Describes information used to set up an EBS volume specified in a block device // mapping. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/EbsInstanceBlockDeviceSpecification type EbsInstanceBlockDeviceSpecification struct { _ struct{} `type:"structure"` @@ -43883,7 +43462,6 @@ func (s *EbsInstanceBlockDeviceSpecification) SetVolumeId(v string) *EbsInstance } // Describes an egress-only Internet gateway. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/EgressOnlyInternetGateway type EgressOnlyInternetGateway struct { _ struct{} `type:"structure"` @@ -43917,7 +43495,6 @@ func (s *EgressOnlyInternetGateway) SetEgressOnlyInternetGatewayId(v string) *Eg } // Describes the association between an instance and an Elastic GPU. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ElasticGpuAssociation type ElasticGpuAssociation struct { _ struct{} `type:"structure"` @@ -43969,7 +43546,6 @@ func (s *ElasticGpuAssociation) SetElasticGpuId(v string) *ElasticGpuAssociation } // Describes the status of an Elastic GPU. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ElasticGpuHealth type ElasticGpuHealth struct { _ struct{} `type:"structure"` @@ -43994,7 +43570,6 @@ func (s *ElasticGpuHealth) SetStatus(v string) *ElasticGpuHealth { } // A specification for an Elastic GPU. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ElasticGpuSpecification type ElasticGpuSpecification struct { _ struct{} `type:"structure"` @@ -44034,7 +43609,6 @@ func (s *ElasticGpuSpecification) SetType(v string) *ElasticGpuSpecification { } // Describes an elastic GPU. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ElasticGpuSpecificationResponse type ElasticGpuSpecificationResponse struct { _ struct{} `type:"structure"` @@ -44059,7 +43633,6 @@ func (s *ElasticGpuSpecificationResponse) SetType(v string) *ElasticGpuSpecifica } // Describes an Elastic GPU. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ElasticGpus type ElasticGpus struct { _ struct{} `type:"structure"` @@ -44129,7 +43702,6 @@ func (s *ElasticGpus) SetInstanceId(v string) *ElasticGpus { } // Contains the parameters for EnableVgwRoutePropagation. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/EnableVgwRoutePropagationRequest type EnableVgwRoutePropagationInput struct { _ struct{} `type:"structure"` @@ -44182,7 +43754,6 @@ func (s *EnableVgwRoutePropagationInput) SetRouteTableId(v string) *EnableVgwRou return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/EnableVgwRoutePropagationOutput type EnableVgwRoutePropagationOutput struct { _ struct{} `type:"structure"` } @@ -44198,7 +43769,6 @@ func (s EnableVgwRoutePropagationOutput) GoString() string { } // Contains the parameters for EnableVolumeIO. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/EnableVolumeIORequest type EnableVolumeIOInput struct { _ struct{} `type:"structure"` @@ -44249,7 +43819,6 @@ func (s *EnableVolumeIOInput) SetVolumeId(v string) *EnableVolumeIOInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/EnableVolumeIOOutput type EnableVolumeIOOutput struct { _ struct{} `type:"structure"` } @@ -44265,7 +43834,6 @@ func (s EnableVolumeIOOutput) GoString() string { } // Contains the parameters for EnableVpcClassicLinkDnsSupport. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/EnableVpcClassicLinkDnsSupportRequest type EnableVpcClassicLinkDnsSupportInput struct { _ struct{} `type:"structure"` @@ -44290,7 +43858,6 @@ func (s *EnableVpcClassicLinkDnsSupportInput) SetVpcId(v string) *EnableVpcClass } // Contains the output of EnableVpcClassicLinkDnsSupport. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/EnableVpcClassicLinkDnsSupportResult type EnableVpcClassicLinkDnsSupportOutput struct { _ struct{} `type:"structure"` @@ -44315,7 +43882,6 @@ func (s *EnableVpcClassicLinkDnsSupportOutput) SetReturn(v bool) *EnableVpcClass } // Contains the parameters for EnableVpcClassicLink. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/EnableVpcClassicLinkRequest type EnableVpcClassicLinkInput struct { _ struct{} `type:"structure"` @@ -44367,7 +43933,6 @@ func (s *EnableVpcClassicLinkInput) SetVpcId(v string) *EnableVpcClassicLinkInpu } // Contains the output of EnableVpcClassicLink. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/EnableVpcClassicLinkResult type EnableVpcClassicLinkOutput struct { _ struct{} `type:"structure"` @@ -44392,7 +43957,6 @@ func (s *EnableVpcClassicLinkOutput) SetReturn(v bool) *EnableVpcClassicLinkOutp } // Describes a Spot Fleet event. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/EventInformation type EventInformation struct { _ struct{} `type:"structure"` @@ -44496,7 +44060,6 @@ func (s *EventInformation) SetInstanceId(v string) *EventInformation { } // Describes an instance export task. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ExportTask type ExportTask struct { _ struct{} `type:"structure"` @@ -44566,7 +44129,6 @@ func (s *ExportTask) SetStatusMessage(v string) *ExportTask { } // Describes the format and location for an instance export task. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ExportToS3Task type ExportToS3Task struct { _ struct{} `type:"structure"` @@ -44620,7 +44182,6 @@ func (s *ExportToS3Task) SetS3Key(v string) *ExportToS3Task { } // Describes an instance export task. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ExportToS3TaskSpecification type ExportToS3TaskSpecification struct { _ struct{} `type:"structure"` @@ -44677,7 +44238,6 @@ func (s *ExportToS3TaskSpecification) SetS3Prefix(v string) *ExportToS3TaskSpeci // A filter name and value pair that is used to return a more specific list // of results. Filters can be used to match a set of resources by various criteria, // such as tags, attributes, or IDs. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/Filter type Filter struct { _ struct{} `type:"structure"` @@ -44711,7 +44271,6 @@ func (s *Filter) SetValues(v []*string) *Filter { } // Describes a launch template. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/FleetLaunchTemplateSpecification type FleetLaunchTemplateSpecification struct { _ struct{} `type:"structure"` @@ -44770,7 +44329,6 @@ func (s *FleetLaunchTemplateSpecification) SetVersion(v string) *FleetLaunchTemp } // Describes a flow log. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/FlowLog type FlowLog struct { _ struct{} `type:"structure"` @@ -44872,7 +44430,6 @@ func (s *FlowLog) SetTrafficType(v string) *FlowLog { } // Describes an Amazon FPGA image (AFI). -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/FpgaImage type FpgaImage struct { _ struct{} `type:"structure"` @@ -45014,7 +44571,6 @@ func (s *FpgaImage) SetUpdateTime(v time.Time) *FpgaImage { } // Describes an Amazon FPGA image (AFI) attribute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/FpgaImageAttribute type FpgaImageAttribute struct { _ struct{} `type:"structure"` @@ -45076,7 +44632,6 @@ func (s *FpgaImageAttribute) SetProductCodes(v []*ProductCode) *FpgaImageAttribu // Describes the state of the bitstream generation process for an Amazon FPGA // image (AFI). -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/FpgaImageState type FpgaImageState struct { _ struct{} `type:"structure"` @@ -45118,7 +44673,6 @@ func (s *FpgaImageState) SetMessage(v string) *FpgaImageState { } // Contains the parameters for GetConsoleOutput. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/GetConsoleOutputRequest type GetConsoleOutputInput struct { _ struct{} `type:"structure"` @@ -45170,7 +44724,6 @@ func (s *GetConsoleOutputInput) SetInstanceId(v string) *GetConsoleOutputInput { } // Contains the output of GetConsoleOutput. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/GetConsoleOutputResult type GetConsoleOutputOutput struct { _ struct{} `type:"structure"` @@ -45214,7 +44767,6 @@ func (s *GetConsoleOutputOutput) SetTimestamp(v time.Time) *GetConsoleOutputOutp } // Contains the parameters for the request. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/GetConsoleScreenshotRequest type GetConsoleScreenshotInput struct { _ struct{} `type:"structure"` @@ -45276,7 +44828,6 @@ func (s *GetConsoleScreenshotInput) SetWakeUp(v bool) *GetConsoleScreenshotInput } // Contains the output of the request. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/GetConsoleScreenshotResult type GetConsoleScreenshotOutput struct { _ struct{} `type:"structure"` @@ -45309,7 +44860,6 @@ func (s *GetConsoleScreenshotOutput) SetInstanceId(v string) *GetConsoleScreensh return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/GetHostReservationPurchasePreviewRequest type GetHostReservationPurchasePreviewInput struct { _ struct{} `type:"structure"` @@ -45363,7 +44913,6 @@ func (s *GetHostReservationPurchasePreviewInput) SetOfferingId(v string) *GetHos return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/GetHostReservationPurchasePreviewResult type GetHostReservationPurchasePreviewOutput struct { _ struct{} `type:"structure"` @@ -45416,7 +44965,6 @@ func (s *GetHostReservationPurchasePreviewOutput) SetTotalUpfrontPrice(v string) return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/GetLaunchTemplateDataRequest type GetLaunchTemplateDataInput struct { _ struct{} `type:"structure"` @@ -45467,7 +45015,6 @@ func (s *GetLaunchTemplateDataInput) SetInstanceId(v string) *GetLaunchTemplateD return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/GetLaunchTemplateDataResult type GetLaunchTemplateDataOutput struct { _ struct{} `type:"structure"` @@ -45492,7 +45039,6 @@ func (s *GetLaunchTemplateDataOutput) SetLaunchTemplateData(v *ResponseLaunchTem } // Contains the parameters for GetPasswordData. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/GetPasswordDataRequest type GetPasswordDataInput struct { _ struct{} `type:"structure"` @@ -45544,7 +45090,6 @@ func (s *GetPasswordDataInput) SetInstanceId(v string) *GetPasswordDataInput { } // Contains the output of GetPasswordData. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/GetPasswordDataResult type GetPasswordDataOutput struct { _ struct{} `type:"structure"` @@ -45588,7 +45133,6 @@ func (s *GetPasswordDataOutput) SetTimestamp(v time.Time) *GetPasswordDataOutput } // Contains the parameters for GetReservedInstanceExchangeQuote. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/GetReservedInstancesExchangeQuoteRequest type GetReservedInstancesExchangeQuoteInput struct { _ struct{} `type:"structure"` @@ -45660,7 +45204,6 @@ func (s *GetReservedInstancesExchangeQuoteInput) SetTargetConfigurations(v []*Ta } // Contains the output of GetReservedInstancesExchangeQuote. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/GetReservedInstancesExchangeQuoteResult type GetReservedInstancesExchangeQuoteOutput struct { _ struct{} `type:"structure"` @@ -45757,7 +45300,6 @@ func (s *GetReservedInstancesExchangeQuoteOutput) SetValidationFailureReason(v s } // Describes a security group. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/GroupIdentifier type GroupIdentifier struct { _ struct{} `type:"structure"` @@ -45791,7 +45333,6 @@ func (s *GroupIdentifier) SetGroupName(v string) *GroupIdentifier { } // Describes an event in the history of the Spot Fleet request. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/HistoryRecord type HistoryRecord struct { _ struct{} `type:"structure"` @@ -45849,7 +45390,6 @@ func (s *HistoryRecord) SetTimestamp(v time.Time) *HistoryRecord { } // Describes the properties of the Dedicated Host. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/Host type Host struct { _ struct{} `type:"structure"` @@ -45949,7 +45489,6 @@ func (s *Host) SetState(v string) *Host { } // Describes an instance running on a Dedicated Host. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/HostInstance type HostInstance struct { _ struct{} `type:"structure"` @@ -45983,7 +45522,6 @@ func (s *HostInstance) SetInstanceType(v string) *HostInstance { } // Details about the Dedicated Host Reservation offering. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/HostOffering type HostOffering struct { _ struct{} `type:"structure"` @@ -46062,7 +45600,6 @@ func (s *HostOffering) SetUpfrontPrice(v string) *HostOffering { } // Describes properties of a Dedicated Host. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/HostProperties type HostProperties struct { _ struct{} `type:"structure"` @@ -46114,7 +45651,6 @@ func (s *HostProperties) SetTotalVCpus(v int64) *HostProperties { } // Details about the Dedicated Host Reservation and associated Dedicated Hosts. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/HostReservation type HostReservation struct { _ struct{} `type:"structure"` @@ -46252,7 +45788,6 @@ func (s *HostReservation) SetUpfrontPrice(v string) *HostReservation { } // Describes an IAM instance profile. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/IamInstanceProfile type IamInstanceProfile struct { _ struct{} `type:"structure"` @@ -46286,7 +45821,6 @@ func (s *IamInstanceProfile) SetId(v string) *IamInstanceProfile { } // Describes an association between an IAM instance profile and an instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/IamInstanceProfileAssociation type IamInstanceProfileAssociation struct { _ struct{} `type:"structure"` @@ -46347,7 +45881,6 @@ func (s *IamInstanceProfileAssociation) SetTimestamp(v time.Time) *IamInstancePr } // Describes an IAM instance profile. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/IamInstanceProfileSpecification type IamInstanceProfileSpecification struct { _ struct{} `type:"structure"` @@ -46381,7 +45914,6 @@ func (s *IamInstanceProfileSpecification) SetName(v string) *IamInstanceProfileS } // Describes the ICMP type and code. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/IcmpTypeCode type IcmpTypeCode struct { _ struct{} `type:"structure"` @@ -46415,7 +45947,6 @@ func (s *IcmpTypeCode) SetType(v int64) *IcmpTypeCode { } // Describes the ID format for a resource. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/IdFormat type IdFormat struct { _ struct{} `type:"structure"` @@ -46460,7 +45991,6 @@ func (s *IdFormat) SetUseLongIds(v bool) *IdFormat { } // Describes an image. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/Image type Image struct { _ struct{} `type:"structure"` @@ -46700,7 +46230,6 @@ func (s *Image) SetVirtualizationType(v string) *Image { } // Describes the disk container object for an import image task. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ImageDiskContainer type ImageDiskContainer struct { _ struct{} `type:"structure"` @@ -46773,7 +46302,6 @@ func (s *ImageDiskContainer) SetUserBucket(v *UserBucket) *ImageDiskContainer { } // Contains the parameters for ImportImage. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ImportImageRequest type ImportImageInput struct { _ struct{} `type:"structure"` @@ -46895,7 +46423,6 @@ func (s *ImportImageInput) SetRoleName(v string) *ImportImageInput { } // Contains the output for ImportImage. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ImportImageResult type ImportImageOutput struct { _ struct{} `type:"structure"` @@ -47010,7 +46537,6 @@ func (s *ImportImageOutput) SetStatusMessage(v string) *ImportImageOutput { } // Describes an import image task. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ImportImageTask type ImportImageTask struct { _ struct{} `type:"structure"` @@ -47129,7 +46655,6 @@ func (s *ImportImageTask) SetStatusMessage(v string) *ImportImageTask { } // Contains the parameters for ImportInstance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ImportInstanceRequest type ImportInstanceInput struct { _ struct{} `type:"structure"` @@ -47218,7 +46743,6 @@ func (s *ImportInstanceInput) SetPlatform(v string) *ImportInstanceInput { } // Describes the launch specification for VM import. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ImportInstanceLaunchSpecification type ImportInstanceLaunchSpecification struct { _ struct{} `type:"structure"` @@ -47338,7 +46862,6 @@ func (s *ImportInstanceLaunchSpecification) SetUserData(v *UserData) *ImportInst } // Contains the output for ImportInstance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ImportInstanceResult type ImportInstanceOutput struct { _ struct{} `type:"structure"` @@ -47363,7 +46886,6 @@ func (s *ImportInstanceOutput) SetConversionTask(v *ConversionTask) *ImportInsta } // Describes an import instance task. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ImportInstanceTaskDetails type ImportInstanceTaskDetails struct { _ struct{} `type:"structure"` @@ -47417,7 +46939,6 @@ func (s *ImportInstanceTaskDetails) SetVolumes(v []*ImportInstanceVolumeDetailIt } // Describes an import volume task. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ImportInstanceVolumeDetailItem type ImportInstanceVolumeDetailItem struct { _ struct{} `type:"structure"` @@ -47506,7 +47027,6 @@ func (s *ImportInstanceVolumeDetailItem) SetVolume(v *DiskImageVolumeDescription } // Contains the parameters for ImportKeyPair. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ImportKeyPairRequest type ImportKeyPairInput struct { _ struct{} `type:"structure"` @@ -47575,7 +47095,6 @@ func (s *ImportKeyPairInput) SetPublicKeyMaterial(v []byte) *ImportKeyPairInput } // Contains the output of ImportKeyPair. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ImportKeyPairResult type ImportKeyPairOutput struct { _ struct{} `type:"structure"` @@ -47609,7 +47128,6 @@ func (s *ImportKeyPairOutput) SetKeyName(v string) *ImportKeyPairOutput { } // Contains the parameters for ImportSnapshot. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ImportSnapshotRequest type ImportSnapshotInput struct { _ struct{} `type:"structure"` @@ -47682,7 +47200,6 @@ func (s *ImportSnapshotInput) SetRoleName(v string) *ImportSnapshotInput { } // Contains the output for ImportSnapshot. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ImportSnapshotResult type ImportSnapshotOutput struct { _ struct{} `type:"structure"` @@ -47725,7 +47242,6 @@ func (s *ImportSnapshotOutput) SetSnapshotTaskDetail(v *SnapshotTaskDetail) *Imp } // Describes an import snapshot task. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ImportSnapshotTask type ImportSnapshotTask struct { _ struct{} `type:"structure"` @@ -47768,7 +47284,6 @@ func (s *ImportSnapshotTask) SetSnapshotTaskDetail(v *SnapshotTaskDetail) *Impor } // Contains the parameters for ImportVolume. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ImportVolumeRequest type ImportVolumeInput struct { _ struct{} `type:"structure"` @@ -47867,7 +47382,6 @@ func (s *ImportVolumeInput) SetVolume(v *VolumeDetail) *ImportVolumeInput { } // Contains the output for ImportVolume. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ImportVolumeResult type ImportVolumeOutput struct { _ struct{} `type:"structure"` @@ -47892,7 +47406,6 @@ func (s *ImportVolumeOutput) SetConversionTask(v *ConversionTask) *ImportVolumeO } // Describes an import volume task. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ImportVolumeTaskDetails type ImportVolumeTaskDetails struct { _ struct{} `type:"structure"` @@ -47961,7 +47474,6 @@ func (s *ImportVolumeTaskDetails) SetVolume(v *DiskImageVolumeDescription) *Impo } // Describes an instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/Instance type Instance struct { _ struct{} `type:"structure"` @@ -48350,7 +47862,6 @@ func (s *Instance) SetVpcId(v string) *Instance { } // Describes a block device mapping. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/InstanceBlockDeviceMapping type InstanceBlockDeviceMapping struct { _ struct{} `type:"structure"` @@ -48385,7 +47896,6 @@ func (s *InstanceBlockDeviceMapping) SetEbs(v *EbsInstanceBlockDevice) *Instance } // Describes a block device mapping entry. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/InstanceBlockDeviceMappingSpecification type InstanceBlockDeviceMappingSpecification struct { _ struct{} `type:"structure"` @@ -48438,7 +47948,6 @@ func (s *InstanceBlockDeviceMappingSpecification) SetVirtualName(v string) *Inst } // Information about the instance type that the Dedicated Host supports. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/InstanceCapacity type InstanceCapacity struct { _ struct{} `type:"structure"` @@ -48481,7 +47990,6 @@ func (s *InstanceCapacity) SetTotalCapacity(v int64) *InstanceCapacity { } // Describes a Reserved Instance listing state. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/InstanceCount type InstanceCount struct { _ struct{} `type:"structure"` @@ -48515,7 +48023,6 @@ func (s *InstanceCount) SetState(v string) *InstanceCount { } // Describes the credit option for CPU usage of a T2 instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/InstanceCreditSpecification type InstanceCreditSpecification struct { _ struct{} `type:"structure"` @@ -48550,7 +48057,6 @@ func (s *InstanceCreditSpecification) SetInstanceId(v string) *InstanceCreditSpe } // Describes the credit option for CPU usage of a T2 instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/InstanceCreditSpecificationRequest type InstanceCreditSpecificationRequest struct { _ struct{} `type:"structure"` @@ -48585,7 +48091,6 @@ func (s *InstanceCreditSpecificationRequest) SetInstanceId(v string) *InstanceCr } // Describes an instance to export. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/InstanceExportDetails type InstanceExportDetails struct { _ struct{} `type:"structure"` @@ -48619,7 +48124,6 @@ func (s *InstanceExportDetails) SetTargetEnvironment(v string) *InstanceExportDe } // Describes an IPv6 address. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/InstanceIpv6Address type InstanceIpv6Address struct { _ struct{} `type:"structure"` @@ -48644,7 +48148,6 @@ func (s *InstanceIpv6Address) SetIpv6Address(v string) *InstanceIpv6Address { } // Describes an IPv6 address. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/InstanceIpv6AddressRequest type InstanceIpv6AddressRequest struct { _ struct{} `type:"structure"` @@ -48669,7 +48172,6 @@ func (s *InstanceIpv6AddressRequest) SetIpv6Address(v string) *InstanceIpv6Addre } // Describes the market (purchasing) option for the instances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/InstanceMarketOptionsRequest type InstanceMarketOptionsRequest struct { _ struct{} `type:"structure"` @@ -48703,7 +48205,6 @@ func (s *InstanceMarketOptionsRequest) SetSpotOptions(v *SpotMarketOptions) *Ins } // Describes the monitoring of an instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/InstanceMonitoring type InstanceMonitoring struct { _ struct{} `type:"structure"` @@ -48737,7 +48238,6 @@ func (s *InstanceMonitoring) SetMonitoring(v *Monitoring) *InstanceMonitoring { } // Describes a network interface. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/InstanceNetworkInterface type InstanceNetworkInterface struct { _ struct{} `type:"structure"` @@ -48889,7 +48389,6 @@ func (s *InstanceNetworkInterface) SetVpcId(v string) *InstanceNetworkInterface } // Describes association information for an Elastic IP address (IPv4). -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/InstanceNetworkInterfaceAssociation type InstanceNetworkInterfaceAssociation struct { _ struct{} `type:"structure"` @@ -48932,7 +48431,6 @@ func (s *InstanceNetworkInterfaceAssociation) SetPublicIp(v string) *InstanceNet } // Describes a network interface attachment. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/InstanceNetworkInterfaceAttachment type InstanceNetworkInterfaceAttachment struct { _ struct{} `type:"structure"` @@ -48993,7 +48491,6 @@ func (s *InstanceNetworkInterfaceAttachment) SetStatus(v string) *InstanceNetwor } // Describes a network interface. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/InstanceNetworkInterfaceSpecification type InstanceNetworkInterfaceSpecification struct { _ struct{} `type:"structure"` @@ -49163,7 +48660,6 @@ func (s *InstanceNetworkInterfaceSpecification) SetSubnetId(v string) *InstanceN } // Describes a private IPv4 address. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/InstancePrivateIpAddress type InstancePrivateIpAddress struct { _ struct{} `type:"structure"` @@ -49216,7 +48712,6 @@ func (s *InstancePrivateIpAddress) SetPrivateIpAddress(v string) *InstancePrivat } // Describes the current state of an instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/InstanceState type InstanceState struct { _ struct{} `type:"structure"` @@ -49263,7 +48758,6 @@ func (s *InstanceState) SetName(v string) *InstanceState { } // Describes an instance state change. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/InstanceStateChange type InstanceStateChange struct { _ struct{} `type:"structure"` @@ -49306,7 +48800,6 @@ func (s *InstanceStateChange) SetPreviousState(v *InstanceState) *InstanceStateC } // Describes the status of an instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/InstanceStatus type InstanceStatus struct { _ struct{} `type:"structure"` @@ -49380,7 +48873,6 @@ func (s *InstanceStatus) SetSystemStatus(v *InstanceStatusSummary) *InstanceStat } // Describes the instance status. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/InstanceStatusDetails type InstanceStatusDetails struct { _ struct{} `type:"structure"` @@ -49424,7 +48916,6 @@ func (s *InstanceStatusDetails) SetStatus(v string) *InstanceStatusDetails { } // Describes a scheduled event for an instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/InstanceStatusEvent type InstanceStatusEvent struct { _ struct{} `type:"structure"` @@ -49480,7 +48971,6 @@ func (s *InstanceStatusEvent) SetNotBefore(v time.Time) *InstanceStatusEvent { } // Describes the status of an instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/InstanceStatusSummary type InstanceStatusSummary struct { _ struct{} `type:"structure"` @@ -49514,7 +49004,6 @@ func (s *InstanceStatusSummary) SetStatus(v string) *InstanceStatusSummary { } // Describes an Internet gateway. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/InternetGateway type InternetGateway struct { _ struct{} `type:"structure"` @@ -49558,7 +49047,6 @@ func (s *InternetGateway) SetTags(v []*Tag) *InternetGateway { // Describes the attachment of a VPC to an Internet gateway or an egress-only // Internet gateway. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/InternetGatewayAttachment type InternetGatewayAttachment struct { _ struct{} `type:"structure"` @@ -49593,7 +49081,6 @@ func (s *InternetGatewayAttachment) SetVpcId(v string) *InternetGatewayAttachmen } // Describes a set of permissions for a security group rule. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/IpPermission type IpPermission struct { _ struct{} `type:"structure"` @@ -49687,7 +49174,6 @@ func (s *IpPermission) SetUserIdGroupPairs(v []*UserIdGroupPair) *IpPermission { } // Describes an IPv4 range. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/IpRange type IpRange struct { _ struct{} `type:"structure"` @@ -49726,7 +49212,6 @@ func (s *IpRange) SetDescription(v string) *IpRange { } // Describes an IPv6 CIDR block. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/Ipv6CidrBlock type Ipv6CidrBlock struct { _ struct{} `type:"structure"` @@ -49751,7 +49236,6 @@ func (s *Ipv6CidrBlock) SetIpv6CidrBlock(v string) *Ipv6CidrBlock { } // [EC2-VPC only] Describes an IPv6 range. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/Ipv6Range type Ipv6Range struct { _ struct{} `type:"structure"` @@ -49790,7 +49274,6 @@ func (s *Ipv6Range) SetDescription(v string) *Ipv6Range { } // Describes a key pair. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/KeyPairInfo type KeyPairInfo struct { _ struct{} `type:"structure"` @@ -49827,7 +49310,6 @@ func (s *KeyPairInfo) SetKeyName(v string) *KeyPairInfo { } // Describes a launch permission. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LaunchPermission type LaunchPermission struct { _ struct{} `type:"structure"` @@ -49861,7 +49343,6 @@ func (s *LaunchPermission) SetUserId(v string) *LaunchPermission { } // Describes a launch permission modification. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LaunchPermissionModifications type LaunchPermissionModifications struct { _ struct{} `type:"structure"` @@ -49896,7 +49377,6 @@ func (s *LaunchPermissionModifications) SetRemove(v []*LaunchPermission) *Launch } // Describes the launch specification for an instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LaunchSpecification type LaunchSpecification struct { _ struct{} `type:"structure"` @@ -50058,7 +49538,6 @@ func (s *LaunchSpecification) SetUserData(v string) *LaunchSpecification { } // Describes a launch template. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LaunchTemplate type LaunchTemplate struct { _ struct{} `type:"structure"` @@ -50137,7 +49616,6 @@ func (s *LaunchTemplate) SetTags(v []*Tag) *LaunchTemplate { } // Describes a block device mapping. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LaunchTemplateBlockDeviceMapping type LaunchTemplateBlockDeviceMapping struct { _ struct{} `type:"structure"` @@ -50190,7 +49668,6 @@ func (s *LaunchTemplateBlockDeviceMapping) SetVirtualName(v string) *LaunchTempl } // Describes a block device mapping. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LaunchTemplateBlockDeviceMappingRequest type LaunchTemplateBlockDeviceMappingRequest struct { _ struct{} `type:"structure"` @@ -50248,7 +49725,6 @@ func (s *LaunchTemplateBlockDeviceMappingRequest) SetVirtualName(v string) *Laun } // Describes a launch template and overrides. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LaunchTemplateConfig type LaunchTemplateConfig struct { _ struct{} `type:"structure"` @@ -50298,7 +49774,6 @@ func (s *LaunchTemplateConfig) SetOverrides(v []*LaunchTemplateOverrides) *Launc } // Describes a block device for an EBS volume. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LaunchTemplateEbsBlockDevice type LaunchTemplateEbsBlockDevice struct { _ struct{} `type:"structure"` @@ -50377,7 +49852,6 @@ func (s *LaunchTemplateEbsBlockDevice) SetVolumeType(v string) *LaunchTemplateEb } // The parameters for a block device for an EBS volume. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LaunchTemplateEbsBlockDeviceRequest type LaunchTemplateEbsBlockDeviceRequest struct { _ struct{} `type:"structure"` @@ -50470,7 +49944,6 @@ func (s *LaunchTemplateEbsBlockDeviceRequest) SetVolumeType(v string) *LaunchTem } // Describes an IAM instance profile. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LaunchTemplateIamInstanceProfileSpecification type LaunchTemplateIamInstanceProfileSpecification struct { _ struct{} `type:"structure"` @@ -50504,7 +49977,6 @@ func (s *LaunchTemplateIamInstanceProfileSpecification) SetName(v string) *Launc } // An IAM instance profile. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LaunchTemplateIamInstanceProfileSpecificationRequest type LaunchTemplateIamInstanceProfileSpecificationRequest struct { _ struct{} `type:"structure"` @@ -50538,7 +50010,6 @@ func (s *LaunchTemplateIamInstanceProfileSpecificationRequest) SetName(v string) } // The market (purchasing) option for the instances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LaunchTemplateInstanceMarketOptions type LaunchTemplateInstanceMarketOptions struct { _ struct{} `type:"structure"` @@ -50572,7 +50043,6 @@ func (s *LaunchTemplateInstanceMarketOptions) SetSpotOptions(v *LaunchTemplateSp } // The market (purchasing) option for the instances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LaunchTemplateInstanceMarketOptionsRequest type LaunchTemplateInstanceMarketOptionsRequest struct { _ struct{} `type:"structure"` @@ -50606,7 +50076,6 @@ func (s *LaunchTemplateInstanceMarketOptionsRequest) SetSpotOptions(v *LaunchTem } // Describes a network interface. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LaunchTemplateInstanceNetworkInterfaceSpecification type LaunchTemplateInstanceNetworkInterfaceSpecification struct { _ struct{} `type:"structure"` @@ -50731,7 +50200,6 @@ func (s *LaunchTemplateInstanceNetworkInterfaceSpecification) SetSubnetId(v stri } // The parameters for a network interface. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LaunchTemplateInstanceNetworkInterfaceSpecificationRequest type LaunchTemplateInstanceNetworkInterfaceSpecificationRequest struct { _ struct{} `type:"structure"` @@ -50878,7 +50346,6 @@ func (s *LaunchTemplateInstanceNetworkInterfaceSpecificationRequest) SetSubnetId } // Describes overrides for a launch template. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LaunchTemplateOverrides type LaunchTemplateOverrides struct { _ struct{} `type:"structure"` @@ -50939,7 +50406,6 @@ func (s *LaunchTemplateOverrides) SetWeightedCapacity(v float64) *LaunchTemplate } // Describes the placement of an instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LaunchTemplatePlacement type LaunchTemplatePlacement struct { _ struct{} `type:"structure"` @@ -51010,7 +50476,6 @@ func (s *LaunchTemplatePlacement) SetTenancy(v string) *LaunchTemplatePlacement } // The placement for the instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LaunchTemplatePlacementRequest type LaunchTemplatePlacementRequest struct { _ struct{} `type:"structure"` @@ -51082,7 +50547,6 @@ func (s *LaunchTemplatePlacementRequest) SetTenancy(v string) *LaunchTemplatePla // The launch template to use. You must specify either the launch template ID // or launch template name in the request. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LaunchTemplateSpecification type LaunchTemplateSpecification struct { _ struct{} `type:"structure"` @@ -51127,7 +50591,6 @@ func (s *LaunchTemplateSpecification) SetVersion(v string) *LaunchTemplateSpecif } // The options for Spot Instances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LaunchTemplateSpotMarketOptions type LaunchTemplateSpotMarketOptions struct { _ struct{} `type:"structure"` @@ -51193,7 +50656,6 @@ func (s *LaunchTemplateSpotMarketOptions) SetValidUntil(v time.Time) *LaunchTemp } // The options for Spot Instances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LaunchTemplateSpotMarketOptionsRequest type LaunchTemplateSpotMarketOptionsRequest struct { _ struct{} `type:"structure"` @@ -51260,7 +50722,6 @@ func (s *LaunchTemplateSpotMarketOptionsRequest) SetValidUntil(v time.Time) *Lau } // The tag specification for the launch template. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LaunchTemplateTagSpecification type LaunchTemplateTagSpecification struct { _ struct{} `type:"structure"` @@ -51294,7 +50755,6 @@ func (s *LaunchTemplateTagSpecification) SetTags(v []*Tag) *LaunchTemplateTagSpe } // The tags specification for the launch template. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LaunchTemplateTagSpecificationRequest type LaunchTemplateTagSpecificationRequest struct { _ struct{} `type:"structure"` @@ -51329,7 +50789,6 @@ func (s *LaunchTemplateTagSpecificationRequest) SetTags(v []*Tag) *LaunchTemplat } // Describes a launch template version. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LaunchTemplateVersion type LaunchTemplateVersion struct { _ struct{} `type:"structure"` @@ -51417,7 +50876,6 @@ func (s *LaunchTemplateVersion) SetVersionNumber(v int64) *LaunchTemplateVersion } // Describes the monitoring for the instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LaunchTemplatesMonitoring type LaunchTemplatesMonitoring struct { _ struct{} `type:"structure"` @@ -51443,7 +50901,6 @@ func (s *LaunchTemplatesMonitoring) SetEnabled(v bool) *LaunchTemplatesMonitorin } // Describes the monitoring for the instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LaunchTemplatesMonitoringRequest type LaunchTemplatesMonitoringRequest struct { _ struct{} `type:"structure"` @@ -51470,7 +50927,6 @@ func (s *LaunchTemplatesMonitoringRequest) SetEnabled(v bool) *LaunchTemplatesMo // Describes the Classic Load Balancers and target groups to attach to a Spot // Fleet request. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LoadBalancersConfig type LoadBalancersConfig struct { _ struct{} `type:"structure"` @@ -51524,7 +50980,6 @@ func (s *LoadBalancersConfig) SetTargetGroupsConfig(v *TargetGroupsConfig) *Load } // Describes a load permission. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LoadPermission type LoadPermission struct { _ struct{} `type:"structure"` @@ -51558,7 +51013,6 @@ func (s *LoadPermission) SetUserId(v string) *LoadPermission { } // Describes modifications to the load permissions of an Amazon FPGA image (AFI). -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LoadPermissionModifications type LoadPermissionModifications struct { _ struct{} `type:"structure"` @@ -51592,7 +51046,6 @@ func (s *LoadPermissionModifications) SetRemove(v []*LoadPermissionRequest) *Loa } // Describes a load permission. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/LoadPermissionRequest type LoadPermissionRequest struct { _ struct{} `type:"structure"` @@ -51625,7 +51078,6 @@ func (s *LoadPermissionRequest) SetUserId(v string) *LoadPermissionRequest { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyFpgaImageAttributeRequest type ModifyFpgaImageAttributeInput struct { _ struct{} `type:"structure"` @@ -51752,7 +51204,6 @@ func (s *ModifyFpgaImageAttributeInput) SetUserIds(v []*string) *ModifyFpgaImage return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyFpgaImageAttributeResult type ModifyFpgaImageAttributeOutput struct { _ struct{} `type:"structure"` @@ -51777,7 +51228,6 @@ func (s *ModifyFpgaImageAttributeOutput) SetFpgaImageAttribute(v *FpgaImageAttri } // Contains the parameters for ModifyHosts. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyHostsRequest type ModifyHostsInput struct { _ struct{} `type:"structure"` @@ -51831,7 +51281,6 @@ func (s *ModifyHostsInput) SetHostIds(v []*string) *ModifyHostsInput { } // Contains the output of ModifyHosts. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyHostsResult type ModifyHostsOutput struct { _ struct{} `type:"structure"` @@ -51866,7 +51315,6 @@ func (s *ModifyHostsOutput) SetUnsuccessful(v []*UnsuccessfulItem) *ModifyHostsO } // Contains the parameters of ModifyIdFormat. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyIdFormatRequest type ModifyIdFormatInput struct { _ struct{} `type:"structure"` @@ -51919,7 +51367,6 @@ func (s *ModifyIdFormatInput) SetUseLongIds(v bool) *ModifyIdFormatInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyIdFormatOutput type ModifyIdFormatOutput struct { _ struct{} `type:"structure"` } @@ -51935,7 +51382,6 @@ func (s ModifyIdFormatOutput) GoString() string { } // Contains the parameters of ModifyIdentityIdFormat. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyIdentityIdFormatRequest type ModifyIdentityIdFormatInput struct { _ struct{} `type:"structure"` @@ -52004,7 +51450,6 @@ func (s *ModifyIdentityIdFormatInput) SetUseLongIds(v bool) *ModifyIdentityIdFor return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyIdentityIdFormatOutput type ModifyIdentityIdFormatOutput struct { _ struct{} `type:"structure"` } @@ -52020,7 +51465,6 @@ func (s ModifyIdentityIdFormatOutput) GoString() string { } // Contains the parameters for ModifyImageAttribute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyImageAttributeRequest type ModifyImageAttributeInput struct { _ struct{} `type:"structure"` @@ -52149,7 +51593,6 @@ func (s *ModifyImageAttributeInput) SetValue(v string) *ModifyImageAttributeInpu return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyImageAttributeOutput type ModifyImageAttributeOutput struct { _ struct{} `type:"structure"` } @@ -52165,7 +51608,6 @@ func (s ModifyImageAttributeOutput) GoString() string { } // Contains the parameters for ModifyInstanceAttribute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyInstanceAttributeRequest type ModifyInstanceAttributeInput struct { _ struct{} `type:"structure"` @@ -52381,7 +51823,6 @@ func (s *ModifyInstanceAttributeInput) SetValue(v string) *ModifyInstanceAttribu return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyInstanceAttributeOutput type ModifyInstanceAttributeOutput struct { _ struct{} `type:"structure"` } @@ -52396,7 +51837,6 @@ func (s ModifyInstanceAttributeOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyInstanceCreditSpecificationRequest type ModifyInstanceCreditSpecificationInput struct { _ struct{} `type:"structure"` @@ -52458,7 +51898,6 @@ func (s *ModifyInstanceCreditSpecificationInput) SetInstanceCreditSpecifications return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyInstanceCreditSpecificationResult type ModifyInstanceCreditSpecificationOutput struct { _ struct{} `type:"structure"` @@ -52494,7 +51933,6 @@ func (s *ModifyInstanceCreditSpecificationOutput) SetUnsuccessfulInstanceCreditS } // Contains the parameters for ModifyInstancePlacement. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyInstancePlacementRequest type ModifyInstancePlacementInput struct { _ struct{} `type:"structure"` @@ -52561,7 +51999,6 @@ func (s *ModifyInstancePlacementInput) SetTenancy(v string) *ModifyInstancePlace } // Contains the output of ModifyInstancePlacement. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyInstancePlacementResult type ModifyInstancePlacementOutput struct { _ struct{} `type:"structure"` @@ -52585,7 +52022,6 @@ func (s *ModifyInstancePlacementOutput) SetReturn(v bool) *ModifyInstancePlaceme return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyLaunchTemplateRequest type ModifyLaunchTemplateInput struct { _ struct{} `type:"structure"` @@ -52664,7 +52100,6 @@ func (s *ModifyLaunchTemplateInput) SetLaunchTemplateName(v string) *ModifyLaunc return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyLaunchTemplateResult type ModifyLaunchTemplateOutput struct { _ struct{} `type:"structure"` @@ -52689,7 +52124,6 @@ func (s *ModifyLaunchTemplateOutput) SetLaunchTemplate(v *LaunchTemplate) *Modif } // Contains the parameters for ModifyNetworkInterfaceAttribute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyNetworkInterfaceAttributeRequest type ModifyNetworkInterfaceAttributeInput struct { _ struct{} `type:"structure"` @@ -52784,7 +52218,6 @@ func (s *ModifyNetworkInterfaceAttributeInput) SetSourceDestCheck(v *AttributeBo return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyNetworkInterfaceAttributeOutput type ModifyNetworkInterfaceAttributeOutput struct { _ struct{} `type:"structure"` } @@ -52800,7 +52233,6 @@ func (s ModifyNetworkInterfaceAttributeOutput) GoString() string { } // Contains the parameters for ModifyReservedInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyReservedInstancesRequest type ModifyReservedInstancesInput struct { _ struct{} `type:"structure"` @@ -52864,7 +52296,6 @@ func (s *ModifyReservedInstancesInput) SetTargetConfigurations(v []*ReservedInst } // Contains the output of ModifyReservedInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyReservedInstancesResult type ModifyReservedInstancesOutput struct { _ struct{} `type:"structure"` @@ -52889,7 +52320,6 @@ func (s *ModifyReservedInstancesOutput) SetReservedInstancesModificationId(v str } // Contains the parameters for ModifySnapshotAttribute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifySnapshotAttributeRequest type ModifySnapshotAttributeInput struct { _ struct{} `type:"structure"` @@ -52987,7 +52417,6 @@ func (s *ModifySnapshotAttributeInput) SetUserIds(v []*string) *ModifySnapshotAt return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifySnapshotAttributeOutput type ModifySnapshotAttributeOutput struct { _ struct{} `type:"structure"` } @@ -53003,7 +52432,6 @@ func (s ModifySnapshotAttributeOutput) GoString() string { } // Contains the parameters for ModifySpotFleetRequest. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifySpotFleetRequestRequest type ModifySpotFleetRequestInput struct { _ struct{} `type:"structure"` @@ -53063,7 +52491,6 @@ func (s *ModifySpotFleetRequestInput) SetTargetCapacity(v int64) *ModifySpotFlee } // Contains the output of ModifySpotFleetRequest. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifySpotFleetRequestResponse type ModifySpotFleetRequestOutput struct { _ struct{} `type:"structure"` @@ -53088,7 +52515,6 @@ func (s *ModifySpotFleetRequestOutput) SetReturn(v bool) *ModifySpotFleetRequest } // Contains the parameters for ModifySubnetAttribute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifySubnetAttributeRequest type ModifySubnetAttributeInput struct { _ struct{} `type:"structure"` @@ -53155,7 +52581,6 @@ func (s *ModifySubnetAttributeInput) SetSubnetId(v string) *ModifySubnetAttribut return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifySubnetAttributeOutput type ModifySubnetAttributeOutput struct { _ struct{} `type:"structure"` } @@ -53171,7 +52596,6 @@ func (s ModifySubnetAttributeOutput) GoString() string { } // Contains the parameters for ModifyVolumeAttribute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyVolumeAttributeRequest type ModifyVolumeAttributeInput struct { _ struct{} `type:"structure"` @@ -53231,7 +52655,6 @@ func (s *ModifyVolumeAttributeInput) SetVolumeId(v string) *ModifyVolumeAttribut return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyVolumeAttributeOutput type ModifyVolumeAttributeOutput struct { _ struct{} `type:"structure"` } @@ -53246,7 +52669,6 @@ func (s ModifyVolumeAttributeOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyVolumeRequest type ModifyVolumeInput struct { _ struct{} `type:"structure"` @@ -53338,7 +52760,6 @@ func (s *ModifyVolumeInput) SetVolumeType(v string) *ModifyVolumeInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyVolumeResult type ModifyVolumeOutput struct { _ struct{} `type:"structure"` @@ -53363,7 +52784,6 @@ func (s *ModifyVolumeOutput) SetVolumeModification(v *VolumeModification) *Modif } // Contains the parameters for ModifyVpcAttribute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyVpcAttributeRequest type ModifyVpcAttributeInput struct { _ struct{} `type:"structure"` @@ -53432,7 +52852,6 @@ func (s *ModifyVpcAttributeInput) SetVpcId(v string) *ModifyVpcAttributeInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyVpcAttributeOutput type ModifyVpcAttributeOutput struct { _ struct{} `type:"structure"` } @@ -53447,7 +52866,6 @@ func (s ModifyVpcAttributeOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyVpcEndpointConnectionNotificationRequest type ModifyVpcEndpointConnectionNotificationInput struct { _ struct{} `type:"structure"` @@ -53517,7 +52935,6 @@ func (s *ModifyVpcEndpointConnectionNotificationInput) SetDryRun(v bool) *Modify return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyVpcEndpointConnectionNotificationResult type ModifyVpcEndpointConnectionNotificationOutput struct { _ struct{} `type:"structure"` @@ -53542,7 +52959,6 @@ func (s *ModifyVpcEndpointConnectionNotificationOutput) SetReturnValue(v bool) * } // Contains the parameters for ModifyVpcEndpoint. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyVpcEndpointRequest type ModifyVpcEndpointInput struct { _ struct{} `type:"structure"` @@ -53679,7 +53095,6 @@ func (s *ModifyVpcEndpointInput) SetVpcEndpointId(v string) *ModifyVpcEndpointIn return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyVpcEndpointResult type ModifyVpcEndpointOutput struct { _ struct{} `type:"structure"` @@ -53703,7 +53118,6 @@ func (s *ModifyVpcEndpointOutput) SetReturn(v bool) *ModifyVpcEndpointOutput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyVpcEndpointServiceConfigurationRequest type ModifyVpcEndpointServiceConfigurationInput struct { _ struct{} `type:"structure"` @@ -53783,7 +53197,6 @@ func (s *ModifyVpcEndpointServiceConfigurationInput) SetServiceId(v string) *Mod return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyVpcEndpointServiceConfigurationResult type ModifyVpcEndpointServiceConfigurationOutput struct { _ struct{} `type:"structure"` @@ -53807,7 +53220,6 @@ func (s *ModifyVpcEndpointServiceConfigurationOutput) SetReturn(v bool) *ModifyV return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyVpcEndpointServicePermissionsRequest type ModifyVpcEndpointServicePermissionsInput struct { _ struct{} `type:"structure"` @@ -53878,7 +53290,6 @@ func (s *ModifyVpcEndpointServicePermissionsInput) SetServiceId(v string) *Modif return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyVpcEndpointServicePermissionsResult type ModifyVpcEndpointServicePermissionsOutput struct { _ struct{} `type:"structure"` @@ -53902,7 +53313,6 @@ func (s *ModifyVpcEndpointServicePermissionsOutput) SetReturnValue(v bool) *Modi return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyVpcPeeringConnectionOptionsRequest type ModifyVpcPeeringConnectionOptionsInput struct { _ struct{} `type:"structure"` @@ -53971,7 +53381,6 @@ func (s *ModifyVpcPeeringConnectionOptionsInput) SetVpcPeeringConnectionId(v str return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyVpcPeeringConnectionOptionsResult type ModifyVpcPeeringConnectionOptionsOutput struct { _ struct{} `type:"structure"` @@ -54005,7 +53414,6 @@ func (s *ModifyVpcPeeringConnectionOptionsOutput) SetRequesterPeeringConnectionO } // Contains the parameters for ModifyVpcTenancy. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyVpcTenancyRequest type ModifyVpcTenancyInput struct { _ struct{} `type:"structure"` @@ -54071,7 +53479,6 @@ func (s *ModifyVpcTenancyInput) SetVpcId(v string) *ModifyVpcTenancyInput { } // Contains the output of ModifyVpcTenancy. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ModifyVpcTenancyResult type ModifyVpcTenancyOutput struct { _ struct{} `type:"structure"` @@ -54096,7 +53503,6 @@ func (s *ModifyVpcTenancyOutput) SetReturnValue(v bool) *ModifyVpcTenancyOutput } // Contains the parameters for MonitorInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/MonitorInstancesRequest type MonitorInstancesInput struct { _ struct{} `type:"structure"` @@ -54148,7 +53554,6 @@ func (s *MonitorInstancesInput) SetInstanceIds(v []*string) *MonitorInstancesInp } // Contains the output of MonitorInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/MonitorInstancesResult type MonitorInstancesOutput struct { _ struct{} `type:"structure"` @@ -54173,7 +53578,6 @@ func (s *MonitorInstancesOutput) SetInstanceMonitorings(v []*InstanceMonitoring) } // Describes the monitoring of an instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/Monitoring type Monitoring struct { _ struct{} `type:"structure"` @@ -54199,7 +53603,6 @@ func (s *Monitoring) SetState(v string) *Monitoring { } // Contains the parameters for MoveAddressToVpc. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/MoveAddressToVpcRequest type MoveAddressToVpcInput struct { _ struct{} `type:"structure"` @@ -54251,7 +53654,6 @@ func (s *MoveAddressToVpcInput) SetPublicIp(v string) *MoveAddressToVpcInput { } // Contains the output of MoveAddressToVpc. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/MoveAddressToVpcResult type MoveAddressToVpcOutput struct { _ struct{} `type:"structure"` @@ -54285,7 +53687,6 @@ func (s *MoveAddressToVpcOutput) SetStatus(v string) *MoveAddressToVpcOutput { } // Describes the status of a moving Elastic IP address. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/MovingAddressStatus type MovingAddressStatus struct { _ struct{} `type:"structure"` @@ -54320,7 +53721,6 @@ func (s *MovingAddressStatus) SetPublicIp(v string) *MovingAddressStatus { } // Describes a NAT gateway. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/NatGateway type NatGateway struct { _ struct{} `type:"structure"` @@ -54475,7 +53875,6 @@ func (s *NatGateway) SetVpcId(v string) *NatGateway { } // Describes the IP addresses and network interface associated with a NAT gateway. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/NatGatewayAddress type NatGatewayAddress struct { _ struct{} `type:"structure"` @@ -54528,7 +53927,6 @@ func (s *NatGatewayAddress) SetPublicIp(v string) *NatGatewayAddress { } // Describes a network ACL. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/NetworkAcl type NetworkAcl struct { _ struct{} `type:"structure"` @@ -54598,7 +53996,6 @@ func (s *NetworkAcl) SetVpcId(v string) *NetworkAcl { } // Describes an association between a network ACL and a subnet. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/NetworkAclAssociation type NetworkAclAssociation struct { _ struct{} `type:"structure"` @@ -54641,7 +54038,6 @@ func (s *NetworkAclAssociation) SetSubnetId(v string) *NetworkAclAssociation { } // Describes an entry in a network ACL. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/NetworkAclEntry type NetworkAclEntry struct { _ struct{} `type:"structure"` @@ -54731,7 +54127,6 @@ func (s *NetworkAclEntry) SetRuleNumber(v int64) *NetworkAclEntry { } // Describes a network interface. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/NetworkInterface type NetworkInterface struct { _ struct{} `type:"structure"` @@ -54929,7 +54324,6 @@ func (s *NetworkInterface) SetVpcId(v string) *NetworkInterface { } // Describes association information for an Elastic IP address (IPv4 only). -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/NetworkInterfaceAssociation type NetworkInterfaceAssociation struct { _ struct{} `type:"structure"` @@ -54990,7 +54384,6 @@ func (s *NetworkInterfaceAssociation) SetPublicIp(v string) *NetworkInterfaceAss } // Describes a network interface attachment. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/NetworkInterfaceAttachment type NetworkInterfaceAttachment struct { _ struct{} `type:"structure"` @@ -55069,7 +54462,6 @@ func (s *NetworkInterfaceAttachment) SetStatus(v string) *NetworkInterfaceAttach } // Describes an attachment change. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/NetworkInterfaceAttachmentChanges type NetworkInterfaceAttachmentChanges struct { _ struct{} `type:"structure"` @@ -55103,7 +54495,6 @@ func (s *NetworkInterfaceAttachmentChanges) SetDeleteOnTermination(v bool) *Netw } // Describes an IPv6 address associated with a network interface. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/NetworkInterfaceIpv6Address type NetworkInterfaceIpv6Address struct { _ struct{} `type:"structure"` @@ -55128,7 +54519,6 @@ func (s *NetworkInterfaceIpv6Address) SetIpv6Address(v string) *NetworkInterface } // Describes a permission for a network interface. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/NetworkInterfacePermission type NetworkInterfacePermission struct { _ struct{} `type:"structure"` @@ -55198,7 +54588,6 @@ func (s *NetworkInterfacePermission) SetPermissionState(v *NetworkInterfacePermi } // Describes the state of a network interface permission. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/NetworkInterfacePermissionState type NetworkInterfacePermissionState struct { _ struct{} `type:"structure"` @@ -55232,7 +54621,6 @@ func (s *NetworkInterfacePermissionState) SetStatusMessage(v string) *NetworkInt } // Describes the private IPv4 address of a network interface. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/NetworkInterfacePrivateIpAddress type NetworkInterfacePrivateIpAddress struct { _ struct{} `type:"structure"` @@ -55285,7 +54673,6 @@ func (s *NetworkInterfacePrivateIpAddress) SetPrivateIpAddress(v string) *Networ return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/NewDhcpConfiguration type NewDhcpConfiguration struct { _ struct{} `type:"structure"` @@ -55318,7 +54705,6 @@ func (s *NewDhcpConfiguration) SetValues(v []*string) *NewDhcpConfiguration { // Describes the data that identifies an Amazon FPGA image (AFI) on the PCI // bus. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/PciId type PciId struct { _ struct{} `type:"structure"` @@ -55370,7 +54756,6 @@ func (s *PciId) SetVendorId(v string) *PciId { } // Describes the VPC peering connection options. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/PeeringConnectionOptions type PeeringConnectionOptions struct { _ struct{} `type:"structure"` @@ -55416,7 +54801,6 @@ func (s *PeeringConnectionOptions) SetAllowEgressFromLocalVpcToRemoteClassicLink } // The VPC peering connection options. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/PeeringConnectionOptionsRequest type PeeringConnectionOptionsRequest struct { _ struct{} `type:"structure"` @@ -55462,7 +54846,6 @@ func (s *PeeringConnectionOptionsRequest) SetAllowEgressFromLocalVpcToRemoteClas } // Describes the placement of an instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/Placement type Placement struct { _ struct{} `type:"structure"` @@ -55536,7 +54919,6 @@ func (s *Placement) SetTenancy(v string) *Placement { } // Describes a placement group. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/PlacementGroup type PlacementGroup struct { _ struct{} `type:"structure"` @@ -55579,7 +54961,6 @@ func (s *PlacementGroup) SetStrategy(v string) *PlacementGroup { } // Describes a range of ports. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/PortRange type PortRange struct { _ struct{} `type:"structure"` @@ -55613,7 +54994,6 @@ func (s *PortRange) SetTo(v int64) *PortRange { } // Describes prefixes for AWS services. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/PrefixList type PrefixList struct { _ struct{} `type:"structure"` @@ -55656,7 +55036,6 @@ func (s *PrefixList) SetPrefixListName(v string) *PrefixList { } // [EC2-VPC only] The ID of the prefix. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/PrefixListId type PrefixListId struct { _ struct{} `type:"structure"` @@ -55694,7 +55073,6 @@ func (s *PrefixListId) SetPrefixListId(v string) *PrefixListId { } // Describes the price for a Reserved Instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/PriceSchedule type PriceSchedule struct { _ struct{} `type:"structure"` @@ -55757,7 +55135,6 @@ func (s *PriceSchedule) SetTerm(v int64) *PriceSchedule { } // Describes the price for a Reserved Instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/PriceScheduleSpecification type PriceScheduleSpecification struct { _ struct{} `type:"structure"` @@ -55802,7 +55179,6 @@ func (s *PriceScheduleSpecification) SetTerm(v int64) *PriceScheduleSpecificatio } // Describes a Reserved Instance offering. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/PricingDetail type PricingDetail struct { _ struct{} `type:"structure"` @@ -55836,7 +55212,6 @@ func (s *PricingDetail) SetPrice(v float64) *PricingDetail { } // Describes a secondary private IPv4 address for a network interface. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/PrivateIpAddressSpecification type PrivateIpAddressSpecification struct { _ struct{} `type:"structure"` @@ -55886,7 +55261,6 @@ func (s *PrivateIpAddressSpecification) SetPrivateIpAddress(v string) *PrivateIp } // Describes a product code. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ProductCode type ProductCode struct { _ struct{} `type:"structure"` @@ -55920,7 +55294,6 @@ func (s *ProductCode) SetProductCodeType(v string) *ProductCode { } // Describes a virtual private gateway propagating route. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/PropagatingVgw type PropagatingVgw struct { _ struct{} `type:"structure"` @@ -55947,7 +55320,6 @@ func (s *PropagatingVgw) SetGatewayId(v string) *PropagatingVgw { // Reserved. If you need to sustain traffic greater than the documented limits // (http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/vpc-nat-gateway.html), // contact us through the Support Center (https://console.aws.amazon.com/support/home?). -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ProvisionedBandwidth type ProvisionedBandwidth struct { _ struct{} `type:"structure"` @@ -56018,7 +55390,6 @@ func (s *ProvisionedBandwidth) SetStatus(v string) *ProvisionedBandwidth { } // Describes the result of the purchase. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/Purchase type Purchase struct { _ struct{} `type:"structure"` @@ -56107,7 +55478,6 @@ func (s *Purchase) SetUpfrontPrice(v string) *Purchase { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/PurchaseHostReservationRequest type PurchaseHostReservationInput struct { _ struct{} `type:"structure"` @@ -56197,7 +55567,6 @@ func (s *PurchaseHostReservationInput) SetOfferingId(v string) *PurchaseHostRese return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/PurchaseHostReservationResult type PurchaseHostReservationOutput struct { _ struct{} `type:"structure"` @@ -56262,7 +55631,6 @@ func (s *PurchaseHostReservationOutput) SetTotalUpfrontPrice(v string) *Purchase } // Describes a request to purchase Scheduled Instances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/PurchaseRequest type PurchaseRequest struct { _ struct{} `type:"structure"` @@ -56316,7 +55684,6 @@ func (s *PurchaseRequest) SetPurchaseToken(v string) *PurchaseRequest { } // Contains the parameters for PurchaseReservedInstancesOffering. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/PurchaseReservedInstancesOfferingRequest type PurchaseReservedInstancesOfferingInput struct { _ struct{} `type:"structure"` @@ -56393,7 +55760,6 @@ func (s *PurchaseReservedInstancesOfferingInput) SetReservedInstancesOfferingId( } // Contains the output of PurchaseReservedInstancesOffering. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/PurchaseReservedInstancesOfferingResult type PurchaseReservedInstancesOfferingOutput struct { _ struct{} `type:"structure"` @@ -56418,7 +55784,6 @@ func (s *PurchaseReservedInstancesOfferingOutput) SetReservedInstancesId(v strin } // Contains the parameters for PurchaseScheduledInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/PurchaseScheduledInstancesRequest type PurchaseScheduledInstancesInput struct { _ struct{} `type:"structure"` @@ -56493,7 +55858,6 @@ func (s *PurchaseScheduledInstancesInput) SetPurchaseRequests(v []*PurchaseReque } // Contains the output of PurchaseScheduledInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/PurchaseScheduledInstancesResult type PurchaseScheduledInstancesOutput struct { _ struct{} `type:"structure"` @@ -56518,7 +55882,6 @@ func (s *PurchaseScheduledInstancesOutput) SetScheduledInstanceSet(v []*Schedule } // Contains the parameters for RebootInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/RebootInstancesRequest type RebootInstancesInput struct { _ struct{} `type:"structure"` @@ -56569,7 +55932,6 @@ func (s *RebootInstancesInput) SetInstanceIds(v []*string) *RebootInstancesInput return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/RebootInstancesOutput type RebootInstancesOutput struct { _ struct{} `type:"structure"` } @@ -56585,7 +55947,6 @@ func (s RebootInstancesOutput) GoString() string { } // Describes a recurring charge. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/RecurringCharge type RecurringCharge struct { _ struct{} `type:"structure"` @@ -56619,7 +55980,6 @@ func (s *RecurringCharge) SetFrequency(v string) *RecurringCharge { } // Describes a region. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/Region type Region struct { _ struct{} `type:"structure"` @@ -56653,7 +56013,6 @@ func (s *Region) SetRegionName(v string) *Region { } // Contains the parameters for RegisterImage. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/RegisterImageRequest type RegisterImageInput struct { _ struct{} `type:"structure"` @@ -56826,7 +56185,6 @@ func (s *RegisterImageInput) SetVirtualizationType(v string) *RegisterImageInput } // Contains the output of RegisterImage. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/RegisterImageResult type RegisterImageOutput struct { _ struct{} `type:"structure"` @@ -56850,7 +56208,6 @@ func (s *RegisterImageOutput) SetImageId(v string) *RegisterImageOutput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/RejectVpcEndpointConnectionsRequest type RejectVpcEndpointConnectionsInput struct { _ struct{} `type:"structure"` @@ -56915,7 +56272,6 @@ func (s *RejectVpcEndpointConnectionsInput) SetVpcEndpointIds(v []*string) *Reje return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/RejectVpcEndpointConnectionsResult type RejectVpcEndpointConnectionsOutput struct { _ struct{} `type:"structure"` @@ -56940,7 +56296,6 @@ func (s *RejectVpcEndpointConnectionsOutput) SetUnsuccessful(v []*UnsuccessfulIt } // Contains the parameters for RejectVpcPeeringConnection. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/RejectVpcPeeringConnectionRequest type RejectVpcPeeringConnectionInput struct { _ struct{} `type:"structure"` @@ -56992,7 +56347,6 @@ func (s *RejectVpcPeeringConnectionInput) SetVpcPeeringConnectionId(v string) *R } // Contains the output of RejectVpcPeeringConnection. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/RejectVpcPeeringConnectionResult type RejectVpcPeeringConnectionOutput struct { _ struct{} `type:"structure"` @@ -57017,7 +56371,6 @@ func (s *RejectVpcPeeringConnectionOutput) SetReturn(v bool) *RejectVpcPeeringCo } // Contains the parameters for ReleaseAddress. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ReleaseAddressRequest type ReleaseAddressInput struct { _ struct{} `type:"structure"` @@ -57062,7 +56415,6 @@ func (s *ReleaseAddressInput) SetPublicIp(v string) *ReleaseAddressInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ReleaseAddressOutput type ReleaseAddressOutput struct { _ struct{} `type:"structure"` } @@ -57078,7 +56430,6 @@ func (s ReleaseAddressOutput) GoString() string { } // Contains the parameters for ReleaseHosts. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ReleaseHostsRequest type ReleaseHostsInput struct { _ struct{} `type:"structure"` @@ -57118,7 +56469,6 @@ func (s *ReleaseHostsInput) SetHostIds(v []*string) *ReleaseHostsInput { } // Contains the output of ReleaseHosts. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ReleaseHostsResult type ReleaseHostsOutput struct { _ struct{} `type:"structure"` @@ -57152,7 +56502,6 @@ func (s *ReleaseHostsOutput) SetUnsuccessful(v []*UnsuccessfulItem) *ReleaseHost return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ReplaceIamInstanceProfileAssociationRequest type ReplaceIamInstanceProfileAssociationInput struct { _ struct{} `type:"structure"` @@ -57205,7 +56554,6 @@ func (s *ReplaceIamInstanceProfileAssociationInput) SetIamInstanceProfile(v *Iam return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ReplaceIamInstanceProfileAssociationResult type ReplaceIamInstanceProfileAssociationOutput struct { _ struct{} `type:"structure"` @@ -57230,7 +56578,6 @@ func (s *ReplaceIamInstanceProfileAssociationOutput) SetIamInstanceProfileAssoci } // Contains the parameters for ReplaceNetworkAclAssociation. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ReplaceNetworkAclAssociationRequest type ReplaceNetworkAclAssociationInput struct { _ struct{} `type:"structure"` @@ -57297,7 +56644,6 @@ func (s *ReplaceNetworkAclAssociationInput) SetNetworkAclId(v string) *ReplaceNe } // Contains the output of ReplaceNetworkAclAssociation. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ReplaceNetworkAclAssociationResult type ReplaceNetworkAclAssociationOutput struct { _ struct{} `type:"structure"` @@ -57322,7 +56668,6 @@ func (s *ReplaceNetworkAclAssociationOutput) SetNewAssociationId(v string) *Repl } // Contains the parameters for ReplaceNetworkAclEntry. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ReplaceNetworkAclEntryRequest type ReplaceNetworkAclEntryInput struct { _ struct{} `type:"structure"` @@ -57475,7 +56820,6 @@ func (s *ReplaceNetworkAclEntryInput) SetRuleNumber(v int64) *ReplaceNetworkAclE return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ReplaceNetworkAclEntryOutput type ReplaceNetworkAclEntryOutput struct { _ struct{} `type:"structure"` } @@ -57491,7 +56835,6 @@ func (s ReplaceNetworkAclEntryOutput) GoString() string { } // Contains the parameters for ReplaceRoute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ReplaceRouteRequest type ReplaceRouteInput struct { _ struct{} `type:"structure"` @@ -57616,7 +56959,6 @@ func (s *ReplaceRouteInput) SetVpcPeeringConnectionId(v string) *ReplaceRouteInp return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ReplaceRouteOutput type ReplaceRouteOutput struct { _ struct{} `type:"structure"` } @@ -57632,7 +56974,6 @@ func (s ReplaceRouteOutput) GoString() string { } // Contains the parameters for ReplaceRouteTableAssociation. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ReplaceRouteTableAssociationRequest type ReplaceRouteTableAssociationInput struct { _ struct{} `type:"structure"` @@ -57698,7 +57039,6 @@ func (s *ReplaceRouteTableAssociationInput) SetRouteTableId(v string) *ReplaceRo } // Contains the output of ReplaceRouteTableAssociation. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ReplaceRouteTableAssociationResult type ReplaceRouteTableAssociationOutput struct { _ struct{} `type:"structure"` @@ -57723,7 +57063,6 @@ func (s *ReplaceRouteTableAssociationOutput) SetNewAssociationId(v string) *Repl } // Contains the parameters for ReportInstanceStatus. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ReportInstanceStatusRequest type ReportInstanceStatusInput struct { _ struct{} `type:"structure"` @@ -57850,7 +57189,6 @@ func (s *ReportInstanceStatusInput) SetStatus(v string) *ReportInstanceStatusInp return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ReportInstanceStatusOutput type ReportInstanceStatusOutput struct { _ struct{} `type:"structure"` } @@ -57866,7 +57204,6 @@ func (s ReportInstanceStatusOutput) GoString() string { } // The information to include in the launch template. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/RequestLaunchTemplateData type RequestLaunchTemplateData struct { _ struct{} `type:"structure"` @@ -58135,7 +57472,6 @@ func (s *RequestLaunchTemplateData) SetUserData(v string) *RequestLaunchTemplate } // Contains the parameters for RequestSpotFleet. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/RequestSpotFleetRequest type RequestSpotFleetInput struct { _ struct{} `type:"structure"` @@ -58192,7 +57528,6 @@ func (s *RequestSpotFleetInput) SetSpotFleetRequestConfig(v *SpotFleetRequestCon } // Contains the output of RequestSpotFleet. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/RequestSpotFleetResponse type RequestSpotFleetOutput struct { _ struct{} `type:"structure"` @@ -58219,7 +57554,6 @@ func (s *RequestSpotFleetOutput) SetSpotFleetRequestId(v string) *RequestSpotFle } // Contains the parameters for RequestSpotInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/RequestSpotInstancesRequest type RequestSpotInstancesInput struct { _ struct{} `type:"structure"` @@ -58407,7 +57741,6 @@ func (s *RequestSpotInstancesInput) SetValidUntil(v time.Time) *RequestSpotInsta } // Contains the output of RequestSpotInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/RequestSpotInstancesResult type RequestSpotInstancesOutput struct { _ struct{} `type:"structure"` @@ -58432,7 +57765,6 @@ func (s *RequestSpotInstancesOutput) SetSpotInstanceRequests(v []*SpotInstanceRe } // Describes the launch specification for an instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/RequestSpotLaunchSpecification type RequestSpotLaunchSpecification struct { _ struct{} `type:"structure"` @@ -58633,7 +57965,6 @@ func (s *RequestSpotLaunchSpecification) SetUserData(v string) *RequestSpotLaunc } // Describes a reservation. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/Reservation type Reservation struct { _ struct{} `type:"structure"` @@ -58695,7 +58026,6 @@ func (s *Reservation) SetReservationId(v string) *Reservation { } // The cost associated with the Reserved Instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ReservationValue type ReservationValue struct { _ struct{} `type:"structure"` @@ -58739,7 +58069,6 @@ func (s *ReservationValue) SetRemainingUpfrontValue(v string) *ReservationValue } // Describes the limit price of a Reserved Instance offering. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ReservedInstanceLimitPrice type ReservedInstanceLimitPrice struct { _ struct{} `type:"structure"` @@ -58775,7 +58104,6 @@ func (s *ReservedInstanceLimitPrice) SetCurrencyCode(v string) *ReservedInstance } // The total value of the Convertible Reserved Instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ReservedInstanceReservationValue type ReservedInstanceReservationValue struct { _ struct{} `type:"structure"` @@ -58809,7 +58137,6 @@ func (s *ReservedInstanceReservationValue) SetReservedInstanceId(v string) *Rese } // Describes a Reserved Instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ReservedInstances type ReservedInstances struct { _ struct{} `type:"structure"` @@ -58988,7 +58315,6 @@ func (s *ReservedInstances) SetUsagePrice(v float64) *ReservedInstances { } // Describes the configuration settings for the modified Reserved Instances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ReservedInstancesConfiguration type ReservedInstancesConfiguration struct { _ struct{} `type:"structure"` @@ -59051,7 +58377,6 @@ func (s *ReservedInstancesConfiguration) SetScope(v string) *ReservedInstancesCo } // Describes the ID of a Reserved Instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ReservedInstancesId type ReservedInstancesId struct { _ struct{} `type:"structure"` @@ -59076,7 +58401,6 @@ func (s *ReservedInstancesId) SetReservedInstancesId(v string) *ReservedInstance } // Describes a Reserved Instance listing. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ReservedInstancesListing type ReservedInstancesListing struct { _ struct{} `type:"structure"` @@ -59184,7 +58508,6 @@ func (s *ReservedInstancesListing) SetUpdateDate(v time.Time) *ReservedInstances } // Describes a Reserved Instance modification. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ReservedInstancesModification type ReservedInstancesModification struct { _ struct{} `type:"structure"` @@ -59283,7 +58606,6 @@ func (s *ReservedInstancesModification) SetUpdateDate(v time.Time) *ReservedInst } // Describes the modification request/s. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ReservedInstancesModificationResult type ReservedInstancesModificationResult struct { _ struct{} `type:"structure"` @@ -59319,7 +58641,6 @@ func (s *ReservedInstancesModificationResult) SetTargetConfiguration(v *Reserved } // Describes a Reserved Instance offering. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ReservedInstancesOffering type ReservedInstancesOffering struct { _ struct{} `type:"structure"` @@ -59477,7 +58798,6 @@ func (s *ReservedInstancesOffering) SetUsagePrice(v float64) *ReservedInstancesO return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ResetFpgaImageAttributeRequest type ResetFpgaImageAttributeInput struct { _ struct{} `type:"structure"` @@ -59537,7 +58857,6 @@ func (s *ResetFpgaImageAttributeInput) SetFpgaImageId(v string) *ResetFpgaImageA return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ResetFpgaImageAttributeResult type ResetFpgaImageAttributeOutput struct { _ struct{} `type:"structure"` @@ -59562,7 +58881,6 @@ func (s *ResetFpgaImageAttributeOutput) SetReturn(v bool) *ResetFpgaImageAttribu } // Contains the parameters for ResetImageAttribute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ResetImageAttributeRequest type ResetImageAttributeInput struct { _ struct{} `type:"structure"` @@ -59628,7 +58946,6 @@ func (s *ResetImageAttributeInput) SetImageId(v string) *ResetImageAttributeInpu return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ResetImageAttributeOutput type ResetImageAttributeOutput struct { _ struct{} `type:"structure"` } @@ -59644,7 +58961,6 @@ func (s ResetImageAttributeOutput) GoString() string { } // Contains the parameters for ResetInstanceAttribute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ResetInstanceAttributeRequest type ResetInstanceAttributeInput struct { _ struct{} `type:"structure"` @@ -59712,7 +59028,6 @@ func (s *ResetInstanceAttributeInput) SetInstanceId(v string) *ResetInstanceAttr return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ResetInstanceAttributeOutput type ResetInstanceAttributeOutput struct { _ struct{} `type:"structure"` } @@ -59728,7 +59043,6 @@ func (s ResetInstanceAttributeOutput) GoString() string { } // Contains the parameters for ResetNetworkInterfaceAttribute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ResetNetworkInterfaceAttributeRequest type ResetNetworkInterfaceAttributeInput struct { _ struct{} `type:"structure"` @@ -59788,7 +59102,6 @@ func (s *ResetNetworkInterfaceAttributeInput) SetSourceDestCheck(v string) *Rese return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ResetNetworkInterfaceAttributeOutput type ResetNetworkInterfaceAttributeOutput struct { _ struct{} `type:"structure"` } @@ -59804,7 +59117,6 @@ func (s ResetNetworkInterfaceAttributeOutput) GoString() string { } // Contains the parameters for ResetSnapshotAttribute. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ResetSnapshotAttributeRequest type ResetSnapshotAttributeInput struct { _ struct{} `type:"structure"` @@ -59870,7 +59182,6 @@ func (s *ResetSnapshotAttributeInput) SetSnapshotId(v string) *ResetSnapshotAttr return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ResetSnapshotAttributeOutput type ResetSnapshotAttributeOutput struct { _ struct{} `type:"structure"` } @@ -59887,7 +59198,6 @@ func (s ResetSnapshotAttributeOutput) GoString() string { // Describes the error that's returned when you cannot delete a launch template // version. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ResponseError type ResponseError struct { _ struct{} `type:"structure"` @@ -59921,7 +59231,6 @@ func (s *ResponseError) SetMessage(v string) *ResponseError { } // The information for a launch template. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ResponseLaunchTemplateData type ResponseLaunchTemplateData struct { _ struct{} `type:"structure"` @@ -60119,7 +59428,6 @@ func (s *ResponseLaunchTemplateData) SetUserData(v string) *ResponseLaunchTempla } // Contains the parameters for RestoreAddressToClassic. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/RestoreAddressToClassicRequest type RestoreAddressToClassicInput struct { _ struct{} `type:"structure"` @@ -60171,7 +59479,6 @@ func (s *RestoreAddressToClassicInput) SetPublicIp(v string) *RestoreAddressToCl } // Contains the output of RestoreAddressToClassic. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/RestoreAddressToClassicResult type RestoreAddressToClassicOutput struct { _ struct{} `type:"structure"` @@ -60205,7 +59512,6 @@ func (s *RestoreAddressToClassicOutput) SetStatus(v string) *RestoreAddressToCla } // Contains the parameters for RevokeSecurityGroupEgress. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/RevokeSecurityGroupEgressRequest type RevokeSecurityGroupEgressInput struct { _ struct{} `type:"structure"` @@ -60323,7 +59629,6 @@ func (s *RevokeSecurityGroupEgressInput) SetToPort(v int64) *RevokeSecurityGroup return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/RevokeSecurityGroupEgressOutput type RevokeSecurityGroupEgressOutput struct { _ struct{} `type:"structure"` } @@ -60339,7 +59644,6 @@ func (s RevokeSecurityGroupEgressOutput) GoString() string { } // Contains the parameters for RevokeSecurityGroupIngress. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/RevokeSecurityGroupIngressRequest type RevokeSecurityGroupIngressInput struct { _ struct{} `type:"structure"` @@ -60465,7 +59769,6 @@ func (s *RevokeSecurityGroupIngressInput) SetToPort(v int64) *RevokeSecurityGrou return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/RevokeSecurityGroupIngressOutput type RevokeSecurityGroupIngressOutput struct { _ struct{} `type:"structure"` } @@ -60481,7 +59784,6 @@ func (s RevokeSecurityGroupIngressOutput) GoString() string { } // Describes a route in a route table. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/Route type Route struct { _ struct{} `type:"structure"` @@ -60614,7 +59916,6 @@ func (s *Route) SetVpcPeeringConnectionId(v string) *Route { } // Describes a route table. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/RouteTable type RouteTable struct { _ struct{} `type:"structure"` @@ -60684,7 +59985,6 @@ func (s *RouteTable) SetVpcId(v string) *RouteTable { } // Describes an association between a route table and a subnet. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/RouteTableAssociation type RouteTableAssociation struct { _ struct{} `type:"structure"` @@ -60736,7 +60036,6 @@ func (s *RouteTableAssociation) SetSubnetId(v string) *RouteTableAssociation { } // Contains the parameters for RunInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/RunInstancesRequest type RunInstancesInput struct { _ struct{} `type:"structure"` @@ -61159,7 +60458,6 @@ func (s *RunInstancesInput) SetUserData(v string) *RunInstancesInput { } // Describes the monitoring of an instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/RunInstancesMonitoringEnabled type RunInstancesMonitoringEnabled struct { _ struct{} `type:"structure"` @@ -61200,7 +60498,6 @@ func (s *RunInstancesMonitoringEnabled) SetEnabled(v bool) *RunInstancesMonitori } // Contains the parameters for RunScheduledInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/RunScheduledInstancesRequest type RunScheduledInstancesInput struct { _ struct{} `type:"structure"` @@ -61293,7 +60590,6 @@ func (s *RunScheduledInstancesInput) SetScheduledInstanceId(v string) *RunSchedu } // Contains the output of RunScheduledInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/RunScheduledInstancesResult type RunScheduledInstancesOutput struct { _ struct{} `type:"structure"` @@ -61319,7 +60615,6 @@ func (s *RunScheduledInstancesOutput) SetInstanceIdSet(v []*string) *RunSchedule // Describes the storage parameters for S3 and S3 buckets for an instance store-backed // AMI. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/S3Storage type S3Storage struct { _ struct{} `type:"structure"` @@ -61387,7 +60682,6 @@ func (s *S3Storage) SetUploadPolicySignature(v string) *S3Storage { } // Describes a Scheduled Instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ScheduledInstance type ScheduledInstance struct { _ struct{} `type:"structure"` @@ -61538,7 +60832,6 @@ func (s *ScheduledInstance) SetTotalScheduledInstanceHours(v int64) *ScheduledIn } // Describes a schedule that is available for your Scheduled Instances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ScheduledInstanceAvailability type ScheduledInstanceAvailability struct { _ struct{} `type:"structure"` @@ -61672,7 +60965,6 @@ func (s *ScheduledInstanceAvailability) SetTotalScheduledInstanceHours(v int64) } // Describes the recurring schedule for a Scheduled Instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ScheduledInstanceRecurrence type ScheduledInstanceRecurrence struct { _ struct{} `type:"structure"` @@ -61737,7 +61029,6 @@ func (s *ScheduledInstanceRecurrence) SetOccurrenceUnit(v string) *ScheduledInst } // Describes the recurring schedule for a Scheduled Instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ScheduledInstanceRecurrenceRequest type ScheduledInstanceRecurrenceRequest struct { _ struct{} `type:"structure"` @@ -61805,7 +61096,6 @@ func (s *ScheduledInstanceRecurrenceRequest) SetOccurrenceUnit(v string) *Schedu } // Describes a block device mapping for a Scheduled Instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ScheduledInstancesBlockDeviceMapping type ScheduledInstancesBlockDeviceMapping struct { _ struct{} `type:"structure"` @@ -61868,7 +61158,6 @@ func (s *ScheduledInstancesBlockDeviceMapping) SetVirtualName(v string) *Schedul } // Describes an EBS volume for a Scheduled Instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ScheduledInstancesEbs type ScheduledInstancesEbs struct { _ struct{} `type:"structure"` @@ -61957,7 +61246,6 @@ func (s *ScheduledInstancesEbs) SetVolumeType(v string) *ScheduledInstancesEbs { } // Describes an IAM instance profile for a Scheduled Instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ScheduledInstancesIamInstanceProfile type ScheduledInstancesIamInstanceProfile struct { _ struct{} `type:"structure"` @@ -61991,7 +61279,6 @@ func (s *ScheduledInstancesIamInstanceProfile) SetName(v string) *ScheduledInsta } // Describes an IPv6 address. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ScheduledInstancesIpv6Address type ScheduledInstancesIpv6Address struct { _ struct{} `type:"structure"` @@ -62020,7 +61307,6 @@ func (s *ScheduledInstancesIpv6Address) SetIpv6Address(v string) *ScheduledInsta // If you are launching the Scheduled Instance in EC2-VPC, you must specify // the ID of the subnet. You can specify the subnet using either SubnetId or // NetworkInterface. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ScheduledInstancesLaunchSpecification type ScheduledInstancesLaunchSpecification struct { _ struct{} `type:"structure"` @@ -62183,7 +61469,6 @@ func (s *ScheduledInstancesLaunchSpecification) SetUserData(v string) *Scheduled } // Describes whether monitoring is enabled for a Scheduled Instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ScheduledInstancesMonitoring type ScheduledInstancesMonitoring struct { _ struct{} `type:"structure"` @@ -62208,7 +61493,6 @@ func (s *ScheduledInstancesMonitoring) SetEnabled(v bool) *ScheduledInstancesMon } // Describes a network interface for a Scheduled Instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ScheduledInstancesNetworkInterface type ScheduledInstancesNetworkInterface struct { _ struct{} `type:"structure"` @@ -62337,7 +61621,6 @@ func (s *ScheduledInstancesNetworkInterface) SetSubnetId(v string) *ScheduledIns } // Describes the placement for a Scheduled Instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ScheduledInstancesPlacement type ScheduledInstancesPlacement struct { _ struct{} `type:"structure"` @@ -62371,7 +61654,6 @@ func (s *ScheduledInstancesPlacement) SetGroupName(v string) *ScheduledInstances } // Describes a private IPv4 address for a Scheduled Instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ScheduledInstancesPrivateIpAddressConfig type ScheduledInstancesPrivateIpAddressConfig struct { _ struct{} `type:"structure"` @@ -62406,7 +61688,6 @@ func (s *ScheduledInstancesPrivateIpAddressConfig) SetPrivateIpAddress(v string) } // Describes a security group -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/SecurityGroup type SecurityGroup struct { _ struct{} `type:"structure"` @@ -62494,7 +61775,6 @@ func (s *SecurityGroup) SetVpcId(v string) *SecurityGroup { } // Describes a security group. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/SecurityGroupIdentifier type SecurityGroupIdentifier struct { _ struct{} `type:"structure"` @@ -62528,7 +61808,6 @@ func (s *SecurityGroupIdentifier) SetGroupName(v string) *SecurityGroupIdentifie } // Describes a VPC with a security group that references your security group. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/SecurityGroupReference type SecurityGroupReference struct { _ struct{} `type:"structure"` @@ -62575,7 +61854,6 @@ func (s *SecurityGroupReference) SetVpcPeeringConnectionId(v string) *SecurityGr } // Describes a service configuration for a VPC endpoint service. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ServiceConfiguration type ServiceConfiguration struct { _ struct{} `type:"structure"` @@ -62673,7 +61951,6 @@ func (s *ServiceConfiguration) SetServiceType(v []*ServiceTypeDetail) *ServiceCo } // Describes a VPC endpoint service. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ServiceDetail type ServiceDetail struct { _ struct{} `type:"structure"` @@ -62762,7 +62039,6 @@ func (s *ServiceDetail) SetVpcEndpointPolicySupported(v bool) *ServiceDetail { } // Describes the type of service for a VPC endpoint. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/ServiceTypeDetail type ServiceTypeDetail struct { _ struct{} `type:"structure"` @@ -62788,7 +62064,6 @@ func (s *ServiceTypeDetail) SetServiceType(v string) *ServiceTypeDetail { // Describes the time period for a Scheduled Instance to start its first schedule. // The time period must span less than one day. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/SlotDateTimeRangeRequest type SlotDateTimeRangeRequest struct { _ struct{} `type:"structure"` @@ -62844,7 +62119,6 @@ func (s *SlotDateTimeRangeRequest) SetLatestTime(v time.Time) *SlotDateTimeRange } // Describes the time period for a Scheduled Instance to start its first schedule. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/SlotStartTimeRangeRequest type SlotStartTimeRangeRequest struct { _ struct{} `type:"structure"` @@ -62878,7 +62152,6 @@ func (s *SlotStartTimeRangeRequest) SetLatestTime(v time.Time) *SlotStartTimeRan } // Describes a snapshot. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/Snapshot type Snapshot struct { _ struct{} `type:"structure"` @@ -63036,7 +62309,6 @@ func (s *Snapshot) SetVolumeSize(v int64) *Snapshot { } // Describes the snapshot created from the imported disk. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/SnapshotDetail type SnapshotDetail struct { _ struct{} `type:"structure"` @@ -63142,7 +62414,6 @@ func (s *SnapshotDetail) SetUserBucket(v *UserBucketDetails) *SnapshotDetail { } // The disk container object for the import snapshot request. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/SnapshotDiskContainer type SnapshotDiskContainer struct { _ struct{} `type:"structure"` @@ -63197,7 +62468,6 @@ func (s *SnapshotDiskContainer) SetUserBucket(v *UserBucket) *SnapshotDiskContai } // Details about the import snapshot task. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/SnapshotTaskDetail type SnapshotTaskDetail struct { _ struct{} `type:"structure"` @@ -63294,7 +62564,6 @@ func (s *SnapshotTaskDetail) SetUserBucket(v *UserBucketDetails) *SnapshotTaskDe } // Describes the data feed for a Spot Instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/SpotDatafeedSubscription type SpotDatafeedSubscription struct { _ struct{} `type:"structure"` @@ -63355,7 +62624,6 @@ func (s *SpotDatafeedSubscription) SetState(v string) *SpotDatafeedSubscription } // Describes the launch specification for one or more Spot Instances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/SpotFleetLaunchSpecification type SpotFleetLaunchSpecification struct { _ struct{} `type:"structure"` @@ -63577,7 +62845,6 @@ func (s *SpotFleetLaunchSpecification) SetWeightedCapacity(v float64) *SpotFleet } // Describes whether monitoring is enabled. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/SpotFleetMonitoring type SpotFleetMonitoring struct { _ struct{} `type:"structure"` @@ -63604,7 +62871,6 @@ func (s *SpotFleetMonitoring) SetEnabled(v bool) *SpotFleetMonitoring { } // Describes a Spot Fleet request. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/SpotFleetRequestConfig type SpotFleetRequestConfig struct { _ struct{} `type:"structure"` @@ -63677,7 +62943,6 @@ func (s *SpotFleetRequestConfig) SetSpotFleetRequestState(v string) *SpotFleetRe } // Describes the configuration of a Spot Fleet request. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/SpotFleetRequestConfigData type SpotFleetRequestConfigData struct { _ struct{} `type:"structure"` @@ -63912,7 +63177,6 @@ func (s *SpotFleetRequestConfigData) SetValidUntil(v time.Time) *SpotFleetReques } // The tags for a Spot Fleet resource. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/SpotFleetTagSpecification type SpotFleetTagSpecification struct { _ struct{} `type:"structure"` @@ -63947,7 +63211,6 @@ func (s *SpotFleetTagSpecification) SetTags(v []*Tag) *SpotFleetTagSpecification } // Describes a Spot Instance request. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/SpotInstanceRequest type SpotInstanceRequest struct { _ struct{} `type:"structure"` @@ -64148,7 +63411,6 @@ func (s *SpotInstanceRequest) SetValidUntil(v time.Time) *SpotInstanceRequest { } // Describes a Spot Instance state change. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/SpotInstanceStateFault type SpotInstanceStateFault struct { _ struct{} `type:"structure"` @@ -64182,7 +63444,6 @@ func (s *SpotInstanceStateFault) SetMessage(v string) *SpotInstanceStateFault { } // Describes the status of a Spot Instance request. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/SpotInstanceStatus type SpotInstanceStatus struct { _ struct{} `type:"structure"` @@ -64227,7 +63488,6 @@ func (s *SpotInstanceStatus) SetUpdateTime(v time.Time) *SpotInstanceStatus { } // The options for Spot Instances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/SpotMarketOptions type SpotMarketOptions struct { _ struct{} `type:"structure"` @@ -64295,7 +63555,6 @@ func (s *SpotMarketOptions) SetValidUntil(v time.Time) *SpotMarketOptions { } // Describes Spot Instance placement. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/SpotPlacement type SpotPlacement struct { _ struct{} `type:"structure"` @@ -64344,7 +63603,6 @@ func (s *SpotPlacement) SetTenancy(v string) *SpotPlacement { // Describes the maximum price per hour that you are willing to pay for a Spot // Instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/SpotPrice type SpotPrice struct { _ struct{} `type:"structure"` @@ -64405,7 +63663,6 @@ func (s *SpotPrice) SetTimestamp(v time.Time) *SpotPrice { } // Describes a stale rule in a security group. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/StaleIpPermission type StaleIpPermission struct { _ struct{} `type:"structure"` @@ -64480,7 +63737,6 @@ func (s *StaleIpPermission) SetUserIdGroupPairs(v []*UserIdGroupPair) *StaleIpPe } // Describes a stale security group (a security group that contains stale rules). -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/StaleSecurityGroup type StaleSecurityGroup struct { _ struct{} `type:"structure"` @@ -64552,7 +63808,6 @@ func (s *StaleSecurityGroup) SetVpcId(v string) *StaleSecurityGroup { } // Contains the parameters for StartInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/StartInstancesRequest type StartInstancesInput struct { _ struct{} `type:"structure"` @@ -64613,7 +63868,6 @@ func (s *StartInstancesInput) SetInstanceIds(v []*string) *StartInstancesInput { } // Contains the output of StartInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/StartInstancesResult type StartInstancesOutput struct { _ struct{} `type:"structure"` @@ -64638,7 +63892,6 @@ func (s *StartInstancesOutput) SetStartingInstances(v []*InstanceStateChange) *S } // Describes a state change. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/StateReason type StateReason struct { _ struct{} `type:"structure"` @@ -64701,7 +63954,6 @@ func (s *StateReason) SetMessage(v string) *StateReason { } // Contains the parameters for StopInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/StopInstancesRequest type StopInstancesInput struct { _ struct{} `type:"structure"` @@ -64767,7 +64019,6 @@ func (s *StopInstancesInput) SetInstanceIds(v []*string) *StopInstancesInput { } // Contains the output of StopInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/StopInstancesResult type StopInstancesOutput struct { _ struct{} `type:"structure"` @@ -64792,7 +64043,6 @@ func (s *StopInstancesOutput) SetStoppingInstances(v []*InstanceStateChange) *St } // Describes the storage location for an instance store-backed AMI. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/Storage type Storage struct { _ struct{} `type:"structure"` @@ -64817,7 +64067,6 @@ func (s *Storage) SetS3(v *S3Storage) *Storage { } // Describes a storage location in Amazon S3. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/StorageLocation type StorageLocation struct { _ struct{} `type:"structure"` @@ -64851,7 +64100,6 @@ func (s *StorageLocation) SetKey(v string) *StorageLocation { } // Describes a subnet. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/Subnet type Subnet struct { _ struct{} `type:"structure"` @@ -64969,7 +64217,6 @@ func (s *Subnet) SetVpcId(v string) *Subnet { } // Describes the state of a CIDR block. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/SubnetCidrBlockState type SubnetCidrBlockState struct { _ struct{} `type:"structure"` @@ -65003,7 +64250,6 @@ func (s *SubnetCidrBlockState) SetStatusMessage(v string) *SubnetCidrBlockState } // Describes an IPv6 CIDR block associated with a subnet. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/SubnetIpv6CidrBlockAssociation type SubnetIpv6CidrBlockAssociation struct { _ struct{} `type:"structure"` @@ -65047,7 +64293,6 @@ func (s *SubnetIpv6CidrBlockAssociation) SetIpv6CidrBlockState(v *SubnetCidrBloc // Describes the T2 instance whose credit option for CPU usage was successfully // modified. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/SuccessfulInstanceCreditSpecificationItem type SuccessfulInstanceCreditSpecificationItem struct { _ struct{} `type:"structure"` @@ -65072,7 +64317,6 @@ func (s *SuccessfulInstanceCreditSpecificationItem) SetInstanceId(v string) *Suc } // Describes a tag. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/Tag type Tag struct { _ struct{} `type:"structure"` @@ -65112,7 +64356,6 @@ func (s *Tag) SetValue(v string) *Tag { } // Describes a tag. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/TagDescription type TagDescription struct { _ struct{} `type:"structure"` @@ -65164,7 +64407,6 @@ func (s *TagDescription) SetValue(v string) *TagDescription { } // The tags to apply to a resource when the resource is being created. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/TagSpecification type TagSpecification struct { _ struct{} `type:"structure"` @@ -65199,7 +64441,6 @@ func (s *TagSpecification) SetTags(v []*Tag) *TagSpecification { } // Information about the Convertible Reserved Instance offering. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/TargetConfiguration type TargetConfiguration struct { _ struct{} `type:"structure"` @@ -65234,7 +64475,6 @@ func (s *TargetConfiguration) SetOfferingId(v string) *TargetConfiguration { } // Details about the target configuration. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/TargetConfigurationRequest type TargetConfigurationRequest struct { _ struct{} `type:"structure"` @@ -65284,7 +64524,6 @@ func (s *TargetConfigurationRequest) SetOfferingId(v string) *TargetConfiguratio } // Describes a load balancer target group. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/TargetGroup type TargetGroup struct { _ struct{} `type:"structure"` @@ -65325,7 +64564,6 @@ func (s *TargetGroup) SetArn(v string) *TargetGroup { // Describes the target groups to attach to a Spot Fleet. Spot Fleet registers // the running Spot Instances with these target groups. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/TargetGroupsConfig type TargetGroupsConfig struct { _ struct{} `type:"structure"` @@ -65378,7 +64616,6 @@ func (s *TargetGroupsConfig) SetTargetGroups(v []*TargetGroup) *TargetGroupsConf } // The total value of the new Convertible Reserved Instances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/TargetReservationValue type TargetReservationValue struct { _ struct{} `type:"structure"` @@ -65415,7 +64652,6 @@ func (s *TargetReservationValue) SetTargetConfiguration(v *TargetConfiguration) } // Contains the parameters for TerminateInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/TerminateInstancesRequest type TerminateInstancesInput struct { _ struct{} `type:"structure"` @@ -65470,7 +64706,6 @@ func (s *TerminateInstancesInput) SetInstanceIds(v []*string) *TerminateInstance } // Contains the output of TerminateInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/TerminateInstancesResult type TerminateInstancesOutput struct { _ struct{} `type:"structure"` @@ -65494,7 +64729,6 @@ func (s *TerminateInstancesOutput) SetTerminatingInstances(v []*InstanceStateCha return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/UnassignIpv6AddressesRequest type UnassignIpv6AddressesInput struct { _ struct{} `type:"structure"` @@ -65547,7 +64781,6 @@ func (s *UnassignIpv6AddressesInput) SetNetworkInterfaceId(v string) *UnassignIp return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/UnassignIpv6AddressesResult type UnassignIpv6AddressesOutput struct { _ struct{} `type:"structure"` @@ -65581,7 +64814,6 @@ func (s *UnassignIpv6AddressesOutput) SetUnassignedIpv6Addresses(v []*string) *U } // Contains the parameters for UnassignPrivateIpAddresses. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/UnassignPrivateIpAddressesRequest type UnassignPrivateIpAddressesInput struct { _ struct{} `type:"structure"` @@ -65635,7 +64867,6 @@ func (s *UnassignPrivateIpAddressesInput) SetPrivateIpAddresses(v []*string) *Un return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/UnassignPrivateIpAddressesOutput type UnassignPrivateIpAddressesOutput struct { _ struct{} `type:"structure"` } @@ -65651,7 +64882,6 @@ func (s UnassignPrivateIpAddressesOutput) GoString() string { } // Contains the parameters for UnmonitorInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/UnmonitorInstancesRequest type UnmonitorInstancesInput struct { _ struct{} `type:"structure"` @@ -65703,7 +64933,6 @@ func (s *UnmonitorInstancesInput) SetInstanceIds(v []*string) *UnmonitorInstance } // Contains the output of UnmonitorInstances. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/UnmonitorInstancesResult type UnmonitorInstancesOutput struct { _ struct{} `type:"structure"` @@ -65728,7 +64957,6 @@ func (s *UnmonitorInstancesOutput) SetInstanceMonitorings(v []*InstanceMonitorin } // Describes the T2 instance whose credit option for CPU usage was not modified. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/UnsuccessfulInstanceCreditSpecificationItem type UnsuccessfulInstanceCreditSpecificationItem struct { _ struct{} `type:"structure"` @@ -65764,7 +64992,6 @@ func (s *UnsuccessfulInstanceCreditSpecificationItem) SetInstanceId(v string) *U // Information about the error for the T2 instance whose credit option for CPU // usage was not modified. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/UnsuccessfulInstanceCreditSpecificationItemError type UnsuccessfulInstanceCreditSpecificationItemError struct { _ struct{} `type:"structure"` @@ -65798,7 +65025,6 @@ func (s *UnsuccessfulInstanceCreditSpecificationItemError) SetMessage(v string) } // Information about items that were not successfully processed in a batch call. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/UnsuccessfulItem type UnsuccessfulItem struct { _ struct{} `type:"structure"` @@ -65835,7 +65061,6 @@ func (s *UnsuccessfulItem) SetResourceId(v string) *UnsuccessfulItem { // Information about the error that occurred. For more information about errors, // see Error Codes (http://docs.aws.amazon.com/AWSEC2/latest/APIReference/errors-overview.html). -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/UnsuccessfulItemError type UnsuccessfulItemError struct { _ struct{} `type:"structure"` @@ -65873,7 +65098,6 @@ func (s *UnsuccessfulItemError) SetMessage(v string) *UnsuccessfulItemError { } // Contains the parameters for UpdateSecurityGroupRuleDescriptionsEgress. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/UpdateSecurityGroupRuleDescriptionsEgressRequest type UpdateSecurityGroupRuleDescriptionsEgressInput struct { _ struct{} `type:"structure"` @@ -65946,7 +65170,6 @@ func (s *UpdateSecurityGroupRuleDescriptionsEgressInput) SetIpPermissions(v []*I } // Contains the output of UpdateSecurityGroupRuleDescriptionsEgress. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/UpdateSecurityGroupRuleDescriptionsEgressResult type UpdateSecurityGroupRuleDescriptionsEgressOutput struct { _ struct{} `type:"structure"` @@ -65971,7 +65194,6 @@ func (s *UpdateSecurityGroupRuleDescriptionsEgressOutput) SetReturn(v bool) *Upd } // Contains the parameters for UpdateSecurityGroupRuleDescriptionsIngress. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/UpdateSecurityGroupRuleDescriptionsIngressRequest type UpdateSecurityGroupRuleDescriptionsIngressInput struct { _ struct{} `type:"structure"` @@ -66044,7 +65266,6 @@ func (s *UpdateSecurityGroupRuleDescriptionsIngressInput) SetIpPermissions(v []* } // Contains the output of UpdateSecurityGroupRuleDescriptionsIngress. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/UpdateSecurityGroupRuleDescriptionsIngressResult type UpdateSecurityGroupRuleDescriptionsIngressOutput struct { _ struct{} `type:"structure"` @@ -66069,7 +65290,6 @@ func (s *UpdateSecurityGroupRuleDescriptionsIngressOutput) SetReturn(v bool) *Up } // Describes the S3 bucket for the disk image. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/UserBucket type UserBucket struct { _ struct{} `type:"structure"` @@ -66103,7 +65323,6 @@ func (s *UserBucket) SetS3Key(v string) *UserBucket { } // Describes the S3 bucket for the disk image. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/UserBucketDetails type UserBucketDetails struct { _ struct{} `type:"structure"` @@ -66137,7 +65356,6 @@ func (s *UserBucketDetails) SetS3Key(v string) *UserBucketDetails { } // Describes the user data for an instance. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/UserData type UserData struct { _ struct{} `type:"structure"` @@ -66164,7 +65382,6 @@ func (s *UserData) SetData(v string) *UserData { } // Describes a security group and AWS account ID pair. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/UserIdGroupPair type UserIdGroupPair struct { _ struct{} `type:"structure"` @@ -66259,7 +65476,6 @@ func (s *UserIdGroupPair) SetVpcPeeringConnectionId(v string) *UserIdGroupPair { } // Describes telemetry for a VPN tunnel. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/VgwTelemetry type VgwTelemetry struct { _ struct{} `type:"structure"` @@ -66321,7 +65537,6 @@ func (s *VgwTelemetry) SetStatusMessage(v string) *VgwTelemetry { } // Describes a volume. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/Volume type Volume struct { _ struct{} `type:"structure"` @@ -66460,7 +65675,6 @@ func (s *Volume) SetVolumeType(v string) *Volume { } // Describes volume attachment details. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/VolumeAttachment type VolumeAttachment struct { _ struct{} `type:"structure"` @@ -66530,7 +65744,6 @@ func (s *VolumeAttachment) SetVolumeId(v string) *VolumeAttachment { } // Describes an EBS volume. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/VolumeDetail type VolumeDetail struct { _ struct{} `type:"structure"` @@ -66572,7 +65785,6 @@ func (s *VolumeDetail) SetSize(v int64) *VolumeDetail { // Describes the modification status of an EBS volume. // // If the volume has never been modified, some element values will be null. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/VolumeModification type VolumeModification struct { _ struct{} `type:"structure"` @@ -66697,7 +65909,6 @@ func (s *VolumeModification) SetVolumeId(v string) *VolumeModification { } // Describes a volume status operation code. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/VolumeStatusAction type VolumeStatusAction struct { _ struct{} `type:"structure"` @@ -66749,7 +65960,6 @@ func (s *VolumeStatusAction) SetEventType(v string) *VolumeStatusAction { } // Describes a volume status. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/VolumeStatusDetails type VolumeStatusDetails struct { _ struct{} `type:"structure"` @@ -66783,7 +65993,6 @@ func (s *VolumeStatusDetails) SetStatus(v string) *VolumeStatusDetails { } // Describes a volume status event. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/VolumeStatusEvent type VolumeStatusEvent struct { _ struct{} `type:"structure"` @@ -66844,7 +66053,6 @@ func (s *VolumeStatusEvent) SetNotBefore(v time.Time) *VolumeStatusEvent { } // Describes the status of a volume. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/VolumeStatusInfo type VolumeStatusInfo struct { _ struct{} `type:"structure"` @@ -66878,7 +66086,6 @@ func (s *VolumeStatusInfo) SetStatus(v string) *VolumeStatusInfo { } // Describes the volume status. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/VolumeStatusItem type VolumeStatusItem struct { _ struct{} `type:"structure"` @@ -66939,7 +66146,6 @@ func (s *VolumeStatusItem) SetVolumeStatus(v *VolumeStatusInfo) *VolumeStatusIte } // Describes a VPC. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/Vpc type Vpc struct { _ struct{} `type:"structure"` @@ -67037,7 +66243,6 @@ func (s *Vpc) SetVpcId(v string) *Vpc { } // Describes an attachment between a virtual private gateway and a VPC. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/VpcAttachment type VpcAttachment struct { _ struct{} `type:"structure"` @@ -67071,7 +66276,6 @@ func (s *VpcAttachment) SetVpcId(v string) *VpcAttachment { } // Describes an IPv4 CIDR block associated with a VPC. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/VpcCidrBlockAssociation type VpcCidrBlockAssociation struct { _ struct{} `type:"structure"` @@ -67114,7 +66318,6 @@ func (s *VpcCidrBlockAssociation) SetCidrBlockState(v *VpcCidrBlockState) *VpcCi } // Describes the state of a CIDR block. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/VpcCidrBlockState type VpcCidrBlockState struct { _ struct{} `type:"structure"` @@ -67148,7 +66351,6 @@ func (s *VpcCidrBlockState) SetStatusMessage(v string) *VpcCidrBlockState { } // Describes whether a VPC is enabled for ClassicLink. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/VpcClassicLink type VpcClassicLink struct { _ struct{} `type:"structure"` @@ -67191,7 +66393,6 @@ func (s *VpcClassicLink) SetVpcId(v string) *VpcClassicLink { } // Describes a VPC endpoint. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/VpcEndpoint type VpcEndpoint struct { _ struct{} `type:"structure"` @@ -67326,7 +66527,6 @@ func (s *VpcEndpoint) SetVpcId(v string) *VpcEndpoint { } // Describes a VPC endpoint connection to a service. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/VpcEndpointConnection type VpcEndpointConnection struct { _ struct{} `type:"structure"` @@ -67387,7 +66587,6 @@ func (s *VpcEndpointConnection) SetVpcEndpointState(v string) *VpcEndpointConnec } // Describes an IPv6 CIDR block associated with a VPC. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/VpcIpv6CidrBlockAssociation type VpcIpv6CidrBlockAssociation struct { _ struct{} `type:"structure"` @@ -67430,7 +66629,6 @@ func (s *VpcIpv6CidrBlockAssociation) SetIpv6CidrBlockState(v *VpcCidrBlockState } // Describes a VPC peering connection. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/VpcPeeringConnection type VpcPeeringConnection struct { _ struct{} `type:"structure"` @@ -67502,7 +66700,6 @@ func (s *VpcPeeringConnection) SetVpcPeeringConnectionId(v string) *VpcPeeringCo } // Describes the VPC peering connection options. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/VpcPeeringConnectionOptionsDescription type VpcPeeringConnectionOptionsDescription struct { _ struct{} `type:"structure"` @@ -67548,7 +66745,6 @@ func (s *VpcPeeringConnectionOptionsDescription) SetAllowEgressFromLocalVpcToRem } // Describes the status of a VPC peering connection. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/VpcPeeringConnectionStateReason type VpcPeeringConnectionStateReason struct { _ struct{} `type:"structure"` @@ -67582,7 +66778,6 @@ func (s *VpcPeeringConnectionStateReason) SetMessage(v string) *VpcPeeringConnec } // Describes a VPC in a VPC peering connection. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/VpcPeeringConnectionVpcInfo type VpcPeeringConnectionVpcInfo struct { _ struct{} `type:"structure"` @@ -67662,7 +66857,6 @@ func (s *VpcPeeringConnectionVpcInfo) SetVpcId(v string) *VpcPeeringConnectionVp } // Describes a VPN connection. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/VpnConnection type VpnConnection struct { _ struct{} `type:"structure"` @@ -67783,7 +66977,6 @@ func (s *VpnConnection) SetVpnGatewayId(v string) *VpnConnection { } // Describes VPN connection options. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/VpnConnectionOptions type VpnConnectionOptions struct { _ struct{} `type:"structure"` @@ -67809,7 +67002,6 @@ func (s *VpnConnectionOptions) SetStaticRoutesOnly(v bool) *VpnConnectionOptions } // Describes VPN connection options. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/VpnConnectionOptionsSpecification type VpnConnectionOptionsSpecification struct { _ struct{} `type:"structure"` @@ -67847,7 +67039,6 @@ func (s *VpnConnectionOptionsSpecification) SetTunnelOptions(v []*VpnTunnelOptio } // Describes a virtual private gateway. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/VpnGateway type VpnGateway struct { _ struct{} `type:"structure"` @@ -67927,7 +67118,6 @@ func (s *VpnGateway) SetVpnGatewayId(v string) *VpnGateway { } // Describes a static route for a VPN connection. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/VpnStaticRoute type VpnStaticRoute struct { _ struct{} `type:"structure"` @@ -67970,7 +67160,6 @@ func (s *VpnStaticRoute) SetState(v string) *VpnStaticRoute { } // The tunnel options for a VPN connection. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/VpnTunnelOptionsSpecification type VpnTunnelOptionsSpecification struct { _ struct{} `type:"structure"` diff --git a/vendor/github.com/aws/aws-sdk-go/service/ecr/api.go b/vendor/github.com/aws/aws-sdk-go/service/ecr/api.go index 984aec965..bf9630e4c 100644 --- a/vendor/github.com/aws/aws-sdk-go/service/ecr/api.go +++ b/vendor/github.com/aws/aws-sdk-go/service/ecr/api.go @@ -2236,7 +2236,6 @@ func (c *ECR) UploadLayerPartWithContext(ctx aws.Context, input *UploadLayerPart } // An object representing authorization data for an Amazon ECR registry. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/AuthorizationData type AuthorizationData struct { _ struct{} `type:"structure"` @@ -2283,7 +2282,6 @@ func (s *AuthorizationData) SetProxyEndpoint(v string) *AuthorizationData { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/BatchCheckLayerAvailabilityRequest type BatchCheckLayerAvailabilityInput struct { _ struct{} `type:"structure"` @@ -2352,7 +2350,6 @@ func (s *BatchCheckLayerAvailabilityInput) SetRepositoryName(v string) *BatchChe return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/BatchCheckLayerAvailabilityResponse type BatchCheckLayerAvailabilityOutput struct { _ struct{} `type:"structure"` @@ -2388,7 +2385,6 @@ func (s *BatchCheckLayerAvailabilityOutput) SetLayers(v []*Layer) *BatchCheckLay // Deletes specified images within a specified repository. Images are specified // with either the imageTag or imageDigest. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/BatchDeleteImageRequest type BatchDeleteImageInput struct { _ struct{} `type:"structure"` @@ -2458,7 +2454,6 @@ func (s *BatchDeleteImageInput) SetRepositoryName(v string) *BatchDeleteImageInp return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/BatchDeleteImageResponse type BatchDeleteImageOutput struct { _ struct{} `type:"structure"` @@ -2491,7 +2486,6 @@ func (s *BatchDeleteImageOutput) SetImageIds(v []*ImageIdentifier) *BatchDeleteI return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/BatchGetImageRequest type BatchGetImageInput struct { _ struct{} `type:"structure"` @@ -2576,7 +2570,6 @@ func (s *BatchGetImageInput) SetRepositoryName(v string) *BatchGetImageInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/BatchGetImageResponse type BatchGetImageOutput struct { _ struct{} `type:"structure"` @@ -2609,7 +2602,6 @@ func (s *BatchGetImageOutput) SetImages(v []*Image) *BatchGetImageOutput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/CompleteLayerUploadRequest type CompleteLayerUploadInput struct { _ struct{} `type:"structure"` @@ -2693,7 +2685,6 @@ func (s *CompleteLayerUploadInput) SetUploadId(v string) *CompleteLayerUploadInp return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/CompleteLayerUploadResponse type CompleteLayerUploadOutput struct { _ struct{} `type:"structure"` @@ -2744,7 +2735,6 @@ func (s *CompleteLayerUploadOutput) SetUploadId(v string) *CompleteLayerUploadOu return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/CreateRepositoryRequest type CreateRepositoryInput struct { _ struct{} `type:"structure"` @@ -2788,7 +2778,6 @@ func (s *CreateRepositoryInput) SetRepositoryName(v string) *CreateRepositoryInp return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/CreateRepositoryResponse type CreateRepositoryOutput struct { _ struct{} `type:"structure"` @@ -2812,7 +2801,6 @@ func (s *CreateRepositoryOutput) SetRepository(v *Repository) *CreateRepositoryO return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/DeleteLifecyclePolicyRequest type DeleteLifecyclePolicyInput struct { _ struct{} `type:"structure"` @@ -2865,7 +2853,6 @@ func (s *DeleteLifecyclePolicyInput) SetRepositoryName(v string) *DeleteLifecycl return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/DeleteLifecyclePolicyResponse type DeleteLifecyclePolicyOutput struct { _ struct{} `type:"structure"` @@ -2916,7 +2903,6 @@ func (s *DeleteLifecyclePolicyOutput) SetRepositoryName(v string) *DeleteLifecyc return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/DeleteRepositoryRequest type DeleteRepositoryInput struct { _ struct{} `type:"structure"` @@ -2977,7 +2963,6 @@ func (s *DeleteRepositoryInput) SetRepositoryName(v string) *DeleteRepositoryInp return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/DeleteRepositoryResponse type DeleteRepositoryOutput struct { _ struct{} `type:"structure"` @@ -3001,7 +2986,6 @@ func (s *DeleteRepositoryOutput) SetRepository(v *Repository) *DeleteRepositoryO return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/DeleteRepositoryPolicyRequest type DeleteRepositoryPolicyInput struct { _ struct{} `type:"structure"` @@ -3055,7 +3039,6 @@ func (s *DeleteRepositoryPolicyInput) SetRepositoryName(v string) *DeleteReposit return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/DeleteRepositoryPolicyResponse type DeleteRepositoryPolicyOutput struct { _ struct{} `type:"structure"` @@ -3098,7 +3081,6 @@ func (s *DeleteRepositoryPolicyOutput) SetRepositoryName(v string) *DeleteReposi } // An object representing a filter on a DescribeImages operation. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/DescribeImagesFilter type DescribeImagesFilter struct { _ struct{} `type:"structure"` @@ -3123,7 +3105,6 @@ func (s *DescribeImagesFilter) SetTagStatus(v string) *DescribeImagesFilter { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/DescribeImagesRequest type DescribeImagesInput struct { _ struct{} `type:"structure"` @@ -3228,7 +3209,6 @@ func (s *DescribeImagesInput) SetRepositoryName(v string) *DescribeImagesInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/DescribeImagesResponse type DescribeImagesOutput struct { _ struct{} `type:"structure"` @@ -3264,7 +3244,6 @@ func (s *DescribeImagesOutput) SetNextToken(v string) *DescribeImagesOutput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/DescribeRepositoriesRequest type DescribeRepositoriesInput struct { _ struct{} `type:"structure"` @@ -3347,7 +3326,6 @@ func (s *DescribeRepositoriesInput) SetRepositoryNames(v []*string) *DescribeRep return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/DescribeRepositoriesResponse type DescribeRepositoriesOutput struct { _ struct{} `type:"structure"` @@ -3383,7 +3361,6 @@ func (s *DescribeRepositoriesOutput) SetRepositories(v []*Repository) *DescribeR return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/GetAuthorizationTokenRequest type GetAuthorizationTokenInput struct { _ struct{} `type:"structure"` @@ -3422,7 +3399,6 @@ func (s *GetAuthorizationTokenInput) SetRegistryIds(v []*string) *GetAuthorizati return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/GetAuthorizationTokenResponse type GetAuthorizationTokenOutput struct { _ struct{} `type:"structure"` @@ -3447,7 +3423,6 @@ func (s *GetAuthorizationTokenOutput) SetAuthorizationData(v []*AuthorizationDat return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/GetDownloadUrlForLayerRequest type GetDownloadUrlForLayerInput struct { _ struct{} `type:"structure"` @@ -3513,7 +3488,6 @@ func (s *GetDownloadUrlForLayerInput) SetRepositoryName(v string) *GetDownloadUr return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/GetDownloadUrlForLayerResponse type GetDownloadUrlForLayerOutput struct { _ struct{} `type:"structure"` @@ -3546,7 +3520,6 @@ func (s *GetDownloadUrlForLayerOutput) SetLayerDigest(v string) *GetDownloadUrlF return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/GetLifecyclePolicyRequest type GetLifecyclePolicyInput struct { _ struct{} `type:"structure"` @@ -3598,7 +3571,6 @@ func (s *GetLifecyclePolicyInput) SetRepositoryName(v string) *GetLifecyclePolic return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/GetLifecyclePolicyResponse type GetLifecyclePolicyOutput struct { _ struct{} `type:"structure"` @@ -3649,7 +3621,6 @@ func (s *GetLifecyclePolicyOutput) SetRepositoryName(v string) *GetLifecyclePoli return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/GetLifecyclePolicyPreviewRequest type GetLifecyclePolicyPreviewInput struct { _ struct{} `type:"structure"` @@ -3755,7 +3726,6 @@ func (s *GetLifecyclePolicyPreviewInput) SetRepositoryName(v string) *GetLifecyc return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/GetLifecyclePolicyPreviewResponse type GetLifecyclePolicyPreviewOutput struct { _ struct{} `type:"structure"` @@ -3836,7 +3806,6 @@ func (s *GetLifecyclePolicyPreviewOutput) SetSummary(v *LifecyclePolicyPreviewSu return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/GetRepositoryPolicyRequest type GetRepositoryPolicyInput struct { _ struct{} `type:"structure"` @@ -3888,7 +3857,6 @@ func (s *GetRepositoryPolicyInput) SetRepositoryName(v string) *GetRepositoryPol return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/GetRepositoryPolicyResponse type GetRepositoryPolicyOutput struct { _ struct{} `type:"structure"` @@ -3931,7 +3899,6 @@ func (s *GetRepositoryPolicyOutput) SetRepositoryName(v string) *GetRepositoryPo } // An object representing an Amazon ECR image. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/Image type Image struct { _ struct{} `type:"structure"` @@ -3983,7 +3950,6 @@ func (s *Image) SetRepositoryName(v string) *Image { } // An object that describes an image returned by a DescribeImages operation. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/ImageDetail type ImageDetail struct { _ struct{} `type:"structure"` @@ -4059,7 +4025,6 @@ func (s *ImageDetail) SetRepositoryName(v string) *ImageDetail { } // An object representing an Amazon ECR image failure. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/ImageFailure type ImageFailure struct { _ struct{} `type:"structure"` @@ -4102,7 +4067,6 @@ func (s *ImageFailure) SetImageId(v *ImageIdentifier) *ImageFailure { } // An object with identifying information for an Amazon ECR image. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/ImageIdentifier type ImageIdentifier struct { _ struct{} `type:"structure"` @@ -4135,7 +4099,6 @@ func (s *ImageIdentifier) SetImageTag(v string) *ImageIdentifier { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/InitiateLayerUploadRequest type InitiateLayerUploadInput struct { _ struct{} `type:"structure"` @@ -4187,7 +4150,6 @@ func (s *InitiateLayerUploadInput) SetRepositoryName(v string) *InitiateLayerUpl return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/InitiateLayerUploadResponse type InitiateLayerUploadOutput struct { _ struct{} `type:"structure"` @@ -4223,7 +4185,6 @@ func (s *InitiateLayerUploadOutput) SetUploadId(v string) *InitiateLayerUploadOu } // An object representing an Amazon ECR image layer. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/Layer type Layer struct { _ struct{} `type:"structure"` @@ -4276,7 +4237,6 @@ func (s *Layer) SetMediaType(v string) *Layer { } // An object representing an Amazon ECR image layer failure. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/LayerFailure type LayerFailure struct { _ struct{} `type:"structure"` @@ -4319,7 +4279,6 @@ func (s *LayerFailure) SetLayerDigest(v string) *LayerFailure { } // The filter for the lifecycle policy preview. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/LifecyclePolicyPreviewFilter type LifecyclePolicyPreviewFilter struct { _ struct{} `type:"structure"` @@ -4344,7 +4303,6 @@ func (s *LifecyclePolicyPreviewFilter) SetTagStatus(v string) *LifecyclePolicyPr } // The result of the lifecycle policy preview. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/LifecyclePolicyPreviewResult type LifecyclePolicyPreviewResult struct { _ struct{} `type:"structure"` @@ -4406,7 +4364,6 @@ func (s *LifecyclePolicyPreviewResult) SetImageTags(v []*string) *LifecyclePolic } // The summary of the lifecycle policy preview request. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/LifecyclePolicyPreviewSummary type LifecyclePolicyPreviewSummary struct { _ struct{} `type:"structure"` @@ -4431,7 +4388,6 @@ func (s *LifecyclePolicyPreviewSummary) SetExpiringImageTotalCount(v int64) *Lif } // The type of action to be taken. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/LifecyclePolicyRuleAction type LifecyclePolicyRuleAction struct { _ struct{} `type:"structure"` @@ -4456,7 +4412,6 @@ func (s *LifecyclePolicyRuleAction) SetType(v string) *LifecyclePolicyRuleAction } // An object representing a filter on a ListImages operation. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/ListImagesFilter type ListImagesFilter struct { _ struct{} `type:"structure"` @@ -4481,7 +4436,6 @@ func (s *ListImagesFilter) SetTagStatus(v string) *ListImagesFilter { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/ListImagesRequest type ListImagesInput struct { _ struct{} `type:"structure"` @@ -4576,7 +4530,6 @@ func (s *ListImagesInput) SetRepositoryName(v string) *ListImagesInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/ListImagesResponse type ListImagesOutput struct { _ struct{} `type:"structure"` @@ -4612,7 +4565,6 @@ func (s *ListImagesOutput) SetNextToken(v string) *ListImagesOutput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/PutImageRequest type PutImageInput struct { _ struct{} `type:"structure"` @@ -4689,7 +4641,6 @@ func (s *PutImageInput) SetRepositoryName(v string) *PutImageInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/PutImageResponse type PutImageOutput struct { _ struct{} `type:"structure"` @@ -4713,7 +4664,6 @@ func (s *PutImageOutput) SetImage(v *Image) *PutImageOutput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/PutLifecyclePolicyRequest type PutLifecyclePolicyInput struct { _ struct{} `type:"structure"` @@ -4782,7 +4732,6 @@ func (s *PutLifecyclePolicyInput) SetRepositoryName(v string) *PutLifecyclePolic return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/PutLifecyclePolicyResponse type PutLifecyclePolicyOutput struct { _ struct{} `type:"structure"` @@ -4825,7 +4774,6 @@ func (s *PutLifecyclePolicyOutput) SetRepositoryName(v string) *PutLifecyclePoli } // An object representing a repository. -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/Repository type Repository struct { _ struct{} `type:"structure"` @@ -4889,7 +4837,6 @@ func (s *Repository) SetRepositoryUri(v string) *Repository { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/SetRepositoryPolicyRequest type SetRepositoryPolicyInput struct { _ struct{} `type:"structure"` @@ -4966,7 +4913,6 @@ func (s *SetRepositoryPolicyInput) SetRepositoryName(v string) *SetRepositoryPol return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/SetRepositoryPolicyResponse type SetRepositoryPolicyOutput struct { _ struct{} `type:"structure"` @@ -5008,7 +4954,6 @@ func (s *SetRepositoryPolicyOutput) SetRepositoryName(v string) *SetRepositoryPo return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/StartLifecyclePolicyPreviewRequest type StartLifecyclePolicyPreviewInput struct { _ struct{} `type:"structure"` @@ -5073,7 +5018,6 @@ func (s *StartLifecyclePolicyPreviewInput) SetRepositoryName(v string) *StartLif return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/StartLifecyclePolicyPreviewResponse type StartLifecyclePolicyPreviewOutput struct { _ struct{} `type:"structure"` @@ -5124,7 +5068,6 @@ func (s *StartLifecyclePolicyPreviewOutput) SetStatus(v string) *StartLifecycleP return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/UploadLayerPartRequest type UploadLayerPartInput struct { _ struct{} `type:"structure"` @@ -5235,7 +5178,6 @@ func (s *UploadLayerPartInput) SetUploadId(v string) *UploadLayerPartInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/UploadLayerPartResponse type UploadLayerPartOutput struct { _ struct{} `type:"structure"` diff --git a/vendor/github.com/aws/aws-sdk-go/service/s3/api.go b/vendor/github.com/aws/aws-sdk-go/service/s3/api.go index 0d852f59c..2faeee1ec 100644 --- a/vendor/github.com/aws/aws-sdk-go/service/s3/api.go +++ b/vendor/github.com/aws/aws-sdk-go/service/s3/api.go @@ -6173,7 +6173,6 @@ func (c *S3) UploadPartCopyWithContext(ctx aws.Context, input *UploadPartCopyInp // Specifies the days since the initiation of an Incomplete Multipart Upload // that Lifecycle will wait before permanently removing all parts of the upload. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/AbortIncompleteMultipartUpload type AbortIncompleteMultipartUpload struct { _ struct{} `type:"structure"` @@ -6198,7 +6197,6 @@ func (s *AbortIncompleteMultipartUpload) SetDaysAfterInitiation(v int64) *AbortI return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/AbortMultipartUploadRequest type AbortMultipartUploadInput struct { _ struct{} `type:"structure"` @@ -6281,7 +6279,6 @@ func (s *AbortMultipartUploadInput) SetUploadId(v string) *AbortMultipartUploadI return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/AbortMultipartUploadOutput type AbortMultipartUploadOutput struct { _ struct{} `type:"structure"` @@ -6306,7 +6303,6 @@ func (s *AbortMultipartUploadOutput) SetRequestCharged(v string) *AbortMultipart return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/AccelerateConfiguration type AccelerateConfiguration struct { _ struct{} `type:"structure"` @@ -6330,7 +6326,6 @@ func (s *AccelerateConfiguration) SetStatus(v string) *AccelerateConfiguration { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/AccessControlPolicy type AccessControlPolicy struct { _ struct{} `type:"structure"` @@ -6383,7 +6378,6 @@ func (s *AccessControlPolicy) SetOwner(v *Owner) *AccessControlPolicy { } // Container for information regarding the access control for replicas. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/AccessControlTranslation type AccessControlTranslation struct { _ struct{} `type:"structure"` @@ -6422,7 +6416,6 @@ func (s *AccessControlTranslation) SetOwner(v string) *AccessControlTranslation return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/AnalyticsAndOperator type AnalyticsAndOperator struct { _ struct{} `type:"structure"` @@ -6475,7 +6468,6 @@ func (s *AnalyticsAndOperator) SetTags(v []*Tag) *AnalyticsAndOperator { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/AnalyticsConfiguration type AnalyticsConfiguration struct { _ struct{} `type:"structure"` @@ -6550,7 +6542,6 @@ func (s *AnalyticsConfiguration) SetStorageClassAnalysis(v *StorageClassAnalysis return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/AnalyticsExportDestination type AnalyticsExportDestination struct { _ struct{} `type:"structure"` @@ -6594,7 +6585,6 @@ func (s *AnalyticsExportDestination) SetS3BucketDestination(v *AnalyticsS3Bucket return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/AnalyticsFilter type AnalyticsFilter struct { _ struct{} `type:"structure"` @@ -6657,7 +6647,6 @@ func (s *AnalyticsFilter) SetTag(v *Tag) *AnalyticsFilter { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/AnalyticsS3BucketDestination type AnalyticsS3BucketDestination struct { _ struct{} `type:"structure"` @@ -6737,7 +6726,6 @@ func (s *AnalyticsS3BucketDestination) SetPrefix(v string) *AnalyticsS3BucketDes return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/Bucket type Bucket struct { _ struct{} `type:"structure"` @@ -6770,7 +6758,6 @@ func (s *Bucket) SetName(v string) *Bucket { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/BucketLifecycleConfiguration type BucketLifecycleConfiguration struct { _ struct{} `type:"structure"` @@ -6817,7 +6804,6 @@ func (s *BucketLifecycleConfiguration) SetRules(v []*LifecycleRule) *BucketLifec return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/BucketLoggingStatus type BucketLoggingStatus struct { _ struct{} `type:"structure"` @@ -6855,7 +6841,6 @@ func (s *BucketLoggingStatus) SetLoggingEnabled(v *LoggingEnabled) *BucketLoggin return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/CORSConfiguration type CORSConfiguration struct { _ struct{} `type:"structure"` @@ -6902,7 +6887,6 @@ func (s *CORSConfiguration) SetCORSRules(v []*CORSRule) *CORSConfiguration { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/CORSRule type CORSRule struct { _ struct{} `type:"structure"` @@ -6987,7 +6971,6 @@ func (s *CORSRule) SetMaxAgeSeconds(v int64) *CORSRule { } // Describes how a CSV-formatted input object is formatted. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/CSVInput type CSVInput struct { _ struct{} `type:"structure"` @@ -7059,7 +7042,6 @@ func (s *CSVInput) SetRecordDelimiter(v string) *CSVInput { } // Describes how CSV-formatted results are formatted. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/CSVOutput type CSVOutput struct { _ struct{} `type:"structure"` @@ -7120,7 +7102,6 @@ func (s *CSVOutput) SetRecordDelimiter(v string) *CSVOutput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/CloudFunctionConfiguration type CloudFunctionConfiguration struct { _ struct{} `type:"structure"` @@ -7178,7 +7159,6 @@ func (s *CloudFunctionConfiguration) SetInvocationRole(v string) *CloudFunctionC return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/CommonPrefix type CommonPrefix struct { _ struct{} `type:"structure"` @@ -7201,7 +7181,6 @@ func (s *CommonPrefix) SetPrefix(v string) *CommonPrefix { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/CompleteMultipartUploadRequest type CompleteMultipartUploadInput struct { _ struct{} `type:"structure" payload:"MultipartUpload"` @@ -7292,7 +7271,6 @@ func (s *CompleteMultipartUploadInput) SetUploadId(v string) *CompleteMultipartU return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/CompleteMultipartUploadOutput type CompleteMultipartUploadOutput struct { _ struct{} `type:"structure"` @@ -7396,7 +7374,6 @@ func (s *CompleteMultipartUploadOutput) SetVersionId(v string) *CompleteMultipar return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/CompletedMultipartUpload type CompletedMultipartUpload struct { _ struct{} `type:"structure"` @@ -7419,7 +7396,6 @@ func (s *CompletedMultipartUpload) SetParts(v []*CompletedPart) *CompletedMultip return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/CompletedPart type CompletedPart struct { _ struct{} `type:"structure"` @@ -7453,7 +7429,6 @@ func (s *CompletedPart) SetPartNumber(v int64) *CompletedPart { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/Condition type Condition struct { _ struct{} `type:"structure"` @@ -7496,7 +7471,6 @@ func (s *Condition) SetKeyPrefixEquals(v string) *Condition { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/CopyObjectRequest type CopyObjectInput struct { _ struct{} `type:"structure"` @@ -7880,7 +7854,6 @@ func (s *CopyObjectInput) SetWebsiteRedirectLocation(v string) *CopyObjectInput return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/CopyObjectOutput type CopyObjectOutput struct { _ struct{} `type:"structure" payload:"CopyObjectResult"` @@ -7981,7 +7954,6 @@ func (s *CopyObjectOutput) SetVersionId(v string) *CopyObjectOutput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/CopyObjectResult type CopyObjectResult struct { _ struct{} `type:"structure"` @@ -8012,7 +7984,6 @@ func (s *CopyObjectResult) SetLastModified(v time.Time) *CopyObjectResult { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/CopyPartResult type CopyPartResult struct { _ struct{} `type:"structure"` @@ -8045,7 +8016,6 @@ func (s *CopyPartResult) SetLastModified(v time.Time) *CopyPartResult { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/CreateBucketConfiguration type CreateBucketConfiguration struct { _ struct{} `type:"structure"` @@ -8070,7 +8040,6 @@ func (s *CreateBucketConfiguration) SetLocationConstraint(v string) *CreateBucke return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/CreateBucketRequest type CreateBucketInput struct { _ struct{} `type:"structure" payload:"CreateBucketConfiguration"` @@ -8177,7 +8146,6 @@ func (s *CreateBucketInput) SetGrantWriteACP(v string) *CreateBucketInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/CreateBucketOutput type CreateBucketOutput struct { _ struct{} `type:"structure"` @@ -8200,7 +8168,6 @@ func (s *CreateBucketOutput) SetLocation(v string) *CreateBucketOutput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/CreateMultipartUploadRequest type CreateMultipartUploadInput struct { _ struct{} `type:"structure"` @@ -8472,7 +8439,6 @@ func (s *CreateMultipartUploadInput) SetWebsiteRedirectLocation(v string) *Creat return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/CreateMultipartUploadOutput type CreateMultipartUploadOutput struct { _ struct{} `type:"structure"` @@ -8592,7 +8558,6 @@ func (s *CreateMultipartUploadOutput) SetUploadId(v string) *CreateMultipartUplo return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/Delete type Delete struct { _ struct{} `type:"structure"` @@ -8649,7 +8614,6 @@ func (s *Delete) SetQuiet(v bool) *Delete { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteBucketAnalyticsConfigurationRequest type DeleteBucketAnalyticsConfigurationInput struct { _ struct{} `type:"structure"` @@ -8709,7 +8673,6 @@ func (s *DeleteBucketAnalyticsConfigurationInput) SetId(v string) *DeleteBucketA return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteBucketAnalyticsConfigurationOutput type DeleteBucketAnalyticsConfigurationOutput struct { _ struct{} `type:"structure"` } @@ -8724,7 +8687,6 @@ func (s DeleteBucketAnalyticsConfigurationOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteBucketCorsRequest type DeleteBucketCorsInput struct { _ struct{} `type:"structure"` @@ -8768,7 +8730,6 @@ func (s *DeleteBucketCorsInput) getBucket() (v string) { return *s.Bucket } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteBucketCorsOutput type DeleteBucketCorsOutput struct { _ struct{} `type:"structure"` } @@ -8783,7 +8744,6 @@ func (s DeleteBucketCorsOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteBucketEncryptionRequest type DeleteBucketEncryptionInput struct { _ struct{} `type:"structure"` @@ -8830,7 +8790,6 @@ func (s *DeleteBucketEncryptionInput) getBucket() (v string) { return *s.Bucket } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteBucketEncryptionOutput type DeleteBucketEncryptionOutput struct { _ struct{} `type:"structure"` } @@ -8845,7 +8804,6 @@ func (s DeleteBucketEncryptionOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteBucketRequest type DeleteBucketInput struct { _ struct{} `type:"structure"` @@ -8889,7 +8847,6 @@ func (s *DeleteBucketInput) getBucket() (v string) { return *s.Bucket } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteBucketInventoryConfigurationRequest type DeleteBucketInventoryConfigurationInput struct { _ struct{} `type:"structure"` @@ -8949,7 +8906,6 @@ func (s *DeleteBucketInventoryConfigurationInput) SetId(v string) *DeleteBucketI return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteBucketInventoryConfigurationOutput type DeleteBucketInventoryConfigurationOutput struct { _ struct{} `type:"structure"` } @@ -8964,7 +8920,6 @@ func (s DeleteBucketInventoryConfigurationOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteBucketLifecycleRequest type DeleteBucketLifecycleInput struct { _ struct{} `type:"structure"` @@ -9008,7 +8963,6 @@ func (s *DeleteBucketLifecycleInput) getBucket() (v string) { return *s.Bucket } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteBucketLifecycleOutput type DeleteBucketLifecycleOutput struct { _ struct{} `type:"structure"` } @@ -9023,7 +8977,6 @@ func (s DeleteBucketLifecycleOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteBucketMetricsConfigurationRequest type DeleteBucketMetricsConfigurationInput struct { _ struct{} `type:"structure"` @@ -9083,7 +9036,6 @@ func (s *DeleteBucketMetricsConfigurationInput) SetId(v string) *DeleteBucketMet return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteBucketMetricsConfigurationOutput type DeleteBucketMetricsConfigurationOutput struct { _ struct{} `type:"structure"` } @@ -9098,7 +9050,6 @@ func (s DeleteBucketMetricsConfigurationOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteBucketOutput type DeleteBucketOutput struct { _ struct{} `type:"structure"` } @@ -9113,7 +9064,6 @@ func (s DeleteBucketOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteBucketPolicyRequest type DeleteBucketPolicyInput struct { _ struct{} `type:"structure"` @@ -9157,7 +9107,6 @@ func (s *DeleteBucketPolicyInput) getBucket() (v string) { return *s.Bucket } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteBucketPolicyOutput type DeleteBucketPolicyOutput struct { _ struct{} `type:"structure"` } @@ -9172,7 +9121,6 @@ func (s DeleteBucketPolicyOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteBucketReplicationRequest type DeleteBucketReplicationInput struct { _ struct{} `type:"structure"` @@ -9216,7 +9164,6 @@ func (s *DeleteBucketReplicationInput) getBucket() (v string) { return *s.Bucket } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteBucketReplicationOutput type DeleteBucketReplicationOutput struct { _ struct{} `type:"structure"` } @@ -9231,7 +9178,6 @@ func (s DeleteBucketReplicationOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteBucketTaggingRequest type DeleteBucketTaggingInput struct { _ struct{} `type:"structure"` @@ -9275,7 +9221,6 @@ func (s *DeleteBucketTaggingInput) getBucket() (v string) { return *s.Bucket } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteBucketTaggingOutput type DeleteBucketTaggingOutput struct { _ struct{} `type:"structure"` } @@ -9290,7 +9235,6 @@ func (s DeleteBucketTaggingOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteBucketWebsiteRequest type DeleteBucketWebsiteInput struct { _ struct{} `type:"structure"` @@ -9334,7 +9278,6 @@ func (s *DeleteBucketWebsiteInput) getBucket() (v string) { return *s.Bucket } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteBucketWebsiteOutput type DeleteBucketWebsiteOutput struct { _ struct{} `type:"structure"` } @@ -9349,7 +9292,6 @@ func (s DeleteBucketWebsiteOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteMarkerEntry type DeleteMarkerEntry struct { _ struct{} `type:"structure"` @@ -9409,7 +9351,6 @@ func (s *DeleteMarkerEntry) SetVersionId(v string) *DeleteMarkerEntry { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteObjectRequest type DeleteObjectInput struct { _ struct{} `type:"structure"` @@ -9499,7 +9440,6 @@ func (s *DeleteObjectInput) SetVersionId(v string) *DeleteObjectInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteObjectOutput type DeleteObjectOutput struct { _ struct{} `type:"structure"` @@ -9544,7 +9484,6 @@ func (s *DeleteObjectOutput) SetVersionId(v string) *DeleteObjectOutput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteObjectTaggingRequest type DeleteObjectTaggingInput struct { _ struct{} `type:"structure"` @@ -9612,7 +9551,6 @@ func (s *DeleteObjectTaggingInput) SetVersionId(v string) *DeleteObjectTaggingIn return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteObjectTaggingOutput type DeleteObjectTaggingOutput struct { _ struct{} `type:"structure"` @@ -9636,7 +9574,6 @@ func (s *DeleteObjectTaggingOutput) SetVersionId(v string) *DeleteObjectTaggingO return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteObjectsRequest type DeleteObjectsInput struct { _ struct{} `type:"structure" payload:"Delete"` @@ -9719,7 +9656,6 @@ func (s *DeleteObjectsInput) SetRequestPayer(v string) *DeleteObjectsInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteObjectsOutput type DeleteObjectsOutput struct { _ struct{} `type:"structure"` @@ -9760,7 +9696,6 @@ func (s *DeleteObjectsOutput) SetRequestCharged(v string) *DeleteObjectsOutput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeletedObject type DeletedObject struct { _ struct{} `type:"structure"` @@ -9808,7 +9743,6 @@ func (s *DeletedObject) SetVersionId(v string) *DeletedObject { } // Container for replication destination information. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/Destination type Destination struct { _ struct{} `type:"structure"` @@ -9899,7 +9833,6 @@ func (s *Destination) SetStorageClass(v string) *Destination { // Describes the server-side encryption that will be applied to the restore // results. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/Encryption type Encryption struct { _ struct{} `type:"structure"` @@ -9960,7 +9893,6 @@ func (s *Encryption) SetKMSKeyId(v string) *Encryption { } // Container for information regarding encryption based configuration for replicas. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/EncryptionConfiguration type EncryptionConfiguration struct { _ struct{} `type:"structure"` @@ -9984,7 +9916,6 @@ func (s *EncryptionConfiguration) SetReplicaKmsKeyID(v string) *EncryptionConfig return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/Error type Error struct { _ struct{} `type:"structure"` @@ -10031,7 +9962,6 @@ func (s *Error) SetVersionId(v string) *Error { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/ErrorDocument type ErrorDocument struct { _ struct{} `type:"structure"` @@ -10074,7 +10004,6 @@ func (s *ErrorDocument) SetKey(v string) *ErrorDocument { } // Container for key value pair that defines the criteria for the filter rule. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/FilterRule type FilterRule struct { _ struct{} `type:"structure"` @@ -10109,7 +10038,6 @@ func (s *FilterRule) SetValue(v string) *FilterRule { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketAccelerateConfigurationRequest type GetBucketAccelerateConfigurationInput struct { _ struct{} `type:"structure"` @@ -10155,7 +10083,6 @@ func (s *GetBucketAccelerateConfigurationInput) getBucket() (v string) { return *s.Bucket } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketAccelerateConfigurationOutput type GetBucketAccelerateConfigurationOutput struct { _ struct{} `type:"structure"` @@ -10179,7 +10106,6 @@ func (s *GetBucketAccelerateConfigurationOutput) SetStatus(v string) *GetBucketA return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketAclRequest type GetBucketAclInput struct { _ struct{} `type:"structure"` @@ -10223,7 +10149,6 @@ func (s *GetBucketAclInput) getBucket() (v string) { return *s.Bucket } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketAclOutput type GetBucketAclOutput struct { _ struct{} `type:"structure"` @@ -10255,7 +10180,6 @@ func (s *GetBucketAclOutput) SetOwner(v *Owner) *GetBucketAclOutput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketAnalyticsConfigurationRequest type GetBucketAnalyticsConfigurationInput struct { _ struct{} `type:"structure"` @@ -10315,7 +10239,6 @@ func (s *GetBucketAnalyticsConfigurationInput) SetId(v string) *GetBucketAnalyti return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketAnalyticsConfigurationOutput type GetBucketAnalyticsConfigurationOutput struct { _ struct{} `type:"structure" payload:"AnalyticsConfiguration"` @@ -10339,7 +10262,6 @@ func (s *GetBucketAnalyticsConfigurationOutput) SetAnalyticsConfiguration(v *Ana return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketCorsRequest type GetBucketCorsInput struct { _ struct{} `type:"structure"` @@ -10383,7 +10305,6 @@ func (s *GetBucketCorsInput) getBucket() (v string) { return *s.Bucket } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketCorsOutput type GetBucketCorsOutput struct { _ struct{} `type:"structure"` @@ -10406,7 +10327,6 @@ func (s *GetBucketCorsOutput) SetCORSRules(v []*CORSRule) *GetBucketCorsOutput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketEncryptionRequest type GetBucketEncryptionInput struct { _ struct{} `type:"structure"` @@ -10453,7 +10373,6 @@ func (s *GetBucketEncryptionInput) getBucket() (v string) { return *s.Bucket } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketEncryptionOutput type GetBucketEncryptionOutput struct { _ struct{} `type:"structure" payload:"ServerSideEncryptionConfiguration"` @@ -10478,7 +10397,6 @@ func (s *GetBucketEncryptionOutput) SetServerSideEncryptionConfiguration(v *Serv return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketInventoryConfigurationRequest type GetBucketInventoryConfigurationInput struct { _ struct{} `type:"structure"` @@ -10538,7 +10456,6 @@ func (s *GetBucketInventoryConfigurationInput) SetId(v string) *GetBucketInvento return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketInventoryConfigurationOutput type GetBucketInventoryConfigurationOutput struct { _ struct{} `type:"structure" payload:"InventoryConfiguration"` @@ -10562,7 +10479,6 @@ func (s *GetBucketInventoryConfigurationOutput) SetInventoryConfiguration(v *Inv return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketLifecycleConfigurationRequest type GetBucketLifecycleConfigurationInput struct { _ struct{} `type:"structure"` @@ -10606,7 +10522,6 @@ func (s *GetBucketLifecycleConfigurationInput) getBucket() (v string) { return *s.Bucket } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketLifecycleConfigurationOutput type GetBucketLifecycleConfigurationOutput struct { _ struct{} `type:"structure"` @@ -10629,7 +10544,6 @@ func (s *GetBucketLifecycleConfigurationOutput) SetRules(v []*LifecycleRule) *Ge return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketLifecycleRequest type GetBucketLifecycleInput struct { _ struct{} `type:"structure"` @@ -10673,7 +10587,6 @@ func (s *GetBucketLifecycleInput) getBucket() (v string) { return *s.Bucket } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketLifecycleOutput type GetBucketLifecycleOutput struct { _ struct{} `type:"structure"` @@ -10696,7 +10609,6 @@ func (s *GetBucketLifecycleOutput) SetRules(v []*Rule) *GetBucketLifecycleOutput return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketLocationRequest type GetBucketLocationInput struct { _ struct{} `type:"structure"` @@ -10740,7 +10652,6 @@ func (s *GetBucketLocationInput) getBucket() (v string) { return *s.Bucket } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketLocationOutput type GetBucketLocationOutput struct { _ struct{} `type:"structure"` @@ -10763,7 +10674,6 @@ func (s *GetBucketLocationOutput) SetLocationConstraint(v string) *GetBucketLoca return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketLoggingRequest type GetBucketLoggingInput struct { _ struct{} `type:"structure"` @@ -10807,7 +10717,6 @@ func (s *GetBucketLoggingInput) getBucket() (v string) { return *s.Bucket } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketLoggingOutput type GetBucketLoggingOutput struct { _ struct{} `type:"structure"` @@ -10830,7 +10739,6 @@ func (s *GetBucketLoggingOutput) SetLoggingEnabled(v *LoggingEnabled) *GetBucket return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketMetricsConfigurationRequest type GetBucketMetricsConfigurationInput struct { _ struct{} `type:"structure"` @@ -10890,7 +10798,6 @@ func (s *GetBucketMetricsConfigurationInput) SetId(v string) *GetBucketMetricsCo return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketMetricsConfigurationOutput type GetBucketMetricsConfigurationOutput struct { _ struct{} `type:"structure" payload:"MetricsConfiguration"` @@ -10914,7 +10821,6 @@ func (s *GetBucketMetricsConfigurationOutput) SetMetricsConfiguration(v *Metrics return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketNotificationConfigurationRequest type GetBucketNotificationConfigurationRequest struct { _ struct{} `type:"structure"` @@ -10960,7 +10866,6 @@ func (s *GetBucketNotificationConfigurationRequest) getBucket() (v string) { return *s.Bucket } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketPolicyRequest type GetBucketPolicyInput struct { _ struct{} `type:"structure"` @@ -11004,7 +10909,6 @@ func (s *GetBucketPolicyInput) getBucket() (v string) { return *s.Bucket } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketPolicyOutput type GetBucketPolicyOutput struct { _ struct{} `type:"structure" payload:"Policy"` @@ -11028,7 +10932,6 @@ func (s *GetBucketPolicyOutput) SetPolicy(v string) *GetBucketPolicyOutput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketReplicationRequest type GetBucketReplicationInput struct { _ struct{} `type:"structure"` @@ -11072,7 +10975,6 @@ func (s *GetBucketReplicationInput) getBucket() (v string) { return *s.Bucket } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketReplicationOutput type GetBucketReplicationOutput struct { _ struct{} `type:"structure" payload:"ReplicationConfiguration"` @@ -11097,7 +10999,6 @@ func (s *GetBucketReplicationOutput) SetReplicationConfiguration(v *ReplicationC return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketRequestPaymentRequest type GetBucketRequestPaymentInput struct { _ struct{} `type:"structure"` @@ -11141,7 +11042,6 @@ func (s *GetBucketRequestPaymentInput) getBucket() (v string) { return *s.Bucket } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketRequestPaymentOutput type GetBucketRequestPaymentOutput struct { _ struct{} `type:"structure"` @@ -11165,7 +11065,6 @@ func (s *GetBucketRequestPaymentOutput) SetPayer(v string) *GetBucketRequestPaym return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketTaggingRequest type GetBucketTaggingInput struct { _ struct{} `type:"structure"` @@ -11209,7 +11108,6 @@ func (s *GetBucketTaggingInput) getBucket() (v string) { return *s.Bucket } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketTaggingOutput type GetBucketTaggingOutput struct { _ struct{} `type:"structure"` @@ -11233,7 +11131,6 @@ func (s *GetBucketTaggingOutput) SetTagSet(v []*Tag) *GetBucketTaggingOutput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketVersioningRequest type GetBucketVersioningInput struct { _ struct{} `type:"structure"` @@ -11277,7 +11174,6 @@ func (s *GetBucketVersioningInput) getBucket() (v string) { return *s.Bucket } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketVersioningOutput type GetBucketVersioningOutput struct { _ struct{} `type:"structure"` @@ -11312,7 +11208,6 @@ func (s *GetBucketVersioningOutput) SetStatus(v string) *GetBucketVersioningOutp return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketWebsiteRequest type GetBucketWebsiteInput struct { _ struct{} `type:"structure"` @@ -11356,7 +11251,6 @@ func (s *GetBucketWebsiteInput) getBucket() (v string) { return *s.Bucket } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetBucketWebsiteOutput type GetBucketWebsiteOutput struct { _ struct{} `type:"structure"` @@ -11403,7 +11297,6 @@ func (s *GetBucketWebsiteOutput) SetRoutingRules(v []*RoutingRule) *GetBucketWeb return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetObjectAclRequest type GetObjectAclInput struct { _ struct{} `type:"structure"` @@ -11483,7 +11376,6 @@ func (s *GetObjectAclInput) SetVersionId(v string) *GetObjectAclInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetObjectAclOutput type GetObjectAclOutput struct { _ struct{} `type:"structure"` @@ -11525,7 +11417,6 @@ func (s *GetObjectAclOutput) SetRequestCharged(v string) *GetObjectAclOutput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetObjectRequest type GetObjectInput struct { _ struct{} `type:"structure"` @@ -11760,7 +11651,6 @@ func (s *GetObjectInput) SetVersionId(v string) *GetObjectInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetObjectOutput type GetObjectOutput struct { _ struct{} `type:"structure" payload:"Body"` @@ -12044,7 +11934,6 @@ func (s *GetObjectOutput) SetWebsiteRedirectLocation(v string) *GetObjectOutput return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetObjectTaggingRequest type GetObjectTaggingInput struct { _ struct{} `type:"structure"` @@ -12111,7 +12000,6 @@ func (s *GetObjectTaggingInput) SetVersionId(v string) *GetObjectTaggingInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetObjectTaggingOutput type GetObjectTaggingOutput struct { _ struct{} `type:"structure"` @@ -12143,7 +12031,6 @@ func (s *GetObjectTaggingOutput) SetVersionId(v string) *GetObjectTaggingOutput return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetObjectTorrentRequest type GetObjectTorrentInput struct { _ struct{} `type:"structure"` @@ -12214,7 +12101,6 @@ func (s *GetObjectTorrentInput) SetRequestPayer(v string) *GetObjectTorrentInput return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetObjectTorrentOutput type GetObjectTorrentOutput struct { _ struct{} `type:"structure" payload:"Body"` @@ -12247,7 +12133,6 @@ func (s *GetObjectTorrentOutput) SetRequestCharged(v string) *GetObjectTorrentOu return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GlacierJobParameters type GlacierJobParameters struct { _ struct{} `type:"structure"` @@ -12286,7 +12171,6 @@ func (s *GlacierJobParameters) SetTier(v string) *GlacierJobParameters { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/Grant type Grant struct { _ struct{} `type:"structure"` @@ -12333,7 +12217,6 @@ func (s *Grant) SetPermission(v string) *Grant { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/Grantee type Grantee struct { _ struct{} `type:"structure" xmlPrefix:"xsi" xmlURI:"http://www.w3.org/2001/XMLSchema-instance"` @@ -12408,7 +12291,6 @@ func (s *Grantee) SetURI(v string) *Grantee { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/HeadBucketRequest type HeadBucketInput struct { _ struct{} `type:"structure"` @@ -12452,7 +12334,6 @@ func (s *HeadBucketInput) getBucket() (v string) { return *s.Bucket } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/HeadBucketOutput type HeadBucketOutput struct { _ struct{} `type:"structure"` } @@ -12467,7 +12348,6 @@ func (s HeadBucketOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/HeadObjectRequest type HeadObjectInput struct { _ struct{} `type:"structure"` @@ -12649,7 +12529,6 @@ func (s *HeadObjectInput) SetVersionId(v string) *HeadObjectInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/HeadObjectOutput type HeadObjectOutput struct { _ struct{} `type:"structure"` @@ -12906,7 +12785,6 @@ func (s *HeadObjectOutput) SetWebsiteRedirectLocation(v string) *HeadObjectOutpu return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/IndexDocument type IndexDocument struct { _ struct{} `type:"structure"` @@ -12948,7 +12826,6 @@ func (s *IndexDocument) SetSuffix(v string) *IndexDocument { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/Initiator type Initiator struct { _ struct{} `type:"structure"` @@ -12983,7 +12860,6 @@ func (s *Initiator) SetID(v string) *Initiator { } // Describes the serialization format of the object. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/InputSerialization type InputSerialization struct { _ struct{} `type:"structure"` @@ -13007,7 +12883,6 @@ func (s *InputSerialization) SetCSV(v *CSVInput) *InputSerialization { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/InventoryConfiguration type InventoryConfiguration struct { _ struct{} `type:"structure"` @@ -13136,7 +13011,6 @@ func (s *InventoryConfiguration) SetSchedule(v *InventorySchedule) *InventoryCon return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/InventoryDestination type InventoryDestination struct { _ struct{} `type:"structure"` @@ -13183,7 +13057,6 @@ func (s *InventoryDestination) SetS3BucketDestination(v *InventoryS3BucketDestin // Contains the type of server-side encryption used to encrypt the inventory // results. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/InventoryEncryption type InventoryEncryption struct { _ struct{} `type:"structure"` @@ -13231,7 +13104,6 @@ func (s *InventoryEncryption) SetSSES3(v *SSES3) *InventoryEncryption { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/InventoryFilter type InventoryFilter struct { _ struct{} `type:"structure"` @@ -13270,7 +13142,6 @@ func (s *InventoryFilter) SetPrefix(v string) *InventoryFilter { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/InventoryS3BucketDestination type InventoryS3BucketDestination struct { _ struct{} `type:"structure"` @@ -13364,7 +13235,6 @@ func (s *InventoryS3BucketDestination) SetPrefix(v string) *InventoryS3BucketDes return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/InventorySchedule type InventorySchedule struct { _ struct{} `type:"structure"` @@ -13404,7 +13274,6 @@ func (s *InventorySchedule) SetFrequency(v string) *InventorySchedule { } // Container for object key name prefix and suffix filtering rules. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/S3KeyFilter type KeyFilter struct { _ struct{} `type:"structure"` @@ -13430,7 +13299,6 @@ func (s *KeyFilter) SetFilterRules(v []*FilterRule) *KeyFilter { } // Container for specifying the AWS Lambda notification configuration. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/LambdaFunctionConfiguration type LambdaFunctionConfiguration struct { _ struct{} `type:"structure"` @@ -13502,7 +13370,6 @@ func (s *LambdaFunctionConfiguration) SetLambdaFunctionArn(v string) *LambdaFunc return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/LifecycleConfiguration type LifecycleConfiguration struct { _ struct{} `type:"structure"` @@ -13549,7 +13416,6 @@ func (s *LifecycleConfiguration) SetRules(v []*Rule) *LifecycleConfiguration { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/LifecycleExpiration type LifecycleExpiration struct { _ struct{} `type:"structure"` @@ -13596,7 +13462,6 @@ func (s *LifecycleExpiration) SetExpiredObjectDeleteMarker(v bool) *LifecycleExp return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/LifecycleRule type LifecycleRule struct { _ struct{} `type:"structure"` @@ -13720,7 +13585,6 @@ func (s *LifecycleRule) SetTransitions(v []*Transition) *LifecycleRule { // This is used in a Lifecycle Rule Filter to apply a logical AND to two or // more predicates. The Lifecycle Rule will apply to any object matching all // of the predicates configured inside the And operator. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/LifecycleRuleAndOperator type LifecycleRuleAndOperator struct { _ struct{} `type:"structure"` @@ -13775,7 +13639,6 @@ func (s *LifecycleRuleAndOperator) SetTags(v []*Tag) *LifecycleRuleAndOperator { // The Filter is used to identify objects that a Lifecycle Rule applies to. // A Filter must have exactly one of Prefix, Tag, or And specified. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/LifecycleRuleFilter type LifecycleRuleFilter struct { _ struct{} `type:"structure"` @@ -13839,7 +13702,6 @@ func (s *LifecycleRuleFilter) SetTag(v *Tag) *LifecycleRuleFilter { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/ListBucketAnalyticsConfigurationsRequest type ListBucketAnalyticsConfigurationsInput struct { _ struct{} `type:"structure"` @@ -13895,7 +13757,6 @@ func (s *ListBucketAnalyticsConfigurationsInput) SetContinuationToken(v string) return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/ListBucketAnalyticsConfigurationsOutput type ListBucketAnalyticsConfigurationsOutput struct { _ struct{} `type:"structure"` @@ -13950,7 +13811,6 @@ func (s *ListBucketAnalyticsConfigurationsOutput) SetNextContinuationToken(v str return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/ListBucketInventoryConfigurationsRequest type ListBucketInventoryConfigurationsInput struct { _ struct{} `type:"structure"` @@ -14008,7 +13868,6 @@ func (s *ListBucketInventoryConfigurationsInput) SetContinuationToken(v string) return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/ListBucketInventoryConfigurationsOutput type ListBucketInventoryConfigurationsOutput struct { _ struct{} `type:"structure"` @@ -14063,7 +13922,6 @@ func (s *ListBucketInventoryConfigurationsOutput) SetNextContinuationToken(v str return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/ListBucketMetricsConfigurationsRequest type ListBucketMetricsConfigurationsInput struct { _ struct{} `type:"structure"` @@ -14121,7 +13979,6 @@ func (s *ListBucketMetricsConfigurationsInput) SetContinuationToken(v string) *L return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/ListBucketMetricsConfigurationsOutput type ListBucketMetricsConfigurationsOutput struct { _ struct{} `type:"structure"` @@ -14178,7 +14035,6 @@ func (s *ListBucketMetricsConfigurationsOutput) SetNextContinuationToken(v strin return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/ListBucketsInput type ListBucketsInput struct { _ struct{} `type:"structure"` } @@ -14193,7 +14049,6 @@ func (s ListBucketsInput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/ListBucketsOutput type ListBucketsOutput struct { _ struct{} `type:"structure"` @@ -14224,7 +14079,6 @@ func (s *ListBucketsOutput) SetOwner(v *Owner) *ListBucketsOutput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/ListMultipartUploadsRequest type ListMultipartUploadsInput struct { _ struct{} `type:"structure"` @@ -14333,7 +14187,6 @@ func (s *ListMultipartUploadsInput) SetUploadIdMarker(v string) *ListMultipartUp return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/ListMultipartUploadsOutput type ListMultipartUploadsOutput struct { _ struct{} `type:"structure"` @@ -14467,7 +14320,6 @@ func (s *ListMultipartUploadsOutput) SetUploads(v []*MultipartUpload) *ListMulti return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/ListObjectVersionsRequest type ListObjectVersionsInput struct { _ struct{} `type:"structure"` @@ -14571,7 +14423,6 @@ func (s *ListObjectVersionsInput) SetVersionIdMarker(v string) *ListObjectVersio return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/ListObjectVersionsOutput type ListObjectVersionsOutput struct { _ struct{} `type:"structure"` @@ -14699,7 +14550,6 @@ func (s *ListObjectVersionsOutput) SetVersions(v []*ObjectVersion) *ListObjectVe return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/ListObjectsRequest type ListObjectsInput struct { _ struct{} `type:"structure"` @@ -14805,7 +14655,6 @@ func (s *ListObjectsInput) SetRequestPayer(v string) *ListObjectsInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/ListObjectsOutput type ListObjectsOutput struct { _ struct{} `type:"structure"` @@ -14910,7 +14759,6 @@ func (s *ListObjectsOutput) SetPrefix(v string) *ListObjectsOutput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/ListObjectsV2Request type ListObjectsV2Input struct { _ struct{} `type:"structure"` @@ -15036,7 +14884,6 @@ func (s *ListObjectsV2Input) SetStartAfter(v string) *ListObjectsV2Input { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/ListObjectsV2Output type ListObjectsV2Output struct { _ struct{} `type:"structure"` @@ -15170,7 +15017,6 @@ func (s *ListObjectsV2Output) SetStartAfter(v string) *ListObjectsV2Output { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/ListPartsRequest type ListPartsInput struct { _ struct{} `type:"structure"` @@ -15274,7 +15120,6 @@ func (s *ListPartsInput) SetUploadId(v string) *ListPartsInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/ListPartsOutput type ListPartsOutput struct { _ struct{} `type:"structure"` @@ -15425,7 +15270,6 @@ func (s *ListPartsOutput) SetUploadId(v string) *ListPartsOutput { } // Describes an S3 location that will receive the results of the restore request. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/S3Location type Location struct { _ struct{} `type:"structure"` @@ -15553,7 +15397,6 @@ func (s *Location) SetUserMetadata(v []*MetadataEntry) *Location { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/LoggingEnabled type LoggingEnabled struct { _ struct{} `type:"structure"` @@ -15621,7 +15464,6 @@ func (s *LoggingEnabled) SetTargetPrefix(v string) *LoggingEnabled { } // A metadata key-value pair to store with an object. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/MetadataEntry type MetadataEntry struct { _ struct{} `type:"structure"` @@ -15652,7 +15494,6 @@ func (s *MetadataEntry) SetValue(v string) *MetadataEntry { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/MetricsAndOperator type MetricsAndOperator struct { _ struct{} `type:"structure"` @@ -15705,7 +15546,6 @@ func (s *MetricsAndOperator) SetTags(v []*Tag) *MetricsAndOperator { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/MetricsConfiguration type MetricsConfiguration struct { _ struct{} `type:"structure"` @@ -15760,7 +15600,6 @@ func (s *MetricsConfiguration) SetId(v string) *MetricsConfiguration { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/MetricsFilter type MetricsFilter struct { _ struct{} `type:"structure"` @@ -15824,7 +15663,6 @@ func (s *MetricsFilter) SetTag(v *Tag) *MetricsFilter { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/MultipartUpload type MultipartUpload struct { _ struct{} `type:"structure"` @@ -15897,7 +15735,6 @@ func (s *MultipartUpload) SetUploadId(v string) *MultipartUpload { // configuration action on a bucket that has versioning enabled (or suspended) // to request that Amazon S3 delete noncurrent object versions at a specific // period in the object's lifetime. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/NoncurrentVersionExpiration type NoncurrentVersionExpiration struct { _ struct{} `type:"structure"` @@ -15929,7 +15766,6 @@ func (s *NoncurrentVersionExpiration) SetNoncurrentDays(v int64) *NoncurrentVers // versioning-enabled (or versioning is suspended), you can set this action // to request that Amazon S3 transition noncurrent object versions to the STANDARD_IA // or GLACIER storage class at a specific period in the object's lifetime. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/NoncurrentVersionTransition type NoncurrentVersionTransition struct { _ struct{} `type:"structure"` @@ -15967,7 +15803,6 @@ func (s *NoncurrentVersionTransition) SetStorageClass(v string) *NoncurrentVersi // Container for specifying the notification configuration of the bucket. If // this element is empty, notifications are turned off on the bucket. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/NotificationConfiguration type NotificationConfiguration struct { _ struct{} `type:"structure"` @@ -16046,7 +15881,6 @@ func (s *NotificationConfiguration) SetTopicConfigurations(v []*TopicConfigurati return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/NotificationConfigurationDeprecated type NotificationConfigurationDeprecated struct { _ struct{} `type:"structure"` @@ -16087,7 +15921,6 @@ func (s *NotificationConfigurationDeprecated) SetTopicConfiguration(v *TopicConf // Container for object key name filtering rules. For information about key // name filtering, go to Configuring Event Notifications (http://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html) -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/NotificationConfigurationFilter type NotificationConfigurationFilter struct { _ struct{} `type:"structure"` @@ -16111,7 +15944,6 @@ func (s *NotificationConfigurationFilter) SetKey(v *KeyFilter) *NotificationConf return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/Object type Object struct { _ struct{} `type:"structure"` @@ -16175,7 +16007,6 @@ func (s *Object) SetStorageClass(v string) *Object { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/ObjectIdentifier type ObjectIdentifier struct { _ struct{} `type:"structure"` @@ -16226,7 +16057,6 @@ func (s *ObjectIdentifier) SetVersionId(v string) *ObjectIdentifier { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/ObjectVersion type ObjectVersion struct { _ struct{} `type:"structure"` @@ -16313,7 +16143,6 @@ func (s *ObjectVersion) SetVersionId(v string) *ObjectVersion { } // Describes the location where the restore job's output is stored. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/OutputLocation type OutputLocation struct { _ struct{} `type:"structure"` @@ -16353,7 +16182,6 @@ func (s *OutputLocation) SetS3(v *Location) *OutputLocation { } // Describes how results of the Select job are serialized. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/OutputSerialization type OutputSerialization struct { _ struct{} `type:"structure"` @@ -16377,7 +16205,6 @@ func (s *OutputSerialization) SetCSV(v *CSVOutput) *OutputSerialization { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/Owner type Owner struct { _ struct{} `type:"structure"` @@ -16408,7 +16235,6 @@ func (s *Owner) SetID(v string) *Owner { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/Part type Part struct { _ struct{} `type:"structure"` @@ -16460,7 +16286,6 @@ func (s *Part) SetSize(v int64) *Part { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketAccelerateConfigurationRequest type PutBucketAccelerateConfigurationInput struct { _ struct{} `type:"structure" payload:"AccelerateConfiguration"` @@ -16520,7 +16345,6 @@ func (s *PutBucketAccelerateConfigurationInput) getBucket() (v string) { return *s.Bucket } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketAccelerateConfigurationOutput type PutBucketAccelerateConfigurationOutput struct { _ struct{} `type:"structure"` } @@ -16535,7 +16359,6 @@ func (s PutBucketAccelerateConfigurationOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketAclRequest type PutBucketAclInput struct { _ struct{} `type:"structure" payload:"AccessControlPolicy"` @@ -16647,7 +16470,6 @@ func (s *PutBucketAclInput) SetGrantWriteACP(v string) *PutBucketAclInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketAclOutput type PutBucketAclOutput struct { _ struct{} `type:"structure"` } @@ -16662,7 +16484,6 @@ func (s PutBucketAclOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketAnalyticsConfigurationRequest type PutBucketAnalyticsConfigurationInput struct { _ struct{} `type:"structure" payload:"AnalyticsConfiguration"` @@ -16741,7 +16562,6 @@ func (s *PutBucketAnalyticsConfigurationInput) SetId(v string) *PutBucketAnalyti return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketAnalyticsConfigurationOutput type PutBucketAnalyticsConfigurationOutput struct { _ struct{} `type:"structure"` } @@ -16756,7 +16576,6 @@ func (s PutBucketAnalyticsConfigurationOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketCorsRequest type PutBucketCorsInput struct { _ struct{} `type:"structure" payload:"CORSConfiguration"` @@ -16817,7 +16636,6 @@ func (s *PutBucketCorsInput) SetCORSConfiguration(v *CORSConfiguration) *PutBuck return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketCorsOutput type PutBucketCorsOutput struct { _ struct{} `type:"structure"` } @@ -16832,7 +16650,6 @@ func (s PutBucketCorsOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketEncryptionRequest type PutBucketEncryptionInput struct { _ struct{} `type:"structure" payload:"ServerSideEncryptionConfiguration"` @@ -16899,7 +16716,6 @@ func (s *PutBucketEncryptionInput) SetServerSideEncryptionConfiguration(v *Serve return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketEncryptionOutput type PutBucketEncryptionOutput struct { _ struct{} `type:"structure"` } @@ -16914,7 +16730,6 @@ func (s PutBucketEncryptionOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketInventoryConfigurationRequest type PutBucketInventoryConfigurationInput struct { _ struct{} `type:"structure" payload:"InventoryConfiguration"` @@ -16993,7 +16808,6 @@ func (s *PutBucketInventoryConfigurationInput) SetInventoryConfiguration(v *Inve return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketInventoryConfigurationOutput type PutBucketInventoryConfigurationOutput struct { _ struct{} `type:"structure"` } @@ -17008,7 +16822,6 @@ func (s PutBucketInventoryConfigurationOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketLifecycleConfigurationRequest type PutBucketLifecycleConfigurationInput struct { _ struct{} `type:"structure" payload:"LifecycleConfiguration"` @@ -17065,7 +16878,6 @@ func (s *PutBucketLifecycleConfigurationInput) SetLifecycleConfiguration(v *Buck return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketLifecycleConfigurationOutput type PutBucketLifecycleConfigurationOutput struct { _ struct{} `type:"structure"` } @@ -17080,7 +16892,6 @@ func (s PutBucketLifecycleConfigurationOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketLifecycleRequest type PutBucketLifecycleInput struct { _ struct{} `type:"structure" payload:"LifecycleConfiguration"` @@ -17137,7 +16948,6 @@ func (s *PutBucketLifecycleInput) SetLifecycleConfiguration(v *LifecycleConfigur return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketLifecycleOutput type PutBucketLifecycleOutput struct { _ struct{} `type:"structure"` } @@ -17152,7 +16962,6 @@ func (s PutBucketLifecycleOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketLoggingRequest type PutBucketLoggingInput struct { _ struct{} `type:"structure" payload:"BucketLoggingStatus"` @@ -17213,7 +17022,6 @@ func (s *PutBucketLoggingInput) SetBucketLoggingStatus(v *BucketLoggingStatus) * return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketLoggingOutput type PutBucketLoggingOutput struct { _ struct{} `type:"structure"` } @@ -17228,7 +17036,6 @@ func (s PutBucketLoggingOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketMetricsConfigurationRequest type PutBucketMetricsConfigurationInput struct { _ struct{} `type:"structure" payload:"MetricsConfiguration"` @@ -17307,7 +17114,6 @@ func (s *PutBucketMetricsConfigurationInput) SetMetricsConfiguration(v *MetricsC return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketMetricsConfigurationOutput type PutBucketMetricsConfigurationOutput struct { _ struct{} `type:"structure"` } @@ -17322,7 +17128,6 @@ func (s PutBucketMetricsConfigurationOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketNotificationConfigurationRequest type PutBucketNotificationConfigurationInput struct { _ struct{} `type:"structure" payload:"NotificationConfiguration"` @@ -17386,7 +17191,6 @@ func (s *PutBucketNotificationConfigurationInput) SetNotificationConfiguration(v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketNotificationConfigurationOutput type PutBucketNotificationConfigurationOutput struct { _ struct{} `type:"structure"` } @@ -17401,7 +17205,6 @@ func (s PutBucketNotificationConfigurationOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketNotificationRequest type PutBucketNotificationInput struct { _ struct{} `type:"structure" payload:"NotificationConfiguration"` @@ -17457,7 +17260,6 @@ func (s *PutBucketNotificationInput) SetNotificationConfiguration(v *Notificatio return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketNotificationOutput type PutBucketNotificationOutput struct { _ struct{} `type:"structure"` } @@ -17472,7 +17274,6 @@ func (s PutBucketNotificationOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketPolicyRequest type PutBucketPolicyInput struct { _ struct{} `type:"structure" payload:"Policy"` @@ -17540,7 +17341,6 @@ func (s *PutBucketPolicyInput) SetPolicy(v string) *PutBucketPolicyInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketPolicyOutput type PutBucketPolicyOutput struct { _ struct{} `type:"structure"` } @@ -17555,7 +17355,6 @@ func (s PutBucketPolicyOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketReplicationRequest type PutBucketReplicationInput struct { _ struct{} `type:"structure" payload:"ReplicationConfiguration"` @@ -17619,7 +17418,6 @@ func (s *PutBucketReplicationInput) SetReplicationConfiguration(v *ReplicationCo return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketReplicationOutput type PutBucketReplicationOutput struct { _ struct{} `type:"structure"` } @@ -17634,7 +17432,6 @@ func (s PutBucketReplicationOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketRequestPaymentRequest type PutBucketRequestPaymentInput struct { _ struct{} `type:"structure" payload:"RequestPaymentConfiguration"` @@ -17695,7 +17492,6 @@ func (s *PutBucketRequestPaymentInput) SetRequestPaymentConfiguration(v *Request return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketRequestPaymentOutput type PutBucketRequestPaymentOutput struct { _ struct{} `type:"structure"` } @@ -17710,7 +17506,6 @@ func (s PutBucketRequestPaymentOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketTaggingRequest type PutBucketTaggingInput struct { _ struct{} `type:"structure" payload:"Tagging"` @@ -17771,7 +17566,6 @@ func (s *PutBucketTaggingInput) SetTagging(v *Tagging) *PutBucketTaggingInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketTaggingOutput type PutBucketTaggingOutput struct { _ struct{} `type:"structure"` } @@ -17786,7 +17580,6 @@ func (s PutBucketTaggingOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketVersioningRequest type PutBucketVersioningInput struct { _ struct{} `type:"structure" payload:"VersioningConfiguration"` @@ -17852,7 +17645,6 @@ func (s *PutBucketVersioningInput) SetVersioningConfiguration(v *VersioningConfi return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketVersioningOutput type PutBucketVersioningOutput struct { _ struct{} `type:"structure"` } @@ -17867,7 +17659,6 @@ func (s PutBucketVersioningOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketWebsiteRequest type PutBucketWebsiteInput struct { _ struct{} `type:"structure" payload:"WebsiteConfiguration"` @@ -17928,7 +17719,6 @@ func (s *PutBucketWebsiteInput) SetWebsiteConfiguration(v *WebsiteConfiguration) return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutBucketWebsiteOutput type PutBucketWebsiteOutput struct { _ struct{} `type:"structure"` } @@ -17943,7 +17733,6 @@ func (s PutBucketWebsiteOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutObjectAclRequest type PutObjectAclInput struct { _ struct{} `type:"structure" payload:"AccessControlPolicy"` @@ -18091,7 +17880,6 @@ func (s *PutObjectAclInput) SetVersionId(v string) *PutObjectAclInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutObjectAclOutput type PutObjectAclOutput struct { _ struct{} `type:"structure"` @@ -18116,7 +17904,6 @@ func (s *PutObjectAclOutput) SetRequestCharged(v string) *PutObjectAclOutput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutObjectRequest type PutObjectInput struct { _ struct{} `type:"structure" payload:"Body"` @@ -18420,7 +18207,6 @@ func (s *PutObjectInput) SetWebsiteRedirectLocation(v string) *PutObjectInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutObjectOutput type PutObjectOutput struct { _ struct{} `type:"structure"` @@ -18515,7 +18301,6 @@ func (s *PutObjectOutput) SetVersionId(v string) *PutObjectOutput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutObjectTaggingRequest type PutObjectTaggingInput struct { _ struct{} `type:"structure" payload:"Tagging"` @@ -18599,7 +18384,6 @@ func (s *PutObjectTaggingInput) SetVersionId(v string) *PutObjectTaggingInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutObjectTaggingOutput type PutObjectTaggingOutput struct { _ struct{} `type:"structure"` @@ -18624,7 +18408,6 @@ func (s *PutObjectTaggingOutput) SetVersionId(v string) *PutObjectTaggingOutput // Container for specifying an configuration when you want Amazon S3 to publish // events to an Amazon Simple Queue Service (Amazon SQS) queue. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/QueueConfiguration type QueueConfiguration struct { _ struct{} `type:"structure"` @@ -18696,7 +18479,6 @@ func (s *QueueConfiguration) SetQueueArn(v string) *QueueConfiguration { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/QueueConfigurationDeprecated type QueueConfigurationDeprecated struct { _ struct{} `type:"structure"` @@ -18746,7 +18528,6 @@ func (s *QueueConfigurationDeprecated) SetQueue(v string) *QueueConfigurationDep return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/Redirect type Redirect struct { _ struct{} `type:"structure"` @@ -18815,7 +18596,6 @@ func (s *Redirect) SetReplaceKeyWith(v string) *Redirect { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/RedirectAllRequestsTo type RedirectAllRequestsTo struct { _ struct{} `type:"structure"` @@ -18866,7 +18646,6 @@ func (s *RedirectAllRequestsTo) SetProtocol(v string) *RedirectAllRequestsTo { // Container for replication rules. You can add as many as 1,000 rules. Total // replication configuration size can be up to 2 MB. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/ReplicationConfiguration type ReplicationConfiguration struct { _ struct{} `type:"structure"` @@ -18932,7 +18711,6 @@ func (s *ReplicationConfiguration) SetRules(v []*ReplicationRule) *ReplicationCo } // Container for information about a particular replication rule. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/ReplicationRule type ReplicationRule struct { _ struct{} `type:"structure"` @@ -19029,7 +18807,6 @@ func (s *ReplicationRule) SetStatus(v string) *ReplicationRule { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/RequestPaymentConfiguration type RequestPaymentConfiguration struct { _ struct{} `type:"structure"` @@ -19068,7 +18845,6 @@ func (s *RequestPaymentConfiguration) SetPayer(v string) *RequestPaymentConfigur return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/RestoreObjectRequest type RestoreObjectInput struct { _ struct{} `type:"structure" payload:"RestoreRequest"` @@ -19161,7 +18937,6 @@ func (s *RestoreObjectInput) SetVersionId(v string) *RestoreObjectInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/RestoreObjectOutput type RestoreObjectOutput struct { _ struct{} `type:"structure"` @@ -19197,7 +18972,6 @@ func (s *RestoreObjectOutput) SetRestoreOutputPath(v string) *RestoreObjectOutpu } // Container for restore job parameters. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/RestoreRequest type RestoreRequest struct { _ struct{} `type:"structure"` @@ -19302,7 +19076,6 @@ func (s *RestoreRequest) SetType(v string) *RestoreRequest { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/RoutingRule type RoutingRule struct { _ struct{} `type:"structure"` @@ -19355,7 +19128,6 @@ func (s *RoutingRule) SetRedirect(v *Redirect) *RoutingRule { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/Rule type Rule struct { _ struct{} `type:"structure"` @@ -19471,7 +19243,6 @@ func (s *Rule) SetTransition(v *Transition) *Rule { } // Specifies the use of SSE-KMS to encrypt delievered Inventory reports. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/SSEKMS type SSEKMS struct { _ struct{} `locationName:"SSE-KMS" type:"structure"` @@ -19512,7 +19283,6 @@ func (s *SSEKMS) SetKeyId(v string) *SSEKMS { } // Specifies the use of SSE-S3 to encrypt delievered Inventory reports. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/SSES3 type SSES3 struct { _ struct{} `locationName:"SSE-S3" type:"structure"` } @@ -19528,7 +19298,6 @@ func (s SSES3) GoString() string { } // Describes the parameters for Select job types. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/SelectParameters type SelectParameters struct { _ struct{} `type:"structure"` @@ -19612,7 +19381,6 @@ func (s *SelectParameters) SetOutputSerialization(v *OutputSerialization) *Selec // Describes the default server-side encryption to apply to new objects in the // bucket. If Put Object request does not specify any server-side encryption, // this default encryption will be applied. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/ServerSideEncryptionByDefault type ServerSideEncryptionByDefault struct { _ struct{} `type:"structure"` @@ -19663,7 +19431,6 @@ func (s *ServerSideEncryptionByDefault) SetSSEAlgorithm(v string) *ServerSideEnc // Container for server-side encryption configuration rules. Currently S3 supports // one rule only. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/ServerSideEncryptionConfiguration type ServerSideEncryptionConfiguration struct { _ struct{} `type:"structure"` @@ -19715,7 +19482,6 @@ func (s *ServerSideEncryptionConfiguration) SetRules(v []*ServerSideEncryptionRu // Container for information about a particular server-side encryption configuration // rule. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/ServerSideEncryptionRule type ServerSideEncryptionRule struct { _ struct{} `type:"structure"` @@ -19757,7 +19523,6 @@ func (s *ServerSideEncryptionRule) SetApplyServerSideEncryptionByDefault(v *Serv } // Container for filters that define which source objects should be replicated. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/SourceSelectionCriteria type SourceSelectionCriteria struct { _ struct{} `type:"structure"` @@ -19797,7 +19562,6 @@ func (s *SourceSelectionCriteria) SetSseKmsEncryptedObjects(v *SseKmsEncryptedOb } // Container for filter information of selection of KMS Encrypted S3 objects. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/SseKmsEncryptedObjects type SseKmsEncryptedObjects struct { _ struct{} `type:"structure"` @@ -19837,7 +19601,6 @@ func (s *SseKmsEncryptedObjects) SetStatus(v string) *SseKmsEncryptedObjects { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/StorageClassAnalysis type StorageClassAnalysis struct { _ struct{} `type:"structure"` @@ -19877,7 +19640,6 @@ func (s *StorageClassAnalysis) SetDataExport(v *StorageClassAnalysisDataExport) return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/StorageClassAnalysisDataExport type StorageClassAnalysisDataExport struct { _ struct{} `type:"structure"` @@ -19935,7 +19697,6 @@ func (s *StorageClassAnalysisDataExport) SetOutputSchemaVersion(v string) *Stora return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/Tag type Tag struct { _ struct{} `type:"structure"` @@ -19991,7 +19752,6 @@ func (s *Tag) SetValue(v string) *Tag { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/Tagging type Tagging struct { _ struct{} `type:"structure"` @@ -20038,7 +19798,6 @@ func (s *Tagging) SetTagSet(v []*Tag) *Tagging { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/TargetGrant type TargetGrant struct { _ struct{} `type:"structure"` @@ -20087,7 +19846,6 @@ func (s *TargetGrant) SetPermission(v string) *TargetGrant { // Container for specifying the configuration when you want Amazon S3 to publish // events to an Amazon Simple Notification Service (Amazon SNS) topic. -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/TopicConfiguration type TopicConfiguration struct { _ struct{} `type:"structure"` @@ -20159,7 +19917,6 @@ func (s *TopicConfiguration) SetTopicArn(v string) *TopicConfiguration { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/TopicConfigurationDeprecated type TopicConfigurationDeprecated struct { _ struct{} `type:"structure"` @@ -20211,7 +19968,6 @@ func (s *TopicConfigurationDeprecated) SetTopic(v string) *TopicConfigurationDep return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/Transition type Transition struct { _ struct{} `type:"structure"` @@ -20255,7 +20011,6 @@ func (s *Transition) SetStorageClass(v string) *Transition { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/UploadPartCopyRequest type UploadPartCopyInput struct { _ struct{} `type:"structure"` @@ -20499,7 +20254,6 @@ func (s *UploadPartCopyInput) SetUploadId(v string) *UploadPartCopyInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/UploadPartCopyOutput type UploadPartCopyOutput struct { _ struct{} `type:"structure" payload:"CopyPartResult"` @@ -20584,7 +20338,6 @@ func (s *UploadPartCopyOutput) SetServerSideEncryption(v string) *UploadPartCopy return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/UploadPartRequest type UploadPartInput struct { _ struct{} `type:"structure" payload:"Body"` @@ -20757,7 +20510,6 @@ func (s *UploadPartInput) SetUploadId(v string) *UploadPartInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/UploadPartOutput type UploadPartOutput struct { _ struct{} `type:"structure"` @@ -20833,7 +20585,6 @@ func (s *UploadPartOutput) SetServerSideEncryption(v string) *UploadPartOutput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/VersioningConfiguration type VersioningConfiguration struct { _ struct{} `type:"structure"` @@ -20868,7 +20619,6 @@ func (s *VersioningConfiguration) SetStatus(v string) *VersioningConfiguration { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/WebsiteConfiguration type WebsiteConfiguration struct { _ struct{} `type:"structure"` diff --git a/vendor/github.com/aws/aws-sdk-go/service/sts/api.go b/vendor/github.com/aws/aws-sdk-go/service/sts/api.go index 23f0a06db..22a0a1285 100644 --- a/vendor/github.com/aws/aws-sdk-go/service/sts/api.go +++ b/vendor/github.com/aws/aws-sdk-go/service/sts/api.go @@ -1049,7 +1049,6 @@ func (c *STS) GetSessionTokenWithContext(ctx aws.Context, input *GetSessionToken return out, req.Send() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/AssumeRoleRequest type AssumeRoleInput struct { _ struct{} `type:"structure"` @@ -1241,7 +1240,6 @@ func (s *AssumeRoleInput) SetTokenCode(v string) *AssumeRoleInput { // Contains the response to a successful AssumeRole request, including temporary // AWS credentials that can be used to make AWS requests. -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/AssumeRoleResponse type AssumeRoleOutput struct { _ struct{} `type:"structure"` @@ -1295,7 +1293,6 @@ func (s *AssumeRoleOutput) SetPackedPolicySize(v int64) *AssumeRoleOutput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/AssumeRoleWithSAMLRequest type AssumeRoleWithSAMLInput struct { _ struct{} `type:"structure"` @@ -1436,7 +1433,6 @@ func (s *AssumeRoleWithSAMLInput) SetSAMLAssertion(v string) *AssumeRoleWithSAML // Contains the response to a successful AssumeRoleWithSAML request, including // temporary AWS credentials that can be used to make AWS requests. -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/AssumeRoleWithSAMLResponse type AssumeRoleWithSAMLOutput struct { _ struct{} `type:"structure"` @@ -1548,7 +1544,6 @@ func (s *AssumeRoleWithSAMLOutput) SetSubjectType(v string) *AssumeRoleWithSAMLO return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/AssumeRoleWithWebIdentityRequest type AssumeRoleWithWebIdentityInput struct { _ struct{} `type:"structure"` @@ -1711,7 +1706,6 @@ func (s *AssumeRoleWithWebIdentityInput) SetWebIdentityToken(v string) *AssumeRo // Contains the response to a successful AssumeRoleWithWebIdentity request, // including temporary AWS credentials that can be used to make AWS requests. -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/AssumeRoleWithWebIdentityResponse type AssumeRoleWithWebIdentityOutput struct { _ struct{} `type:"structure"` @@ -1804,7 +1798,6 @@ func (s *AssumeRoleWithWebIdentityOutput) SetSubjectFromWebIdentityToken(v strin // The identifiers for the temporary security credentials that the operation // returns. -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/AssumedRoleUser type AssumedRoleUser struct { _ struct{} `type:"structure"` @@ -1847,7 +1840,6 @@ func (s *AssumedRoleUser) SetAssumedRoleId(v string) *AssumedRoleUser { } // AWS credentials for API authentication. -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/Credentials type Credentials struct { _ struct{} `type:"structure"` @@ -1906,7 +1898,6 @@ func (s *Credentials) SetSessionToken(v string) *Credentials { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/DecodeAuthorizationMessageRequest type DecodeAuthorizationMessageInput struct { _ struct{} `type:"structure"` @@ -1951,7 +1942,6 @@ func (s *DecodeAuthorizationMessageInput) SetEncodedMessage(v string) *DecodeAut // A document that contains additional information about the authorization status // of a request from an encoded message that is returned in response to an AWS // request. -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/DecodeAuthorizationMessageResponse type DecodeAuthorizationMessageOutput struct { _ struct{} `type:"structure"` @@ -1976,7 +1966,6 @@ func (s *DecodeAuthorizationMessageOutput) SetDecodedMessage(v string) *DecodeAu } // Identifiers for the federated user that is associated with the credentials. -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/FederatedUser type FederatedUser struct { _ struct{} `type:"structure"` @@ -2017,7 +2006,6 @@ func (s *FederatedUser) SetFederatedUserId(v string) *FederatedUser { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/GetCallerIdentityRequest type GetCallerIdentityInput struct { _ struct{} `type:"structure"` } @@ -2034,7 +2022,6 @@ func (s GetCallerIdentityInput) GoString() string { // Contains the response to a successful GetCallerIdentity request, including // information about the entity making the request. -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/GetCallerIdentityResponse type GetCallerIdentityOutput struct { _ struct{} `type:"structure"` @@ -2080,7 +2067,6 @@ func (s *GetCallerIdentityOutput) SetUserId(v string) *GetCallerIdentityOutput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/GetFederationTokenRequest type GetFederationTokenInput struct { _ struct{} `type:"structure"` @@ -2189,7 +2175,6 @@ func (s *GetFederationTokenInput) SetPolicy(v string) *GetFederationTokenInput { // Contains the response to a successful GetFederationToken request, including // temporary AWS credentials that can be used to make AWS requests. -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/GetFederationTokenResponse type GetFederationTokenOutput struct { _ struct{} `type:"structure"` @@ -2242,7 +2227,6 @@ func (s *GetFederationTokenOutput) SetPackedPolicySize(v int64) *GetFederationTo return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/GetSessionTokenRequest type GetSessionTokenInput struct { _ struct{} `type:"structure"` @@ -2327,7 +2311,6 @@ func (s *GetSessionTokenInput) SetTokenCode(v string) *GetSessionTokenInput { // Contains the response to a successful GetSessionToken request, including // temporary AWS credentials that can be used to make AWS requests. -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/GetSessionTokenResponse type GetSessionTokenOutput struct { _ struct{} `type:"structure"` diff --git a/vendor/vendor.json b/vendor/vendor.json index 97a1a27f8..05fc0faa4 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -232,8 +232,8 @@ "path": "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk", "revision": "c2e73f942591b0f033a3c6df00f44badb2347c38", "revisionTime": "2018-01-10T05:50:12Z" - }, - { + }, + { "checksumSHA1": "8dVO3L8yAdQ17X3lAhIziyF3OFk=", "path": "github.com/Sirupsen/logrus", "revision": "10f801ebc38b33738c9d17d50860f484a0988ff5", @@ -278,244 +278,244 @@ "revision": "4239b77079c7b5d1243b7b4736304ce8ddb6f0f2" }, { - "checksumSHA1": "tN8pbihy8mw+m0UVqNNFJmx7p+Y=", + "checksumSHA1": "2GR/f+rwuhlBVooyOGVaxNIYbEg=", "path": "github.com/aws/aws-sdk-go", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { - "checksumSHA1": "NQu/L+9CIJLpgrZt3UMlbma9Pk0=", + "checksumSHA1": "mFHEkH8cgZqBuQ5qVqNP1SLN4QA=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/aws", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { "checksumSHA1": "Y9W+4GimK4Fuxq+vyIskVYFRnX4=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/aws/awserr", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { "checksumSHA1": "yyYr41HZ1Aq0hWc3J5ijXwYEcac=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/aws/awsutil", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { "checksumSHA1": "9nE/FjZ4pYrT883KtV2/aI+Gayo=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/aws/client", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { "checksumSHA1": "ieAJ+Cvp/PKv1LpUEnUXpc3OI6E=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/aws/client/metadata", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { "checksumSHA1": "7/8j/q0TWtOgXyvEcv4B2Dhl00o=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/aws/corehandlers", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { "checksumSHA1": "Y+cPwQL0dZMyqp3wI+KJWmA9KQ8=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/aws/credentials", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { "checksumSHA1": "u3GOAJLmdvbuNUeUEcZSEAOeL/0=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { "checksumSHA1": "NUJUTWlc1sV8b7WjfiYc4JZbXl0=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/aws/credentials/endpointcreds", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { "checksumSHA1": "JEYqmF83O5n5bHkupAzA6STm0no=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/aws/credentials/stscreds", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { "checksumSHA1": "OnU/n7R33oYXiB4SAGd5pK7I0Bs=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/aws/defaults", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { "checksumSHA1": "/EXbk/z2TWjWc1Hvb4QYs3Wmhb8=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/aws/ec2metadata", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { - "checksumSHA1": "bV8wC0xzF08ztv57EXbeLjNxmsI=", + "checksumSHA1": "CJNEM69cgdO9tZi6c5Lj07jI+dk=", "path": "github.com/aws/aws-sdk-go/aws/endpoints", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { "checksumSHA1": "JZ49s4cNe3nIttx3hWp04CQif4o=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/aws/request", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { - "checksumSHA1": "HcGL4e6Uep4/80eCUI5xkcWjpQ0=", + "checksumSHA1": "DIn7B+oP++/nw603OB95fmupzu8=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/aws/session", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { "checksumSHA1": "iU00ZjhAml/13g+1YXT21IqoXqg=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/aws/signer/v4", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { "checksumSHA1": "04ypv4x12l4q0TksA1zEVsmgpvw=", "path": "github.com/aws/aws-sdk-go/internal/shareddefaults", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { "checksumSHA1": "NStHCXEvYqG72GknZyv1jaKaeH0=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/private/protocol", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { "checksumSHA1": "1QmQ3FqV37w0Zi44qv8pA1GeR0A=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/private/protocol/ec2query", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { "checksumSHA1": "yHfT5DTbeCLs4NE2Rgnqrhe15ls=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { "checksumSHA1": "R00RL5jJXRYq1iiK1+PGvMfvXyM=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/private/protocol/jsonrpc", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { "checksumSHA1": "ZqY5RWavBLWTo6j9xqdyBEaNFRk=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/private/protocol/query", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { "checksumSHA1": "9V1PvtFQ9MObZTc3sa86WcuOtOU=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/private/protocol/query/queryutil", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { "checksumSHA1": "pkeoOfZpHRvFG/AOZeTf0lwtsFg=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/private/protocol/rest", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { "checksumSHA1": "ODo+ko8D6unAxZuN1jGzMcN4QCc=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/private/protocol/restxml", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { "checksumSHA1": "0qYPUga28aQVkxZgBR3Z86AbGUQ=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { "checksumSHA1": "Eo9yODN5U99BK0pMzoqnBm7PCrY=", @@ -523,62 +523,62 @@ "path": "github.com/aws/aws-sdk-go/private/waiter", "revision": "1bd588c8b2dba4da57dd4664b2b2750d260a5915", "revisionTime": "2017-02-24T22:28:38Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { - "checksumSHA1": "4igS6faf4hrhDj6Jj9ErVcN1qKo=", + "checksumSHA1": "Sj6NTKuc/6+amv4RsMqrZkvkvpc=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/service/ec2", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { - "checksumSHA1": "uEv9kkBsVIjg7K4+Y8TVlU0Cc8o=", + "checksumSHA1": "kEgV0dSAj3M3M1waEkC27JS7VnU=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/service/ecr", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { - "checksumSHA1": "sCaHoPWsJXRHFbilUKwN71qFTOI=", + "checksumSHA1": "fXQn3V0ZRBZpTXUEHl4/yOjR4mQ=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/service/s3", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { "checksumSHA1": "ZP6QI0X9BNKk8o1p3AyLfjabS20=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/service/s3/s3iface", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { "checksumSHA1": "g6Eo2gEoj6YEZ+tLwydnfhwo7zg=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/service/s3/s3manager", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { - "checksumSHA1": "W1oFtpaT4TWIIJrAvFcn/XdcT7g=", + "checksumSHA1": "x7HCNPJnQi+4P6FKpBTY1hm3m6o=", "comment": "v1.7.1", "path": "github.com/aws/aws-sdk-go/service/sts", - "revision": "0cbaf57ea311890e62f0b01652529295079e9e95", - "revisionTime": "2018-02-05T21:18:42Z", - "version": "v1.12.71", - "versionExact": "v1.12.71" + "revision": "586c9ba6027a527800564282bb843d7e6e7985c9", + "revisionTime": "2018-02-07T00:16:19Z", + "version": "v1.12.72", + "versionExact": "v1.12.72" }, { "checksumSHA1": "7SbTaY0kaYxgQrG3/9cjrI+BcyU=", @@ -1353,7 +1353,7 @@ "revision": "453249f01cfeb54c3d549ddb75ff152ca243f9d8", "revisionTime": "2017-02-08T20:51:15Z" }, - { + { "checksumSHA1": "xiderUuvye8Kpn7yX3niiJg32bE=", "path": "golang.org/x/crypto/ssh/terminal", "revision": "c2303dcbe84172e0c0da4c9f083eeca54c06f298", From d07a2dda7d3e54bca63ebbeb60592bdd2c21ac5e Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 15 Feb 2018 14:28:22 -0800 Subject: [PATCH 0524/1216] fix grammar in docs --- website/source/docs/builders/amazon.html.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/source/docs/builders/amazon.html.md b/website/source/docs/builders/amazon.html.md index e40b2c17c..21dad780a 100644 --- a/website/source/docs/builders/amazon.html.md +++ b/website/source/docs/builders/amazon.html.md @@ -177,9 +177,9 @@ for Packer to work: }] } ``` -### Notes to pay for a spot instance to create the AMI -You need to add two more actions: `ec2:RequestSpotInstances` and `ec2:CancelSpotInstanceRequests` +Note that if you'd like to create a spot instance, you must also add +`ec2:RequestSpotInstances` and `ec2:CancelSpotInstanceRequests` ## Troubleshooting From 28065238348d428158956e4728f7fd1c0a389101 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 15 Feb 2018 14:20:50 -0800 Subject: [PATCH 0525/1216] Fix issue with assume role credentials --- builder/amazon/common/access_config.go | 32 +++++++++++++++----------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/builder/amazon/common/access_config.go b/builder/amazon/common/access_config.go index 61de7ff09..563030f50 100644 --- a/builder/amazon/common/access_config.go +++ b/builder/amazon/common/access_config.go @@ -1,7 +1,6 @@ package common import ( - "errors" "fmt" "log" "os" @@ -96,20 +95,8 @@ func (c *AccessConfig) Session() (*session.Session, error) { } creds := credentials.NewChainCredentials(providers) - cp, err := creds.Get() - if err != nil { - if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoCredentialProviders" { - return nil, errors.New("No valid credential sources found for AWS Builder. " + - "Please see https://www.packer.io/docs/builders/amazon.html#specifying-amazon-credentials " + - "for more information on providing credentials for the AWS Builder.") - } - - return nil, fmt.Errorf("Error loading credentials for AWS Provider: %s", err) - } - log.Printf("[INFO] AWS Auth provider used: %q", cp.ProviderName) config := aws.NewConfig().WithMaxRetries(11).WithCredentialsChainVerboseErrors(true) - config = config.WithCredentials(creds) if c.RawRegion != "" { config = config.WithRegion(c.RawRegion) @@ -126,12 +113,18 @@ func (c *AccessConfig) Session() (*session.Session, error) { Config: *config, } + if c.ProfileName != "" { + opts.Profile = c.ProfileName + } + if c.MFACode != "" { opts.AssumeRoleTokenProvider = func() (string, error) { return c.MFACode, nil } } + config = config.WithCredentials(creds) + if sess, err := session.NewSessionWithOptions(opts); err != nil { return nil, err } else if *sess.Config.Region == "" { @@ -139,8 +132,19 @@ func (c *AccessConfig) Session() (*session.Session, error) { } else { log.Printf("Found region %s", *sess.Config.Region) c.session = sess - } + cp, err := c.session.Config.Credentials.Get() + if err != nil { + if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoCredentialProviders" { + return nil, fmt.Errorf("No valid credential sources found for AWS Builder. " + + "Please see https://www.packer.io/docs/builders/amazon.html#specifying-amazon-credentials " + + "for more information on providing credentials for the AWS Builder.") + } else { + return nil, fmt.Errorf("Error loading credentials for AWS Provider: %s", err) + } + } + log.Printf("[INFO] AWS Auth provider used: %q", cp.ProviderName) + } return c.session, nil } From 4cfbfe5fa177781c0e6c863384d64cd80219da8b Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 16 Feb 2018 11:53:47 -0800 Subject: [PATCH 0526/1216] update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8878587c..0d0f6d49b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ## UNRELEASED +## BUG FIXES: +* builder/amazon: Fix issues using assume role [GH-5914] + ## 1.2.0 (February 9, 2018) From 14e3effa8340abef36599fc8aa78d757f144aff8 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 16 Feb 2018 12:00:33 -0800 Subject: [PATCH 0527/1216] test on go 1.9.x branch, remove 1.7.x from tests --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 755c0ef8f..3b83c4cd4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,8 +6,8 @@ sudo: false language: go go: - - 1.7.x - 1.8.x + - 1.9.x - 1.x install: From 7ede3296cf0b189d3ff562f35422c5180a5cc81d Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 16 Feb 2018 12:06:06 -0800 Subject: [PATCH 0528/1216] remove plugin listing from readme --- README.md | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 1c30fd877..d6b01c47a 100644 --- a/README.md +++ b/README.md @@ -23,25 +23,8 @@ from a single source configuration. Packer is lightweight, runs on every major operating system, and is highly performant, creating machine images for multiple platforms in parallel. Packer -comes out of the box with support for the following platforms: - -* Amazon EC2 (AMI). Both EBS-backed and instance-store AMIs -* Azure -* CloudStack -* DigitalOcean -* Docker -* Google Compute Engine -* Hyper-V -* 1&1 -* OpenStack -* Oracle Cloud Infrastructure -* Parallels -* ProfitBricks -* QEMU. Both KVM and Xen images. -* Scaleway -* Triton (Joyent Public Cloud) -* VMware -* VirtualBox +comes out of the box with support for many platforms, the full list of which can +be found at https://www.packer.io/docs/builders/index.html. Support for other platforms can be added via plugins. From c973dd91c510f74c0a8267d2f860f65c1839ee71 Mon Sep 17 00:00:00 2001 From: Antony Jones Date: Mon, 19 Feb 2018 13:07:03 +0000 Subject: [PATCH 0529/1216] Fix formatting issues --- website/source/docs/builders/azure-setup.html.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/source/docs/builders/azure-setup.html.md b/website/source/docs/builders/azure-setup.html.md index 618e38543..697861c00 100644 --- a/website/source/docs/builders/azure-setup.html.md +++ b/website/source/docs/builders/azure-setup.html.md @@ -123,7 +123,7 @@ $ azure account show --json | jq -r ".[] | .id" ``` Python: -```shell +``` shell $ az account set "$(az account list | jq -r '.[].name')" ``` @@ -214,7 +214,7 @@ Python: ```shell $ id=$(az ad app list | jq -r '.[] | select(.displayName == "Packer") | .appId') -$ az ad sp create --appid "$id" +$ az ad sp create --id "$id" ``` ### Grant Permissions to Your Application From 1418a42bf5dfc4658aa9f709032f7abc7afd3a1e Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Tue, 20 Feb 2018 10:54:18 -0800 Subject: [PATCH 0530/1216] Remove telemetry error reporting message. This seems to cause some confusion, and it's not needed anyway --- packer/telemetry.go | 1 - 1 file changed, 1 deletion(-) diff --git a/packer/telemetry.go b/packer/telemetry.go index a58b1c701..0f493a83a 100644 --- a/packer/telemetry.go +++ b/packer/telemetry.go @@ -146,7 +146,6 @@ func (s *TelemetrySpan) End(err error) { log.Printf("[INFO] (telemetry) ending %s", s.Name) if err != nil { s.Error = err.Error() - log.Printf("[INFO] (telemetry) found error: %s", err.Error()) } } From 2415ca2fd25f5cf6144689d8b62d15da89dabc0b Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Tue, 20 Feb 2018 14:09:51 -0800 Subject: [PATCH 0531/1216] fix for linux/ppc64le compilation closes #5880 --- vendor/github.com/creack/goselect/fdset_64.go | 2 +- vendor/vendor.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/vendor/github.com/creack/goselect/fdset_64.go b/vendor/github.com/creack/goselect/fdset_64.go index 4e032e4ea..71b7c9cc3 100644 --- a/vendor/github.com/creack/goselect/fdset_64.go +++ b/vendor/github.com/creack/goselect/fdset_64.go @@ -1,5 +1,5 @@ // +build !darwin,!netbsd,!openbsd -// +build amd64 arm64 +// +build amd64 arm64 ppc64le package goselect diff --git a/vendor/vendor.json b/vendor/vendor.json index 05fc0faa4..b48f00557 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -596,10 +596,10 @@ "revision": "50da7d4131a3b5c9d063932461cab4d1fafb20b0" }, { - "checksumSHA1": "bFj0ceSRvaFFCfmS4el1PjWhcgw=", + "checksumSHA1": "X2/71FBrn4pA3WcA620ySVO0uHU=", "path": "github.com/creack/goselect", - "revision": "1bd5ca702c6154bccc56ecd598932ee8b295cab2", - "revisionTime": "2016-07-14T17:28:59Z" + "revision": "528c74964609a58f7c17471525659c9b71cd499b", + "revisionTime": "2018-02-10T03:43:46Z" }, { "checksumSHA1": "Lf3uUXTkKK5DJ37BxQvxO1Fq+K8=", From ac2ddbcbf5e2f0c202e43a006d20e6ed95f6bb97 Mon Sep 17 00:00:00 2001 From: Ali Rizvi-Santiago Date: Tue, 20 Feb 2018 19:42:45 -0600 Subject: [PATCH 0532/1216] Fixes the assumption that all the VMware builder's drivers will implement a network mapper for mapping a network name to it's corresponding device. The ESX5 driver doesn't have a way of mapping the network name to its device name because a .vmx template uses different field names for it and so packer let's ESX handle filling this in. This patch will check to see if the driver that packer determines is missing a NetworkMapper implementation (by checking for nil). If it is, then fall back to using "nat" despite ESX not using the network type at all. This is what packer did prior to exposing the network type to the user back in version 1.1.3. This closes issue #5916. --- builder/vmware/iso/driver_esx5.go | 23 ++++++++++++++ builder/vmware/iso/step_create_vmx.go | 45 ++++++++++++++++++--------- 2 files changed, 53 insertions(+), 15 deletions(-) diff --git a/builder/vmware/iso/driver_esx5.go b/builder/vmware/iso/driver_esx5.go index 1dc352999..47fe70666 100644 --- a/builder/vmware/iso/driver_esx5.go +++ b/builder/vmware/iso/driver_esx5.go @@ -147,6 +147,29 @@ func (d *ESX5Driver) ToolsInstall() error { } func (d *ESX5Driver) Verify() error { + // Ensure that NetworkMapper is nil, since the mapping of device<->network + // is handled by ESX and thus can't be performed by packer unless we + // query things. + + // FIXME: If we want to expose the network devices to the user, then we can + // probably use esxcli to enumerate the portgroup and switchId + d.base.NetworkMapper = nil + + // Be safe/friendly and overwrite the rest of the utility functions with + // log functions despite the fact that these shouldn't be called anyways. + d.base.DhcpLeasesPath = func(device string) string { + log.Printf("Unexpected error, ESX5 driver attempted to call DhcpLeasesPath(%#v)\n", device) + return "" + } + d.base.DhcpConfPath = func(device string) string { + log.Printf("Unexpected error, ESX5 driver attempted to call DhcpConfPath(%#v)\n", device) + return "" + } + d.base.VmnetnatConfPath = func(device string) string { + log.Printf("Unexpected error, ESX5 driver attempted to call VmnetnatConfPath(%#v)\n", device) + return "" + } + checks := []func() error{ d.connect, d.checkSystemVersion, diff --git a/builder/vmware/iso/step_create_vmx.go b/builder/vmware/iso/step_create_vmx.go index ffff9d999..c231cfe8a 100644 --- a/builder/vmware/iso/step_create_vmx.go +++ b/builder/vmware/iso/step_create_vmx.go @@ -462,26 +462,41 @@ func (s *stepCreateVMX) Run(_ context.Context, state multistep.StateBag) multist network := config.Network driver := state.Get("driver").(vmwcommon.Driver).GetVmwareDriver() - // read netmap config - netmap, err := driver.NetworkMapper() - if err != nil { - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } + // check to see if the driver implements a network mapper for mapping + // the network-type to its device-name. + if driver.NetworkMapper != nil { - // try and convert the specified network to a device - device, err := netmap.NameIntoDevice(network) + // read network map configuration into a NetworkNameMapper. + netmap, err := driver.NetworkMapper() + if err != nil { + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } - // success. so we know that it's an actual network type inside netmap.conf - if err == nil { - templateData.Network_Type = network - templateData.Network_Device = device - // we were unable to find the type, so assume it's a custom network device. + // try and convert the specified network to a device. + device, err := netmap.NameIntoDevice(network) + + // success. so we know that it's an actual network type inside netmap.conf + if err == nil { + templateData.Network_Type = network + templateData.Network_Device = device + + // otherwise, we were unable to find the type, so assume its a custom device. + } else { + templateData.Network_Type = "custom" + templateData.Network_Device = network + } + + // if NetworkMapper is nil, then we're using something like ESX, so fall + // back to the previous logic of using "nat" despite it not mattering to ESX. } else { - templateData.Network_Type = "custom" + templateData.Network_Type = "nat" templateData.Network_Device = network + + network = "nat" } + // store the network so that we can later figure out what ip address to bind to state.Put("vmnetwork", network) From df45e0916dddf863d88b888f37f9c7750b7d6ab3 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 8 Feb 2018 14:55:04 -0800 Subject: [PATCH 0533/1216] Add the winRM communicator to Oracle Classic builder. update oracle classic docs with a minimal working windows example --- builder/oracle/classic/config.go | 4 - builder/oracle/classic/step_add_keys.go | 13 +- .../oracle/classic/step_create_instance.go | 7 +- builder/oracle/classic/step_security.go | 141 ++++++++++++++---- builder/oracle/classic/step_snapshot.go | 12 +- .../docs/builders/oracle-classic.html.md | 59 ++++++++ 6 files changed, 195 insertions(+), 41 deletions(-) diff --git a/builder/oracle/classic/config.go b/builder/oracle/classic/config.go index 233a3a0d1..7916478f0 100644 --- a/builder/oracle/classic/config.go +++ b/builder/oracle/classic/config.go @@ -99,10 +99,6 @@ func NewConfig(raws ...interface{}) (*Config, error) { if es := c.Comm.Prepare(&c.ctx); len(es) > 0 { errs = packer.MultiErrorAppend(errs, es...) } - if c.Comm.Type == "winrm" { - err = fmt.Errorf("winRM is not supported with the oracle-classic builder yet.") - errs = packer.MultiErrorAppend(errs, err) - } if errs != nil && len(errs.Errors) > 0 { return nil, errs diff --git a/builder/oracle/classic/step_add_keys.go b/builder/oracle/classic/step_add_keys.go index 544c35bf2..b09310a1f 100644 --- a/builder/oracle/classic/step_add_keys.go +++ b/builder/oracle/classic/step_add_keys.go @@ -19,6 +19,11 @@ func (s *stepAddKeysToAPI) Run(_ context.Context, state multistep.StateBag) mult config := state.Get("config").(*Config) client := state.Get("client").(*compute.ComputeClient) + if config.Comm.Type != "ssh" { + ui.Say("Not using SSH communicator; skip generating SSH keys...") + return multistep.ActionContinue + } + // grab packer-generated key from statebag context. sshPublicKey := strings.TrimSpace(state.Get("publicKey").(string)) @@ -49,10 +54,14 @@ func (s *stepAddKeysToAPI) Run(_ context.Context, state multistep.StateBag) mult func (s *stepAddKeysToAPI) Cleanup(state multistep.StateBag) { // Delete the keys we created during this run - keyName := state.Get("key_name").(string) + keyName, ok := state.GetOk("key_name") + if !ok { + // No keys were generated; none need to be cleaned up. + return + } ui := state.Get("ui").(packer.Ui) ui.Say("Deleting SSH keys...") - deleteInput := compute.DeleteSSHKeyInput{Name: keyName} + deleteInput := compute.DeleteSSHKeyInput{Name: keyName.(string)} client := state.Get("client").(*compute.ComputeClient) deleteClient := client.SSHKeys() err := deleteClient.DeleteSSHKey(&deleteInput) diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go index cda67122c..d9d26b3e3 100644 --- a/builder/oracle/classic/step_create_instance.go +++ b/builder/oracle/classic/step_create_instance.go @@ -18,7 +18,6 @@ func (s *stepCreateInstance) Run(_ context.Context, state multistep.StateBag) mu config := state.Get("config").(*Config) client := state.Get("client").(*compute.ComputeClient) - keyName := state.Get("key_name").(string) ipAddName := state.Get("ipres_name").(string) secListName := state.Get("security_list").(string) @@ -35,10 +34,13 @@ func (s *stepCreateInstance) Run(_ context.Context, state multistep.StateBag) mu Name: config.ImageName, Shape: config.Shape, ImageList: config.SourceImageList, - SSHKeys: []string{keyName}, Networking: map[string]compute.NetworkingInfo{"eth0": netInfo}, Attributes: config.attribs, } + if config.Comm.Type == "ssh" { + keyName := state.Get("key_name").(string) + input.SSHKeys = []string{keyName} + } instanceInfo, err := instanceClient.CreateInstance(input) if err != nil { @@ -48,6 +50,7 @@ func (s *stepCreateInstance) Run(_ context.Context, state multistep.StateBag) mu return multistep.ActionHalt } + state.Put("instance_info", instanceInfo) state.Put("instance_id", instanceInfo.ID) ui.Message(fmt.Sprintf("Created instance: %s.", instanceInfo.ID)) return multistep.ActionContinue diff --git a/builder/oracle/classic/step_security.go b/builder/oracle/classic/step_security.go index 22d74de08..892695f83 100644 --- a/builder/oracle/classic/step_security.go +++ b/builder/oracle/classic/step_security.go @@ -3,10 +3,10 @@ package classic import ( "context" "fmt" - "log" "strings" "github.com/hashicorp/go-oracle-terraform/compute" + "github.com/hashicorp/packer/common/uuid" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" ) @@ -15,53 +15,101 @@ type stepSecurity struct{} func (s *stepSecurity) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) - - ui.Say("Configuring security lists and rules to enable SSH access...") - config := state.Get("config").(*Config) - client := state.Get("client").(*compute.ComputeClient) - secListName := fmt.Sprintf("/Compute-%s/%s/Packer_SSH_Allow_%s", - config.IdentityDomain, config.Username, config.ImageName) + commType := "" + if config.Comm.Type == "ssh" { + commType = "SSH" + } else if config.Comm.Type == "winrm" { + commType = "WINRM" + } + + ui.Say(fmt.Sprintf("Configuring security lists and rules to enable %s access...", commType)) + + client := state.Get("client").(*compute.ComputeClient) + runUUID := uuid.TimeOrderedUUID() + + namePrefix := fmt.Sprintf("/Compute-%s/%s/", config.IdentityDomain, config.Username) + secListName := fmt.Sprintf("Packer_%s_Allow_%s_%s", commType, config.ImageName, runUUID) secListClient := client.SecurityLists() secListInput := compute.CreateSecurityListInput{ - Description: "Packer-generated security list to give packer ssh access", - Name: secListName, + Description: fmt.Sprintf("Packer-generated security list to give packer %s access", commType), + Name: namePrefix + secListName, } _, err := secListClient.CreateSecurityList(&secListInput) if err != nil { if !strings.Contains(err.Error(), "already exists") { err = fmt.Errorf("Error creating security List to"+ - " allow Packer to connect to Oracle instance via SSH: %s", err) + " allow Packer to connect to Oracle instance via %s: %s", commType, err) ui.Error(err.Error()) state.Put("error", err) return multistep.ActionHalt } } // DOCS NOTE: user must have Compute_Operations role - // Create security rule that allows Packer to connect via SSH - secRulesClient := client.SecRules() - secRulesInput := compute.CreateSecRuleInput{ - Action: "PERMIT", - Application: "/oracle/public/ssh", - Description: "Packer-generated security rule to allow ssh", - DestinationList: fmt.Sprintf("seclist:%s", secListName), - Name: fmt.Sprintf("Packer-allow-SSH-Rule_%s", config.ImageName), - SourceList: config.SSHSourceList, - } - - secRuleName := fmt.Sprintf("/Compute-%s/%s/Packer-allow-SSH-Rule_%s", - config.IdentityDomain, config.Username, config.ImageName) - _, err = secRulesClient.CreateSecRule(&secRulesInput) - if err != nil { - log.Printf("Error creating security rule to allow SSH: %s", err.Error()) - if !strings.Contains(err.Error(), "already exists") { - err = fmt.Errorf("Error creating security rule to"+ - " allow Packer to connect to Oracle instance via SSH: %s", err) + // Create security rule that allows Packer to connect via SSH or winRM + var application string + if commType == "SSH" { + application = "/oracle/public/ssh" + } else if commType == "WINRM" { + // Check to see whether a winRM security protocol is already defined; + // don't need to do this for SSH becasue it is built into the Oracle API. + protocolClient := client.SecurityProtocols() + winrmProtocol := fmt.Sprintf("WINRM_%s", runUUID) + input := compute.CreateSecurityProtocolInput{ + Name: winrmProtocol, + Description: "packer-generated protocol to allow winRM communicator", + DstPortSet: []string{"5985", "5986", "443"}, // TODO make configurable + IPProtocol: "tcp", + } + _, err = protocolClient.CreateSecurityProtocol(&input) + if err != nil { + err = fmt.Errorf("Error creating security protocol to"+ + " allow Packer to connect to Oracle instance via %s: %s", commType, err) ui.Error(err.Error()) state.Put("error", err) return multistep.ActionHalt } + state.Put("winrm_protocol", winrmProtocol) + + // Check to see whether a winRM security application is already defined + applicationClient := client.SecurityApplications() + application = fmt.Sprintf("packer_winRM_%s", runUUID) + applicationInput := compute.CreateSecurityApplicationInput{ + Description: "Allows Packer to connect to instance via winRM", + DPort: "5985-5986", + Name: application, + Protocol: "TCP", + } + _, err = applicationClient.CreateSecurityApplication(&applicationInput) + if err != nil { + err = fmt.Errorf("Error creating security application to"+ + " allow Packer to connect to Oracle instance via %s: %s", commType, err) + ui.Error(err.Error()) + state.Put("error", err) + return multistep.ActionHalt + } + state.Put("winrm_application", application) + } + secRulesClient := client.SecRules() + secRuleName := fmt.Sprintf("Packer-allow-%s-Rule_%s_%s", commType, + config.ImageName, runUUID) + secRulesInput := compute.CreateSecRuleInput{ + Action: "PERMIT", + Application: application, + Description: "Packer-generated security rule to allow ssh/winrm", + DestinationList: "seclist:" + namePrefix + secListName, + Name: namePrefix + secRuleName, + SourceList: config.SSHSourceList, + } + + _, err = secRulesClient.CreateSecRule(&secRulesInput) + if err != nil { + err = fmt.Errorf("Error creating security rule to"+ + " allow Packer to connect to Oracle instance: %s", err) + ui.Error(err.Error()) + state.Put("error", err) + return multistep.ActionHalt } state.Put("security_rule_name", secRuleName) state.Put("security_list", secListName) @@ -71,12 +119,15 @@ func (s *stepSecurity) Run(_ context.Context, state multistep.StateBag) multiste func (s *stepSecurity) Cleanup(state multistep.StateBag) { client := state.Get("client").(*compute.ComputeClient) ui := state.Get("ui").(packer.Ui) + config := state.Get("config").(*Config) + ui.Say("Deleting temporary rules and lists...") + namePrefix := fmt.Sprintf("/Compute-%s/%s/", config.IdentityDomain, config.Username) // delete security rules that Packer generated secRuleName := state.Get("security_rule_name").(string) secRulesClient := client.SecRules() - ruleInput := compute.DeleteSecRuleInput{Name: secRuleName} + ruleInput := compute.DeleteSecRuleInput{Name: namePrefix + secRuleName} err := secRulesClient.DeleteSecRule(&ruleInput) if err != nil { ui.Say(fmt.Sprintf("Error deleting the packer-generated security rule %s; "+ @@ -86,10 +137,38 @@ func (s *stepSecurity) Cleanup(state multistep.StateBag) { // delete security list that Packer generated secListName := state.Get("security_list").(string) secListClient := client.SecurityLists() - input := compute.DeleteSecurityListInput{Name: secListName} + input := compute.DeleteSecurityListInput{Name: namePrefix + secListName} err = secListClient.DeleteSecurityList(&input) if err != nil { ui.Say(fmt.Sprintf("Error deleting the packer-generated security list %s; "+ "please delete manually. (error : %s)", secListName, err.Error())) } + + // Some extra cleanup if we used the winRM communicator + if config.Comm.Type == "winrm" { + // Delete the packer-generated protocol + protocol := state.Get("winrm_protocol").(string) + protocolClient := client.SecurityProtocols() + deleteProtocolInput := compute.DeleteSecurityProtocolInput{ + Name: namePrefix + protocol, + } + err = protocolClient.DeleteSecurityProtocol(&deleteProtocolInput) + if err != nil { + ui.Say(fmt.Sprintf("Error deleting the packer-generated winrm security protocol %s; "+ + "please delete manually. (error : %s)", protocol, err.Error())) + } + + // Delete the packer-generated application + application := state.Get("winrm_application").(string) + applicationClient := client.SecurityApplications() + deleteApplicationInput := compute.DeleteSecurityApplicationInput{ + Name: namePrefix + application, + } + err = applicationClient.DeleteSecurityApplication(&deleteApplicationInput) + if err != nil { + ui.Say(fmt.Sprintf("Error deleting the packer-generated winrm security application %s; "+ + "please delete manually. (error : %s)", application, err.Error())) + } + } + } diff --git a/builder/oracle/classic/step_snapshot.go b/builder/oracle/classic/step_snapshot.go index b7cbbf008..68c059897 100644 --- a/builder/oracle/classic/step_snapshot.go +++ b/builder/oracle/classic/step_snapshot.go @@ -3,16 +3,20 @@ package classic import ( "context" "fmt" + "time" "github.com/hashicorp/go-oracle-terraform/compute" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" ) -type stepSnapshot struct{} +type stepSnapshot struct { + cleanupSnap bool +} func (s *stepSnapshot) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { // get variables from state + s.cleanupSnap = false ui := state.Get("ui").(packer.Ui) ui.Say("Creating Snapshot...") config := state.Get("config").(*Config) @@ -26,6 +30,7 @@ func (s *stepSnapshot) Run(_ context.Context, state multistep.StateBag) multiste snapshotInput := &compute.CreateSnapshotInput{ Instance: fmt.Sprintf("%s/%s", config.ImageName, instanceID), MachineImage: config.ImageName, + Timeout: time.Minute * 20, } snap, err := snapshotClient.CreateSnapshot(snapshotInput) @@ -35,7 +40,7 @@ func (s *stepSnapshot) Run(_ context.Context, state multistep.StateBag) multiste state.Put("error", err) return multistep.ActionHalt } - + s.cleanupSnap = true state.Put("snapshot", snap) ui.Message(fmt.Sprintf("Created snapshot: %s.", snap.Name)) return multistep.ActionContinue @@ -44,6 +49,9 @@ func (s *stepSnapshot) Run(_ context.Context, state multistep.StateBag) multiste func (s *stepSnapshot) Cleanup(state multistep.StateBag) { // Delete the snapshot ui := state.Get("ui").(packer.Ui) + if !s.cleanupSnap { + return + } ui.Say("Deleting Snapshot...") client := state.Get("client").(*compute.ComputeClient) snap := state.Get("snapshot").(*compute.Snapshot) diff --git a/website/source/docs/builders/oracle-classic.html.md b/website/source/docs/builders/oracle-classic.html.md index 98db5f2aa..208a0a088 100644 --- a/website/source/docs/builders/oracle-classic.html.md +++ b/website/source/docs/builders/oracle-classic.html.md @@ -114,3 +114,62 @@ obfuscated; you will need to add a working `username`, `password`, ] } ``` + +## Basic Example -- Windows + +Attributes file is optional for connecting via ssh, but required for winrm. + +The following file contains the bare minimum necessary to get winRM working; +you have to give it the password to give to the "Administrator" user, which +will be the one winrm connects to. You must also whitelist your computer +to connect via winRM -- the empty braces below whitelist any computer to access +winRM but you can make it more secure by only allowing your build machine +access. See the [docs](https://docs.oracle.com/en/cloud/iaas/compute-iaas-cloud/stcsg/automating-instance-initialization-using-opc-init.html#GUID-A0A107D6-3B38-47F4-8FC8-96D50D99379B) +for more details on how to define a trusted host. + +Save this file as `windows_attributes.json`: + +```{.json} +{ + "userdata": { + "administrator_password": "password", + "winrm": {} + } +} +``` + +Following is a minimal but working Packer config that references this attributes +file: +```{.json} +{ + "variables": { + "opc_username": "{{ env `OPC_USERNAME`}}", + "opc_password": "{{ env `OPC_PASSWORD`}}", + "opc_identity_domain": "{{env `OPC_IDENTITY_DOMAIN`}}", + "opc_api_endpoint": "{{ env `OPC_ENDPOINT`}}" + }, + "builders": [ + { + "type": "oracle-classic", + "username": "{{ user `opc_username`}}", + "password": "{{ user `opc_password`}}", + "identity_domain": "{{ user `opc_identity_domain`}}", + "api_endpoint": "{{ user `opc_api_endpoint`}}", + "source_image_list": "/Compute-{{ user `opc_identity_domain` }}/{{ user opc_username`}}/Microsoft_Windows_Server_2012_R2-17.3.6-20170930-124649", + "attributes_file": "./windows_attributes.json", + "shape": "oc3", + "image_name": "Packer_Windows_Demo_{{timestamp}}", + "dest_image_list": "Packer_Windows_Demo", + "communicator": "winrm", + "winrm_username": "Administrator", + "winrm_password": "password" + } + ], + "provisioners": [ + { + "type": "powershell", + "inline": "Write-Output(\"HELLO WORLD\")" + } + ] +} +``` From 33acdbf3bf1518bba67fd1f7030d14726c4463bf Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Wed, 21 Feb 2018 14:57:58 -0800 Subject: [PATCH 0534/1216] move comments so indentation is more logical --- builder/vmware/iso/step_create_vmx.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/builder/vmware/iso/step_create_vmx.go b/builder/vmware/iso/step_create_vmx.go index c231cfe8a..ca5fe7bbe 100644 --- a/builder/vmware/iso/step_create_vmx.go +++ b/builder/vmware/iso/step_create_vmx.go @@ -477,13 +477,12 @@ func (s *stepCreateVMX) Run(_ context.Context, state multistep.StateBag) multist // try and convert the specified network to a device. device, err := netmap.NameIntoDevice(network) - // success. so we know that it's an actual network type inside netmap.conf if err == nil { + // success. so we know that it's an actual network type inside netmap.conf templateData.Network_Type = network templateData.Network_Device = device - - // otherwise, we were unable to find the type, so assume its a custom device. } else { + // otherwise, we were unable to find the type, so assume its a custom device. templateData.Network_Type = "custom" templateData.Network_Device = network } From 4befdce47e6c7816193ffa1337c144b863847292 Mon Sep 17 00:00:00 2001 From: Christophe Courtaut Date: Wed, 21 Feb 2018 18:22:39 +0100 Subject: [PATCH 0535/1216] builder/googlecompute: Adds ability to specify service account This commit allows user to specify the service account they want to associate with the virtual machine provisionned by setting the service_account_email field in the config. It allows to manage permissions of the instantiated VM properly, using a service account that can be tied up to IAM roles and permissions. --- builder/googlecompute/config.go | 2 ++ builder/googlecompute/driver.go | 1 + builder/googlecompute/driver_gce.go | 13 +++++++++---- builder/googlecompute/step_create_instance.go | 1 + 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/builder/googlecompute/config.go b/builder/googlecompute/config.go index c8863d4d9..3d9194c17 100644 --- a/builder/googlecompute/config.go +++ b/builder/googlecompute/config.go @@ -58,6 +58,8 @@ type Config struct { UseInternalIP bool `mapstructure:"use_internal_ip"` Zone string `mapstructure:"zone"` + ServiceAccountEmail string `mapstructure:"service_account_email"` + Account AccountFile stateTimeout time.Duration imageAlreadyExists bool diff --git a/builder/googlecompute/driver.go b/builder/googlecompute/driver.go index fdb7d9442..c99c38359 100644 --- a/builder/googlecompute/driver.go +++ b/builder/googlecompute/driver.go @@ -75,6 +75,7 @@ type InstanceConfig struct { OnHostMaintenance string Preemptible bool Region string + ServiceAccountEmail string Scopes []string Subnetwork string Tags []string diff --git a/builder/googlecompute/driver_gce.go b/builder/googlecompute/driver_gce.go index c84ab222f..5f286fa71 100644 --- a/builder/googlecompute/driver_gce.go +++ b/builder/googlecompute/driver_gce.go @@ -343,6 +343,14 @@ func (d *driverGCE) RunInstance(c *InstanceConfig) (<-chan error, error) { guestAccelerators = append(guestAccelerators, ac) } + serviceAccount := &compute.ServiceAccount{ + Email: "default", + Scopes: c.Scopes, + } + if c.ServiceAccountEmail != "" { + serviceAccount.Email = c.ServiceAccountEmail + } + // Create the instance information instance := compute.Instance{ Description: c.Description, @@ -379,10 +387,7 @@ func (d *driverGCE) RunInstance(c *InstanceConfig) (<-chan error, error) { Preemptible: c.Preemptible, }, ServiceAccounts: []*compute.ServiceAccount{ - { - Email: "default", - Scopes: c.Scopes, - }, + serviceAccount, }, Tags: &compute.Tags{ Items: c.Tags, diff --git a/builder/googlecompute/step_create_instance.go b/builder/googlecompute/step_create_instance.go index ee3b6643b..9ff74d85f 100644 --- a/builder/googlecompute/step_create_instance.go +++ b/builder/googlecompute/step_create_instance.go @@ -117,6 +117,7 @@ func (s *StepCreateInstance) Run(_ context.Context, state multistep.StateBag) mu OnHostMaintenance: c.OnHostMaintenance, Preemptible: c.Preemptible, Region: c.Region, + ServiceAccountEmail: c.ServiceAccountEmail, Scopes: c.Scopes, Subnetwork: c.Subnetwork, Tags: c.Tags, From 16882c1252c56de28306871e5f436d9c9bd8987c Mon Sep 17 00:00:00 2001 From: Christophe Courtaut Date: Wed, 21 Feb 2018 18:25:08 +0100 Subject: [PATCH 0536/1216] builder/googlecompute: Go code formatting --- builder/googlecompute/driver.go | 42 +++++++++---------- builder/googlecompute/step_create_instance.go | 42 +++++++++---------- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/builder/googlecompute/driver.go b/builder/googlecompute/driver.go index c99c38359..4c07a06cf 100644 --- a/builder/googlecompute/driver.go +++ b/builder/googlecompute/driver.go @@ -58,28 +58,28 @@ type Driver interface { } type InstanceConfig struct { - AcceleratorType string - AcceleratorCount int64 - Address string - Description string - DiskSizeGb int64 - DiskType string - Image *Image - Labels map[string]string - MachineType string - Metadata map[string]string - Name string - Network string - NetworkProjectId string - OmitExternalIP bool - OnHostMaintenance string - Preemptible bool - Region string + AcceleratorType string + AcceleratorCount int64 + Address string + Description string + DiskSizeGb int64 + DiskType string + Image *Image + Labels map[string]string + MachineType string + Metadata map[string]string + Name string + Network string + NetworkProjectId string + OmitExternalIP bool + OnHostMaintenance string + Preemptible bool + Region string ServiceAccountEmail string - Scopes []string - Subnetwork string - Tags []string - Zone string + Scopes []string + Subnetwork string + Tags []string + Zone string } // WindowsPasswordConfig is the data structue that GCE needs to encrypt the created diff --git a/builder/googlecompute/step_create_instance.go b/builder/googlecompute/step_create_instance.go index 9ff74d85f..bc37aa7b2 100644 --- a/builder/googlecompute/step_create_instance.go +++ b/builder/googlecompute/step_create_instance.go @@ -100,28 +100,28 @@ func (s *StepCreateInstance) Run(_ context.Context, state multistep.StateBag) mu var metadata map[string]string metadata, err = c.createInstanceMetadata(sourceImage, sshPublicKey) errCh, err = d.RunInstance(&InstanceConfig{ - AcceleratorType: c.AcceleratorType, - AcceleratorCount: c.AcceleratorCount, - Address: c.Address, - Description: "New instance created by Packer", - DiskSizeGb: c.DiskSizeGb, - DiskType: c.DiskType, - Image: sourceImage, - Labels: c.Labels, - MachineType: c.MachineType, - Metadata: metadata, - Name: name, - Network: c.Network, - NetworkProjectId: c.NetworkProjectId, - OmitExternalIP: c.OmitExternalIP, - OnHostMaintenance: c.OnHostMaintenance, - Preemptible: c.Preemptible, - Region: c.Region, + AcceleratorType: c.AcceleratorType, + AcceleratorCount: c.AcceleratorCount, + Address: c.Address, + Description: "New instance created by Packer", + DiskSizeGb: c.DiskSizeGb, + DiskType: c.DiskType, + Image: sourceImage, + Labels: c.Labels, + MachineType: c.MachineType, + Metadata: metadata, + Name: name, + Network: c.Network, + NetworkProjectId: c.NetworkProjectId, + OmitExternalIP: c.OmitExternalIP, + OnHostMaintenance: c.OnHostMaintenance, + Preemptible: c.Preemptible, + Region: c.Region, ServiceAccountEmail: c.ServiceAccountEmail, - Scopes: c.Scopes, - Subnetwork: c.Subnetwork, - Tags: c.Tags, - Zone: c.Zone, + Scopes: c.Scopes, + Subnetwork: c.Subnetwork, + Tags: c.Tags, + Zone: c.Zone, }) if err == nil { From bda07497e9e11604f4d1c999b0295faae2e063e0 Mon Sep 17 00:00:00 2001 From: Christophe Courtaut Date: Thu, 22 Feb 2018 18:46:13 +0100 Subject: [PATCH 0537/1216] website/docs: Adds documentation for GCE builder service account email new field --- website/source/docs/builders/googlecompute.html.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/website/source/docs/builders/googlecompute.html.md b/website/source/docs/builders/googlecompute.html.md index cda97b02c..fb006c7ae 100644 --- a/website/source/docs/builders/googlecompute.html.md +++ b/website/source/docs/builders/googlecompute.html.md @@ -268,6 +268,9 @@ builder. - `region` (string) - The region in which to launch the instance. Defaults to to the region hosting the specified `zone`. +- `service_account_email` (string) - The service account to be used for launched instance. Defaults to + the project's default service account. + - `scopes` (array of strings) - The service account scopes for launched instance. Defaults to: From 597ddc2192392624970b4e832c352deb6f6de6d8 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Wed, 21 Feb 2018 14:33:00 -0800 Subject: [PATCH 0538/1216] add configurable snapshot timeout to oracle-classic builder --- builder/oracle/classic/config.go | 14 ++++++++++---- builder/oracle/classic/step_snapshot.go | 3 +-- builder/vmware/common/driver.go | 3 +++ .../source/docs/builders/oracle-classic.html.md | 8 ++++++++ 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/builder/oracle/classic/config.go b/builder/oracle/classic/config.go index 7916478f0..b9f5d808f 100644 --- a/builder/oracle/classic/config.go +++ b/builder/oracle/classic/config.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "net/url" "os" + "time" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -27,10 +28,11 @@ type Config struct { apiEndpointURL *url.URL // Image - ImageName string `mapstructure:"image_name"` - Shape string `mapstructure:"shape"` - SourceImageList string `mapstructure:"source_image_list"` - DestImageList string `mapstructure:"dest_image_list"` + ImageName string `mapstructure:"image_name"` + Shape string `mapstructure:"shape"` + SourceImageList string `mapstructure:"source_image_list"` + SnapshotTimeout time.Duration `mapstructure:"snapshot_timeout"` + DestImageList string `mapstructure:"dest_image_list"` // Attributes and Atributes file are both optional and mutually exclusive. Attributes string `mapstructure:"attributes"` AttributesFile string `mapstructure:"attributes_file"` @@ -71,6 +73,10 @@ func NewConfig(raws ...interface{}) (*Config, error) { c.Comm.SSHUsername = "opc" } + if c.SnapshotTimeout == 0 { + c.SnapshotTimeout = 20 * time.Minute + } + // Validate that all required fields are present var errs *packer.MultiError required := map[string]string{ diff --git a/builder/oracle/classic/step_snapshot.go b/builder/oracle/classic/step_snapshot.go index 68c059897..e0ebf3091 100644 --- a/builder/oracle/classic/step_snapshot.go +++ b/builder/oracle/classic/step_snapshot.go @@ -3,7 +3,6 @@ package classic import ( "context" "fmt" - "time" "github.com/hashicorp/go-oracle-terraform/compute" "github.com/hashicorp/packer/helper/multistep" @@ -30,7 +29,7 @@ func (s *stepSnapshot) Run(_ context.Context, state multistep.StateBag) multiste snapshotInput := &compute.CreateSnapshotInput{ Instance: fmt.Sprintf("%s/%s", config.ImageName, instanceID), MachineImage: config.ImageName, - Timeout: time.Minute * 20, + Timeout: config.SnapshotTimeout, } snap, err := snapshotClient.CreateSnapshot(snapshotInput) diff --git a/builder/vmware/common/driver.go b/builder/vmware/common/driver.go index 8fa185efd..c8fea88f4 100644 --- a/builder/vmware/common/driver.go +++ b/builder/vmware/common/driver.go @@ -309,12 +309,14 @@ func (d *VmwareDriver) GuestIP(state multistep.StateBag) (string, error) { // grab network mapper netmap, err := d.NetworkMapper() + log.Printf("MEGAN: NEtworkMapper is %#v", netmap) if err != nil { return "", err } // convert the stashed network to a device network := state.Get("vmnetwork").(string) + log.Printf("MEGAN: network is %#v", network) device, err := netmap.NameIntoDevice(network) // we were unable to find the device, maybe it's a custom one... @@ -337,6 +339,7 @@ func (d *VmwareDriver) GuestIP(state multistep.StateBag) (string, error) { if err != nil { return "", err } + log.Printf("MEGAN mac address is %s", MACAddress) // figure out the correct dhcp leases dhcpLeasesPath := d.DhcpLeasesPath(device) diff --git a/website/source/docs/builders/oracle-classic.html.md b/website/source/docs/builders/oracle-classic.html.md index 208a0a088..1ea03dc46 100644 --- a/website/source/docs/builders/oracle-classic.html.md +++ b/website/source/docs/builders/oracle-classic.html.md @@ -84,6 +84,14 @@ This builder currently only works with the SSH communicator. - `image_name` (string) - The name to assign to the resulting custom image. + - `snapshot_timeout` (string) - How long to wait for a snapshot to be + created. Expects a positive golang Time.Duration string, which is + a sequence of decimal numbers and a unit suffix; valid suffixes are `ns` + (nanoseconds), `us` (microseconds), `ms` (milliseconds), `s` (seconds), `m` + (minutes), and `h` (hours). Examples of valid inputs: `100ms`, `250ms`, `1s`, + `2.5s`, `2.5m`, `1m30s`. + Example: `"snapshot_timeout": "15m"`. Default: `20m`. + ## Basic Example Here is a basic example. Note that account specific configuration has been From d90b67ba235cbcb519b95086036ff67f407c4e1b Mon Sep 17 00:00:00 2001 From: Taeho Kim Date: Fri, 23 Feb 2018 11:51:43 +0900 Subject: [PATCH 0539/1216] Fix docs on Alicloud builder - Fix the link to instance types page. - Disk configurations should go into `image_disk_mappings` array. - If `io_optimized` is not set, it's always `false`. (NOT inferred by instance type) --- .../source/docs/builders/alicloud-ecs.html.md | 62 ++++++++++--------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/website/source/docs/builders/alicloud-ecs.html.md b/website/source/docs/builders/alicloud-ecs.html.md index dc708379d..172bdab5c 100644 --- a/website/source/docs/builders/alicloud-ecs.html.md +++ b/website/source/docs/builders/alicloud-ecs.html.md @@ -35,9 +35,9 @@ builder. can also be sourced from the `ALICLOUD_REGION` environment variables. - `instance_type` (string) - Type of the instance. For values, see [Instance - Type Table](). You can also obtain the latest instance type table by invoking - the [Querying Instance Type - Table](https://intl.aliyun.com/help/doc-detail/25620.htm?spm=a3c0i.o25499en.a3.6.Dr1bik) + Type Table](https://www.alibabacloud.com/help/doc-detail/25378.htm?spm=a3c0i.o25499en.a3.9.14a36ac8iYqKRA). + You can also obtain the latest instance type table by invoking the [Querying + Instance Type Table](https://intl.aliyun.com/help/doc-detail/25620.htm?spm=a3c0i.o25499en.a3.6.Dr1bik) interface. - `image_name` (string) - The name of the user-defined image, \[2, 128\] English @@ -80,45 +80,47 @@ builder. duplicated existing image, the source snapshot of this image will be delete either. -- `disk_name` (string) - The value of disk name is blank by default. \[2, 128\] - English or Chinese characters, must begin with an uppercase/lowercase letter - or Chinese character. Can contain numbers, `.`, `_` and `-`. The disk name - will appear on the console. It cannot begin with `http://` or `https://`. +-   `image_disk_mappings` (array of image disk mappings) - Add one or more data disks + to the image. -- `disk_category` (string) - Category of the data disk. Optional values are: - - cloud - general cloud disk - - cloud\_efficiency - efficiency cloud disk - - cloud\_ssd - cloud SSD + - `disk_name` (string) - The value of disk name is blank by default. \[2, 128\] + English or Chinese characters, must begin with an uppercase/lowercase letter + or Chinese character. Can contain numbers, `.`, `_` and `-`. The disk name + will appear on the console. It cannot begin with `http://` or `https://`. - Default value: cloud. + - `disk_category` (string) - Category of the data disk. Optional values are: + - cloud - general cloud disk + - cloud\_efficiency - efficiency cloud disk + - cloud\_ssd - cloud SSD -- `disk_size` (number) - Size of the system disk, in GB, values range: - - cloud - 5 ~ 2000 - - cloud\_efficiency - 20 ~ 2048 - - cloud\_ssd - 20 ~ 2048 + Default value: cloud. - The value should be equal to or greater than the size of the specific SnapshotId. + - `disk_size` (number) - Size of the system disk, in GB, values range: + - cloud - 5 ~ 2000 + - cloud\_efficiency - 20 ~ 2048 + - cloud\_ssd - 20 ~ 2048 -- `disk_snapshot_id` (string) - Snapshots are used to create the data disk - After this parameter is specified, Size is ignored. The actual size of the - created disk is the size of the specified snapshot. + The value should be equal to or greater than the size of the specific SnapshotId. - Snapshots from on or before July 15, 2013 cannot be used to create a disk. + - `disk_snapshot_id` (string) - Snapshots are used to create the data disk + After this parameter is specified, Size is ignored. The actual size of the + created disk is the size of the specified snapshot. -- `disk_description` (string) - The value of disk description is blank by default. \[2, 256\] characters. The disk description will appear on the console. It cannot begin with `http://` or `https://`. + Snapshots from on or before July 15, 2013 cannot be used to create a disk. -- `disk_delete_with_instance` (string) - Whether or not the disk is released along with the instance: -- True indicates that when the instance is released, this disk will be released with it -- False indicates that when the instance is released, this disk will be retained. + - `disk_description` (string) - The value of disk description is blank by default. \[2, 256\] characters. The disk description will appear on the console. It cannot begin with `http://` or `https://`. -- `disk_device` (string) - Device information of the related instance: such as - `/dev/xvdb` It is null unless the Status is In\_use. + - `disk_delete_with_instance` (string) - Whether or not the disk is released along with the instance: + - True indicates that when the instance is released, this disk will be released with it + - False indicates that when the instance is released, this disk will be retained. + + - `disk_device` (string) - Device information of the related instance: such as + `/dev/xvdb` It is null unless the Status is In\_use. - `zone_id` (string) - ID of the zone to which the disk belongs. -- `io_optimized` (boolean) - I/O optimized. - - Default value: false for Generation I instances; true for other instances. +- `io_optimized` (boolean) - Whether an ECS instance is I/O optimized or not. + The default value is `false`. - `force_stop_instance` (boolean) - Whether to force shutdown upon device restart. The default value is `false`. From b1ab60031ac18abec7f8f55f169b69eff6ac8d61 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 23 Feb 2018 07:35:41 -0800 Subject: [PATCH 0540/1216] update changelog --- CHANGELOG.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d0f6d49b..0506bcca6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,15 @@ -## UNRELEASED +## 1.2.1 (February 23, 2918) -## BUG FIXES: +### BUG FIXES: * builder/amazon: Fix issues using assume role [GH-5914] +* builder/vmware-esxi: Fall back to "nat" if driver does not impelment a NetworkMapper [GH-5916] +* builder/vmware: Fix VMware Workstation methodology for finding dhcp.conf and dhcpd.leases files [GH-5882] +* provisioner/ansible-local: Fix conflicting escaping schemes for vars provided + to "--extra-vars" [GH-5888] + +### IMPROVEMENTS: +* builder/oracle-classic: Implement winRM communicator for oracle-classic builder [GH-5918] +* builder/oracle-classic: Add snapshot_timeout option to builder [GH-5930] ## 1.2.0 (February 9, 2018) From 1530861866ede0fc8a4b53b574a21384df690fe8 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 23 Feb 2018 10:07:45 -0800 Subject: [PATCH 0541/1216] update changelog --- CHANGELOG.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0506bcca6..38055710d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,20 +1,25 @@ ## 1.2.1 (February 23, 2918) ### BUG FIXES: -* builder/amazon: Fix issues using assume role [GH-5914] -* builder/vmware-esxi: Fall back to "nat" if driver does not impelment a NetworkMapper [GH-5916] -* builder/vmware: Fix VMware Workstation methodology for finding dhcp.conf and dhcpd.leases files [GH-5882] + +* builder/amazon: Fix authorization using assume role. [GH-5914] +* builder/vmware-iso: Fix panic when building on esx5 remotes. [GH-5931] +* builder/vmware: Correctly locate dhcp.conf and dhcpd.leases files. [GH-5898] + [GH-5900] * provisioner/ansible-local: Fix conflicting escaping schemes for vars provided - to "--extra-vars" [GH-5888] + via `--extra-vars`. [GH-5888] ### IMPROVEMENTS: -* builder/oracle-classic: Implement winRM communicator for oracle-classic builder [GH-5918] -* builder/oracle-classic: Add snapshot_timeout option to builder [GH-5930] + +* builder/oracle-classic: Add `snapshot_timeout` option to control how long we + wait for the snapshot to be created. [GH-5932] +* builder/oracle-classic: Add support for WinRM connections. [GH-5929] ## 1.2.0 (February 9, 2018) ### BACKWARDS INCOMPATIBILITIES: + * 3rd party plugins: We have moved internal dependencies, meaning your 3rd party plugins will no longer compile (however existing builds will still work fine); the work to fix them is minimal and documented in GH-5810. From 0028c82c2b103522fa5cb6ed9ae6bbd640ac6841 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 23 Feb 2018 10:11:32 -0800 Subject: [PATCH 0542/1216] update changelog --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38055710d..89650932c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,7 @@ * builder/amazon: Fix authorization using assume role. [GH-5914] * builder/vmware-iso: Fix panic when building on esx5 remotes. [GH-5931] -* builder/vmware: Correctly locate dhcp.conf and dhcpd.leases files. [GH-5898] - [GH-5900] +* builder/vmware: Fix issue detecting host IP. [GH-5898] [GH-5900] * provisioner/ansible-local: Fix conflicting escaping schemes for vars provided via `--extra-vars`. [GH-5888] From 170a233e5f7eab050906961dcb7b2ca92c8d606f Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 23 Feb 2018 10:35:42 -0800 Subject: [PATCH 0543/1216] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89650932c..044f747e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### BUG FIXES: * builder/amazon: Fix authorization using assume role. [GH-5914] +* builder/hyper-v: Fix command collisions with VMWare PowerCLI. [GH-5861] * builder/vmware-iso: Fix panic when building on esx5 remotes. [GH-5931] * builder/vmware: Fix issue detecting host IP. [GH-5898] [GH-5900] * provisioner/ansible-local: Fix conflicting escaping schemes for vars provided From 103186af8650d8168603031fc65c82c6f2df1fcf Mon Sep 17 00:00:00 2001 From: Stefan Henseler Date: Fri, 23 Feb 2018 20:19:26 +0100 Subject: [PATCH 0544/1216] Adds Support to configure hyper-v disk block size --- builder/hyperv/common/driver.go | 4 +- builder/hyperv/common/driver_mock.go | 8 +++- builder/hyperv/common/driver_ps_4.go | 8 ++-- builder/hyperv/common/step_create_vm.go | 8 +++- builder/hyperv/iso/builder.go | 32 +++++++++++++++ builder/hyperv/iso/builder_test.go | 53 +++++++++++++++++++++++++ common/powershell/hyperv/hyperv.go | 42 ++++++++++++++------ 7 files changed, 133 insertions(+), 22 deletions(-) diff --git a/builder/hyperv/common/driver.go b/builder/hyperv/common/driver.go index 571214acd..e7b58464e 100644 --- a/builder/hyperv/common/driver.go +++ b/builder/hyperv/common/driver.go @@ -66,9 +66,9 @@ type Driver interface { DeleteVirtualSwitch(string) error - CreateVirtualMachine(string, string, string, string, int64, int64, string, uint, bool) error + CreateVirtualMachine(string, string, string, string, int64, int64, int64, string, uint, bool) error - AddVirtualMachineHardDrive(string, string, string, int64, string) error + AddVirtualMachineHardDrive(string, string, string, int64, int64, string) error CloneVirtualMachine(string, string, string, bool, string, string, string, int64, string) error diff --git a/builder/hyperv/common/driver_mock.go b/builder/hyperv/common/driver_mock.go index 7d8e02c6a..8af637e66 100644 --- a/builder/hyperv/common/driver_mock.go +++ b/builder/hyperv/common/driver_mock.go @@ -112,6 +112,7 @@ type DriverMock struct { AddVirtualMachineHardDrive_VhdFile string AddVirtualMachineHardDrive_VhdName string AddVirtualMachineHardDrive_VhdSizeBytes int64 + AddVirtualMachineHardDrive_VhdBlockSize int64 AddVirtualMachineHardDrive_ControllerType string AddVirtualMachineHardDrive_Err error @@ -122,6 +123,7 @@ type DriverMock struct { CreateVirtualMachine_VhdPath string CreateVirtualMachine_Ram int64 CreateVirtualMachine_DiskSize int64 + CreateVirtualMachine_DiskBlockSize int64 CreateVirtualMachine_SwitchName string CreateVirtualMachine_Generation uint CreateVirtualMachine_DifferentialDisk bool @@ -377,17 +379,18 @@ func (d *DriverMock) CreateVirtualSwitch(switchName string, switchType string) ( return d.CreateVirtualSwitch_Return, d.CreateVirtualSwitch_Err } -func (d *DriverMock) AddVirtualMachineHardDrive(vmName string, vhdFile string, vhdName string, vhdSizeBytes int64, controllerType string) error { +func (d *DriverMock) AddVirtualMachineHardDrive(vmName string, vhdFile string, vhdName string, vhdSizeBytes int64, vhdDiskBlockSize int64, controllerType string) error { d.AddVirtualMachineHardDrive_Called = true d.AddVirtualMachineHardDrive_VmName = vmName d.AddVirtualMachineHardDrive_VhdFile = vhdFile d.AddVirtualMachineHardDrive_VhdName = vhdName d.AddVirtualMachineHardDrive_VhdSizeBytes = vhdSizeBytes + d.AddVirtualMachineHardDrive_VhdSizeBytes = vhdDiskBlockSize d.AddVirtualMachineHardDrive_ControllerType = controllerType return d.AddVirtualMachineHardDrive_Err } -func (d *DriverMock) CreateVirtualMachine(vmName string, path string, harddrivePath string, vhdPath string, ram int64, diskSize int64, switchName string, generation uint, diffDisks bool) error { +func (d *DriverMock) CreateVirtualMachine(vmName string, path string, harddrivePath string, vhdPath string, ram int64, diskSize int64, diskBlockSize int64, switchName string, generation uint, diffDisks bool) error { d.CreateVirtualMachine_Called = true d.CreateVirtualMachine_VmName = vmName d.CreateVirtualMachine_Path = path @@ -395,6 +398,7 @@ func (d *DriverMock) CreateVirtualMachine(vmName string, path string, harddriveP d.CreateVirtualMachine_VhdPath = vhdPath d.CreateVirtualMachine_Ram = ram d.CreateVirtualMachine_DiskSize = diskSize + d.CreateVirtualMachine_DiskBlockSize = diskBlockSize d.CreateVirtualMachine_SwitchName = switchName d.CreateVirtualMachine_Generation = generation d.CreateVirtualMachine_DifferentialDisk = diffDisks diff --git a/builder/hyperv/common/driver_ps_4.go b/builder/hyperv/common/driver_ps_4.go index a6c1b7352..34a9edbb5 100644 --- a/builder/hyperv/common/driver_ps_4.go +++ b/builder/hyperv/common/driver_ps_4.go @@ -174,12 +174,12 @@ func (d *HypervPS4Driver) CreateVirtualSwitch(switchName string, switchType stri return hyperv.CreateVirtualSwitch(switchName, switchType) } -func (d *HypervPS4Driver) AddVirtualMachineHardDrive(vmName string, vhdFile string, vhdName string, vhdSizeBytes int64, controllerType string) error { - return hyperv.AddVirtualMachineHardDiskDrive(vmName, vhdFile, vhdName, vhdSizeBytes, controllerType) +func (d *HypervPS4Driver) AddVirtualMachineHardDrive(vmName string, vhdFile string, vhdName string, vhdSizeBytes int64, diskBlockSize int64, controllerType string) error { + return hyperv.AddVirtualMachineHardDiskDrive(vmName, vhdFile, vhdName, vhdSizeBytes, diskBlockSize, controllerType) } -func (d *HypervPS4Driver) CreateVirtualMachine(vmName string, path string, harddrivePath string, vhdPath string, ram int64, diskSize int64, switchName string, generation uint, diffDisks bool) error { - return hyperv.CreateVirtualMachine(vmName, path, harddrivePath, vhdPath, ram, diskSize, switchName, generation, diffDisks) +func (d *HypervPS4Driver) CreateVirtualMachine(vmName string, path string, harddrivePath string, vhdPath string, ram int64, diskSize int64, diskBlockSize int64, switchName string, generation uint, diffDisks bool) error { + return hyperv.CreateVirtualMachine(vmName, path, harddrivePath, vhdPath, ram, diskSize, diskBlockSize, switchName, generation, diffDisks) } func (d *HypervPS4Driver) CloneVirtualMachine(cloneFromVmxcPath string, cloneFromVmName string, cloneFromSnapshotName string, cloneAllSnapshots bool, vmName string, path string, harddrivePath string, ram int64, switchName string) error { diff --git a/builder/hyperv/common/step_create_vm.go b/builder/hyperv/common/step_create_vm.go index f046d1a48..f8125ed91 100644 --- a/builder/hyperv/common/step_create_vm.go +++ b/builder/hyperv/common/step_create_vm.go @@ -21,6 +21,7 @@ type StepCreateVM struct { HarddrivePath string RamSize uint DiskSize uint + DiskBlockSize uint Generation uint Cpu uint EnableMacSpoofing bool @@ -64,8 +65,9 @@ func (s *StepCreateVM) Run(_ context.Context, state multistep.StateBag) multiste // convert the MB to bytes ramSize := int64(s.RamSize * 1024 * 1024) diskSize := int64(s.DiskSize * 1024 * 1024) + diskBlockSize := int64(s.DiskBlockSize * 1024 * 1024) - err := driver.CreateVirtualMachine(s.VMName, path, harddrivePath, vhdPath, ramSize, diskSize, s.SwitchName, s.Generation, s.DifferencingDisk) + err := driver.CreateVirtualMachine(s.VMName, path, harddrivePath, vhdPath, ramSize, diskSize, diskBlockSize, s.SwitchName, s.Generation, s.DifferencingDisk) if err != nil { err := fmt.Errorf("Error creating virtual machine: %s", err) state.Put("error", err) @@ -124,7 +126,7 @@ func (s *StepCreateVM) Run(_ context.Context, state multistep.StateBag) multiste for index, size := range s.AdditionalDiskSize { diskSize := int64(size * 1024 * 1024) diskFile := fmt.Sprintf("%s-%d.vhdx", s.VMName, index) - err = driver.AddVirtualMachineHardDrive(s.VMName, vhdPath, diskFile, diskSize, "SCSI") + err = driver.AddVirtualMachineHardDrive(s.VMName, vhdPath, diskFile, diskSize, diskBlockSize, "SCSI") if err != nil { err := fmt.Errorf("Error creating and attaching additional disk drive: %s", err) state.Put("error", err) @@ -163,4 +165,6 @@ func (s *StepCreateVM) Cleanup(state multistep.StateBag) { if err != nil { ui.Error(fmt.Sprintf("Error deleting virtual machine: %s", err)) } + + // TODO: Clean up created VHDX } diff --git a/builder/hyperv/iso/builder.go b/builder/hyperv/iso/builder.go index b92e779df..a95814630 100644 --- a/builder/hyperv/iso/builder.go +++ b/builder/hyperv/iso/builder.go @@ -24,6 +24,10 @@ const ( MinDiskSize = 256 // 256MB MaxDiskSize = 64 * 1024 * 1024 // 64TB + DefaultDiskBlockSize = 32 // 32MB + MinDiskBlockSize = 1 // 1MB + MaxDiskBlockSize = 256 // 256MB + DefaultRamSize = 1 * 1024 // 1GB MinRamSize = 32 // 32MB MaxRamSize = 32 * 1024 // 32GB @@ -55,9 +59,15 @@ type Config struct { // The size, in megabytes, of the hard disk to create for the VM. // By default, this is 130048 (about 127 GB). DiskSize uint `mapstructure:"disk_size"` + + // The size, in megabytes, of the block size used to create the hard disk. + // By default, this is 32768 (about 32 MB) + DiskBlockSize uint `mapstructure:"disk_block_size"` + // The size, in megabytes, of the computer memory in the VM. // By default, this is 1024 (about 1 GB). RamSize uint `mapstructure:"ram_size"` + // SecondaryDvdImages []string `mapstructure:"secondary_iso_images"` @@ -141,6 +151,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } } + err = b.checkDiskBlockSize() + if err != nil { + errs = packer.MultiErrorAppend(errs, err) + } + err = b.checkRamSize() if err != nil { errs = packer.MultiErrorAppend(errs, err) @@ -352,6 +367,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe SwitchName: b.config.SwitchName, RamSize: b.config.RamSize, DiskSize: b.config.DiskSize, + DiskBlockSize: b.config.DiskBlockSize, Generation: b.config.Generation, Cpu: b.config.Cpu, EnableMacSpoofing: b.config.EnableMacSpoofing, @@ -492,6 +508,22 @@ func (b *Builder) checkDiskSize() error { return nil } +func (b *Builder) checkDiskBlockSize() error { + if b.config.DiskBlockSize == 0 { + b.config.DiskBlockSize = DefaultDiskBlockSize + } + + log.Println(fmt.Sprintf("%s: %v", "DiskBlockSize", b.config.DiskBlockSize)) + + if b.config.DiskBlockSize < MinDiskBlockSize { + return fmt.Errorf("disk_block_size: Virtual machine requires disk block size >= %v MB, but defined: %v", MinDiskBlockSize, b.config.DiskBlockSize) + } else if b.config.DiskBlockSize > MaxDiskBlockSize { + return fmt.Errorf("disk_block_size: Virtual machine requires disk block size <= %v MB, but defined: %v", MaxDiskBlockSize, b.config.DiskBlockSize) + } + + return nil +} + func (b *Builder) checkRamSize() error { if b.config.RamSize == 0 { b.config.RamSize = DefaultRamSize diff --git a/builder/hyperv/iso/builder_test.go b/builder/hyperv/iso/builder_test.go index b08a683f9..8523876ef 100644 --- a/builder/hyperv/iso/builder_test.go +++ b/builder/hyperv/iso/builder_test.go @@ -23,6 +23,7 @@ func testConfig() map[string]interface{} { "ssh_username": "foo", "ram_size": 64, "disk_size": 256, + "disk_block_size": 1, "guest_additions_mode": "none", "disk_additional_size": "50000,40000,30000", packer.BuildNameConfigKey: "foo", @@ -86,6 +87,58 @@ func TestBuilderPrepare_DiskSize(t *testing.T) { } } +func TestBuilderPrepare_DiskBlockSize(t *testing.T) { + var b Builder + config := testConfig() + expected_default_block_size := uint(32) + expected_min_block_size := uint(0) + expected_max_block_size := uint(256) + + // Test default with empty disk_block_size + delete(config, "disk_block_size") + warns, err := b.Prepare(config) + if len(warns) > 0 { + t.Fatalf("bad: %#v", warns) + } + if err != nil { + t.Fatalf("bad err: %s", err) + } + if b.config.DiskBlockSize != expected_default_block_size { + t.Fatalf("bad default block size with empty config: %d. Expected %d", b.config.DiskBlockSize, expected_default_block_size) + } + + test_sizes := []uint{0, 1, 32, 256, 512, 1 * 1024, 32 * 1024} + for _, test_size := range test_sizes { + config["disk_block_size"] = test_size + b = Builder{} + warns, err = b.Prepare(config) + if test_size > expected_max_block_size || test_size < expected_min_block_size { + if len(warns) > 0 { + t.Fatalf("bad, should have no warns: %#v", warns) + } + if err == nil { + t.Fatalf("bad, should have error but didn't. disk_block_size=%d outside expected valid range [%d,%d]", test_size, expected_min_block_size, expected_max_block_size) + } + } else { + if len(warns) > 0 { + t.Fatalf("bad: %#v", warns) + } + if err != nil { + t.Fatalf("bad, should not have error: %s", err) + } + if test_size == 0 { + if b.config.DiskBlockSize != expected_default_block_size { + t.Fatalf("bad default block size with 0 value config: %d. Expected: %d", b.config.DiskBlockSize, expected_default_block_size) + } + } else { + if b.config.DiskBlockSize != test_size { + t.Fatalf("bad block size with 0 value config: %d. Expected: %d", b.config.DiskBlockSize, expected_default_block_size) + } + } + } + } +} + func TestBuilderPrepare_FloppyFiles(t *testing.T) { var b Builder config := testConfig() diff --git a/common/powershell/hyperv/hyperv.go b/common/powershell/hyperv/hyperv.go index d26ec5eeb..0a296cce2 100644 --- a/common/powershell/hyperv/hyperv.go +++ b/common/powershell/hyperv/hyperv.go @@ -53,6 +53,22 @@ $ip return cmdOut, err } +//func CreateVirtualHardDiskDrive(vmName string, diskPath string, diskSize int64, diskBlockSize int64, generation uint) (uint, uint, error) { +// +// var script = ` +//param([string]$vmName, [string]$path, +// +// +//[long]$memoryStartupBytes, [long]$newVHDSizeBytes, [string]$switchName, [int]$generation) +//$vhdx = $vmName + '.vhdx' +//$vhdPath = Join-Path -Path $path -ChildPath $vhdx +//New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -NewVHDPath $vhdPath -NewVHDSizeBytes $newVHDSizeBytes -SwitchName $switchName -Generation $generation +//` +// var ps powershell.PowerShellCmd +// err := ps.Run(script, vmName, path, strconv.FormatInt(ram, 10), strconv.FormatInt(diskSize, 10), switchName, strconv.FormatInt(int64(generation), 10)) +// return err +//} + func CreateDvdDrive(vmName string, isoPath string, generation uint) (uint, uint, error) { var ps powershell.PowerShellCmd var script string @@ -187,45 +203,47 @@ Set-VMFloppyDiskDrive -VMName $vmName -Path $null return err } -func CreateVirtualMachine(vmName string, path string, harddrivePath string, vhdRoot string, ram int64, diskSize int64, switchName string, generation uint, diffDisks bool) error { +func CreateVirtualMachine(vmName string, path string, harddrivePath string, vhdRoot string, ram int64, diskSize int64, diskBlockSize int64, switchName string, generation uint, diffDisks bool) error { if generation == 2 { var script = ` -param([string]$vmName, [string]$path, [string]$harddrivePath, [string]$vhdRoot, [long]$memoryStartupBytes, [long]$newVHDSizeBytes, [string]$switchName, [int]$generation, [string]$diffDisks) +param([string]$vmName, [string]$path, [string]$harddrivePath, [string]$vhdRoot, [long]$memoryStartupBytes, [long]$newVHDSizeBytes, [long]$vhdBlockSizeBytes, [string]$switchName, [int]$generation, [string]$diffDisks) $vhdx = $vmName + '.vhdx' $vhdPath = Join-Path -Path $vhdRoot -ChildPath $vhdx if ($harddrivePath){ if($diffDisks -eq "true"){ - New-VHD -Path $vhdPath -ParentPath $harddrivePath -Differencing + New-VHD -Path $vhdPath -ParentPath $harddrivePath -Differencing -BlockSizeBytes $vhdBlockSizeBytes } else { Copy-Item -Path $harddrivePath -Destination $vhdPath } New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -VHDPath $vhdPath -SwitchName $switchName -Generation $generation } else { - New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -NewVHDPath $vhdPath -NewVHDSizeBytes $newVHDSizeBytes -SwitchName $switchName -Generation $generation + New-VHD -Path $vhdPath -SizeBytes $newVHDSizeBytes -BlockSizeBytes $vhdBlockSizeBytes + New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -VHDPath $vhdPath -SwitchName $switchName -Generation $generation } ` var ps powershell.PowerShellCmd - if err := ps.Run(script, vmName, path, harddrivePath, vhdRoot, strconv.FormatInt(ram, 10), strconv.FormatInt(diskSize, 10), switchName, strconv.FormatInt(int64(generation), 10), strconv.FormatBool(diffDisks)); err != nil { + if err := ps.Run(script, vmName, path, harddrivePath, vhdRoot, strconv.FormatInt(ram, 10), strconv.FormatInt(diskSize, 10), strconv.FormatInt(diskBlockSize, 10), switchName, strconv.FormatInt(int64(generation), 10), strconv.FormatBool(diffDisks)); err != nil { return err } return DisableAutomaticCheckpoints(vmName) } else { var script = ` -param([string]$vmName, [string]$path, [string]$harddrivePath, [string]$vhdRoot, [long]$memoryStartupBytes, [long]$newVHDSizeBytes, [string]$switchName, [string]$diffDisks) +param([string]$vmName, [string]$path, [string]$harddrivePath, [string]$vhdRoot, [long]$memoryStartupBytes, [long]$newVHDSizeBytes, [long]$vhdBlockSizeBytes, [string]$switchName, [string]$diffDisks) $vhdx = $vmName + '.vhdx' $vhdPath = Join-Path -Path $vhdRoot -ChildPath $vhdx if ($harddrivePath){ if($diffDisks -eq "true"){ - New-VHD -Path $vhdPath -ParentPath $harddrivePath -Differencing + New-VHD -Path $vhdPath -ParentPath $harddrivePath -Differencing -BlockSizeBytes $vhdBlockSizeBytes } else{ Copy-Item -Path $harddrivePath -Destination $vhdPath } New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -VHDPath $vhdPath -SwitchName $switchName } else { - New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -NewVHDPath $vhdPath -NewVHDSizeBytes $newVHDSizeBytes -SwitchName $switchName + New-VHD -Path $vhdPath -SizeBytes $newVHDSizeBytes -BlockSizeBytes $vhdBlockSizeBytes + New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -VHDPath $vhdPath -SwitchName $switchName } ` var ps powershell.PowerShellCmd @@ -872,16 +890,16 @@ Get-VMNetworkAdapter -VMName $vmName | Connect-VMNetworkAdapter -SwitchName $swi return err } -func AddVirtualMachineHardDiskDrive(vmName string, vhdRoot string, vhdName string, vhdSizeBytes int64, controllerType string) error { +func AddVirtualMachineHardDiskDrive(vmName string, vhdRoot string, vhdName string, vhdSizeBytes int64, vhdBlockSize int64, controllerType string) error { var script = ` -param([string]$vmName,[string]$vhdRoot, [string]$vhdName, [string]$vhdSizeInBytes, [string]$controllerType) +param([string]$vmName,[string]$vhdRoot, [string]$vhdName, [string]$vhdSizeInBytes,[string]$vhdBlockSizeInBize [string]$controllerType) $vhdPath = Join-Path -Path $vhdRoot -ChildPath $vhdName -New-VHD $vhdPath -SizeBytes $vhdSizeInBytes +New-VHD -path $vhdPath -SizeBytes $vhdSizeInBytes -BlockSizeBytes $vhdBlockSizeInBize Add-VMHardDiskDrive -VMName $vmName -path $vhdPath -controllerType $controllerType ` var ps powershell.PowerShellCmd - err := ps.Run(script, vmName, vhdRoot, vhdName, strconv.FormatInt(vhdSizeBytes, 10), controllerType) + err := ps.Run(script, vmName, vhdRoot, vhdName, strconv.FormatInt(vhdSizeBytes, 10), strconv.FormatInt(vhdBlockSize, 10), controllerType) return err } From 3d5592d040456e91aa4b7223d939df2ddfd3dc24 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 23 Feb 2018 11:31:07 -0800 Subject: [PATCH 0545/1216] prepare for Packer 1.2.1 --- version/version.go | 2 +- website/config.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/version/version.go b/version/version.go index 93fffef3e..c48756cab 100644 --- a/version/version.go +++ b/version/version.go @@ -14,7 +14,7 @@ const Version = "1.2.1" // A pre-release marker for the version. If this is "" (empty string) // then it means that it is a final release. Otherwise, this is a pre-release // such as "dev" (in development), "beta", "rc1", etc. -const VersionPrerelease = "dev" +const VersionPrerelease = "" func FormattedVersion() string { var versionString bytes.Buffer diff --git a/website/config.rb b/website/config.rb index e6d530fdc..63b2de323 100644 --- a/website/config.rb +++ b/website/config.rb @@ -2,7 +2,7 @@ set :base_url, "https://www.packer.io/" activate :hashicorp do |h| h.name = "packer" - h.version = "1.2.0" + h.version = "1.2.1" h.github_slug = "hashicorp/packer" h.website_root = "website" end From fb86a61100586dc9b4a85f533f86a943d6282850 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 23 Feb 2018 11:57:57 -0800 Subject: [PATCH 0547/1216] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 044f747e5..2cc52892f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## 1.2.1 (February 23, 2918) +## 1.2.1 (February 23, 2018) ### BUG FIXES: From 19bec4351448068e6f76f996a67cf2989fea9e08 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 23 Feb 2018 11:59:02 -0800 Subject: [PATCH 0548/1216] next version is 1.2.2 --- version/version.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version/version.go b/version/version.go index c48756cab..870a3badd 100644 --- a/version/version.go +++ b/version/version.go @@ -9,12 +9,12 @@ import ( var GitCommit string // The main version number that is being run at the moment. -const Version = "1.2.1" +const Version = "1.2.2" // A pre-release marker for the version. If this is "" (empty string) // then it means that it is a final release. Otherwise, this is a pre-release // such as "dev" (in development), "beta", "rc1", etc. -const VersionPrerelease = "" +const VersionPrerelease = "dev" func FormattedVersion() string { var versionString bytes.Buffer From 487b1d716706c746b5eca793b64a4b3830e0b7ae Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 23 Feb 2018 12:13:47 -0800 Subject: [PATCH 0549/1216] somehow fix gemfile.lock --- website/Gemfile.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/website/Gemfile.lock b/website/Gemfile.lock index 3f43b760a..acf65a9ef 100644 --- a/website/Gemfile.lock +++ b/website/Gemfile.lock @@ -6,7 +6,7 @@ GEM minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) - autoprefixer-rails (7.2.4) + autoprefixer-rails (7.1.5) execjs bootstrap-sass (3.3.7) autoprefixer-rails (>= 5.2.1) @@ -51,7 +51,7 @@ GEM http_parser.rb (0.6.0) i18n (0.7.0) json (2.1.0) - kramdown (1.16.2) + kramdown (1.15.0) listen (3.0.8) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) @@ -102,8 +102,8 @@ GEM mime-types-data (~> 3.2015) mime-types-data (3.2016.0521) mini_portile2 (2.3.0) - minitest (5.11.1) - multi_json (1.13.0) + minitest (5.10.3) + multi_json (1.12.2) nokogiri (1.8.1) mini_portile2 (~> 2.3.0) padrino-helpers (0.12.8.1) @@ -115,7 +115,7 @@ GEM rack (1.6.8) rack-livereload (0.3.16) rack - rack-test (0.8.2) + rack-test (0.7.0) rack (>= 1.0, < 3) rb-fsevent (0.10.2) rb-inotify (0.9.10) @@ -137,10 +137,10 @@ GEM thor (0.20.0) thread_safe (0.3.6) tilt (1.4.1) - turbolinks (5.1.0) - turbolinks-source (~> 5.1) - turbolinks-source (5.1.0) - tzinfo (1.2.4) + turbolinks (5.0.1) + turbolinks-source (~> 5) + turbolinks-source (5.0.3) + tzinfo (1.2.3) thread_safe (~> 0.1) uber (0.0.15) uglifier (2.7.2) @@ -156,4 +156,4 @@ DEPENDENCIES middleman-hashicorp (= 0.3.29) BUNDLED WITH - 1.16.1 + 1.15.4 From efcdbfeab97b8a65d5a514f4671c53227468b61a Mon Sep 17 00:00:00 2001 From: jmajoor Date: Fri, 23 Feb 2018 15:34:13 -0800 Subject: [PATCH 0550/1216] Add support for optionally building Azure VMs with additional disks. --- builder/azure/arm/artifact.go | 30 +++ builder/azure/arm/artifact_test.go | 113 +++++++++ builder/azure/arm/builder.go | 2 + builder/azure/arm/capture_template.go | 3 +- builder/azure/arm/capture_template_test.go | 64 ++++- builder/azure/arm/config.go | 3 + .../azure/arm/step_delete_additional_disks.go | 106 ++++++++ .../arm/step_delete_additional_disks_test.go | 227 ++++++++++++++++++ .../azure/arm/step_get_additional_disks.go | 78 ++++++ .../arm/step_get_additional_disks_test.go | 131 ++++++++++ builder/azure/arm/template_factory.go | 4 + ...stVirtualMachineDeployment11.approved.json | 177 ++++++++++++++ ...stVirtualMachineDeployment12.approved.json | 178 ++++++++++++++ builder/azure/arm/template_factory_test.go | 70 ++++++ builder/azure/common/constants/stateBag.go | 1 + builder/azure/common/template/template.go | 13 + .../azure/common/template/template_builder.go | 29 +++ ...lder_test.TestBuildWindows01.approved.json | 202 ++++++++++++++++ ...lder_test.TestBuildWindows02.approved.json | 195 +++++++++++++++ .../common/template/template_builder_test.go | 61 +++++ website/source/docs/builders/azure.html.md | 4 + 21 files changed, 1689 insertions(+), 2 deletions(-) create mode 100644 builder/azure/arm/step_delete_additional_disks.go create mode 100644 builder/azure/arm/step_delete_additional_disks_test.go create mode 100644 builder/azure/arm/step_get_additional_disks.go create mode 100644 builder/azure/arm/step_get_additional_disks_test.go create mode 100644 builder/azure/arm/template_factory_test.TestVirtualMachineDeployment11.approved.json create mode 100644 builder/azure/arm/template_factory_test.TestVirtualMachineDeployment12.approved.json create mode 100644 builder/azure/common/template/template_builder_test.TestBuildWindows01.approved.json create mode 100644 builder/azure/common/template/template_builder_test.TestBuildWindows02.approved.json diff --git a/builder/azure/arm/artifact.go b/builder/azure/arm/artifact.go index e8a72091f..c2abcc4d5 100644 --- a/builder/azure/arm/artifact.go +++ b/builder/azure/arm/artifact.go @@ -12,6 +12,11 @@ const ( BuilderId = "Azure.ResourceManagement.VMImage" ) +type AdditionalDiskArtifact struct { + AdditionalDiskUri string + AdditionalDiskUriReadOnlySas string +} + type Artifact struct { // VHD StorageAccountLocation string @@ -24,6 +29,9 @@ type Artifact struct { ManagedImageResourceGroupName string ManagedImageName string ManagedImageLocation string + + // Additional Disks + AdditionalDisks *[]AdditionalDiskArtifact } func NewManagedImageArtifact(resourceGroup, name, location string) (*Artifact, error) { @@ -53,12 +61,28 @@ func NewArtifact(template *CaptureTemplate, getSasUrl func(name string) string) return nil, err } + var additional_disks *[]AdditionalDiskArtifact + if template.Resources[0].Properties.StorageProfile.DataDisks != nil { + data_disks := make([]AdditionalDiskArtifact, len(template.Resources[0].Properties.StorageProfile.DataDisks)) + for i, additionaldisk := range template.Resources[0].Properties.StorageProfile.DataDisks { + additionalVhdUri, err := url.Parse(additionaldisk.Image.Uri) + if err != nil { + return nil, err + } + data_disks[i].AdditionalDiskUri = additionalVhdUri.String() + data_disks[i].AdditionalDiskUriReadOnlySas = getSasUrl(getStorageUrlPath(additionalVhdUri)) + } + additional_disks = &data_disks + } + return &Artifact{ OSDiskUri: vhdUri.String(), OSDiskUriReadOnlySas: getSasUrl(getStorageUrlPath(vhdUri)), TemplateUri: templateUri.String(), TemplateUriReadOnlySas: getSasUrl(getStorageUrlPath(templateUri)), + AdditionalDisks: additional_disks, + StorageAccountLocation: template.Resources[0].Location, }, nil } @@ -128,6 +152,12 @@ func (a *Artifact) String() string { buf.WriteString(fmt.Sprintf("OSDiskUriReadOnlySas: %s\n", a.OSDiskUriReadOnlySas)) buf.WriteString(fmt.Sprintf("TemplateUri: %s\n", a.TemplateUri)) buf.WriteString(fmt.Sprintf("TemplateUriReadOnlySas: %s\n", a.TemplateUriReadOnlySas)) + if a.AdditionalDisks != nil { + for i, additionaldisk := range *a.AdditionalDisks { + buf.WriteString(fmt.Sprintf("AdditionalDiskUri (datadisk-%d): %s\n", i+1, additionaldisk.AdditionalDiskUri)) + buf.WriteString(fmt.Sprintf("AdditionalDiskUriReadOnlySas (datadisk-%d): %s\n", i+1, additionaldisk.AdditionalDiskUriReadOnlySas)) + } + } } return buf.String() diff --git a/builder/azure/arm/artifact_test.go b/builder/azure/arm/artifact_test.go index 1b9a472af..13c77e2e3 100644 --- a/builder/azure/arm/artifact_test.go +++ b/builder/azure/arm/artifact_test.go @@ -82,6 +82,60 @@ func TestArtifactString(t *testing.T) { } } +func TestAdditionalDiskArtifactString(t *testing.T) { + template := CaptureTemplate{ + Resources: []CaptureResources{ + { + Properties: CaptureProperties{ + StorageProfile: CaptureStorageProfile{ + OSDisk: CaptureDisk{ + Image: CaptureUri{ + Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd", + }, + }, + DataDisks: []CaptureDisk{ + CaptureDisk{ + Image: CaptureUri{ + Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-1.4085bb15-3644-4641-b9cd-f575918640b4.vhd", + }, + }, + }, + }, + }, + Location: "southcentralus", + }, + }, + } + + artifact, err := NewArtifact(&template, getFakeSasUrl) + if err != nil { + t.Fatalf("err=%s", err) + } + + testSubject := artifact.String() + if !strings.Contains(testSubject, "OSDiskUri: https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd") { + t.Errorf("Expected String() output to contain OSDiskUri") + } + if !strings.Contains(testSubject, "OSDiskUriReadOnlySas: SAS-Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd") { + t.Errorf("Expected String() output to contain OSDiskUriReadOnlySas") + } + if !strings.Contains(testSubject, "TemplateUri: https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json") { + t.Errorf("Expected String() output to contain TemplateUri") + } + if !strings.Contains(testSubject, "TemplateUriReadOnlySas: SAS-Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json") { + t.Errorf("Expected String() output to contain TemplateUriReadOnlySas") + } + if !strings.Contains(testSubject, "StorageAccountLocation: southcentralus") { + t.Errorf("Expected String() output to contain StorageAccountLocation") + } + if !strings.Contains(testSubject, "AdditionalDiskUri (datadisk-1): https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-1.4085bb15-3644-4641-b9cd-f575918640b4.vhd") { + t.Errorf("Expected String() output to contain AdditionalDiskUri") + } + if !strings.Contains(testSubject, "AdditionalDiskUriReadOnlySas (datadisk-1): SAS-Images/images/packer-datadisk-1.4085bb15-3644-4641-b9cd-f575918640b4.vhd") { + t.Errorf("Expected String() output to contain AdditionalDiskUriReadOnlySas") + } +} + func TestArtifactProperties(t *testing.T) { template := CaptureTemplate{ Resources: []CaptureResources{ @@ -122,6 +176,65 @@ func TestArtifactProperties(t *testing.T) { } } +func TestAdditionalDiskArtifactProperties(t *testing.T) { + template := CaptureTemplate{ + Resources: []CaptureResources{ + { + Properties: CaptureProperties{ + StorageProfile: CaptureStorageProfile{ + OSDisk: CaptureDisk{ + Image: CaptureUri{ + Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd", + }, + }, + DataDisks: []CaptureDisk{ + CaptureDisk{ + Image: CaptureUri{ + Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-1.4085bb15-3644-4641-b9cd-f575918640b4.vhd", + }, + }, + }, + }, + }, + Location: "southcentralus", + }, + }, + } + + testSubject, err := NewArtifact(&template, getFakeSasUrl) + if err != nil { + t.Fatalf("err=%s", err) + } + + if testSubject.OSDiskUri != "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd" { + t.Errorf("Expected template to be 'https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd', but got %s", testSubject.OSDiskUri) + } + if testSubject.OSDiskUriReadOnlySas != "SAS-Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd" { + t.Errorf("Expected template to be 'SAS-Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd', but got %s", testSubject.OSDiskUriReadOnlySas) + } + if testSubject.TemplateUri != "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json" { + t.Errorf("Expected template to be 'https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json', but got %s", testSubject.TemplateUri) + } + if testSubject.TemplateUriReadOnlySas != "SAS-Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json" { + t.Errorf("Expected template to be 'SAS-Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json', but got %s", testSubject.TemplateUriReadOnlySas) + } + if testSubject.StorageAccountLocation != "southcentralus" { + t.Errorf("Expected StorageAccountLocation to be 'southcentral', but got %s", testSubject.StorageAccountLocation) + } + if testSubject.AdditionalDisks == nil { + t.Errorf("Expected AdditionalDisks to be not nil") + } + if len(*testSubject.AdditionalDisks) != 1 { + t.Errorf("Expected AdditionalDisks to have one additional disk, but got %d", len(*testSubject.AdditionalDisks)) + } + if (*testSubject.AdditionalDisks)[0].AdditionalDiskUri != "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-1.4085bb15-3644-4641-b9cd-f575918640b4.vhd" { + t.Errorf("Expected additional disk uri to be 'https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-1.4085bb15-3644-4641-b9cd-f575918640b4.vhd', but got %s", (*testSubject.AdditionalDisks)[0].AdditionalDiskUri) + } + if (*testSubject.AdditionalDisks)[0].AdditionalDiskUriReadOnlySas != "SAS-Images/images/packer-datadisk-1.4085bb15-3644-4641-b9cd-f575918640b4.vhd" { + t.Errorf("Expected additional disk sas to be 'SAS-Images/images/packer-datadisk-1.4085bb15-3644-4641-b9cd-f575918640b4.vhd', but got %s", (*testSubject.AdditionalDisks)[0].AdditionalDiskUriReadOnlySas) + } +} + func TestArtifactOverHypenatedCaptureUri(t *testing.T) { template := CaptureTemplate{ Resources: []CaptureResources{ diff --git a/builder/azure/arm/builder.go b/builder/azure/arm/builder.go index 62afe6fa5..64d791ffa 100644 --- a/builder/azure/arm/builder.go +++ b/builder/azure/arm/builder.go @@ -181,10 +181,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe }, &packerCommon.StepProvision{}, NewStepGetOSDisk(azureClient, ui), + NewStepGetAdditionalDisks(azureClient, ui), NewStepPowerOffCompute(azureClient, ui), NewStepCaptureImage(azureClient, ui), NewStepDeleteResourceGroup(azureClient, ui), NewStepDeleteOSDisk(azureClient, ui), + NewStepDeleteAdditionalDisks(azureClient, ui), } } else { return nil, fmt.Errorf("Builder does not support the os_type '%s'", b.config.OSType) diff --git a/builder/azure/arm/capture_template.go b/builder/azure/arm/capture_template.go index 0332a02f4..2ba2c48b8 100644 --- a/builder/azure/arm/capture_template.go +++ b/builder/azure/arm/capture_template.go @@ -23,7 +23,8 @@ type CaptureDisk struct { } type CaptureStorageProfile struct { - OSDisk CaptureDisk `json:"osDisk"` + OSDisk CaptureDisk `json:"osDisk"` + DataDisks []CaptureDisk `json:"dataDisks"` } type CaptureOSProfile struct { diff --git a/builder/azure/arm/capture_template_test.go b/builder/azure/arm/capture_template_test.go index 8c7a71405..10be51ee8 100644 --- a/builder/azure/arm/capture_template_test.go +++ b/builder/azure/arm/capture_template_test.go @@ -51,7 +51,33 @@ var captureTemplate01 = `{ "uri": "http://storage.blob.core.windows.net/vmcontainerce1a1b75-f480-47cb-8e6e-55142e4a5f68/osDisk.ce1a1b75-f480-47cb-8e6e-55142e4a5f68.vhd" }, "caching": "ReadWrite" - } + }, + "dataDisks": [ + { + "lun": 0, + "name": "packer-datadisk-0.32118633-6dc9-449f-83b6-a7d2983bec14.vhd", + "createOption": "Empty", + "image": { + "uri": "http://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-0.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" + }, + "vhd": { + "uri": "http://storage.blob.core.windows.net/vmcontainerce1a1b75-f480-47cb-8e6e-55142e4a5f68/datadisk-0.ce1a1b75-f480-47cb-8e6e-55142e4a5f68.vhd" + }, + "caching": "ReadWrite" + }, + { + "lun": 1, + "name": "packer-datadisk-1.32118633-6dc9-449f-83b6-a7d2983bec14.vhd", + "createOption": "Empty", + "image": { + "uri": "http://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-1.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" + }, + "vhd": { + "uri": "http://storage.blob.core.windows.net/vmcontainerce1a1b75-f480-47cb-8e6e-55142e4a5f68/datadisk-1.ce1a1b75-f480-47cb-8e6e-55142e4a5f68.vhd" + }, + "caching": "ReadWrite" + } + ] }, "osProfile": { "computerName": "[parameters('vmName')]", @@ -169,6 +195,42 @@ func TestCaptureParseJson(t *testing.T) { t.Errorf("Resources[0].Properties.StorageProfile.OSDisk.Caching's value was unexpected: %s", osDisk.Caching) } + // == Resources/Properties/StorageProfile/DataDisks ================ + dataDisks := testSubject.Resources[0].Properties.StorageProfile.DataDisks + if len(dataDisks) != 2 { + t.Errorf("Resources[0].Properties.StorageProfile.DataDisks, 2 disks expected but was: %d", len(dataDisks)) + } + if dataDisks[0].Name != "packer-datadisk-0.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" { + t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[0].Name's value was unexpected: %s", dataDisks[0].Name) + } + if dataDisks[0].CreateOption != "Empty" { + t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[0].CreateOption's value was unexpected: %s", dataDisks[0].CreateOption) + } + if dataDisks[0].Image.Uri != "http://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-0.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" { + t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[0].Image.Uri's value was unexpected: %s", dataDisks[0].Image.Uri) + } + if dataDisks[0].Vhd.Uri != "http://storage.blob.core.windows.net/vmcontainerce1a1b75-f480-47cb-8e6e-55142e4a5f68/datadisk-0.ce1a1b75-f480-47cb-8e6e-55142e4a5f68.vhd" { + t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[0].Vhd.Uri's value was unexpected: %s", dataDisks[0].Vhd.Uri) + } + if dataDisks[0].Caching != "ReadWrite" { + t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[0].Caching's value was unexpected: %s", dataDisks[0].Caching) + } + if dataDisks[1].Name != "packer-datadisk-1.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" { + t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[1].Name's value was unexpected: %s", dataDisks[1].Name) + } + if dataDisks[1].CreateOption != "Empty" { + t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[1].CreateOption's value was unexpected: %s", dataDisks[1].CreateOption) + } + if dataDisks[1].Image.Uri != "http://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-1.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" { + t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[1].Image.Uri's value was unexpected: %s", dataDisks[1].Image.Uri) + } + if dataDisks[1].Vhd.Uri != "http://storage.blob.core.windows.net/vmcontainerce1a1b75-f480-47cb-8e6e-55142e4a5f68/datadisk-1.ce1a1b75-f480-47cb-8e6e-55142e4a5f68.vhd" { + t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[1].Vhd.Uri's value was unexpected: %s", dataDisks[1].Vhd.Uri) + } + if dataDisks[1].Caching != "ReadWrite" { + t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[1].Caching's value was unexpected: %s", dataDisks[1].Caching) + } + // == Resources/Properties/OSProfile ============================ osProfile := testSubject.Resources[0].Properties.OSProfile if osProfile.AdminPassword != "[parameters('adminPassword')]" { diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index f384c88bc..afa5f62a7 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -112,6 +112,9 @@ type Config struct { OSType string `mapstructure:"os_type"` OSDiskSizeGB int32 `mapstructure:"os_disk_size_gb"` + // Additional Disks + AdditionalDiskSize []int32 `mapstructure:"disk_additional_size"` + // Runtime Values UserName string Password string diff --git a/builder/azure/arm/step_delete_additional_disks.go b/builder/azure/arm/step_delete_additional_disks.go new file mode 100644 index 000000000..d43bf8253 --- /dev/null +++ b/builder/azure/arm/step_delete_additional_disks.go @@ -0,0 +1,106 @@ +package arm + +import ( + "context" + "errors" + "fmt" + "net/url" + "strings" + + "github.com/hashicorp/packer/builder/azure/common/constants" + + "github.com/hashicorp/packer/helper/multistep" + "github.com/hashicorp/packer/packer" +) + +type StepDeleteAdditionalDisk struct { + client *AzureClient + delete func(string, string) error + deleteManaged func(string, string) error + say func(message string) + error func(e error) +} + +func NewStepDeleteAdditionalDisks(client *AzureClient, ui packer.Ui) *StepDeleteAdditionalDisk { + var step = &StepDeleteAdditionalDisk{ + client: client, + say: func(message string) { ui.Say(message) }, + error: func(e error) { ui.Error(e.Error()) }, + } + + step.delete = step.deleteBlob + step.deleteManaged = step.deleteManagedDisk + return step +} + +func (s *StepDeleteAdditionalDisk) deleteBlob(storageContainerName string, blobName string) error { + blob := s.client.BlobStorageClient.GetContainerReference(storageContainerName).GetBlobReference(blobName) + err := blob.Delete(nil) + + if err != nil { + s.say(s.client.LastError.Error()) + } + return err +} + +func (s *StepDeleteAdditionalDisk) deleteManagedDisk(resourceGroupName string, imageName string) error { + xs := strings.Split(imageName, "/") + diskName := xs[len(xs)-1] + _, errChan := s.client.DisksClient.Delete(resourceGroupName, diskName, nil) + err := <-errChan + return err +} + +func (s *StepDeleteAdditionalDisk) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { + s.say("Deleting the temporary Additional disk ...") + + var dataDisks = state.Get(constants.ArmAdditionalDiskVhds).([]string) + var isManagedDisk = state.Get(constants.ArmIsManagedImage).(bool) + var isExistingResourceGroup = state.Get(constants.ArmIsExistingResourceGroup).(bool) + var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) + + if dataDisks == nil { + s.say(fmt.Sprintf(" -> No Additional Disks specified")) + return multistep.ActionContinue + } + + if isManagedDisk && !isExistingResourceGroup { + s.say(fmt.Sprintf(" -> Additional Disk : skipping, managed disk was used...")) + return multistep.ActionContinue + } + + for i, additionaldisk := range dataDisks { + s.say(fmt.Sprintf(" -> Additional Disk %d: '%s'", i+1, additionaldisk)) + var err error + if isManagedDisk { + err = s.deleteManaged(resourceGroupName, additionaldisk) + if err != nil { + s.say("Failed to delete the managed Additional Disk!") + return processStepResult(err, s.error, state) + } + } else { + u, err := url.Parse(additionaldisk) + if err != nil { + s.say("Failed to parse the Additional Disk's VHD URI!") + return processStepResult(err, s.error, state) + } + + xs := strings.Split(u.Path, "/") + if len(xs) < 3 { + err = errors.New("Failed to parse Additional Disk's VHD URI!") + } else { + var storageAccountName = xs[1] + var blobName = strings.Join(xs[2:], "/") + + err = s.delete(storageAccountName, blobName) + } + if err != nil { + return processStepResult(err, s.error, state) + } + } + } + return multistep.ActionContinue +} + +func (*StepDeleteAdditionalDisk) Cleanup(multistep.StateBag) { +} diff --git a/builder/azure/arm/step_delete_additional_disks_test.go b/builder/azure/arm/step_delete_additional_disks_test.go new file mode 100644 index 000000000..80ec5b42e --- /dev/null +++ b/builder/azure/arm/step_delete_additional_disks_test.go @@ -0,0 +1,227 @@ +package arm + +import ( + "context" + "errors" + "fmt" + "testing" + + "github.com/hashicorp/packer/builder/azure/common/constants" + "github.com/hashicorp/packer/helper/multistep" +) + +func TestStepDeleteAdditionalDiskShouldFailIfGetFails(t *testing.T) { + var testSubject = &StepDeleteAdditionalDisk{ + delete: func(string, string) error { return fmt.Errorf("!! Unit Test FAIL !!") }, + deleteManaged: func(string, string) error { return nil }, + say: func(message string) {}, + error: func(e error) {}, + } + + stateBag := DeleteTestStateBagStepDeleteAdditionalDisk([]string{"http://storage.blob.core.windows.net/images/pkrvm_os.vhd"}) + + var result = testSubject.Run(context.Background(), stateBag) + if result != multistep.ActionHalt { + t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result) + } + + if _, ok := stateBag.GetOk(constants.Error); ok == false { + t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error) + } +} + +func TestStepDeleteAdditionalDiskShouldPassIfGetPasses(t *testing.T) { + var testSubject = &StepDeleteAdditionalDisk{ + delete: func(string, string) error { return nil }, + say: func(message string) {}, + error: func(e error) {}, + } + + stateBag := DeleteTestStateBagStepDeleteAdditionalDisk([]string{"http://storage.blob.core.windows.net/images/pkrvm_os.vhd"}) + + var result = testSubject.Run(context.Background(), stateBag) + if result != multistep.ActionContinue { + t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) + } + + if _, ok := stateBag.GetOk(constants.Error); ok == true { + t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error) + } +} + +func TestStepDeleteAdditionalDiskShouldTakeStepArgumentsFromStateBag(t *testing.T) { + var actualStorageContainerName string + var actualBlobName string + + var testSubject = &StepDeleteAdditionalDisk{ + delete: func(storageContainerName string, blobName string) error { + actualStorageContainerName = storageContainerName + actualBlobName = blobName + return nil + }, + say: func(message string) {}, + error: func(e error) {}, + } + + stateBag := DeleteTestStateBagStepDeleteAdditionalDisk([]string{"http://storage.blob.core.windows.net/images/pkrvm_os.vhd"}) + var result = testSubject.Run(context.Background(), stateBag) + + if result != multistep.ActionContinue { + t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) + } + + if actualStorageContainerName != "images" { + t.Fatalf("Expected the storage container name to be 'images', but found '%s'.", actualStorageContainerName) + } + + if actualBlobName != "pkrvm_os.vhd" { + t.Fatalf("Expected the blob name to be 'pkrvm_os.vhd', but found '%s'.", actualBlobName) + } +} + +func TestStepDeleteAdditionalDiskShouldHandleComplexStorageContainerNames(t *testing.T) { + var actualStorageContainerName string + var actualBlobName string + + var testSubject = &StepDeleteAdditionalDisk{ + delete: func(storageContainerName string, blobName string) error { + actualStorageContainerName = storageContainerName + actualBlobName = blobName + return nil + }, + say: func(message string) {}, + error: func(e error) {}, + } + + stateBag := DeleteTestStateBagStepDeleteAdditionalDisk([]string{"http://storage.blob.core.windows.net/abc/def/pkrvm_os.vhd"}) + testSubject.Run(context.Background(), stateBag) + + if actualStorageContainerName != "abc" { + t.Fatalf("Expected the storage container name to be 'abc/def', but found '%s'.", actualStorageContainerName) + } + + if actualBlobName != "def/pkrvm_os.vhd" { + t.Fatalf("Expected the blob name to be 'pkrvm_os.vhd', but found '%s'.", actualBlobName) + } +} + +func TestStepDeleteAdditionalDiskShouldFailIfVHDNameCannotBeURLParsed(t *testing.T) { + var testSubject = &StepDeleteAdditionalDisk{ + delete: func(string, string) error { return nil }, + say: func(message string) {}, + error: func(e error) {}, + deleteManaged: func(string, string) error { return nil }, + } + + // Invalid URL per https://golang.org/src/net/url/url_test.go + stateBag := DeleteTestStateBagStepDeleteAdditionalDisk([]string{"http://[fe80::1%en0]/"}) + + var result = testSubject.Run(context.Background(), stateBag) + if result != multistep.ActionHalt { + t.Fatalf("Expected the step to return 'ActionHalt', but got '%v'.", result) + } + + if _, ok := stateBag.GetOk(constants.Error); ok == false { + t.Fatalf("Expected the step to not stateBag['%s'], but it was.", constants.Error) + } +} +func TestStepDeleteAdditionalDiskShouldFailIfVHDNameIsTooShort(t *testing.T) { + var testSubject = &StepDeleteAdditionalDisk{ + delete: func(string, string) error { return nil }, + say: func(message string) {}, + error: func(e error) {}, + deleteManaged: func(string, string) error { return nil }, + } + + stateBag := DeleteTestStateBagStepDeleteAdditionalDisk([]string{"storage.blob.core.windows.net/abc"}) + + var result = testSubject.Run(context.Background(), stateBag) + if result != multistep.ActionHalt { + t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result) + } + + if _, ok := stateBag.GetOk(constants.Error); ok == false { + t.Fatalf("Expected the step to not stateBag['%s'], but it was.", constants.Error) + } +} + +func TestStepDeleteAdditionalDiskShouldPassIfManagedDiskInTempResourceGroup(t *testing.T) { + var testSubject = &StepDeleteAdditionalDisk{ + delete: func(string, string) error { return nil }, + say: func(message string) {}, + error: func(e error) {}, + } + + stateBag := new(multistep.BasicStateBag) + stateBag.Put(constants.ArmAdditionalDiskVhds, []string{"subscriptions/123-456-789/resourceGroups/existingresourcegroup/providers/Microsoft.Compute/disks/osdisk"}) + stateBag.Put(constants.ArmIsManagedImage, true) + stateBag.Put(constants.ArmIsExistingResourceGroup, false) + stateBag.Put(constants.ArmResourceGroupName, "testgroup") + + var result = testSubject.Run(context.Background(), stateBag) + if result != multistep.ActionContinue { + t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) + } + + if _, ok := stateBag.GetOk(constants.Error); ok == true { + t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error) + } +} + +func TestStepDeleteAdditionalDiskShouldFailIfManagedDiskInExistingResourceGroupFailsToDelete(t *testing.T) { + var testSubject = &StepDeleteAdditionalDisk{ + delete: func(string, string) error { return nil }, + say: func(message string) {}, + error: func(e error) {}, + deleteManaged: func(string, string) error { return errors.New("UNIT TEST FAIL!") }, + } + + stateBag := new(multistep.BasicStateBag) + stateBag.Put(constants.ArmAdditionalDiskVhds, []string{"subscriptions/123-456-789/resourceGroups/existingresourcegroup/providers/Microsoft.Compute/disks/osdisk"}) + stateBag.Put(constants.ArmIsManagedImage, true) + stateBag.Put(constants.ArmIsExistingResourceGroup, true) + stateBag.Put(constants.ArmResourceGroupName, "testgroup") + + var result = testSubject.Run(context.Background(), stateBag) + if result != multistep.ActionHalt { + t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result) + } + + if _, ok := stateBag.GetOk(constants.Error); ok == false { + t.Fatalf("Expected the step to not stateBag['%s'], but it was.", constants.Error) + } +} + +func TestStepDeleteAdditionalDiskShouldFailIfManagedDiskInExistingResourceGroupIsDeleted(t *testing.T) { + var testSubject = &StepDeleteAdditionalDisk{ + delete: func(string, string) error { return nil }, + say: func(message string) {}, + error: func(e error) {}, + deleteManaged: func(string, string) error { return nil }, + } + + stateBag := new(multistep.BasicStateBag) + stateBag.Put(constants.ArmAdditionalDiskVhds, []string{"subscriptions/123-456-789/resourceGroups/existingresourcegroup/providers/Microsoft.Compute/disks/osdisk"}) + stateBag.Put(constants.ArmIsManagedImage, true) + stateBag.Put(constants.ArmIsExistingResourceGroup, true) + stateBag.Put(constants.ArmResourceGroupName, "testgroup") + + var result = testSubject.Run(context.Background(), stateBag) + if result != multistep.ActionContinue { + t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) + } + + if _, ok := stateBag.GetOk(constants.Error); ok == true { + t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error) + } +} + +func DeleteTestStateBagStepDeleteAdditionalDisk(osDiskVhds []string) multistep.StateBag { + stateBag := new(multistep.BasicStateBag) + stateBag.Put(constants.ArmAdditionalDiskVhds, osDiskVhds) + stateBag.Put(constants.ArmIsManagedImage, false) + stateBag.Put(constants.ArmIsExistingResourceGroup, false) + stateBag.Put(constants.ArmResourceGroupName, "testgroup") + + return stateBag +} diff --git a/builder/azure/arm/step_get_additional_disks.go b/builder/azure/arm/step_get_additional_disks.go new file mode 100644 index 000000000..68af51724 --- /dev/null +++ b/builder/azure/arm/step_get_additional_disks.go @@ -0,0 +1,78 @@ +package arm + +import ( + "context" + "fmt" + + "github.com/Azure/azure-sdk-for-go/arm/compute" + + "github.com/hashicorp/packer/builder/azure/common/constants" + + "github.com/hashicorp/packer/helper/multistep" + "github.com/hashicorp/packer/packer" +) + +type StepGetDataDisk struct { + client *AzureClient + query func(resourceGroupName string, computeName string) (compute.VirtualMachine, error) + say func(message string) + error func(e error) +} + +func NewStepGetAdditionalDisks(client *AzureClient, ui packer.Ui) *StepGetDataDisk { + var step = &StepGetDataDisk{ + client: client, + say: func(message string) { ui.Say(message) }, + error: func(e error) { ui.Error(e.Error()) }, + } + + step.query = step.queryCompute + return step +} + +func (s *StepGetDataDisk) queryCompute(resourceGroupName string, computeName string) (compute.VirtualMachine, error) { + vm, err := s.client.VirtualMachinesClient.Get(resourceGroupName, computeName, "") + if err != nil { + s.say(s.client.LastError.Error()) + } + return vm, err +} + +func (s *StepGetDataDisk) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { + s.say("Querying the machine's additional disks properties ...") + + var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) + var computeName = state.Get(constants.ArmComputeName).(string) + + s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName)) + s.say(fmt.Sprintf(" -> ComputeName : '%s'", computeName)) + + vm, err := s.query(resourceGroupName, computeName) + if err != nil { + state.Put(constants.Error, err) + s.error(err) + + return multistep.ActionHalt + } + + if vm.StorageProfile.DataDisks != nil { + var vhdUri string + additional_disks := make([]string, len(*vm.StorageProfile.DataDisks)) + for i, additionaldisk := range *vm.StorageProfile.DataDisks { + if additionaldisk.Vhd != nil { + vhdUri = *additionaldisk.Vhd.URI + s.say(fmt.Sprintf(" -> Additional Disk %d : '%s'", i+1, vhdUri)) + } else { + vhdUri = *additionaldisk.ManagedDisk.ID + s.say(fmt.Sprintf(" -> Managed Additional Disk %d : '%s'", i+1, vhdUri)) + } + additional_disks[i] = vhdUri + } + state.Put(constants.ArmAdditionalDiskVhds, additional_disks) + } + + return multistep.ActionContinue +} + +func (*StepGetDataDisk) Cleanup(multistep.StateBag) { +} diff --git a/builder/azure/arm/step_get_additional_disks_test.go b/builder/azure/arm/step_get_additional_disks_test.go new file mode 100644 index 000000000..1c6dd3380 --- /dev/null +++ b/builder/azure/arm/step_get_additional_disks_test.go @@ -0,0 +1,131 @@ +package arm + +import ( + "context" + "fmt" + "testing" + + "github.com/Azure/azure-sdk-for-go/arm/compute" + + "github.com/hashicorp/packer/builder/azure/common/constants" + + "github.com/hashicorp/packer/helper/multistep" +) + +func TestStepGetAdditionalDiskShouldFailIfGetFails(t *testing.T) { + var testSubject = &StepGetDataDisk{ + query: func(string, string) (compute.VirtualMachine, error) { + return createVirtualMachineWithDataDisksFromUri("test.vhd"), fmt.Errorf("!! Unit Test FAIL !!") + }, + say: func(message string) {}, + error: func(e error) {}, + } + + stateBag := createTestStateBagStepGetAdditionalDisks() + + var result = testSubject.Run(context.Background(), stateBag) + if result != multistep.ActionHalt { + t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result) + } + + if _, ok := stateBag.GetOk(constants.Error); ok == false { + t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error) + } +} + +func TestStepGetAdditionalDiskShouldPassIfGetPasses(t *testing.T) { + var testSubject = &StepGetDataDisk{ + query: func(string, string) (compute.VirtualMachine, error) { + return createVirtualMachineWithDataDisksFromUri("test.vhd"), nil + }, + say: func(message string) {}, + error: func(e error) {}, + } + + stateBag := createTestStateBagStepGetAdditionalDisks() + + var result = testSubject.Run(context.Background(), stateBag) + if result != multistep.ActionContinue { + t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) + } + + if _, ok := stateBag.GetOk(constants.Error); ok == true { + t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error) + } +} + +func TestStepGetAdditionalDiskShouldTakeValidateArgumentsFromStateBag(t *testing.T) { + var actualResourceGroupName string + var actualComputeName string + + var testSubject = &StepGetDataDisk{ + query: func(resourceGroupName string, computeName string) (compute.VirtualMachine, error) { + actualResourceGroupName = resourceGroupName + actualComputeName = computeName + + return createVirtualMachineWithDataDisksFromUri("test.vhd"), nil + }, + say: func(message string) {}, + error: func(e error) {}, + } + + stateBag := createTestStateBagStepGetAdditionalDisks() + var result = testSubject.Run(context.Background(), stateBag) + + if result != multistep.ActionContinue { + t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) + } + + var expectedComputeName = stateBag.Get(constants.ArmComputeName).(string) + var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string) + + if actualComputeName != expectedComputeName { + t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.") + } + + if actualResourceGroupName != expectedResourceGroupName { + t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.") + } + + expectedAdditionalDiskVhds, ok := stateBag.GetOk(constants.ArmAdditionalDiskVhds) + if !ok { + t.Fatalf("Expected the state bag to have a value for '%s', but it did not.", constants.ArmAdditionalDiskVhds) + } + + expectedAdditionalDiskVhd := expectedAdditionalDiskVhds.([]string) + if expectedAdditionalDiskVhd[0] != "test.vhd" { + t.Fatalf("Expected the value of stateBag[%s] to be 'test.vhd', but got '%s'.", constants.ArmAdditionalDiskVhds, expectedAdditionalDiskVhd[0]) + } +} + +func createTestStateBagStepGetAdditionalDisks() multistep.StateBag { + stateBag := new(multistep.BasicStateBag) + + stateBag.Put(constants.ArmComputeName, "Unit Test: ComputeName") + stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName") + + return stateBag +} + +func createVirtualMachineWithDataDisksFromUri(vhdUri string) compute.VirtualMachine { + vm := compute.VirtualMachine{ + VirtualMachineProperties: &compute.VirtualMachineProperties{ + StorageProfile: &compute.StorageProfile{ + OsDisk: &compute.OSDisk{ + Vhd: &compute.VirtualHardDisk{ + URI: &vhdUri, + }, + }, + DataDisks: &[]compute.DataDisk{ + compute.DataDisk{ + Vhd: &compute.VirtualHardDisk{ + URI: &vhdUri, + }, + }, + }, + }, + }, + } + + return vm +} diff --git a/builder/azure/arm/template_factory.go b/builder/azure/arm/template_factory.go index 07f2313b5..ebc7ef91c 100644 --- a/builder/azure/arm/template_factory.go +++ b/builder/azure/arm/template_factory.go @@ -73,6 +73,10 @@ func GetVirtualMachineDeployment(config *Config) (*resources.Deployment, error) builder.SetOSDiskSizeGB(config.OSDiskSizeGB) } + if len(config.AdditionalDiskSize) > 0 { + builder.SetAdditionalDisks(config.AdditionalDiskSize, config.CustomManagedImageName != "" || (config.ManagedImageName != "" && config.ImagePublisher != "")) + } + if config.customData != "" { builder.SetCustomData(config.customData) } diff --git a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment11.approved.json b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment11.approved.json new file mode 100644 index 000000000..b5fbfaf2f --- /dev/null +++ b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment11.approved.json @@ -0,0 +1,177 @@ +{ + "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", + "contentVersion": "1.0.0.0", + "parameters": { + "adminPassword": { + "type": "string" + }, + "adminUsername": { + "type": "string" + }, + "dnsNameForPublicIP": { + "type": "string" + }, + "osDiskName": { + "type": "string" + }, + "storageAccountBlobEndpoint": { + "type": "string" + }, + "vmName": { + "type": "string" + }, + "vmSize": { + "type": "string" + } + }, + "resources": [ + { + "apiVersion": "[variables('publicIPAddressApiVersion')]", + "location": "[variables('location')]", + "name": "[variables('publicIPAddressName')]", + "properties": { + "dnsSettings": { + "domainNameLabel": "[parameters('dnsNameForPublicIP')]" + }, + "publicIPAllocationMethod": "[variables('publicIPAddressType')]" + }, + "type": "Microsoft.Network/publicIPAddresses" + }, + { + "apiVersion": "[variables('virtualNetworksApiVersion')]", + "location": "[variables('location')]", + "name": "[variables('virtualNetworkName')]", + "properties": { + "addressSpace": { + "addressPrefixes": [ + "[variables('addressPrefix')]" + ] + }, + "subnets": [ + { + "name": "[variables('subnetName')]", + "properties": { + "addressPrefix": "[variables('subnetAddressPrefix')]" + } + } + ] + }, + "type": "Microsoft.Network/virtualNetworks" + }, + { + "apiVersion": "[variables('networkInterfacesApiVersion')]", + "dependsOn": [ + "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]", + "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" + ], + "location": "[variables('location')]", + "name": "[variables('nicName')]", + "properties": { + "ipConfigurations": [ + { + "name": "ipconfig", + "properties": { + "privateIPAllocationMethod": "Dynamic", + "publicIPAddress": { + "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]" + }, + "subnet": { + "id": "[variables('subnetRef')]" + } + } + } + ] + }, + "type": "Microsoft.Network/networkInterfaces" + }, + { + "apiVersion": "[variables('apiVersion')]", + "dependsOn": [ + "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" + ], + "location": "[variables('location')]", + "name": "[parameters('vmName')]", + "properties": { + "diagnosticsProfile": { + "bootDiagnostics": { + "enabled": false + } + }, + "hardwareProfile": { + "vmSize": "[parameters('vmSize')]" + }, + "networkProfile": { + "networkInterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" + } + ] + }, + "osProfile": { + "adminPassword": "[parameters('adminPassword')]", + "adminUsername": "[parameters('adminUsername')]", + "computerName": "[parameters('vmName')]", + "linuxConfiguration": { + "ssh": { + "publicKeys": [ + { + "keyData": "", + "path": "[variables('sshKeyPath')]" + } + ] + } + } + }, + "storageProfile": { + "dataDisks": [ + { + "caching": "ReadWrite", + "createOption": "Empty", + "diskSizeGB": 32, + "lun": 0, + "name": "datadisk-1", + "vhd": { + "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/datadisk-', '1','.vhd')]" + } + } + ], + "imageReference": { + "offer": "--image-offer--", + "publisher": "--image-publisher--", + "sku": "--image-sku--", + "version": "--version--" + }, + "osDisk": { + "caching": "ReadWrite", + "createOption": "FromImage", + "name": "osdisk", + "vhd": { + "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]" + } + } + } + }, + "type": "Microsoft.Compute/virtualMachines" + } + ], + "variables": { + "addressPrefix": "10.0.0.0/16", + "apiVersion": "2017-03-30", + "location": "[resourceGroup().location]", + "managedDiskApiVersion": "2017-03-30", + "networkInterfacesApiVersion": "2017-04-01", + "nicName": "packerNic", + "publicIPAddressApiVersion": "2017-04-01", + "publicIPAddressName": "packerPublicIP", + "publicIPAddressType": "Dynamic", + "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", + "subnetAddressPrefix": "10.0.0.0/24", + "subnetName": "packerSubnet", + "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", + "virtualNetworkName": "packerNetwork", + "virtualNetworkResourceGroup": "[resourceGroup().name]", + "virtualNetworksApiVersion": "2017-04-01", + "vmStorageAccountContainerName": "images", + "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" + } +} \ No newline at end of file diff --git a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment12.approved.json b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment12.approved.json new file mode 100644 index 000000000..531b2654b --- /dev/null +++ b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment12.approved.json @@ -0,0 +1,178 @@ +{ + "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", + "contentVersion": "1.0.0.0", + "parameters": { + "adminPassword": { + "type": "string" + }, + "adminUsername": { + "type": "string" + }, + "dnsNameForPublicIP": { + "type": "string" + }, + "osDiskName": { + "type": "string" + }, + "storageAccountBlobEndpoint": { + "type": "string" + }, + "vmName": { + "type": "string" + }, + "vmSize": { + "type": "string" + } + }, + "resources": [ + { + "apiVersion": "[variables('publicIPAddressApiVersion')]", + "location": "[variables('location')]", + "name": "[variables('publicIPAddressName')]", + "properties": { + "dnsSettings": { + "domainNameLabel": "[parameters('dnsNameForPublicIP')]" + }, + "publicIPAllocationMethod": "[variables('publicIPAddressType')]" + }, + "type": "Microsoft.Network/publicIPAddresses" + }, + { + "apiVersion": "[variables('virtualNetworksApiVersion')]", + "location": "[variables('location')]", + "name": "[variables('virtualNetworkName')]", + "properties": { + "addressSpace": { + "addressPrefixes": [ + "[variables('addressPrefix')]" + ] + }, + "subnets": [ + { + "name": "[variables('subnetName')]", + "properties": { + "addressPrefix": "[variables('subnetAddressPrefix')]" + } + } + ] + }, + "type": "Microsoft.Network/virtualNetworks" + }, + { + "apiVersion": "[variables('networkInterfacesApiVersion')]", + "dependsOn": [ + "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]", + "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" + ], + "location": "[variables('location')]", + "name": "[variables('nicName')]", + "properties": { + "ipConfigurations": [ + { + "name": "ipconfig", + "properties": { + "privateIPAllocationMethod": "Dynamic", + "publicIPAddress": { + "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]" + }, + "subnet": { + "id": "[variables('subnetRef')]" + } + } + } + ] + }, + "type": "Microsoft.Network/networkInterfaces" + }, + { + "apiVersion": "[variables('apiVersion')]", + "dependsOn": [ + "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" + ], + "location": "[variables('location')]", + "name": "[parameters('vmName')]", + "properties": { + "diagnosticsProfile": { + "bootDiagnostics": { + "enabled": false + } + }, + "hardwareProfile": { + "vmSize": "[parameters('vmSize')]" + }, + "networkProfile": { + "networkInterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" + } + ] + }, + "osProfile": { + "adminPassword": "[parameters('adminPassword')]", + "adminUsername": "[parameters('adminUsername')]", + "computerName": "[parameters('vmName')]", + "linuxConfiguration": { + "ssh": { + "publicKeys": [ + { + "keyData": "", + "path": "[variables('sshKeyPath')]" + } + ] + } + } + }, + "storageProfile": { + "dataDisks": [ + { + "caching": "ReadWrite", + "createOption": "Empty", + "diskSizeGB": 32, + "lun": 0, + "managedDisk": { + "storageAccountType": "Standard_LRS" + }, + "name": "datadisk-1" + } + ], + "imageReference": { + "offer": "--image-offer--", + "publisher": "--image-publisher--", + "sku": "--image-sku--", + "version": "--version--" + }, + "osDisk": { + "caching": "ReadWrite", + "createOption": "fromImage", + "managedDisk": { + "storageAccountType": "Standard_LRS" + }, + "name": "osdisk", + "osType": "Linux" + } + } + }, + "type": "Microsoft.Compute/virtualMachines" + } + ], + "variables": { + "addressPrefix": "10.0.0.0/16", + "apiVersion": "2017-03-30", + "location": "[resourceGroup().location]", + "managedDiskApiVersion": "2017-03-30", + "networkInterfacesApiVersion": "2017-04-01", + "nicName": "packerNic", + "publicIPAddressApiVersion": "2017-04-01", + "publicIPAddressName": "packerPublicIP", + "publicIPAddressType": "Dynamic", + "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", + "subnetAddressPrefix": "10.0.0.0/24", + "subnetName": "packerSubnet", + "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", + "virtualNetworkName": "packerNetwork", + "virtualNetworkResourceGroup": "[resourceGroup().name]", + "virtualNetworksApiVersion": "2017-04-01", + "vmStorageAccountContainerName": "images", + "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" + } +} \ No newline at end of file diff --git a/builder/azure/arm/template_factory_test.go b/builder/azure/arm/template_factory_test.go index b571871ef..7909a0cee 100644 --- a/builder/azure/arm/template_factory_test.go +++ b/builder/azure/arm/template_factory_test.go @@ -355,6 +355,76 @@ func TestVirtualMachineDeployment10(t *testing.T) { } } +// Ensure the VM template is correct when building with additional unmanaged disks +func TestVirtualMachineDeployment11(t *testing.T) { + config := map[string]interface{}{ + "location": "ignore", + "subscription_id": "ignore", + "os_type": constants.Target_Linux, + "communicator": "none", + "image_publisher": "--image-publisher--", + "image_offer": "--image-offer--", + "image_sku": "--image-sku--", + "image_version": "--version--", + + "disk_additional_size": []uint{32}, + + "resource_group_name": "packergroup", + "storage_account": "packerartifacts", + "capture_name_prefix": "packer", + "capture_container_name": "packerimages", + } + + c, _, err := newConfig(config, getPackerConfiguration()) + if err != nil { + t.Fatal(err) + } + + deployment, err := GetVirtualMachineDeployment(c) + if err != nil { + t.Fatal(err) + } + + err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template) + if err != nil { + t.Fatal(err) + } +} + +// Ensure the VM template is correct when building with additional managed disks +func TestVirtualMachineDeployment12(t *testing.T) { + config := map[string]interface{}{ + "location": "ignore", + "subscription_id": "ignore", + "os_type": constants.Target_Linux, + "communicator": "none", + "image_publisher": "--image-publisher--", + "image_offer": "--image-offer--", + "image_sku": "--image-sku--", + "image_version": "--version--", + + "disk_additional_size": []uint{32}, + + "managed_image_name": "ManagedImageName", + "managed_image_resource_group_name": "ManagedImageResourceGroupName", + } + + c, _, err := newConfig(config, getPackerConfiguration()) + if err != nil { + t.Fatal(err) + } + + deployment, err := GetVirtualMachineDeployment(c) + if err != nil { + t.Fatal(err) + } + + err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template) + if err != nil { + t.Fatal(err) + } +} + // Ensure the link values are not set, and the concrete values are set. func TestKeyVaultDeployment00(t *testing.T) { c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) diff --git a/builder/azure/common/constants/stateBag.go b/builder/azure/common/constants/stateBag.go index 720979b9c..3576f6820 100644 --- a/builder/azure/common/constants/stateBag.go +++ b/builder/azure/common/constants/stateBag.go @@ -21,6 +21,7 @@ const ( ArmKeyVaultName string = "arm.KeyVaultName" ArmLocation string = "arm.Location" ArmOSDiskVhd string = "arm.OSDiskVhd" + ArmAdditionalDiskVhds string = "arm.AdditionalDiskVhds" ArmPublicIPAddressName string = "arm.PublicIPAddressName" ArmResourceGroupName string = "arm.ResourceGroupName" ArmIsResourceGroupCreated string = "arm.IsResourceGroupCreated" diff --git a/builder/azure/common/template/template.go b/builder/azure/common/template/template.go index cdea487f0..070ca3392 100644 --- a/builder/azure/common/template/template.go +++ b/builder/azure/common/template/template.go @@ -48,10 +48,23 @@ type OSDiskUnion struct { ManagedDisk *compute.ManagedDiskParameters `json:"managedDisk,omitempty"` } +type DataDiskUnion struct { + Lun *int `json:"lun,omitempty"` + BlobURI *string `json:"blobUri,omitempty"` + Name *string `json:"name,omitempty"` + Vhd *compute.VirtualHardDisk `json:"vhd,omitempty"` + Image *compute.VirtualHardDisk `json:"image,omitempty"` + Caching compute.CachingTypes `json:"caching,omitempty"` + CreateOption compute.DiskCreateOptionTypes `json:"createOption,omitempty"` + DiskSizeGB *int32 `json:"diskSizeGB,omitempty"` + ManagedDisk *compute.ManagedDiskParameters `json:"managedDisk,omitempty"` +} + // Union of the StorageProfile and ImageStorageProfile types. type StorageProfileUnion struct { ImageReference *compute.ImageReference `json:"imageReference,omitempty"` OsDisk *OSDiskUnion `json:"osDisk,omitempty"` + DataDisks *[]DataDiskUnion `json:"dataDisks,omitempty"` } ///////////////////////////////////////////////// diff --git a/builder/azure/common/template/template_builder.go b/builder/azure/common/template/template_builder.go index 7311817c2..835aff096 100644 --- a/builder/azure/common/template/template_builder.go +++ b/builder/azure/common/template/template_builder.go @@ -191,6 +191,35 @@ func (s *TemplateBuilder) SetOSDiskSizeGB(diskSizeGB int32) error { return nil } +func (s *TemplateBuilder) SetAdditionalDisks(diskSizeGB []int32, isManaged bool) error { + resource, err := s.getResourceByType(resourceVirtualMachine) + if err != nil { + return err + } + + profile := resource.Properties.StorageProfile + dataDisks := make([]DataDiskUnion, len(diskSizeGB)) + + for i, additionalsize := range diskSizeGB { + dataDisks[i].DiskSizeGB = to.Int32Ptr(additionalsize) + dataDisks[i].Lun = to.IntPtr(i) + dataDisks[i].Name = to.StringPtr(fmt.Sprintf("datadisk-%d", i+1)) + dataDisks[i].CreateOption = "Empty" + dataDisks[i].Caching = "ReadWrite" + if isManaged { + dataDisks[i].Vhd = nil + dataDisks[i].ManagedDisk = profile.OsDisk.ManagedDisk + } else { + dataDisks[i].Vhd = &compute.VirtualHardDisk{ + URI: to.StringPtr(fmt.Sprintf("[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/datadisk-', '%d','.vhd')]", i+1)), + } + dataDisks[i].ManagedDisk = nil + } + } + profile.DataDisks = &dataDisks + return nil +} + func (s *TemplateBuilder) SetCustomData(customData string) error { resource, err := s.getResourceByType(resourceVirtualMachine) if err != nil { diff --git a/builder/azure/common/template/template_builder_test.TestBuildWindows01.approved.json b/builder/azure/common/template/template_builder_test.TestBuildWindows01.approved.json new file mode 100644 index 000000000..b7a145df9 --- /dev/null +++ b/builder/azure/common/template/template_builder_test.TestBuildWindows01.approved.json @@ -0,0 +1,202 @@ +{ + "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", + "contentVersion": "1.0.0.0", + "parameters": { + "adminPassword": { + "type": "string" + }, + "adminUsername": { + "type": "string" + }, + "dnsNameForPublicIP": { + "type": "string" + }, + "osDiskName": { + "type": "string" + }, + "storageAccountBlobEndpoint": { + "type": "string" + }, + "vmName": { + "type": "string" + }, + "vmSize": { + "type": "string" + } + }, + "resources": [ + { + "apiVersion": "[variables('publicIPAddressApiVersion')]", + "location": "[variables('location')]", + "name": "[variables('publicIPAddressName')]", + "properties": { + "dnsSettings": { + "domainNameLabel": "[parameters('dnsNameForPublicIP')]" + }, + "publicIPAllocationMethod": "[variables('publicIPAddressType')]" + }, + "type": "Microsoft.Network/publicIPAddresses" + }, + { + "apiVersion": "[variables('virtualNetworksApiVersion')]", + "location": "[variables('location')]", + "name": "[variables('virtualNetworkName')]", + "properties": { + "addressSpace": { + "addressPrefixes": [ + "[variables('addressPrefix')]" + ] + }, + "subnets": [ + { + "name": "[variables('subnetName')]", + "properties": { + "addressPrefix": "[variables('subnetAddressPrefix')]" + } + } + ] + }, + "type": "Microsoft.Network/virtualNetworks" + }, + { + "apiVersion": "[variables('networkInterfacesApiVersion')]", + "dependsOn": [ + "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]", + "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" + ], + "location": "[variables('location')]", + "name": "[variables('nicName')]", + "properties": { + "ipConfigurations": [ + { + "name": "ipconfig", + "properties": { + "privateIPAllocationMethod": "Dynamic", + "publicIPAddress": { + "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]" + }, + "subnet": { + "id": "[variables('subnetRef')]" + } + } + } + ] + }, + "type": "Microsoft.Network/networkInterfaces" + }, + { + "apiVersion": "[variables('apiVersion')]", + "dependsOn": [ + "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" + ], + "location": "[variables('location')]", + "name": "[parameters('vmName')]", + "properties": { + "diagnosticsProfile": { + "bootDiagnostics": { + "enabled": false + } + }, + "hardwareProfile": { + "vmSize": "[parameters('vmSize')]" + }, + "networkProfile": { + "networkInterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" + } + ] + }, + "osProfile": { + "adminPassword": "[parameters('adminPassword')]", + "adminUsername": "[parameters('adminUsername')]", + "computerName": "[parameters('vmName')]", + "secrets": [ + { + "sourceVault": { + "id": "[resourceId(resourceGroup().name, 'Microsoft.KeyVault/vaults', '--test-key-vault-name')]" + }, + "vaultCertificates": [ + { + "certificateStore": "My", + "certificateUrl": "--test-winrm-certificate-url--" + } + ] + } + ], + "windowsConfiguration": { + "provisionVMAgent": true, + "winRM": { + "listeners": [ + { + "certificateUrl": "--test-winrm-certificate-url--", + "protocol": "https" + } + ] + } + } + }, + "storageProfile": { + "dataDisks": [ + { + "caching": "ReadWrite", + "createOption": "Empty", + "diskSizeGB": 32, + "lun": 0, + "managedDisk": { + "storageAccountType": "Premium_LRS" + }, + "name": "datadisk-1" + }, + { + "caching": "ReadWrite", + "createOption": "Empty", + "diskSizeGB": 64, + "lun": 1, + "managedDisk": { + "storageAccountType": "Premium_LRS" + }, + "name": "datadisk-2" + } + ], + "imageReference": { + "offer": "2012-R2-Datacenter", + "publisher": "WindowsServer", + "sku": "latest", + "version": "2015-1" + }, + "osDisk": { + "caching": "ReadWrite", + "createOption": "fromImage", + "managedDisk": { + "storageAccountType": "Premium_LRS" + }, + "name": "osdisk", + "osType": "Windows" + } + } + }, + "type": "Microsoft.Compute/virtualMachines" + } + ], + "variables": { + "addressPrefix": "10.0.0.0/16", + "apiVersion": "2017-03-30", + "location": "[resourceGroup().location]", + "managedDiskApiVersion": "2017-03-30", + "networkInterfacesApiVersion": "2017-04-01", + "nicName": "packerNic", + "publicIPAddressApiVersion": "2017-04-01", + "publicIPAddressName": "packerPublicIP", + "publicIPAddressType": "Dynamic", + "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", + "subnetAddressPrefix": "10.0.0.0/24", + "subnetName": "packerSubnet", + "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", + "virtualNetworkName": "packerNetwork", + "virtualNetworkResourceGroup": "[resourceGroup().name]", + "virtualNetworksApiVersion": "2017-04-01", + "vmStorageAccountContainerName": "images", + "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" + } +} \ No newline at end of file diff --git a/builder/azure/common/template/template_builder_test.TestBuildWindows02.approved.json b/builder/azure/common/template/template_builder_test.TestBuildWindows02.approved.json new file mode 100644 index 000000000..61b05a125 --- /dev/null +++ b/builder/azure/common/template/template_builder_test.TestBuildWindows02.approved.json @@ -0,0 +1,195 @@ +{ + "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", + "contentVersion": "1.0.0.0", + "parameters": { + "adminPassword": { + "type": "string" + }, + "adminUsername": { + "type": "string" + }, + "dnsNameForPublicIP": { + "type": "string" + }, + "osDiskName": { + "type": "string" + }, + "storageAccountBlobEndpoint": { + "type": "string" + }, + "vmName": { + "type": "string" + }, + "vmSize": { + "type": "string" + } + }, + "resources": [ + { + "apiVersion": "[variables('publicIPAddressApiVersion')]", + "location": "[variables('location')]", + "name": "[variables('publicIPAddressName')]", + "properties": { + "dnsSettings": { + "domainNameLabel": "[parameters('dnsNameForPublicIP')]" + }, + "publicIPAllocationMethod": "[variables('publicIPAddressType')]" + }, + "type": "Microsoft.Network/publicIPAddresses" + }, + { + "apiVersion": "[variables('virtualNetworksApiVersion')]", + "location": "[variables('location')]", + "name": "[variables('virtualNetworkName')]", + "properties": { + "addressSpace": { + "addressPrefixes": [ + "[variables('addressPrefix')]" + ] + }, + "subnets": [ + { + "name": "[variables('subnetName')]", + "properties": { + "addressPrefix": "[variables('subnetAddressPrefix')]" + } + } + ] + }, + "type": "Microsoft.Network/virtualNetworks" + }, + { + "apiVersion": "[variables('networkInterfacesApiVersion')]", + "dependsOn": [ + "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]", + "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" + ], + "location": "[variables('location')]", + "name": "[variables('nicName')]", + "properties": { + "ipConfigurations": [ + { + "name": "ipconfig", + "properties": { + "privateIPAllocationMethod": "Dynamic", + "publicIPAddress": { + "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]" + }, + "subnet": { + "id": "[variables('subnetRef')]" + } + } + } + ] + }, + "type": "Microsoft.Network/networkInterfaces" + }, + { + "apiVersion": "[variables('apiVersion')]", + "dependsOn": [ + "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" + ], + "location": "[variables('location')]", + "name": "[parameters('vmName')]", + "properties": { + "diagnosticsProfile": { + "bootDiagnostics": { + "enabled": false + } + }, + "hardwareProfile": { + "vmSize": "[parameters('vmSize')]" + }, + "networkProfile": { + "networkInterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" + } + ] + }, + "osProfile": { + "adminPassword": "[parameters('adminPassword')]", + "adminUsername": "[parameters('adminUsername')]", + "computerName": "[parameters('vmName')]", + "secrets": [ + { + "sourceVault": { + "id": "[resourceId(resourceGroup().name, 'Microsoft.KeyVault/vaults', '--test-key-vault-name')]" + }, + "vaultCertificates": [ + { + "certificateStore": "My", + "certificateUrl": "--test-winrm-certificate-url--" + } + ] + } + ], + "windowsConfiguration": { + "provisionVMAgent": true, + "winRM": { + "listeners": [ + { + "certificateUrl": "--test-winrm-certificate-url--", + "protocol": "https" + } + ] + } + } + }, + "storageProfile": { + "dataDisks": [ + { + "caching": "ReadWrite", + "createOption": "Empty", + "diskSizeGB": 32, + "lun": 0, + "name": "datadisk-1", + "vhd": { + "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/datadisk-', '1','.vhd')]" + } + }, + { + "caching": "ReadWrite", + "createOption": "Empty", + "diskSizeGB": 64, + "lun": 1, + "name": "datadisk-2", + "vhd": { + "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/datadisk-', '2','.vhd')]" + } + } + ], + "osDisk": { + "caching": "ReadWrite", + "createOption": "FromImage", + "name": "osdisk", + "vhd": { + "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]" + } + } + } + }, + "type": "Microsoft.Compute/virtualMachines" + } + ], + "variables": { + "addressPrefix": "10.0.0.0/16", + "apiVersion": "2017-03-30", + "location": "[resourceGroup().location]", + "managedDiskApiVersion": "2017-03-30", + "networkInterfacesApiVersion": "2017-04-01", + "nicName": "packerNic", + "publicIPAddressApiVersion": "2017-04-01", + "publicIPAddressName": "packerPublicIP", + "publicIPAddressType": "Dynamic", + "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", + "subnetAddressPrefix": "10.0.0.0/24", + "subnetName": "packerSubnet", + "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", + "virtualNetworkName": "packerNetwork", + "virtualNetworkResourceGroup": "[resourceGroup().name]", + "virtualNetworksApiVersion": "2017-04-01", + "vmStorageAccountContainerName": "images", + "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" + } +} \ No newline at end of file diff --git a/builder/azure/common/template/template_builder_test.go b/builder/azure/common/template/template_builder_test.go index ec93a61a7..6eac5334f 100644 --- a/builder/azure/common/template/template_builder_test.go +++ b/builder/azure/common/template/template_builder_test.go @@ -120,3 +120,64 @@ func TestBuildWindows00(t *testing.T) { t.Fatal(err) } } + +// Windows build with additional disk for an managed build +func TestBuildWindows01(t *testing.T) { + testSubject, err := NewTemplateBuilder(BasicTemplate) + if err != nil { + t.Fatal(err) + } + + err = testSubject.BuildWindows("--test-key-vault-name", "--test-winrm-certificate-url--") + if err != nil { + t.Fatal(err) + } + + err = testSubject.SetManagedMarketplaceImage("MicrosoftWindowsServer", "WindowsServer", "2012-R2-Datacenter", "latest", "2015-1", "1", "Premium_LRS") + if err != nil { + t.Fatal(err) + } + + err = testSubject.SetAdditionalDisks([]int32{32, 64}, true) + if err != nil { + t.Fatal(err) + } + + doc, err := testSubject.ToJSON() + if err != nil { + t.Fatal(err) + } + + err = approvaltests.VerifyJSONBytes(t, []byte(*doc)) + if err != nil { + t.Fatal(err) + } +} + +// Windows build with additional disk for an unmanaged build +func TestBuildWindows02(t *testing.T) { + testSubject, err := NewTemplateBuilder(BasicTemplate) + if err != nil { + t.Fatal(err) + } + + err = testSubject.BuildWindows("--test-key-vault-name", "--test-winrm-certificate-url--") + if err != nil { + t.Fatal(err) + } + + err = testSubject.SetAdditionalDisks([]int32{32, 64}, false) + if err != nil { + t.Fatal(err) + } + + doc, err := testSubject.ToJSON() + if err != nil { + t.Fatal(err) + } + + err = approvaltests.VerifyJSONBytes(t, []byte(*doc)) + if err != nil { + t.Fatal(err) + } +} diff --git a/website/source/docs/builders/azure.html.md b/website/source/docs/builders/azure.html.md index 8fa59f4e8..71e8be385 100644 --- a/website/source/docs/builders/azure.html.md +++ b/website/source/docs/builders/azure.html.md @@ -149,6 +149,10 @@ Providing `temp_resource_group_name` or `location` in combination with `build_re - `os_disk_size_gb` (number) Specify the size of the OS disk in GB (gigabytes). Values of zero or less than zero are ignored. +- `disk_additional_size` (array of integers) - The size(s) of any additional + hard disks for the VM in gigabytes. If this is not specified then the VM + will only contain an OS disk. + - `os_type` (string) If either `Linux` or `Windows` is specified Packer will automatically configure authentication credentials for the provisioned machine. For `Linux` this configures an SSH authorized key. For `Windows` this From 47947bd0f6753cfad327177705efc485023166cf Mon Sep 17 00:00:00 2001 From: jmajoor Date: Fri, 23 Feb 2018 18:43:55 -0800 Subject: [PATCH 0551/1216] Apply gofmt --- builder/azure/arm/artifact_test.go | 4 ++-- builder/azure/arm/step_get_additional_disks_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/builder/azure/arm/artifact_test.go b/builder/azure/arm/artifact_test.go index 13c77e2e3..4aff7cee6 100644 --- a/builder/azure/arm/artifact_test.go +++ b/builder/azure/arm/artifact_test.go @@ -94,7 +94,7 @@ func TestAdditionalDiskArtifactString(t *testing.T) { }, }, DataDisks: []CaptureDisk{ - CaptureDisk{ + { Image: CaptureUri{ Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-1.4085bb15-3644-4641-b9cd-f575918640b4.vhd", }, @@ -188,7 +188,7 @@ func TestAdditionalDiskArtifactProperties(t *testing.T) { }, }, DataDisks: []CaptureDisk{ - CaptureDisk{ + { Image: CaptureUri{ Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-1.4085bb15-3644-4641-b9cd-f575918640b4.vhd", }, diff --git a/builder/azure/arm/step_get_additional_disks_test.go b/builder/azure/arm/step_get_additional_disks_test.go index 1c6dd3380..b0ca112c3 100644 --- a/builder/azure/arm/step_get_additional_disks_test.go +++ b/builder/azure/arm/step_get_additional_disks_test.go @@ -117,7 +117,7 @@ func createVirtualMachineWithDataDisksFromUri(vhdUri string) compute.VirtualMach }, }, DataDisks: &[]compute.DataDisk{ - compute.DataDisk{ + { Vhd: &compute.VirtualHardDisk{ URI: &vhdUri, }, From dfc75acb632af78a02474257df858da87d3ebb8b Mon Sep 17 00:00:00 2001 From: Taeho Kim Date: Sat, 24 Feb 2018 12:29:50 +0900 Subject: [PATCH 0552/1216] Sort options alphabetically --- .../source/docs/builders/alicloud-ecs.html.md | 169 +++++++++--------- 1 file changed, 84 insertions(+), 85 deletions(-) diff --git a/website/source/docs/builders/alicloud-ecs.html.md b/website/source/docs/builders/alicloud-ecs.html.md index 172bdab5c..5c4330778 100644 --- a/website/source/docs/builders/alicloud-ecs.html.md +++ b/website/source/docs/builders/alicloud-ecs.html.md @@ -27,12 +27,10 @@ builder. but it can also be sourced from the `ALICLOUD_ACCESS_KEY` environment variable. -- `secret_key` (string) - This is the Alicloud secret key. It must be provided, - but it can also be sourced from the `ALICLOUD_SECRET_KEY` environment - variable. - -- `region` (string) - This is the Alicloud region. It must be provided, but it - can also be sourced from the `ALICLOUD_REGION` environment variables. +- `image_name` (string) - The name of the user-defined image, \[2, 128\] English + or Chinese characters. It must begin with an uppercase/lowercase letter or + a Chinese character, and may contain numbers, `_` or `-`. It cannot begin with + `http://` or `https://`. - `instance_type` (string) - Type of the instance. For values, see [Instance Type Table](https://www.alibabacloud.com/help/doc-detail/25378.htm?spm=a3c0i.o25499en.a3.9.14a36ac8iYqKRA). @@ -40,54 +38,38 @@ builder. Instance Type Table](https://intl.aliyun.com/help/doc-detail/25620.htm?spm=a3c0i.o25499en.a3.6.Dr1bik) interface. -- `image_name` (string) - The name of the user-defined image, \[2, 128\] English - or Chinese characters. It must begin with an uppercase/lowercase letter or - a Chinese character, and may contain numbers, `_` or `-`. It cannot begin with - `http://` or `https://`. +- `region` (string) - This is the Alicloud region. It must be provided, but it + can also be sourced from the `ALICLOUD_REGION` environment variables. + +- `secret_key` (string) - This is the Alicloud secret key. It must be provided, + but it can also be sourced from the `ALICLOUD_SECRET_KEY` environment + variable. - `source_image` (string) - This is the base image id which you want to create your customized images. ### Optional: -- `skip_region_validation` (boolean) - The region validation can be skipped if this - value is true, the default value is false. +- `force_stop_instance` (boolean) - Whether to force shutdown upon device restart. + The default value is `false`. -- `image_description` (string) - The description of the image, with a length - limit of 0 to 256 characters. Leaving it blank means null, which is the - default value. It cannot begin with `http://` or `https://`. - -- `image_version` (string) - The version number of the image, with a length limit - of 1 to 40 English characters. - -- `image_share_account` (array of string) - The IDs of to-be-added Aliyun - accounts to which the image is shared. The number of accounts is 1 to 10. If - number of accounts is greater than 10, this parameter is ignored. - -- `image_copy_regions` (array of string) - Copy to the destination regionIds. + If it is set to `false`, the system is shut down normally; if it is set to + `true`, the system is forced to shut down. - `image_copy_names` (array of string) - The name of the destination image, \[2, 128\] English or Chinese characters. It must begin with an uppercase/lowercase letter or a Chinese character, and may contain numbers, `_` or `-`. It cannot begin with `http://` or `https://`. -- `image_force_delete` (boolean) - If this value is true, when the target image name - is duplicated with an existing image, it will delete the existing image and - then create the target image, otherwise, the creation will fail. The default - value is false. +- `image_copy_regions` (array of string) - Copy to the destination regionIds. -- `image_force_delete_snapshots` (boolean) - If this value is true, when delete the - duplicated existing image, the source snapshot of this image will be delete - either. +- `image_description` (string) - The description of the image, with a length + limit of 0 to 256 characters. Leaving it blank means null, which is the + default value. It cannot begin with `http://` or `https://`. -   `image_disk_mappings` (array of image disk mappings) - Add one or more data disks to the image. - - `disk_name` (string) - The value of disk name is blank by default. \[2, 128\] - English or Chinese characters, must begin with an uppercase/lowercase letter - or Chinese character. Can contain numbers, `.`, `_` and `-`. The disk name - will appear on the console. It cannot begin with `http://` or `https://`. - - `disk_category` (string) - Category of the data disk. Optional values are: - cloud - general cloud disk - cloud\_efficiency - efficiency cloud disk @@ -95,6 +77,20 @@ builder. Default value: cloud. + - `disk_delete_with_instance` (string) - Whether or not the disk is released along with the instance: + - True indicates that when the instance is released, this disk will be released with it + - False indicates that when the instance is released, this disk will be retained. + + - `disk_description` (string) - The value of disk description is blank by default. \[2, 256\] characters. The disk description will appear on the console. It cannot begin with `http://` or `https://`. + + - `disk_device` (string) - Device information of the related instance: such as + `/dev/xvdb` It is null unless the Status is In\_use. + + - `disk_name` (string) - The value of disk name is blank by default. \[2, 128\] + English or Chinese characters, must begin with an uppercase/lowercase letter + or Chinese character. Can contain numbers, `.`, `_` and `-`. The disk name + will appear on the console. It cannot begin with `http://` or `https://`. + - `disk_size` (number) - Size of the system disk, in GB, values range: - cloud - 5 ~ 2000 - cloud\_efficiency - 20 ~ 2048 @@ -108,54 +104,21 @@ builder. Snapshots from on or before July 15, 2013 cannot be used to create a disk. - - `disk_description` (string) - The value of disk description is blank by default. \[2, 256\] characters. The disk description will appear on the console. It cannot begin with `http://` or `https://`. +- `image_force_delete` (boolean) - If this value is true, when the target image name + is duplicated with an existing image, it will delete the existing image and + then create the target image, otherwise, the creation will fail. The default + value is false. - - `disk_delete_with_instance` (string) - Whether or not the disk is released along with the instance: - - True indicates that when the instance is released, this disk will be released with it - - False indicates that when the instance is released, this disk will be retained. +- `image_force_delete_snapshots` (boolean) - If this value is true, when delete the + duplicated existing image, the source snapshot of this image will be delete + either. - - `disk_device` (string) - Device information of the related instance: such as - `/dev/xvdb` It is null unless the Status is In\_use. +- `image_share_account` (array of string) - The IDs of to-be-added Aliyun + accounts to which the image is shared. The number of accounts is 1 to 10. If + number of accounts is greater than 10, this parameter is ignored. -- `zone_id` (string) - ID of the zone to which the disk belongs. - -- `io_optimized` (boolean) - Whether an ECS instance is I/O optimized or not. - The default value is `false`. - -- `force_stop_instance` (boolean) - Whether to force shutdown upon device restart. - The default value is `false`. - - If it is set to `false`, the system is shut down normally; if it is set to - `true`, the system is forced to shut down. - -- `security_group_id` (string) - ID of the security group to which a newly - created instance belongs. Mutual access is allowed between instances in one - security group. If not specified, the newly created instance will be added to - the default security group. If the default group doesn’t exist, or the number - of instances in it has reached the maximum limit, a new security group will - be created automatically. - -- `security_group_name` (string) - The security group name. The default value is - blank. \[2, 128\] English or Chinese characters, must begin with an - uppercase/lowercase letter or Chinese character. Can contain numbers, `.`, - `_` or `-`. It cannot begin with `http://` or `https://`. - -- `user_data` (string) - The UserData of an instance must be encoded in `Base64` - format, and the maximum size of the raw data is `16 KB`. - -- `user_data_file` (string) - The file name of the userdata. - -- `vpc_id` (string) - VPC ID allocated by the system. - -- `vpc_name` (string) - The VPC name. The default value is blank. \[2, 128\] - English or Chinese characters, must begin with an uppercase/lowercase letter - or Chinese character. Can contain numbers, `_` and `-`. The disk description - will appear on the console. Cannot begin with `http://` or `https://`. - -- `vpc_cidr_block` (string) - Value options: `192.168.0.0/16` and `172.16.0.0/16`. - When not specified, the default value is `172.16.0.0/16`. - -- `vswitch_id` (string) - The ID of the VSwitch to be used. +- `image_version` (string) - The version number of the image, with a length limit + of 1 to 40 English characters. - `instance_name` (string) - Display name of the instance, which is a string of 2 to 128 Chinese or English characters. It must begin with an @@ -173,7 +136,6 @@ builder. For the regions out of China, currently only support `PayByTraffic`, you must set it manfully. - - `internet_max_bandwidth_out` (string) - Maximum outgoing bandwidth to the public network, measured in Mbps (Mega bit per second). @@ -181,16 +143,53 @@ builder. - PayByBandwidth: \[0, 100\]. If this parameter is not specified, API automatically sets it to 0 Mbps. - PayByTraffic: \[1, 100\]. If this parameter is not specified, an error is returned. -- `temporary_key_pair_name` (string) - The name of the temporary key pair to - generate. By default, Packer generates a name that looks like `packer_`, - where `` is a 36 character unique identifier. +- `io_optimized` (boolean) - Whether an ECS instance is I/O optimized or not. + The default value is `false`. + +- `security_group_id` (string) - ID of the security group to which a newly + created instance belongs. Mutual access is allowed between instances in one + security group. If not specified, the newly created instance will be added to + the default security group. If the default group doesn’t exist, or the number + of instances in it has reached the maximum limit, a new security group will + be created automatically. + +- `security_group_name` (string) - The security group name. The default value is + blank. \[2, 128\] English or Chinese characters, must begin with an + uppercase/lowercase letter or Chinese character. Can contain numbers, `.`, + `_` or `-`. It cannot begin with `http://` or `https://`. - `security_token` (string) - STS access token, can be set through template or by exporting as environment vairalbe such "export SecurityToken=value". +- `skip_region_validation` (boolean) - The region validation can be skipped if this + value is true, the default value is false. + +- `temporary_key_pair_name` (string) - The name of the temporary key pair to + generate. By default, Packer generates a name that looks like `packer_`, + where `` is a 36 character unique identifier. + - `TLSHandshakeTimeout` (int) - When happen "net/http: TLS handshake timeout" problem, set this environment variable to a bigger such as "export TLSHandshakeTimeout=30", it will set the TLS handshake timeout value to 30s. +- `user_data` (string) - The UserData of an instance must be encoded in `Base64` + format, and the maximum size of the raw data is `16 KB`. + +- `user_data_file` (string) - The file name of the userdata. + +- `vpc_cidr_block` (string) - Value options: `192.168.0.0/16` and `172.16.0.0/16`. + When not specified, the default value is `172.16.0.0/16`. + +- `vpc_id` (string) - VPC ID allocated by the system. + +- `vpc_name` (string) - The VPC name. The default value is blank. \[2, 128\] + English or Chinese characters, must begin with an uppercase/lowercase letter + or Chinese character. Can contain numbers, `_` and `-`. The disk description + will appear on the console. Cannot begin with `http://` or `https://`. + +- `vswitch_id` (string) - The ID of the VSwitch to be used. + +- `zone_id` (string) - ID of the zone to which the disk belongs. + ## Basic Example Here is a basic example for Alicloud. From 33c3d2885de042d027ec1460c4818aaa9a14fdcd Mon Sep 17 00:00:00 2001 From: jmajoor Date: Mon, 26 Feb 2018 17:33:40 -0800 Subject: [PATCH 0553/1216] Add the additional disk steps to the Linux build --- builder/azure/arm/builder.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/builder/azure/arm/builder.go b/builder/azure/arm/builder.go index 64d791ffa..f2bc66135 100644 --- a/builder/azure/arm/builder.go +++ b/builder/azure/arm/builder.go @@ -151,10 +151,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe }, &packerCommon.StepProvision{}, NewStepGetOSDisk(azureClient, ui), + NewStepGetAdditionalDisks(azureClient, ui), NewStepPowerOffCompute(azureClient, ui), NewStepCaptureImage(azureClient, ui), NewStepDeleteResourceGroup(azureClient, ui), NewStepDeleteOSDisk(azureClient, ui), + NewStepDeleteAdditionalDisks(azureClient, ui), } } else if b.config.OSType == constants.Target_Windows { keyVaultDeploymentName := b.stateBag.Get(constants.ArmKeyVaultDeploymentName).(string) From 675dc0696782f4927f424c053ebe523b3db36581 Mon Sep 17 00:00:00 2001 From: jmajoor Date: Mon, 26 Feb 2018 17:37:46 -0800 Subject: [PATCH 0554/1216] Tests for the optional disk_additional_size configuration. --- builder/azure/arm/config_test.go | 40 ++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/builder/azure/arm/config_test.go b/builder/azure/arm/config_test.go index 4cefd6730..d69c24a33 100644 --- a/builder/azure/arm/config_test.go +++ b/builder/azure/arm/config_test.go @@ -1057,6 +1057,46 @@ func TestConfigShouldRejectManagedDiskNames(t *testing.T) { } } +func TestConfigAdditionalDiskDefaultIsNil(t *testing.T) { + + c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) + if c.AdditionalDiskSize != nil { + t.Errorf("Expected Config to not have a set of additional disks, but got a non nil value") + } +} + +func TestConfigAdditionalDiskOverrideDefault(t *testing.T) { + config := map[string]string{ + "capture_name_prefix": "ignore", + "capture_container_name": "ignore", + "location": "ignore", + "image_url": "ignore", + "storage_account": "ignore", + "resource_group_name": "ignore", + "subscription_id": "ignore", + "os_type": constants.Target_Linux, + "communicator": "none", + } + + diskconfig := map[string][]int32{ + "disk_additional_size": {32, 64}, + } + + c, _, _ := newConfig(config, diskconfig, getPackerConfiguration()) + if c.AdditionalDiskSize == nil { + t.Errorf("Expected Config to have a set of additional disks, but got nil") + } + if len(c.AdditionalDiskSize) != 2 { + t.Errorf("Expected Config to have a 2 additional disks, but got %d additional disks", len(c.AdditionalDiskSize)) + } + if c.AdditionalDiskSize[0] != 32 { + t.Errorf("Expected Config to have the first additional disks of size 32Gb, but got %dGb", c.AdditionalDiskSize[0]) + } + if c.AdditionalDiskSize[1] != 64 { + t.Errorf("Expected Config to have the second additional disks of size 64Gb, but got %dGb", c.AdditionalDiskSize[1]) + } +} + func getArmBuilderConfiguration() map[string]string { m := make(map[string]string) for _, v := range requiredConfigValues { From 863f4ec1daaced25afdab7e217015601a276f692 Mon Sep 17 00:00:00 2001 From: jmajoor Date: Mon, 26 Feb 2018 17:41:28 -0800 Subject: [PATCH 0555/1216] Add additional documentation for the additional disks configuration --- website/source/docs/builders/azure.html.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/website/source/docs/builders/azure.html.md b/website/source/docs/builders/azure.html.md index 71e8be385..b7d937acd 100644 --- a/website/source/docs/builders/azure.html.md +++ b/website/source/docs/builders/azure.html.md @@ -151,7 +151,11 @@ Providing `temp_resource_group_name` or `location` in combination with `build_re - `disk_additional_size` (array of integers) - The size(s) of any additional hard disks for the VM in gigabytes. If this is not specified then the VM - will only contain an OS disk. + will only contain an OS disk. The number of additional disks and maximum size of a disk depends on the configuration of your VM. See [Windows](https://docs.microsoft.com/en-us/azure/virtual-machines/windows/about-disks-and-vhds) or [Linux](https://docs.microsoft.com/en-us/azure/virtual-machines/linux/about-disks-and-vhds) for more information. + + For VHD builds the final artifacts will be named `PREFIX-dataDisk-.UUID.vhd` and stored in the specified capture container along side the OS disk. The additional disks are included in the deployment template `PREFIX-vmTemplate.UUID`. + + For Managed build the final artifacts are included in the managed image. The additional disk will have the same storage account type as the OS disk, as specified with the `managed_image_storage_account_type` setting. - `os_type` (string) If either `Linux` or `Windows` is specified Packer will automatically configure authentication credentials for the provisioned machine. For From 686cacb4359da332341ac102db049641f2faee0e Mon Sep 17 00:00:00 2001 From: Less Mo Date: Fri, 2 Mar 2018 16:34:19 +0800 Subject: [PATCH 0556/1216] builder/amazon: Added new region cn-northwest-1 Add new region China Ningxia cn-northwest-1. --- builder/amazon/common/regions.go | 1 + 1 file changed, 1 insertion(+) diff --git a/builder/amazon/common/regions.go b/builder/amazon/common/regions.go index 9b4492459..66b0c490e 100644 --- a/builder/amazon/common/regions.go +++ b/builder/amazon/common/regions.go @@ -21,6 +21,7 @@ func listEC2Regions() []string { // not part of autogenerated list "us-gov-west-1", "cn-north-1", + "cn-northwest-1", } } From 2e174220d80e665cde0929f3445a688d65f693ff Mon Sep 17 00:00:00 2001 From: Christopher Boumenot Date: Sat, 3 Mar 2018 00:14:35 -0800 Subject: [PATCH 0557/1216] azure: remove azure-merge.sh This script was used to import the Azure builder source code, and it is no longer necessary. --- azure-merge.sh | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100755 azure-merge.sh diff --git a/azure-merge.sh b/azure-merge.sh deleted file mode 100755 index 07c79b8b3..000000000 --- a/azure-merge.sh +++ /dev/null @@ -1,18 +0,0 @@ -PACKER=$GOPATH/src/github.com/mitchellh/packer -AZURE=/tmp/packer-azure - -ls $AZURE >/dev/null || git clone https://github.com/Azure/packer-azure /tmp/packer-azure -PWD=`pwd` -cd $AZURE && git pull -cd $PWD - -# copy things -cp -r $AZURE/packer/builder/azure $PACKER/builder/ -cp -r $AZURE/packer/communicator/* $PACKER/communicator/ -cp -r $AZURE/packer/provisioner/azureVmCustomScriptExtension $PACKER/provisioner/ - -# remove legacy API client -rm -rf $PACKER/builder/azure/smapi - -# fix imports -find $PACKER/builder/azure/ -type f | grep ".go" | xargs sed -e 's/Azure\/packer-azure\/packer\/builder\/azure/mitchellh\/packer\/builder\/azure/g' -i '' From 00ed312e047f311cddc5c0e1f315def50b8fba76 Mon Sep 17 00:00:00 2001 From: Christopher Boumenot Date: Sat, 3 Mar 2018 08:46:22 +0000 Subject: [PATCH 0558/1216] azure: fix commands, improve UX --- contrib/azure-setup.sh | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/contrib/azure-setup.sh b/contrib/azure-setup.sh index 105cea88c..ef855b353 100755 --- a/contrib/azure-setup.sh +++ b/contrib/azure-setup.sh @@ -49,7 +49,7 @@ showhelp() { requirements() { found=0 - azureversion=$(az -v) + azureversion=$(az --version) if [ $? -eq 0 ]; then found=$((found + 1)) echo "Found azure-cli version: $azureversion" @@ -74,7 +74,7 @@ requirements() { } askSubscription() { - az account list + az account list -otable echo "" echo "Please enter the Id of the account you wish to use. If you do not see" echo "a valid account in the list press Ctrl+C to abort and create one." @@ -120,7 +120,7 @@ askSecret() { } askLocation() { - az account list-locations + az account list-locations -otable echo "" echo "Choose which region your resource group and storage account will be created. example: westus" echo -n "> " @@ -140,7 +140,7 @@ createResourceGroup() { createStorageAccount() { echo "==> Creating storage account" - az storage account create --name $meta_name --resource-group $meta_name --location $location --kind Storage + az storage account create --name $meta_name --resource-group $meta_name --location $location --kind Storage --sku Standard_LRS if [ $? -eq 0 ]; then azure_storage_name=$meta_name else @@ -170,21 +170,8 @@ createApplication() { createServicePrincipal() { echo "==> Creating service principal" - # Azure CLI 0.10.2 introduced a breaking change, where appId must be supplied with the -a switch - # prior version accepted appId as the only parameter without a switch - newer_syntax=false - IFS='.' read -ra azureversionsemver <<< "$azureversion" - if [ ${azureversionsemver[0]} -ge 0 ] && [ ${azureversionsemver[1]} -ge 10 ] && [ ${azureversionsemver[2]} -ge 2 ]; then - newer_syntax=true - fi - - if [ "${newer_syntax}" = true ]; then - azure_object_id=$(az ad sp create --id $azure_client_id | jq -r .objectId) - echo $azure_object_id "was selected." - else - azure_object_id=$(az ad sp create --id $azure_client_id | jq -r .objectId) - echo $azure_object_id "was selected." - fi + azure_object_id=$(az ad sp create --id $azure_client_id | jq -r .objectId) + echo $azure_object_id "was selected." if [ $? -ne 0 ]; then echo "Error creating service principal: $azure_client_id" @@ -195,8 +182,9 @@ createServicePrincipal() { createPermissions() { echo "==> Creating permissions" az role assignment create --assignee $azure_object_id --role "Owner" --scope /subscriptions/$azure_subscription_id - # We want to use this more conservative scope but it does not work with the - # current implementation which uses temporary resource groups + # If the user wants to use a more conserative scope, she can. She must + # configure the Azure builder to use build_resource_group_name. The + # easiest solution is subscription wide permission. # az role assignment create --spn http://$meta_name -g $azure_group_name -o "API Management Service Contributor" if [ $? -ne 0 ]; then echo "Error creating permissions for: http://$meta_name" From e0ac07f5db87559326151cc84d323c13a7c7f7c5 Mon Sep 17 00:00:00 2001 From: Christopher Boumenot Date: Sat, 3 Mar 2018 08:53:10 +0000 Subject: [PATCH 0559/1216] azure: correct function name spelling --- builder/azure/arm/artifact.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builder/azure/arm/artifact.go b/builder/azure/arm/artifact.go index c2abcc4d5..8e42fa43d 100644 --- a/builder/azure/arm/artifact.go +++ b/builder/azure/arm/artifact.go @@ -113,7 +113,7 @@ func storageUriToTemplateUri(su *url.URL) (*url.URL, error) { return url.Parse(strings.Replace(su.String(), filename, templateFilename, 1)) } -func (a *Artifact) isMangedImage() bool { +func (a *Artifact) isManagedImage() bool { return a.ManagedImageResourceGroupName != "" } @@ -142,7 +142,7 @@ func (a *Artifact) String() string { var buf bytes.Buffer buf.WriteString(fmt.Sprintf("%s:\n\n", a.BuilderId())) - if a.isMangedImage() { + if a.isManagedImage() { buf.WriteString(fmt.Sprintf("ManagedImageResourceGroupName: %s\n", a.ManagedImageResourceGroupName)) buf.WriteString(fmt.Sprintf("ManagedImageName: %s\n", a.ManagedImageName)) buf.WriteString(fmt.Sprintf("ManagedImageLocation: %s\n", a.ManagedImageLocation)) From 8166ba2d8dda96bd3b3e53b114e48de32d8976f6 Mon Sep 17 00:00:00 2001 From: Christopher Boumenot Date: Sun, 4 Mar 2018 21:21:02 -0800 Subject: [PATCH 0560/1216] azure: better error message --- builder/azure/arm/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index afa5f62a7..8b879f93e 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -604,7 +604,7 @@ func assertRequiredParametersSet(c *Config, errs *packer.MultiError) { } if !xor(c.Location != "", c.BuildResourceGroupName != "") { - errs = packer.MultiErrorAppend(errs, fmt.Errorf("Must specify either a location to create the resource group in or an existing build_resource_group_name.")) + errs = packer.MultiErrorAppend(errs, fmt.Errorf("Specify either a location to create the resource group in or an existing build_resource_group_name, but not both.")) } if c.ManagedImageName == "" && c.ManagedImageResourceGroupName == "" { From 676fb590901c7445198f83da458a9afa2ef87bd5 Mon Sep 17 00:00:00 2001 From: Christopher Boumenot Date: Mon, 5 Mar 2018 01:58:30 -0800 Subject: [PATCH 0561/1216] Better override support for PS build script Developers can now independently controls XC_OS and XC_ARCH. --- scripts/build.ps1 | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/scripts/build.ps1 b/scripts/build.ps1 index 5603ea078..972dcd414 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -53,10 +53,18 @@ if ($LastExitCode -eq 0) { if (Test-Path env:PACKER_DEV) { $XC_OS=$(go.exe env GOOS) $XC_ARCH=$(go.exe env GOARCH) -} -elseif (-not (Test-Path env:XC_ARCH)) { - $XC_ARCH="386 amd64 arm arm64 ppc64le" - $XC_OS="linux darwin windows freebsd openbsd solaris" +} else { + if (Test-Path env:XC_ARCH) { + $XC_ARCH = $(Get-Content env:XC_ARCH) + } else { + $XC_ARCH="386 amd64 arm arm64 ppc64le" + } + + if (Test-Path env:XC_OS) { + $XC_OS = $(Get-Content env:XC_OS) + } else { + $XC_OS = "linux darwin windows freebsd openbsd solaris" + } } # Delete the old dir From d2e593de378e24b476482bfe1ca61594d30f4391 Mon Sep 17 00:00:00 2001 From: Christopher Boumenot Date: Mon, 5 Mar 2018 01:27:52 -0800 Subject: [PATCH 0562/1216] azure: support for marketplace plan information --- builder/azure/arm/config.go | 14 ++ builder/azure/arm/config_test.go | 51 ++++++ builder/azure/arm/template_factory.go | 4 + ..._factory_test.TestPlanInfo01.approved.json | 170 +++++++++++++++++ ..._factory_test.TestPlanInfo02.approved.json | 171 ++++++++++++++++++ builder/azure/arm/template_factory_test.go | 39 ++++ builder/azure/common/template/template.go | 8 + .../azure/common/template/template_builder.go | 31 ++++ website/source/docs/builders/azure.html.md | 11 ++ 9 files changed, 499 insertions(+) create mode 100644 builder/azure/arm/template_factory_test.TestPlanInfo01.approved.json create mode 100644 builder/azure/arm/template_factory_test.TestPlanInfo02.approved.json diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index afa5f62a7..e8ed6bd81 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -115,6 +115,12 @@ type Config struct { // Additional Disks AdditionalDiskSize []int32 `mapstructure:"disk_additional_size"` + // Plan Info + PlanName string `mapstructure:"plan_name"` + PlanProduct string `mapstructure:"plan_product"` + PlanPublisher string `mapstructure:"plan_publisher"` + PlanPromotionCode string `mapstructure:"plan_promotion_code"` + // Runtime Values UserName string Password string @@ -647,6 +653,14 @@ func assertRequiredParametersSet(c *Config, errs *packer.MultiError) { errs = packer.MultiErrorAppend(errs, fmt.Errorf("If virtual_network_subnet_name is specified, so must virtual_network_name")) } + ///////////////////////////////////////////// + // Plan Info + if c.PlanName != "" || c.PlanProduct != "" || c.PlanPublisher != "" || c.PlanPromotionCode != "" { + if c.PlanName == "" || c.PlanProduct == "" || c.PlanPublisher == "" { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("if either plan_name, plan_product, plan_publisher, or plan_promotion_code are defined then plan_name, plan_product, and plan_publisher must be defined")) + } + } + ///////////////////////////////////////////// // OS if strings.EqualFold(c.OSType, constants.Target_Linux) { diff --git a/builder/azure/arm/config_test.go b/builder/azure/arm/config_test.go index d69c24a33..b56a260e3 100644 --- a/builder/azure/arm/config_test.go +++ b/builder/azure/arm/config_test.go @@ -1097,6 +1097,57 @@ func TestConfigAdditionalDiskOverrideDefault(t *testing.T) { } } +// Test that configuration handles plan info +// +// The use of plan info requires that the following three properties are set. +// +// 1. plan_name +// 2. plan_product +// 3. plan_publisher +func TestPlanInfoConfiguration(t *testing.T) { + config := map[string]interface{}{ + "capture_name_prefix": "ignore", + "capture_container_name": "ignore", + "image_offer": "ignore", + "image_publisher": "ignore", + "image_sku": "ignore", + "location": "ignore", + "storage_account": "ignore", + "resource_group_name": "ignore", + "subscription_id": "ignore", + "os_type": "linux", + "communicator": "none", + } + + config["plan_name"] = "--plan-name--" + _, _, err := newConfig(config, getPackerConfiguration()) + if err == nil { + t.Fatal("expected config to reject the use of plan_name without plan_product and plan_publisher") + } + + config["plan_product"] = "--plan-product--" + _, _, err = newConfig(config, getPackerConfiguration()) + if err == nil { + t.Fatal("expected config to reject the use of plan_name and plan_product without plan_publisher") + } + + config["plan_publisher"] = "--plan-publisher--" + c, _, err := newConfig(config, getPackerConfiguration()) + if err != nil { + t.Fatalf("expected config to accept a complete plan configuration: %s", err) + } + + if c.PlanName != "--plan-name--" { + t.Fatalf("Expected PlanName to be '--plan-name--', but got %q", c.PlanName) + } + if c.PlanProduct != "--plan-product--" { + t.Fatalf("Expected PlanProduct to be '--plan-product--', but got %q", c.PlanProduct) + } + if c.PlanPublisher != "--plan-publisher--" { + t.Fatalf("Expected PlanPublisher to be '--plan-publisher--, but got %q", c.PlanPublisher) + } +} + func getArmBuilderConfiguration() map[string]string { m := make(map[string]string) for _, v := range requiredConfigValues { diff --git a/builder/azure/arm/template_factory.go b/builder/azure/arm/template_factory.go index ebc7ef91c..a68b13f22 100644 --- a/builder/azure/arm/template_factory.go +++ b/builder/azure/arm/template_factory.go @@ -81,6 +81,10 @@ func GetVirtualMachineDeployment(config *Config) (*resources.Deployment, error) builder.SetCustomData(config.customData) } + if config.PlanName != "" { + builder.SetPlanInfo(config.PlanName, config.PlanProduct, config.PlanPublisher, config.PlanPromotionCode) + } + if config.VirtualNetworkName != "" && DefaultPrivateVirtualNetworkWithPublicIp != config.PrivateVirtualNetworkWithPublicIp { builder.SetPrivateVirtualNetworWithPublicIp( config.VirtualNetworkResourceGroupName, diff --git a/builder/azure/arm/template_factory_test.TestPlanInfo01.approved.json b/builder/azure/arm/template_factory_test.TestPlanInfo01.approved.json new file mode 100644 index 000000000..9af488f98 --- /dev/null +++ b/builder/azure/arm/template_factory_test.TestPlanInfo01.approved.json @@ -0,0 +1,170 @@ +{ + "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", + "contentVersion": "1.0.0.0", + "parameters": { + "adminPassword": { + "type": "string" + }, + "adminUsername": { + "type": "string" + }, + "dnsNameForPublicIP": { + "type": "string" + }, + "osDiskName": { + "type": "string" + }, + "storageAccountBlobEndpoint": { + "type": "string" + }, + "vmName": { + "type": "string" + }, + "vmSize": { + "type": "string" + } + }, + "resources": [ + { + "apiVersion": "[variables('publicIPAddressApiVersion')]", + "location": "[variables('location')]", + "name": "[variables('publicIPAddressName')]", + "properties": { + "dnsSettings": { + "domainNameLabel": "[parameters('dnsNameForPublicIP')]" + }, + "publicIPAllocationMethod": "[variables('publicIPAddressType')]" + }, + "type": "Microsoft.Network/publicIPAddresses" + }, + { + "apiVersion": "[variables('virtualNetworksApiVersion')]", + "location": "[variables('location')]", + "name": "[variables('virtualNetworkName')]", + "properties": { + "addressSpace": { + "addressPrefixes": [ + "[variables('addressPrefix')]" + ] + }, + "subnets": [ + { + "name": "[variables('subnetName')]", + "properties": { + "addressPrefix": "[variables('subnetAddressPrefix')]" + } + } + ] + }, + "type": "Microsoft.Network/virtualNetworks" + }, + { + "apiVersion": "[variables('networkInterfacesApiVersion')]", + "dependsOn": [ + "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]", + "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" + ], + "location": "[variables('location')]", + "name": "[variables('nicName')]", + "properties": { + "ipConfigurations": [ + { + "name": "ipconfig", + "properties": { + "privateIPAllocationMethod": "Dynamic", + "publicIPAddress": { + "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]" + }, + "subnet": { + "id": "[variables('subnetRef')]" + } + } + } + ] + }, + "type": "Microsoft.Network/networkInterfaces" + }, + { + "apiVersion": "[variables('apiVersion')]", + "dependsOn": [ + "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" + ], + "location": "[variables('location')]", + "name": "[parameters('vmName')]", + "plan": { + "name": "planName00", + "product": "planProduct00", + "publisher": "planPublisher00" + }, + "properties": { + "diagnosticsProfile": { + "bootDiagnostics": { + "enabled": false + } + }, + "hardwareProfile": { + "vmSize": "[parameters('vmSize')]" + }, + "networkProfile": { + "networkInterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" + } + ] + }, + "osProfile": { + "adminPassword": "[parameters('adminPassword')]", + "adminUsername": "[parameters('adminUsername')]", + "computerName": "[parameters('vmName')]", + "linuxConfiguration": { + "ssh": { + "publicKeys": [ + { + "keyData": "", + "path": "[variables('sshKeyPath')]" + } + ] + } + } + }, + "storageProfile": { + "imageReference": { + "offer": "ignored00", + "publisher": "ignored00", + "sku": "ignored00", + "version": "latest" + }, + "osDisk": { + "caching": "ReadWrite", + "createOption": "FromImage", + "name": "osdisk", + "vhd": { + "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]" + } + } + } + }, + "type": "Microsoft.Compute/virtualMachines" + } + ], + "variables": { + "addressPrefix": "10.0.0.0/16", + "apiVersion": "2017-03-30", + "location": "[resourceGroup().location]", + "managedDiskApiVersion": "2017-03-30", + "networkInterfacesApiVersion": "2017-04-01", + "nicName": "packerNic", + "publicIPAddressApiVersion": "2017-04-01", + "publicIPAddressName": "packerPublicIP", + "publicIPAddressType": "Dynamic", + "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", + "subnetAddressPrefix": "10.0.0.0/24", + "subnetName": "packerSubnet", + "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", + "virtualNetworkName": "packerNetwork", + "virtualNetworkResourceGroup": "[resourceGroup().name]", + "virtualNetworksApiVersion": "2017-04-01", + "vmStorageAccountContainerName": "images", + "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" + } +} \ No newline at end of file diff --git a/builder/azure/arm/template_factory_test.TestPlanInfo02.approved.json b/builder/azure/arm/template_factory_test.TestPlanInfo02.approved.json new file mode 100644 index 000000000..3cd802107 --- /dev/null +++ b/builder/azure/arm/template_factory_test.TestPlanInfo02.approved.json @@ -0,0 +1,171 @@ +{ + "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", + "contentVersion": "1.0.0.0", + "parameters": { + "adminPassword": { + "type": "string" + }, + "adminUsername": { + "type": "string" + }, + "dnsNameForPublicIP": { + "type": "string" + }, + "osDiskName": { + "type": "string" + }, + "storageAccountBlobEndpoint": { + "type": "string" + }, + "vmName": { + "type": "string" + }, + "vmSize": { + "type": "string" + } + }, + "resources": [ + { + "apiVersion": "[variables('publicIPAddressApiVersion')]", + "location": "[variables('location')]", + "name": "[variables('publicIPAddressName')]", + "properties": { + "dnsSettings": { + "domainNameLabel": "[parameters('dnsNameForPublicIP')]" + }, + "publicIPAllocationMethod": "[variables('publicIPAddressType')]" + }, + "type": "Microsoft.Network/publicIPAddresses" + }, + { + "apiVersion": "[variables('virtualNetworksApiVersion')]", + "location": "[variables('location')]", + "name": "[variables('virtualNetworkName')]", + "properties": { + "addressSpace": { + "addressPrefixes": [ + "[variables('addressPrefix')]" + ] + }, + "subnets": [ + { + "name": "[variables('subnetName')]", + "properties": { + "addressPrefix": "[variables('subnetAddressPrefix')]" + } + } + ] + }, + "type": "Microsoft.Network/virtualNetworks" + }, + { + "apiVersion": "[variables('networkInterfacesApiVersion')]", + "dependsOn": [ + "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]", + "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" + ], + "location": "[variables('location')]", + "name": "[variables('nicName')]", + "properties": { + "ipConfigurations": [ + { + "name": "ipconfig", + "properties": { + "privateIPAllocationMethod": "Dynamic", + "publicIPAddress": { + "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]" + }, + "subnet": { + "id": "[variables('subnetRef')]" + } + } + } + ] + }, + "type": "Microsoft.Network/networkInterfaces" + }, + { + "apiVersion": "[variables('apiVersion')]", + "dependsOn": [ + "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" + ], + "location": "[variables('location')]", + "name": "[parameters('vmName')]", + "plan": { + "name": "planName00", + "product": "planProduct00", + "promotionCode": "planPromotionCode00", + "publisher": "planPublisher00" + }, + "properties": { + "diagnosticsProfile": { + "bootDiagnostics": { + "enabled": false + } + }, + "hardwareProfile": { + "vmSize": "[parameters('vmSize')]" + }, + "networkProfile": { + "networkInterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" + } + ] + }, + "osProfile": { + "adminPassword": "[parameters('adminPassword')]", + "adminUsername": "[parameters('adminUsername')]", + "computerName": "[parameters('vmName')]", + "linuxConfiguration": { + "ssh": { + "publicKeys": [ + { + "keyData": "", + "path": "[variables('sshKeyPath')]" + } + ] + } + } + }, + "storageProfile": { + "imageReference": { + "offer": "ignored00", + "publisher": "ignored00", + "sku": "ignored00", + "version": "latest" + }, + "osDisk": { + "caching": "ReadWrite", + "createOption": "FromImage", + "name": "osdisk", + "vhd": { + "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]" + } + } + } + }, + "type": "Microsoft.Compute/virtualMachines" + } + ], + "variables": { + "addressPrefix": "10.0.0.0/16", + "apiVersion": "2017-03-30", + "location": "[resourceGroup().location]", + "managedDiskApiVersion": "2017-03-30", + "networkInterfacesApiVersion": "2017-04-01", + "nicName": "packerNic", + "publicIPAddressApiVersion": "2017-04-01", + "publicIPAddressName": "packerPublicIP", + "publicIPAddressType": "Dynamic", + "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", + "subnetAddressPrefix": "10.0.0.0/24", + "subnetName": "packerSubnet", + "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", + "virtualNetworkName": "packerNetwork", + "virtualNetworkResourceGroup": "[resourceGroup().name]", + "virtualNetworksApiVersion": "2017-04-01", + "vmStorageAccountContainerName": "images", + "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" + } +} \ No newline at end of file diff --git a/builder/azure/arm/template_factory_test.go b/builder/azure/arm/template_factory_test.go index 7909a0cee..292c56389 100644 --- a/builder/azure/arm/template_factory_test.go +++ b/builder/azure/arm/template_factory_test.go @@ -523,3 +523,42 @@ func TestKeyVaultDeployment03(t *testing.T) { t.Fatal(err) } } + +func TestPlanInfo01(t *testing.T) { + planInfo := map[string]interface{}{ + "plan_name": "planName00", + "plan_product": "planProduct00", + "plan_publisher": "planPublisher00", + } + + c, _, _ := newConfig(planInfo, getArmBuilderConfiguration(), getPackerConfiguration()) + deployment, err := GetVirtualMachineDeployment(c) + if err != nil { + t.Fatal(err) + } + + err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template) + if err != nil { + t.Fatal(err) + } +} + +func TestPlanInfo02(t *testing.T) { + planInfo := map[string]interface{}{ + "plan_name": "planName00", + "plan_product": "planProduct00", + "plan_publisher": "planPublisher00", + "plan_promotion_code": "planPromotionCode00", + } + + c, _, _ := newConfig(planInfo, getArmBuilderConfiguration(), getPackerConfiguration()) + deployment, err := GetVirtualMachineDeployment(c) + if err != nil { + t.Fatal(err) + } + + err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template) + if err != nil { + t.Fatal(err) + } +} diff --git a/builder/azure/common/template/template.go b/builder/azure/common/template/template.go index 070ca3392..d6273fa46 100644 --- a/builder/azure/common/template/template.go +++ b/builder/azure/common/template/template.go @@ -30,11 +30,19 @@ type Resource struct { Type *string `json:"type"` Location *string `json:"location,omitempty"` DependsOn *[]string `json:"dependsOn,omitempty"` + Plan *Plan `json:"plan,omitempty"` Properties *Properties `json:"properties,omitempty"` Tags *map[string]*string `json:"tags,omitempty"` Resources *[]Resource `json:"resources,omitempty"` } +type Plan struct { + Name *string `json:"name"` + Product *string `json:"product"` + Publisher *string `json:"publisher"` + PromotionCode *string `json:"promotionCode,omitempty"` +} + type OSDiskUnion struct { OsType compute.OperatingSystemTypes `json:"osType,omitempty"` OsState compute.OperatingSystemStateTypes `json:"osState,omitempty"` diff --git a/builder/azure/common/template/template_builder.go b/builder/azure/common/template/template_builder.go index 835aff096..a05b66ab3 100644 --- a/builder/azure/common/template/template_builder.go +++ b/builder/azure/common/template/template_builder.go @@ -179,6 +179,26 @@ func (s *TemplateBuilder) SetImageUrl(imageUrl string, osType compute.OperatingS return nil } +func (s *TemplateBuilder) SetPlanInfo(name, product, publisher, promotionCode string) error { + var promotionCodeVal *string = nil + if promotionCode != "" { + promotionCodeVal = to.StringPtr(promotionCode) + } + + for i, x := range *s.template.Resources { + if strings.EqualFold(*x.Type, resourceVirtualMachine) { + (*s.template.Resources)[i].Plan = &Plan{ + Name: to.StringPtr(name), + Product: to.StringPtr(product), + Publisher: to.StringPtr(publisher), + PromotionCode: promotionCodeVal, + } + } + } + + return nil +} + func (s *TemplateBuilder) SetOSDiskSizeGB(diskSizeGB int32) error { resource, err := s.getResourceByType(resourceVirtualMachine) if err != nil { @@ -302,6 +322,17 @@ func (s *TemplateBuilder) getResourceByType(t string) (*Resource, error) { return nil, fmt.Errorf("template: could not find a resource of type %s", t) } +func (s *TemplateBuilder) getResourceByType2(t string) (**Resource, error) { + for _, x := range *s.template.Resources { + if strings.EqualFold(*x.Type, t) { + p := &x + return &p, nil + } + } + + return nil, fmt.Errorf("template: could not find a resource of type %s", t) +} + func (s *TemplateBuilder) setVariable(name string, value string) { (*s.template.Variables)[name] = value } diff --git a/website/source/docs/builders/azure.html.md b/website/source/docs/builders/azure.html.md index b7d937acd..94e0d0a0c 100644 --- a/website/source/docs/builders/azure.html.md +++ b/website/source/docs/builders/azure.html.md @@ -162,6 +162,17 @@ Providing `temp_resource_group_name` or `location` in combination with `build_re `Linux` this configures an SSH authorized key. For `Windows` this configures a WinRM certificate. +- `plan_name` (string) The plan name. This setting (`plan_product`, `plan_publisher`, and `plan_promotion_code`) are + only needed for Marketplace images. Please refer to [Deploy an image with Marketplace terms](https://docs.microsoft.com/en-us/azure/virtual-machines/linux/cli-ps-findimage#deploy-an-image-with-marketplace-terms) for more details. + +- `plan_product` (string) The plan product. See `plan_name` for more information. + +- `plan_publisher` (string) Specifies the produce of the image from the Marketplace. This value is the same value as + Offer (`image_offer`). See `plan_name` for more information. + +- `plan_promotion_code` (string) Some Marketplace images use a promotion code. See `plan_name` for more + information. + - `temp_compute_name` (string) temporary name assigned to the VM. If this value is not set, a random value will be assigned. Knowing the resource group and VM name allows one to execute commands to update the VM during a Packer build, e.g. attach a resource disk to the VM. From df6cdcc7f7e123b122b94b191f800a098f6fbf6e Mon Sep 17 00:00:00 2001 From: Ali Rizvi-Santiago Date: Mon, 5 Mar 2018 20:58:43 -0600 Subject: [PATCH 0563/1216] Disable the usage of the XHCI bus for USB on the vmware-iso builder. Some platforms with incomplete XHCI implementations (i.e. FreeBSD) will poll the bus despite there being no usb devices available. This disables XHCI by default and documents how to enable it using the vmx_data option. This closes issue #5961. --- builder/vmware/iso/step_create_vmx.go | 1 - website/source/docs/builders/vmware-iso.html.md | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/builder/vmware/iso/step_create_vmx.go b/builder/vmware/iso/step_create_vmx.go index ca5fe7bbe..ffeb724ec 100644 --- a/builder/vmware/iso/step_create_vmx.go +++ b/builder/vmware/iso/step_create_vmx.go @@ -716,7 +716,6 @@ tools.upgrade.policy = "upgradeAtPowerCycle" // USB usb.pciSlotNumber = "32" usb.present = "{{ .Usb_Present }}" -usb_xhci.present = "TRUE" // Serial serial0.present = "{{ .Serial_Present }}" diff --git a/website/source/docs/builders/vmware-iso.html.md b/website/source/docs/builders/vmware-iso.html.md index 7784ac439..86d79e93b 100644 --- a/website/source/docs/builders/vmware-iso.html.md +++ b/website/source/docs/builders/vmware-iso.html.md @@ -357,7 +357,9 @@ builder. By default the upload path is set to `{{.Flavor}}.iso`. This setting is not used when `remote_type` is "esx5". -- `usb` (boolean) - Enable VMware's USB bus for the VM. +- `usb` (boolean) - Enable VMware's USB bus for the guest VM. To enable usage + of the XHCI bus for USB 3 (5 Gbit/s), one can use the `vmx_data` option to + enable it by specifying "true" for the `usb_xhci.present` property. - `version` (string) - The [vmx hardware version](http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1003746) From ac2e02b938e3913a98c6f66b620b4ad53e889496 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Tue, 6 Mar 2018 14:12:39 -0800 Subject: [PATCH 0564/1216] remove loglines that should not have made it onto master --- builder/vmware/common/driver.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/builder/vmware/common/driver.go b/builder/vmware/common/driver.go index c8fea88f4..8fa185efd 100644 --- a/builder/vmware/common/driver.go +++ b/builder/vmware/common/driver.go @@ -309,14 +309,12 @@ func (d *VmwareDriver) GuestIP(state multistep.StateBag) (string, error) { // grab network mapper netmap, err := d.NetworkMapper() - log.Printf("MEGAN: NEtworkMapper is %#v", netmap) if err != nil { return "", err } // convert the stashed network to a device network := state.Get("vmnetwork").(string) - log.Printf("MEGAN: network is %#v", network) device, err := netmap.NameIntoDevice(network) // we were unable to find the device, maybe it's a custom one... @@ -339,7 +337,6 @@ func (d *VmwareDriver) GuestIP(state multistep.StateBag) (string, error) { if err != nil { return "", err } - log.Printf("MEGAN mac address is %s", MACAddress) // figure out the correct dhcp leases dhcpLeasesPath := d.DhcpLeasesPath(device) From edd9df14ed6f34c7c032dc6f667c87612ce79f0b Mon Sep 17 00:00:00 2001 From: Jeff Escalante Date: Tue, 6 Mar 2018 19:05:39 -0500 Subject: [PATCH 0565/1216] prepend 'Packer' with 'HashiCorp' on first instance --- website/source/index.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/index.html.erb b/website/source/index.html.erb index 61990b4ba..18222370b 100644 --- a/website/source/index.html.erb +++ b/website/source/index.html.erb @@ -59,7 +59,7 @@ description: |- Infrastructure as code

Modern, Automated

- Packer is easy to use and automates the creation of any type of + HashiCorp Packer is easy to use and automates the creation of any type of machine image. It embraces modern configuration management by encouraging you to use automated scripts to install and configure the software within your Packer-made images. Packer brings machine From 5132b684b20cf920b9525cd9f2ff354a8184fc15 Mon Sep 17 00:00:00 2001 From: Jeff Escalante Date: Tue, 6 Mar 2018 19:06:05 -0500 Subject: [PATCH 0566/1216] update dependencies + middleman-hashicorp --- website/Gemfile | 2 +- website/Gemfile.lock | 42 +++++++++++++++++++++--------------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/website/Gemfile b/website/Gemfile index bb10a536e..d21d35e45 100644 --- a/website/Gemfile +++ b/website/Gemfile @@ -1,3 +1,3 @@ source "https://rubygems.org" -gem "middleman-hashicorp", "0.3.29" +gem "middleman-hashicorp", "0.3.30" diff --git a/website/Gemfile.lock b/website/Gemfile.lock index acf65a9ef..e567bf2ed 100644 --- a/website/Gemfile.lock +++ b/website/Gemfile.lock @@ -6,7 +6,7 @@ GEM minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) - autoprefixer-rails (7.1.5) + autoprefixer-rails (8.1.0) execjs bootstrap-sass (3.3.7) autoprefixer-rails (>= 5.2.1) @@ -18,7 +18,7 @@ GEM rack (>= 1.0.0) rack-test (>= 0.5.4) xpath (~> 2.0) - chunky_png (1.3.8) + chunky_png (1.3.10) coffee-script (2.4.1) coffee-script-source execjs @@ -41,7 +41,7 @@ GEM erubis (2.7.0) eventmachine (1.2.5) execjs (2.7.0) - ffi (1.9.18) + ffi (1.9.23) haml (5.0.4) temple (>= 0.8.0) tilt @@ -51,7 +51,7 @@ GEM http_parser.rb (0.6.0) i18n (0.7.0) json (2.1.0) - kramdown (1.15.0) + kramdown (1.16.2) listen (3.0.8) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) @@ -78,7 +78,7 @@ GEM rack (>= 1.4.5, < 2.0) thor (>= 0.15.2, < 2.0) tilt (~> 1.4.1, < 2.0) - middleman-hashicorp (0.3.29) + middleman-hashicorp (0.3.30) bootstrap-sass (~> 3.3) builder (~> 3.2) middleman (~> 3.4) @@ -102,22 +102,22 @@ GEM mime-types-data (~> 3.2015) mime-types-data (3.2016.0521) mini_portile2 (2.3.0) - minitest (5.10.3) - multi_json (1.12.2) - nokogiri (1.8.1) + minitest (5.11.3) + multi_json (1.13.1) + nokogiri (1.8.2) mini_portile2 (~> 2.3.0) - padrino-helpers (0.12.8.1) + padrino-helpers (0.12.9) i18n (~> 0.6, >= 0.6.7) - padrino-support (= 0.12.8.1) - tilt (~> 1.4.1) - padrino-support (0.12.8.1) + padrino-support (= 0.12.9) + tilt (>= 1.4.1, < 3) + padrino-support (0.12.9) activesupport (>= 3.1) - rack (1.6.8) + rack (1.6.9) rack-livereload (0.3.16) rack - rack-test (0.7.0) + rack-test (0.8.3) rack (>= 1.0, < 3) - rb-fsevent (0.10.2) + rb-fsevent (0.10.3) rb-inotify (0.9.10) ffi (>= 0.5.0, < 2) redcarpet (3.4.0) @@ -137,10 +137,10 @@ GEM thor (0.20.0) thread_safe (0.3.6) tilt (1.4.1) - turbolinks (5.0.1) - turbolinks-source (~> 5) - turbolinks-source (5.0.3) - tzinfo (1.2.3) + turbolinks (5.1.0) + turbolinks-source (~> 5.1) + turbolinks-source (5.1.0) + tzinfo (1.2.5) thread_safe (~> 0.1) uber (0.0.15) uglifier (2.7.2) @@ -153,7 +153,7 @@ PLATFORMS ruby DEPENDENCIES - middleman-hashicorp (= 0.3.29) + middleman-hashicorp (= 0.3.30) BUNDLED WITH - 1.15.4 + 1.16.1 From ff92fb883d3cbcccb42036645c6f732b7de69b59 Mon Sep 17 00:00:00 2001 From: Andreas Sommer Date: Wed, 7 Mar 2018 10:59:55 +0100 Subject: [PATCH 0567/1216] Handle multiple devices per VMware network type Fixes #5979 --- builder/vmware/common/driver.go | 239 ++++++++++++++----------- builder/vmware/common/driver_mock.go | 4 +- builder/vmware/common/driver_parser.go | 31 ++-- builder/vmware/iso/step_create_vmx.go | 15 +- 4 files changed, 162 insertions(+), 127 deletions(-) diff --git a/builder/vmware/common/driver.go b/builder/vmware/common/driver.go index 8fa185efd..47dc6e8b6 100644 --- a/builder/vmware/common/driver.go +++ b/builder/vmware/common/driver.go @@ -315,7 +315,7 @@ func (d *VmwareDriver) GuestIP(state multistep.StateBag) (string, error) { // convert the stashed network to a device network := state.Get("vmnetwork").(string) - device, err := netmap.NameIntoDevice(network) + devices, err := netmap.NameIntoDevices(network) // we were unable to find the device, maybe it's a custom one... // so, check to see if it's in the .vmx configuration @@ -326,7 +326,9 @@ func (d *VmwareDriver) GuestIP(state multistep.StateBag) (string, error) { return "", err } + var device string device, err = readCustomDeviceName(vmxData) + devices = append(devices, device) if err != nil { return "", err } @@ -338,64 +340,67 @@ func (d *VmwareDriver) GuestIP(state multistep.StateBag) (string, error) { return "", err } - // figure out the correct dhcp leases - dhcpLeasesPath := d.DhcpLeasesPath(device) - log.Printf("DHCP leases path: %s", dhcpLeasesPath) - if dhcpLeasesPath == "" { - return "", errors.New("no DHCP leases path found.") - } - - // open up the lease and read its contents - fh, err := os.Open(dhcpLeasesPath) - if err != nil { - return "", err - } - defer fh.Close() - - dhcpBytes, err := ioutil.ReadAll(fh) - if err != nil { - return "", err - } - - // start grepping through the file looking for fields that we care about - var lastIp string - var lastLeaseEnd time.Time - - var curIp string - var curLeaseEnd time.Time - - ipLineRe := regexp.MustCompile(`^lease (.+?) {$`) - endTimeLineRe := regexp.MustCompile(`^\s*ends \d (.+?);$`) - macLineRe := regexp.MustCompile(`^\s*hardware ethernet (.+?);$`) - - for _, line := range strings.Split(string(dhcpBytes), "\n") { - // Need to trim off CR character when running in windows - line = strings.TrimRight(line, "\r") - - matches := ipLineRe.FindStringSubmatch(line) - if matches != nil { - lastIp = matches[1] - continue + for _, device := range devices { + // figure out the correct dhcp leases + dhcpLeasesPath := d.DhcpLeasesPath(device) + log.Printf("Trying DHCP leases path: %s", dhcpLeasesPath) + if dhcpLeasesPath == "" { + return "", fmt.Errorf("no DHCP leases path found for device %s", device) } - matches = endTimeLineRe.FindStringSubmatch(line) - if matches != nil { - lastLeaseEnd, _ = time.Parse("2006/01/02 15:04:05", matches[1]) - continue + // open up the lease and read its contents + fh, err := os.Open(dhcpLeasesPath) + if err != nil { + return "", err + } + defer fh.Close() + + dhcpBytes, err := ioutil.ReadAll(fh) + if err != nil { + return "", err } - // If the mac address matches and this lease ends farther in the - // future than the last match we might have, then choose it. - matches = macLineRe.FindStringSubmatch(line) - if matches != nil && strings.EqualFold(matches[1], MACAddress) && curLeaseEnd.Before(lastLeaseEnd) { - curIp = lastIp - curLeaseEnd = lastLeaseEnd + // start grepping through the file looking for fields that we care about + var lastIp string + var lastLeaseEnd time.Time + + var curIp string + var curLeaseEnd time.Time + + ipLineRe := regexp.MustCompile(`^lease (.+?) {$`) + endTimeLineRe := regexp.MustCompile(`^\s*ends \d (.+?);$`) + macLineRe := regexp.MustCompile(`^\s*hardware ethernet (.+?);$`) + + for _, line := range strings.Split(string(dhcpBytes), "\n") { + // Need to trim off CR character when running in windows + line = strings.TrimRight(line, "\r") + + matches := ipLineRe.FindStringSubmatch(line) + if matches != nil { + lastIp = matches[1] + continue + } + + matches = endTimeLineRe.FindStringSubmatch(line) + if matches != nil { + lastLeaseEnd, _ = time.Parse("2006/01/02 15:04:05", matches[1]) + continue + } + + // If the mac address matches and this lease ends farther in the + // future than the last match we might have, then choose it. + matches = macLineRe.FindStringSubmatch(line) + if matches != nil && strings.EqualFold(matches[1], MACAddress) && curLeaseEnd.Before(lastLeaseEnd) { + curIp = lastIp + curLeaseEnd = lastLeaseEnd + } + } + if curIp != "" { + return curIp, nil } } - if curIp == "" { - return "", fmt.Errorf("IP not found for MAC %s in DHCP leases at %s", MACAddress, dhcpLeasesPath) - } - return curIp, nil + + return "", fmt.Errorf("None of the found device(s) %v has a DHCP lease for MAC %s", devices, MACAddress) } func (d *VmwareDriver) HostAddress(state multistep.StateBag) (string, error) { @@ -408,7 +413,7 @@ func (d *VmwareDriver) HostAddress(state multistep.StateBag) (string, error) { // convert network to name network := state.Get("vmnetwork").(string) - device, err := netmap.NameIntoDevice(network) + devices, err := netmap.NameIntoDevices(network) // we were unable to find the device, maybe it's a custom one... // so, check to see if it's in the .vmx configuration @@ -419,49 +424,56 @@ func (d *VmwareDriver) HostAddress(state multistep.StateBag) (string, error) { return "", err } + var device string device, err = readCustomDeviceName(vmxData) + devices = append(devices, device) if err != nil { return "", err } } - // parse dhcpd configuration - pathDhcpConfig := d.DhcpConfPath(device) - if _, err := os.Stat(pathDhcpConfig); err != nil { - return "", fmt.Errorf("Could not find vmnetdhcp conf file: %s", pathDhcpConfig) - } - - config, err := ReadDhcpConfig(pathDhcpConfig) - if err != nil { - return "", err - } - - // find the entry configured in the dhcpd - interfaceConfig, err := config.HostByName(device) - if err != nil { - return "", err - } - - // finally grab the hardware address - address, err := interfaceConfig.Hardware() - if err == nil { - return address.String(), nil - } - - // we didn't find it, so search through our interfaces for the device name - interfaceList, err := net.Interfaces() - if err == nil { - return "", err - } - - names := make([]string, 0) - for _, intf := range interfaceList { - if strings.HasSuffix(strings.ToLower(intf.Name), device) { - return intf.HardwareAddr.String(), nil + var lastError error + for _, device := range devices { + // parse dhcpd configuration + pathDhcpConfig := d.DhcpConfPath(device) + if _, err := os.Stat(pathDhcpConfig); err != nil { + return "", fmt.Errorf("Could not find vmnetdhcp conf file: %s", pathDhcpConfig) + } + + config, err := ReadDhcpConfig(pathDhcpConfig) + if err != nil { + lastError = err + continue + } + + // find the entry configured in the dhcpd + interfaceConfig, err := config.HostByName(device) + if err != nil { + lastError = err + continue + } + + // finally grab the hardware address + address, err := interfaceConfig.Hardware() + if err == nil { + return address.String(), nil + } + + // we didn't find it, so search through our interfaces for the device name + interfaceList, err := net.Interfaces() + if err == nil { + return "", err + } + + names := make([]string, 0) + for _, intf := range interfaceList { + if strings.HasSuffix(strings.ToLower(intf.Name), device) { + return intf.HardwareAddr.String(), nil + } + names = append(names, intf.Name) } - names = append(names, intf.Name) } - return "", fmt.Errorf("Unable to find device %s : %v", device, names) + return "", fmt.Errorf("Unable to find host address from devices %v, last error: %s", devices, lastError) } func (d *VmwareDriver) HostIP(state multistep.StateBag) (string, error) { @@ -474,7 +486,7 @@ func (d *VmwareDriver) HostIP(state multistep.StateBag) (string, error) { // convert network to name network := state.Get("vmnetwork").(string) - device, err := netmap.NameIntoDevice(network) + devices, err := netmap.NameIntoDevices(network) // we were unable to find the device, maybe it's a custom one... // so, check to see if it's in the .vmx configuration @@ -485,32 +497,41 @@ func (d *VmwareDriver) HostIP(state multistep.StateBag) (string, error) { return "", err } + var device string device, err = readCustomDeviceName(vmxData) + devices = append(devices, device) if err != nil { return "", err } } - // parse dhcpd configuration - pathDhcpConfig := d.DhcpConfPath(device) - if _, err := os.Stat(pathDhcpConfig); err != nil { - return "", fmt.Errorf("Could not find vmnetdhcp conf file: %s", pathDhcpConfig) - } - config, err := ReadDhcpConfig(pathDhcpConfig) - if err != nil { - return "", err - } + var lastError error + for _, device := range devices { + // parse dhcpd configuration + pathDhcpConfig := d.DhcpConfPath(device) + if _, err := os.Stat(pathDhcpConfig); err != nil { + return "", fmt.Errorf("Could not find vmnetdhcp conf file: %s", pathDhcpConfig) + } + config, err := ReadDhcpConfig(pathDhcpConfig) + if err != nil { + lastError = err + continue + } - // find the entry configured in the dhcpd - interfaceConfig, err := config.HostByName(device) - if err != nil { - return "", err - } + // find the entry configured in the dhcpd + interfaceConfig, err := config.HostByName(device) + if err != nil { + lastError = err + continue + } - address, err := interfaceConfig.IP4() - if err != nil { - return "", err - } + address, err := interfaceConfig.IP4() + if err != nil { + lastError = err + continue + } - return address.String(), nil + return address.String(), nil + } + return "", fmt.Errorf("Unable to find host IP from devices %v, last error: %s", devices, lastError) } diff --git a/builder/vmware/common/driver_mock.go b/builder/vmware/common/driver_mock.go index a0955db88..8c540913e 100644 --- a/builder/vmware/common/driver_mock.go +++ b/builder/vmware/common/driver_mock.go @@ -98,9 +98,9 @@ type NetworkMapperMock struct { DeviceIntoNameCalled int } -func (m NetworkMapperMock) NameIntoDevice(name string) (string, error) { +func (m NetworkMapperMock) NameIntoDevices(name string) ([]string, error) { m.NameIntoDeviceCalled += 1 - return "", nil + return make([]string, 0), nil } func (m NetworkMapperMock) DeviceIntoName(device string) (string, error) { m.DeviceIntoNameCalled += 1 diff --git a/builder/vmware/common/driver_parser.go b/builder/vmware/common/driver_parser.go index 24ecaf0df..fc54b8221 100644 --- a/builder/vmware/common/driver_parser.go +++ b/builder/vmware/common/driver_parser.go @@ -1065,7 +1065,7 @@ func (e *DhcpConfiguration) HostByName(host string) (configDeclaration, error) { type NetworkMap []map[string]string type NetworkNameMapper interface { - NameIntoDevice(string) (string, error) + NameIntoDevices(string) ([]string, error) DeviceIntoName(string) (string, error) } @@ -1082,13 +1082,17 @@ func ReadNetworkMap(fd *os.File) (NetworkMap, error) { return result, nil } -func (e NetworkMap) NameIntoDevice(name string) (string, error) { +func (e NetworkMap) NameIntoDevices(name string) ([]string, error) { + var devices []string for _, val := range e { if strings.ToLower(val["name"]) == strings.ToLower(name) { - return val["device"], nil + devices = append(devices, val["device"]) } } - return "", fmt.Errorf("Network name not found : %v", name) + if len(devices) > 0 { + return devices, nil + } + return make([]string, 0), fmt.Errorf("Network name not found : %v", name) } func (e NetworkMap) DeviceIntoName(device string) (string, error) { for _, val := range e { @@ -1905,21 +1909,26 @@ func networkingConfig_NamesToVmnet(config NetworkingConfig) map[NetworkingType][ const NetworkingInterfacePrefix = "vmnet" -func (e NetworkingConfig) NameIntoDevice(name string) (string, error) { +func (e NetworkingConfig) NameIntoDevices(name string) ([]string, error) { netmapper := networkingConfig_NamesToVmnet(e) name = strings.ToLower(name) - var vmnet int + var vmnets []string + var networkingType NetworkingType if name == "hostonly" && len(netmapper[NetworkingType_HOSTONLY]) > 0 { - vmnet = netmapper[NetworkingType_HOSTONLY][0] + networkingType = NetworkingType_HOSTONLY } else if name == "nat" && len(netmapper[NetworkingType_NAT]) > 0 { - vmnet = netmapper[NetworkingType_NAT][0] + networkingType = NetworkingType_NAT } else if name == "bridged" && len(netmapper[NetworkingType_BRIDGED]) > 0 { - vmnet = netmapper[NetworkingType_BRIDGED][0] + networkingType = NetworkingType_BRIDGED } else { - return "", fmt.Errorf("Network name not found : %v", name) + return make([]string, 0), fmt.Errorf("Network name not found: %v", name) } - return fmt.Sprintf("%s%d", NetworkingInterfacePrefix, vmnet), nil + + for i := 0; i < len(netmapper[networkingType]); i++ { + vmnets = append(vmnets, fmt.Sprintf("%s%d", NetworkingInterfacePrefix, netmapper[networkingType][i])) + } + return vmnets, nil } func (e NetworkingConfig) DeviceIntoName(device string) (string, error) { diff --git a/builder/vmware/iso/step_create_vmx.go b/builder/vmware/iso/step_create_vmx.go index ffeb724ec..7dbb01b79 100644 --- a/builder/vmware/iso/step_create_vmx.go +++ b/builder/vmware/iso/step_create_vmx.go @@ -475,14 +475,19 @@ func (s *stepCreateVMX) Run(_ context.Context, state multistep.StateBag) multist } // try and convert the specified network to a device. - device, err := netmap.NameIntoDevice(network) + devices, err := netmap.NameIntoDevices(network) - if err == nil { - // success. so we know that it's an actual network type inside netmap.conf + if err == nil && len(devices) > 0 { + // If multiple devices exist, for example for network "nat", VMware chooses + // the actual device. Only type "custom" allows the exact choice of a + // specific virtual network (see below). We allow VMware to choose the device + // and for device-specific operations like GuestIP, try to go over all + // devices that match a name (e.g. "nat"). + // https://pubs.vmware.com/workstation-9/index.jsp?topic=%2Fcom.vmware.ws.using.doc%2FGUID-3B504F2F-7A0B-415F-AE01-62363A95D052.html templateData.Network_Type = network - templateData.Network_Device = device + templateData.Network_Device = "" } else { - // otherwise, we were unable to find the type, so assume its a custom device. + // otherwise, we were unable to find the type, so assume it's a custom device templateData.Network_Type = "custom" templateData.Network_Device = network } From 91d60d6b8197c4cb9cdcaad800d2848e8191614d Mon Sep 17 00:00:00 2001 From: Graham Hayes Date: Tue, 6 Mar 2018 20:02:19 +0000 Subject: [PATCH 0568/1216] Add LXC to vagrant post-processor --- post-processor/vagrant/lxc.go | 34 +++++++++++++++++++ post-processor/vagrant/lxc_test.go | 9 +++++ post-processor/vagrant/post-processor.go | 3 ++ .../docs/post-processors/vagrant.html.md | 15 ++++++-- 4 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 post-processor/vagrant/lxc.go create mode 100644 post-processor/vagrant/lxc_test.go diff --git a/post-processor/vagrant/lxc.go b/post-processor/vagrant/lxc.go new file mode 100644 index 000000000..771df9509 --- /dev/null +++ b/post-processor/vagrant/lxc.go @@ -0,0 +1,34 @@ +package vagrant + +import ( + "fmt" + "path/filepath" + + "github.com/hashicorp/packer/packer" +) + +type LXCProvider struct{} + +func (p *LXCProvider) KeepInputArtifact() bool { + return false +} + +func (p *LXCProvider) Process(ui packer.Ui, artifact packer.Artifact, dir string) (vagrantfile string, metadata map[string]interface{}, err error) { + // Create the metadata + metadata = map[string]interface{}{ + "provider": "lxc", + "version": "1.0.0", + } + + // Copy all of the original contents into the temporary directory + for _, path := range artifact.Files() { + ui.Message(fmt.Sprintf("Copying: %s", path)) + + dstPath := filepath.Join(dir, filepath.Base(path)) + if err = CopyContents(dstPath, path); err != nil { + return + } + } + + return +} diff --git a/post-processor/vagrant/lxc_test.go b/post-processor/vagrant/lxc_test.go new file mode 100644 index 000000000..da5532c3b --- /dev/null +++ b/post-processor/vagrant/lxc_test.go @@ -0,0 +1,9 @@ +package vagrant + +import ( + "testing" +) + +func TestLXCProvider_impl(t *testing.T) { + var _ Provider = new(LXCProvider) +} diff --git a/post-processor/vagrant/post-processor.go b/post-processor/vagrant/post-processor.go index f3bb6e346..de93628c0 100644 --- a/post-processor/vagrant/post-processor.go +++ b/post-processor/vagrant/post-processor.go @@ -30,6 +30,7 @@ var builtins = map[string]string{ "packer.parallels": "parallels", "MSOpenTech.hyperv": "hyperv", "transcend.qemu": "libvirt", + "ustream.lxc": "lxc", } type Config struct { @@ -238,6 +239,8 @@ func providerForName(name string) Provider { return new(LibVirtProvider) case "google": return new(GoogleProvider) + case "lxc": + return new(LXCProvider) default: return nil } diff --git a/website/source/docs/post-processors/vagrant.html.md b/website/source/docs/post-processors/vagrant.html.md index 8feabbbbf..099051a81 100644 --- a/website/source/docs/post-processors/vagrant.html.md +++ b/website/source/docs/post-processors/vagrant.html.md @@ -34,6 +34,7 @@ providers. - DigitalOcean - Google - Hyper-V +- LXC - Parallels - QEMU - VirtualBox @@ -101,8 +102,18 @@ Specify overrides within the `override` configuration by provider name: In the example above, the compression level will be set to 1 except for VMware, where it will be set to 0. -The available provider names are: `aws`, `digitalocean`, `google`, `virtualbox`, -`vmware`, and `parallels`. +The available provider names are: + +- `aws` +- `digitalocean` +- `google` +- `hyperv` +- `parallels` +- `libvirt` +- `lxc` +- `scaleway` +- `virtualbox` +- `vmware` ## Input Artifacts From f305a2f4c22330c8283995b4388003f7c0140acf Mon Sep 17 00:00:00 2001 From: Andrew Pennebaker Date: Wed, 7 Mar 2018 10:59:02 -0600 Subject: [PATCH 0569/1216] send multibyte scancodes accurately to virtualbox --- builder/virtualbox/common/step_type_boot_command.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/builder/virtualbox/common/step_type_boot_command.go b/builder/virtualbox/common/step_type_boot_command.go index cb5b43648..77a82fb4b 100644 --- a/builder/virtualbox/common/step_type_boot_command.go +++ b/builder/virtualbox/common/step_type_boot_command.go @@ -91,7 +91,16 @@ func (s *StepTypeBootCommand) Run(_ context.Context, state multistep.StateBag) m return multistep.ActionHalt } - if err := driver.VBoxManage("controlvm", vmName, "keyboardputscancode", code); err != nil { + var codes []string + + for i := 0; i < len(code)/2; i++ { + codes = append(codes, code[i*2:i*2+2]) + } + + args := []string{"controlvm", vmName, "keyboardputscancode"} + args = append(args, codes...) + + if err := driver.VBoxManage(args...); err != nil { err := fmt.Errorf("Error sending boot command: %s", err) state.Put("error", err) ui.Error(err.Error()) From 4a2c124ea218ccea59850f661217df35701fd8d3 Mon Sep 17 00:00:00 2001 From: Evan Brown Date: Wed, 7 Mar 2018 15:35:01 -0800 Subject: [PATCH 0570/1216] builder/googlecompute: Optionally disable service account The ability to use a service account other than the default was introduced in #5928. This change adds to that by introducing the 'disable_default_service_account' config option. If true - and 'service_account_email' is not set - Packer will create a GCE VM with no service account. --- builder/googlecompute/config.go | 66 +++++++++---------- builder/googlecompute/config_test.go | 26 ++++++++ builder/googlecompute/driver.go | 45 ++++++------- builder/googlecompute/driver_gce.go | 12 +++- builder/googlecompute/step_create_instance.go | 45 ++++++------- .../step_create_instance_test.go | 48 ++++++++++++++ .../docs/builders/googlecompute.html.md | 5 +- 7 files changed, 166 insertions(+), 81 deletions(-) diff --git a/builder/googlecompute/config.go b/builder/googlecompute/config.go index 3d9194c17..537fc7422 100644 --- a/builder/googlecompute/config.go +++ b/builder/googlecompute/config.go @@ -26,39 +26,39 @@ type Config struct { AccountFile string `mapstructure:"account_file"` ProjectId string `mapstructure:"project_id"` - AcceleratorType string `mapstructure:"accelerator_type"` - AcceleratorCount int64 `mapstructure:"accelerator_count"` - Address string `mapstructure:"address"` - DiskName string `mapstructure:"disk_name"` - DiskSizeGb int64 `mapstructure:"disk_size"` - DiskType string `mapstructure:"disk_type"` - ImageName string `mapstructure:"image_name"` - ImageDescription string `mapstructure:"image_description"` - ImageFamily string `mapstructure:"image_family"` - ImageLabels map[string]string `mapstructure:"image_labels"` - ImageLicenses []string `mapstructure:"image_licenses"` - InstanceName string `mapstructure:"instance_name"` - Labels map[string]string `mapstructure:"labels"` - MachineType string `mapstructure:"machine_type"` - Metadata map[string]string `mapstructure:"metadata"` - Network string `mapstructure:"network"` - NetworkProjectId string `mapstructure:"network_project_id"` - OmitExternalIP bool `mapstructure:"omit_external_ip"` - OnHostMaintenance string `mapstructure:"on_host_maintenance"` - Preemptible bool `mapstructure:"preemptible"` - RawStateTimeout string `mapstructure:"state_timeout"` - Region string `mapstructure:"region"` - Scopes []string `mapstructure:"scopes"` - SourceImage string `mapstructure:"source_image"` - SourceImageFamily string `mapstructure:"source_image_family"` - SourceImageProjectId string `mapstructure:"source_image_project_id"` - StartupScriptFile string `mapstructure:"startup_script_file"` - Subnetwork string `mapstructure:"subnetwork"` - Tags []string `mapstructure:"tags"` - UseInternalIP bool `mapstructure:"use_internal_ip"` - Zone string `mapstructure:"zone"` - - ServiceAccountEmail string `mapstructure:"service_account_email"` + AcceleratorType string `mapstructure:"accelerator_type"` + AcceleratorCount int64 `mapstructure:"accelerator_count"` + Address string `mapstructure:"address"` + DisableDefaultServiceAccount bool `mapstructure:"disable_default_service_account"` + DiskName string `mapstructure:"disk_name"` + DiskSizeGb int64 `mapstructure:"disk_size"` + DiskType string `mapstructure:"disk_type"` + ImageName string `mapstructure:"image_name"` + ImageDescription string `mapstructure:"image_description"` + ImageFamily string `mapstructure:"image_family"` + ImageLabels map[string]string `mapstructure:"image_labels"` + ImageLicenses []string `mapstructure:"image_licenses"` + InstanceName string `mapstructure:"instance_name"` + Labels map[string]string `mapstructure:"labels"` + MachineType string `mapstructure:"machine_type"` + Metadata map[string]string `mapstructure:"metadata"` + Network string `mapstructure:"network"` + NetworkProjectId string `mapstructure:"network_project_id"` + OmitExternalIP bool `mapstructure:"omit_external_ip"` + OnHostMaintenance string `mapstructure:"on_host_maintenance"` + Preemptible bool `mapstructure:"preemptible"` + RawStateTimeout string `mapstructure:"state_timeout"` + Region string `mapstructure:"region"` + Scopes []string `mapstructure:"scopes"` + ServiceAccountEmail string `mapstructure:"service_account_email"` + SourceImage string `mapstructure:"source_image"` + SourceImageFamily string `mapstructure:"source_image_family"` + SourceImageProjectId string `mapstructure:"source_image_project_id"` + StartupScriptFile string `mapstructure:"startup_script_file"` + Subnetwork string `mapstructure:"subnetwork"` + Tags []string `mapstructure:"tags"` + UseInternalIP bool `mapstructure:"use_internal_ip"` + Zone string `mapstructure:"zone"` Account AccountFile stateTimeout time.Duration diff --git a/builder/googlecompute/config_test.go b/builder/googlecompute/config_test.go index a4b862a1a..508b3c528 100644 --- a/builder/googlecompute/config_test.go +++ b/builder/googlecompute/config_test.go @@ -170,6 +170,32 @@ func TestConfigPrepare(t *testing.T) { []string{"https://www.googleapis.com/auth/cloud-platform"}, false, }, + + { + "disable_default_service_account", + "", + false, + }, + { + "disable_default_service_account", + nil, + false, + }, + { + "disable_default_service_account", + false, + false, + }, + { + "disable_default_service_account", + true, + false, + }, + { + "disable_default_service_account", + "NOT A BOOL", + true, + }, } for _, tc := range cases { diff --git a/builder/googlecompute/driver.go b/builder/googlecompute/driver.go index 4c07a06cf..22ec62707 100644 --- a/builder/googlecompute/driver.go +++ b/builder/googlecompute/driver.go @@ -58,28 +58,29 @@ type Driver interface { } type InstanceConfig struct { - AcceleratorType string - AcceleratorCount int64 - Address string - Description string - DiskSizeGb int64 - DiskType string - Image *Image - Labels map[string]string - MachineType string - Metadata map[string]string - Name string - Network string - NetworkProjectId string - OmitExternalIP bool - OnHostMaintenance string - Preemptible bool - Region string - ServiceAccountEmail string - Scopes []string - Subnetwork string - Tags []string - Zone string + AcceleratorType string + AcceleratorCount int64 + Address string + Description string + DisableDefaultServiceAccount bool + DiskSizeGb int64 + DiskType string + Image *Image + Labels map[string]string + MachineType string + Metadata map[string]string + Name string + Network string + NetworkProjectId string + OmitExternalIP bool + OnHostMaintenance string + Preemptible bool + Region string + ServiceAccountEmail string + Scopes []string + Subnetwork string + Tags []string + Zone string } // WindowsPasswordConfig is the data structue that GCE needs to encrypt the created diff --git a/builder/googlecompute/driver_gce.go b/builder/googlecompute/driver_gce.go index 5f286fa71..8adbd833e 100644 --- a/builder/googlecompute/driver_gce.go +++ b/builder/googlecompute/driver_gce.go @@ -343,12 +343,18 @@ func (d *driverGCE) RunInstance(c *InstanceConfig) (<-chan error, error) { guestAccelerators = append(guestAccelerators, ac) } - serviceAccount := &compute.ServiceAccount{ - Email: "default", - Scopes: c.Scopes, + // Configure the instance's service account. If the user has set + // disable_default_service_account, then the default service account + // will not be used. If they also do not set service_account_email, then + // the instance will be created with no service account or scopes. + serviceAccount := &compute.ServiceAccount{} + if !c.DisableDefaultServiceAccount { + serviceAccount.Email = "default" + serviceAccount.Scopes = c.Scopes } if c.ServiceAccountEmail != "" { serviceAccount.Email = c.ServiceAccountEmail + serviceAccount.Scopes = c.Scopes } // Create the instance information diff --git a/builder/googlecompute/step_create_instance.go b/builder/googlecompute/step_create_instance.go index bc37aa7b2..06e5b8eb1 100644 --- a/builder/googlecompute/step_create_instance.go +++ b/builder/googlecompute/step_create_instance.go @@ -100,28 +100,29 @@ func (s *StepCreateInstance) Run(_ context.Context, state multistep.StateBag) mu var metadata map[string]string metadata, err = c.createInstanceMetadata(sourceImage, sshPublicKey) errCh, err = d.RunInstance(&InstanceConfig{ - AcceleratorType: c.AcceleratorType, - AcceleratorCount: c.AcceleratorCount, - Address: c.Address, - Description: "New instance created by Packer", - DiskSizeGb: c.DiskSizeGb, - DiskType: c.DiskType, - Image: sourceImage, - Labels: c.Labels, - MachineType: c.MachineType, - Metadata: metadata, - Name: name, - Network: c.Network, - NetworkProjectId: c.NetworkProjectId, - OmitExternalIP: c.OmitExternalIP, - OnHostMaintenance: c.OnHostMaintenance, - Preemptible: c.Preemptible, - Region: c.Region, - ServiceAccountEmail: c.ServiceAccountEmail, - Scopes: c.Scopes, - Subnetwork: c.Subnetwork, - Tags: c.Tags, - Zone: c.Zone, + AcceleratorType: c.AcceleratorType, + AcceleratorCount: c.AcceleratorCount, + Address: c.Address, + Description: "New instance created by Packer", + DisableDefaultServiceAccount: c.DisableDefaultServiceAccount, + DiskSizeGb: c.DiskSizeGb, + DiskType: c.DiskType, + Image: sourceImage, + Labels: c.Labels, + MachineType: c.MachineType, + Metadata: metadata, + Name: name, + Network: c.Network, + NetworkProjectId: c.NetworkProjectId, + OmitExternalIP: c.OmitExternalIP, + OnHostMaintenance: c.OnHostMaintenance, + Preemptible: c.Preemptible, + Region: c.Region, + ServiceAccountEmail: c.ServiceAccountEmail, + Scopes: c.Scopes, + Subnetwork: c.Subnetwork, + Tags: c.Tags, + Zone: c.Zone, }) if err == nil { diff --git a/builder/googlecompute/step_create_instance_test.go b/builder/googlecompute/step_create_instance_test.go index f8196e3d0..e56159138 100644 --- a/builder/googlecompute/step_create_instance_test.go +++ b/builder/googlecompute/step_create_instance_test.go @@ -248,6 +248,54 @@ func TestStepCreateInstance_errorTimeout(t *testing.T) { assert.False(t, ok, "State should not have an instance name.") } +func TestStepCreateInstance_noServiceAccount(t *testing.T) { + state := testState(t) + step := new(StepCreateInstance) + defer step.Cleanup(state) + + state.Put("ssh_public_key", "key") + + c := state.Get("config").(*Config) + c.DisableDefaultServiceAccount = true + c.ServiceAccountEmail = "" + d := state.Get("driver").(*DriverMock) + d.GetImageResult = StubImage("test-image", "test-project", []string{}, 100) + + // run the step + assert.Equal(t, step.Run(context.Background(), state), multistep.ActionContinue, "Step should have passed and continued.") + + // cleanup + step.Cleanup(state) + + // Check args passed to the driver. + assert.Equal(t, d.RunInstanceConfig.DisableDefaultServiceAccount, c.DisableDefaultServiceAccount, "Incorrect value for DisableDefaultServiceAccount passed to driver.") + assert.Equal(t, d.RunInstanceConfig.ServiceAccountEmail, c.ServiceAccountEmail, "Incorrect value for ServiceAccountEmail passed to driver.") +} + +func TestStepCreateInstance_customServiceAccount(t *testing.T) { + state := testState(t) + step := new(StepCreateInstance) + defer step.Cleanup(state) + + state.Put("ssh_public_key", "key") + + c := state.Get("config").(*Config) + c.DisableDefaultServiceAccount = true + c.ServiceAccountEmail = "custom-service-account" + d := state.Get("driver").(*DriverMock) + d.GetImageResult = StubImage("test-image", "test-project", []string{}, 100) + + // run the step + assert.Equal(t, step.Run(context.Background(), state), multistep.ActionContinue, "Step should have passed and continued.") + + // cleanup + step.Cleanup(state) + + // Check args passed to the driver. + assert.Equal(t, d.RunInstanceConfig.DisableDefaultServiceAccount, c.DisableDefaultServiceAccount, "Incorrect value for DisableDefaultServiceAccount passed to driver.") + assert.Equal(t, d.RunInstanceConfig.ServiceAccountEmail, c.ServiceAccountEmail, "Incorrect value for ServiceAccountEmail passed to driver.") +} + func TestCreateInstanceMetadata(t *testing.T) { state := testState(t) c := state.Get("config").(*Config) diff --git a/website/source/docs/builders/googlecompute.html.md b/website/source/docs/builders/googlecompute.html.md index fb006c7ae..89e57a1f7 100644 --- a/website/source/docs/builders/googlecompute.html.md +++ b/website/source/docs/builders/googlecompute.html.md @@ -210,6 +210,9 @@ builder. - `address` (string) - The name of a pre-allocated static external IP address. Note, must be the name and not the actual IP address. +- `disable_default_service_account` (bool) - If true, the default service account will not be used if `service_account_email` + is not specified. Set this value to true and omit `service_account_email` to provision a VM with no service account. + - `disk_name` (string) - The name of the disk, if unset the instance name will be used. @@ -269,7 +272,7 @@ builder. to the region hosting the specified `zone`. - `service_account_email` (string) - The service account to be used for launched instance. Defaults to - the project's default service account. + the project's default service account unless `disable_default_service_account` is true. - `scopes` (array of strings) - The service account scopes for launched instance. Defaults to: From 7dfa827a4c52ef94ed41b0fa361d9c5b03cb9de3 Mon Sep 17 00:00:00 2001 From: Rickard von Essen Date: Thu, 8 Mar 2018 13:56:34 +0100 Subject: [PATCH 0571/1216] builder/docker: Small doc update - Correcting docs about communicator for Docker - Clarifying that the docker builder will not work with remote hosts. --- website/source/docs/builders/docker.html.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/website/source/docs/builders/docker.html.md b/website/source/docs/builders/docker.html.md index e1390ff12..23080e3ce 100644 --- a/website/source/docs/builders/docker.html.md +++ b/website/source/docs/builders/docker.html.md @@ -24,9 +24,11 @@ has a simple mental model: you provision containers much the same way you provision a normal virtualized or dedicated server. For more information, read the section on [Dockerfiles](#dockerfiles). -The Docker builder must run on a machine that has Docker installed. Therefore -the builder only works on machines that support Docker. You can learn about -what [platforms Docker supports and how to install onto them](https://docs.docker.com/engine/installation/) in the Docker documentation. +The Docker builder must run on a machine that has Docker Engine installed. +Therefore the builder only works on machines that support Docker and _does not +support running on a Docker remote host_. You can learn about what +[platforms Docker supports and how to install onto them](https://docs.docker.com/engine/installation/) +in the Docker documentation. ## Basic Example: Export @@ -125,9 +127,8 @@ Configuration options are organized below into two categories: required and optional. Within each category, the available options are alphabetized and described. -In addition to the options listed here, a -[communicator](/docs/templates/communicator.html) can be configured for this -builder. +The Docker builder uses a special Docker communicator _and will not use_ the +standard [communicators](/docs/templates/communicator.html). ### Required: From 1ef491d4c8969385f0030af919cbc12f0a125de2 Mon Sep 17 00:00:00 2001 From: Christopher Boumenot Date: Thu, 8 Mar 2018 22:39:23 -0800 Subject: [PATCH 0572/1216] incorporate reviewer feedback --- builder/azure/arm/config.go | 27 +++-- builder/azure/arm/config_test.go | 98 +++++++++++++++++-- builder/azure/arm/template_factory.go | 4 +- ..._factory_test.TestPlanInfo01.approved.json | 24 +++++ ..._factory_test.TestPlanInfo02.approved.json | 28 ++++++ builder/azure/arm/template_factory_test.go | 21 ++-- examples/azure/marketplace_plan_info.json | 51 ++++++++++ website/source/docs/builders/azure.html.md | 33 +++++-- 8 files changed, 253 insertions(+), 33 deletions(-) create mode 100644 examples/azure/marketplace_plan_info.json diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index e8ed6bd81..df507930a 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -57,6 +57,13 @@ var ( reResourceGroupName = regexp.MustCompile(validResourceGroupNameRe) ) +type PlanInformation struct { + PlanName string `mapstructure:"plan_name"` + PlanProduct string `mapstructure:"plan_product"` + PlanPublisher string `mapstructure:"plan_publisher"` + PlanPromotionCode string `mapstructure:"plan_promotion_code"` +} + type Config struct { common.PackerConfig `mapstructure:",squash"` @@ -107,6 +114,7 @@ type Config struct { VirtualNetworkResourceGroupName string `mapstructure:"virtual_network_resource_group_name"` CustomDataFile string `mapstructure:"custom_data_file"` customData string + PlanInfo PlanInformation `mapstructure:"plan_info"` // OS OSType string `mapstructure:"os_type"` @@ -115,12 +123,6 @@ type Config struct { // Additional Disks AdditionalDiskSize []int32 `mapstructure:"disk_additional_size"` - // Plan Info - PlanName string `mapstructure:"plan_name"` - PlanProduct string `mapstructure:"plan_product"` - PlanPublisher string `mapstructure:"plan_publisher"` - PlanPromotionCode string `mapstructure:"plan_promotion_code"` - // Runtime Values UserName string Password string @@ -655,9 +657,18 @@ func assertRequiredParametersSet(c *Config, errs *packer.MultiError) { ///////////////////////////////////////////// // Plan Info - if c.PlanName != "" || c.PlanProduct != "" || c.PlanPublisher != "" || c.PlanPromotionCode != "" { - if c.PlanName == "" || c.PlanProduct == "" || c.PlanPublisher == "" { + if c.PlanInfo.PlanName != "" || c.PlanInfo.PlanProduct != "" || c.PlanInfo.PlanPublisher != "" || c.PlanInfo.PlanPromotionCode != "" { + if c.PlanInfo.PlanName == "" || c.PlanInfo.PlanProduct == "" || c.PlanInfo.PlanPublisher == "" { errs = packer.MultiErrorAppend(errs, fmt.Errorf("if either plan_name, plan_product, plan_publisher, or plan_promotion_code are defined then plan_name, plan_product, and plan_publisher must be defined")) + } else { + if c.AzureTags == nil { + c.AzureTags = make(map[string]*string) + } + + c.AzureTags["PlanInfo"] = &c.PlanInfo.PlanName + c.AzureTags["PlanProduct"] = &c.PlanInfo.PlanProduct + c.AzureTags["PlanPublisher"] = &c.PlanInfo.PlanPublisher + c.AzureTags["PlanPromotionCode"] = &c.PlanInfo.PlanPromotionCode } } diff --git a/builder/azure/arm/config_test.go b/builder/azure/arm/config_test.go index b56a260e3..c7758fb91 100644 --- a/builder/azure/arm/config_test.go +++ b/builder/azure/arm/config_test.go @@ -1119,32 +1119,112 @@ func TestPlanInfoConfiguration(t *testing.T) { "communicator": "none", } - config["plan_name"] = "--plan-name--" + planInfo := map[string]string{ + "plan_name": "--plan-name--", + } + config["plan_info"] = planInfo + _, _, err := newConfig(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject the use of plan_name without plan_product and plan_publisher") } - config["plan_product"] = "--plan-product--" + planInfo["plan_product"] = "--plan-product--" _, _, err = newConfig(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject the use of plan_name and plan_product without plan_publisher") } - config["plan_publisher"] = "--plan-publisher--" + planInfo["plan_publisher"] = "--plan-publisher--" c, _, err := newConfig(config, getPackerConfiguration()) if err != nil { t.Fatalf("expected config to accept a complete plan configuration: %s", err) } - if c.PlanName != "--plan-name--" { - t.Fatalf("Expected PlanName to be '--plan-name--', but got %q", c.PlanName) + if c.PlanInfo.PlanName != "--plan-name--" { + t.Fatalf("Expected PlanName to be '--plan-name--', but got %q", c.PlanInfo.PlanName) } - if c.PlanProduct != "--plan-product--" { - t.Fatalf("Expected PlanProduct to be '--plan-product--', but got %q", c.PlanProduct) + if c.PlanInfo.PlanProduct != "--plan-product--" { + t.Fatalf("Expected PlanProduct to be '--plan-product--', but got %q", c.PlanInfo.PlanProduct) } - if c.PlanPublisher != "--plan-publisher--" { - t.Fatalf("Expected PlanPublisher to be '--plan-publisher--, but got %q", c.PlanPublisher) + if c.PlanInfo.PlanPublisher != "--plan-publisher--" { + t.Fatalf("Expected PlanPublisher to be '--plan-publisher--, but got %q", c.PlanInfo.PlanPublisher) + } +} + +func TestPlanInfoPromotionCode(t *testing.T) { + config := map[string]interface{}{ + "capture_name_prefix": "ignore", + "capture_container_name": "ignore", + "image_offer": "ignore", + "image_publisher": "ignore", + "image_sku": "ignore", + "location": "ignore", + "storage_account": "ignore", + "resource_group_name": "ignore", + "subscription_id": "ignore", + "os_type": "linux", + "communicator": "none", + "plan_info": map[string]string{ + "plan_name": "--plan-name--", + "plan_product": "--plan-product--", + "plan_publisher": "--plan-publisher--", + "plan_promotion_code": "--plan-promotion-code--", + }, + } + + c, _, err := newConfig(config, getPackerConfiguration()) + if err != nil { + t.Fatalf("expected config to accept plan_info configuration, but got %s", err) + } + + if c.PlanInfo.PlanName != "--plan-name--" { + t.Fatalf("Expected PlanName to be '--plan-name--', but got %q", c.PlanInfo.PlanName) + } + if c.PlanInfo.PlanProduct != "--plan-product--" { + t.Fatalf("Expected PlanProduct to be '--plan-product--', but got %q", c.PlanInfo.PlanProduct) + } + if c.PlanInfo.PlanPublisher != "--plan-publisher--" { + t.Fatalf("Expected PlanPublisher to be '--plan-publisher--, but got %q", c.PlanInfo.PlanPublisher) + } + if c.PlanInfo.PlanPromotionCode != "--plan-promotion-code--" { + t.Fatalf("Expected PlanPublisher to be '--plan-promotion-code----, but got %q", c.PlanInfo.PlanPromotionCode) + } +} + +// plan_info defines 3 or 4 tags based on plan data. +// The user can define up to 15 tags. If the combination of these two +// exceeds the max tag amount, the builder should reject the configuration. +func TestPlanInfoTooManyTagsErrors(t *testing.T) { + exactMaxNumberOfTags := map[string]string{} + for i := 0; i < 15; i++ { + exactMaxNumberOfTags[fmt.Sprintf("tag%.2d", i)] = "ignored" + } + + config := map[string]interface{}{ + "capture_name_prefix": "ignore", + "capture_container_name": "ignore", + "image_offer": "ignore", + "image_publisher": "ignore", + "image_sku": "ignore", + "location": "ignore", + "storage_account": "ignore", + "resource_group_name": "ignore", + "subscription_id": "ignore", + "os_type": "linux", + "communicator": "none", + "azure_tags": exactMaxNumberOfTags, + "plan_info": map[string]string{ + "plan_name": "--plan-name--", + "plan_product": "--plan-product--", + "plan_publisher": "--plan-publisher--", + "plan_promotion_code": "--plan-promotion-code--", + }, + } + + _, _, err := newConfig(config, getPackerConfiguration()) + if err == nil { + t.Fatal("expected config to reject configuration due to excess tags") } } diff --git a/builder/azure/arm/template_factory.go b/builder/azure/arm/template_factory.go index a68b13f22..843580fd2 100644 --- a/builder/azure/arm/template_factory.go +++ b/builder/azure/arm/template_factory.go @@ -81,8 +81,8 @@ func GetVirtualMachineDeployment(config *Config) (*resources.Deployment, error) builder.SetCustomData(config.customData) } - if config.PlanName != "" { - builder.SetPlanInfo(config.PlanName, config.PlanProduct, config.PlanPublisher, config.PlanPromotionCode) + if config.PlanInfo.PlanName != "" { + builder.SetPlanInfo(config.PlanInfo.PlanName, config.PlanInfo.PlanProduct, config.PlanInfo.PlanPublisher, config.PlanInfo.PlanPromotionCode) } if config.VirtualNetworkName != "" && DefaultPrivateVirtualNetworkWithPublicIp != config.PrivateVirtualNetworkWithPublicIp { diff --git a/builder/azure/arm/template_factory_test.TestPlanInfo01.approved.json b/builder/azure/arm/template_factory_test.TestPlanInfo01.approved.json index 9af488f98..74546c490 100644 --- a/builder/azure/arm/template_factory_test.TestPlanInfo01.approved.json +++ b/builder/azure/arm/template_factory_test.TestPlanInfo01.approved.json @@ -35,6 +35,12 @@ }, "publicIPAllocationMethod": "[variables('publicIPAddressType')]" }, + "tags": { + "PlanInfo": "planName00", + "PlanProduct": "planProduct00", + "PlanPromotionCode": "", + "PlanPublisher": "planPublisher00" + }, "type": "Microsoft.Network/publicIPAddresses" }, { @@ -56,6 +62,12 @@ } ] }, + "tags": { + "PlanInfo": "planName00", + "PlanProduct": "planProduct00", + "PlanPromotionCode": "", + "PlanPublisher": "planPublisher00" + }, "type": "Microsoft.Network/virtualNetworks" }, { @@ -82,6 +94,12 @@ } ] }, + "tags": { + "PlanInfo": "planName00", + "PlanProduct": "planProduct00", + "PlanPromotionCode": "", + "PlanPublisher": "planPublisher00" + }, "type": "Microsoft.Network/networkInterfaces" }, { @@ -144,6 +162,12 @@ } } }, + "tags": { + "PlanInfo": "planName00", + "PlanProduct": "planProduct00", + "PlanPromotionCode": "", + "PlanPublisher": "planPublisher00" + }, "type": "Microsoft.Compute/virtualMachines" } ], diff --git a/builder/azure/arm/template_factory_test.TestPlanInfo02.approved.json b/builder/azure/arm/template_factory_test.TestPlanInfo02.approved.json index 3cd802107..8f93bb530 100644 --- a/builder/azure/arm/template_factory_test.TestPlanInfo02.approved.json +++ b/builder/azure/arm/template_factory_test.TestPlanInfo02.approved.json @@ -35,6 +35,13 @@ }, "publicIPAllocationMethod": "[variables('publicIPAddressType')]" }, + "tags": { + "PlanInfo": "planName00", + "PlanProduct": "planProduct00", + "PlanPromotionCode": "planPromotionCode00", + "PlanPublisher": "planPublisher00", + "dept": "engineering" + }, "type": "Microsoft.Network/publicIPAddresses" }, { @@ -56,6 +63,13 @@ } ] }, + "tags": { + "PlanInfo": "planName00", + "PlanProduct": "planProduct00", + "PlanPromotionCode": "planPromotionCode00", + "PlanPublisher": "planPublisher00", + "dept": "engineering" + }, "type": "Microsoft.Network/virtualNetworks" }, { @@ -82,6 +96,13 @@ } ] }, + "tags": { + "PlanInfo": "planName00", + "PlanProduct": "planProduct00", + "PlanPromotionCode": "planPromotionCode00", + "PlanPublisher": "planPublisher00", + "dept": "engineering" + }, "type": "Microsoft.Network/networkInterfaces" }, { @@ -145,6 +166,13 @@ } } }, + "tags": { + "PlanInfo": "planName00", + "PlanProduct": "planProduct00", + "PlanPromotionCode": "planPromotionCode00", + "PlanPublisher": "planPublisher00", + "dept": "engineering" + }, "type": "Microsoft.Compute/virtualMachines" } ], diff --git a/builder/azure/arm/template_factory_test.go b/builder/azure/arm/template_factory_test.go index 292c56389..a6d31d7d9 100644 --- a/builder/azure/arm/template_factory_test.go +++ b/builder/azure/arm/template_factory_test.go @@ -526,9 +526,11 @@ func TestKeyVaultDeployment03(t *testing.T) { func TestPlanInfo01(t *testing.T) { planInfo := map[string]interface{}{ - "plan_name": "planName00", - "plan_product": "planProduct00", - "plan_publisher": "planPublisher00", + "plan_info": map[string]string{ + "plan_name": "planName00", + "plan_product": "planProduct00", + "plan_publisher": "planPublisher00", + }, } c, _, _ := newConfig(planInfo, getArmBuilderConfiguration(), getPackerConfiguration()) @@ -545,10 +547,15 @@ func TestPlanInfo01(t *testing.T) { func TestPlanInfo02(t *testing.T) { planInfo := map[string]interface{}{ - "plan_name": "planName00", - "plan_product": "planProduct00", - "plan_publisher": "planPublisher00", - "plan_promotion_code": "planPromotionCode00", + "azure_tags": map[string]string{ + "dept": "engineering", + }, + "plan_info": map[string]string{ + "plan_name": "planName00", + "plan_product": "planProduct00", + "plan_publisher": "planPublisher00", + "plan_promotion_code": "planPromotionCode00", + }, } c, _, _ := newConfig(planInfo, getArmBuilderConfiguration(), getPackerConfiguration()) diff --git a/examples/azure/marketplace_plan_info.json b/examples/azure/marketplace_plan_info.json new file mode 100644 index 000000000..4891a9e6b --- /dev/null +++ b/examples/azure/marketplace_plan_info.json @@ -0,0 +1,51 @@ +{ + "variables": { + "client_id": "{{env `ARM_CLIENT_ID`}}", + "client_secret": "{{env `ARM_CLIENT_SECRET`}}", + "resource_group": "{{env `ARM_RESOURCE_GROUP`}}", + "storage_account": "{{env `ARM_STORAGE_ACCOUNT`}}", + "subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}" + }, + "builders": [{ + "type": "azure-arm", + + "client_id": "{{user `client_id`}}", + "client_secret": "{{user `client_secret`}}", + "resource_group_name": "{{user `resource_group`}}", + "storage_account": "{{user `storage_account`}}", + "subscription_id": "{{user `subscription_id`}}", + + "capture_container_name": "images", + "capture_name_prefix": "packer", + + "os_type": "Linux", + "image_publisher": "Canonical", + "image_offer": "UbuntuServer", + "image_sku": "16.04-LTS", + + "azure_tags": { + "dept": "engineering", + "task": "image deployment" + }, + + "plan_info": { + "plan_name": "rabbitmq", + "plan_product": "rabbitmq", + "plan_publisher": "bitnami" + }, + + "location": "West US", + "vm_size": "Standard_DS2_v2" + }], + "provisioners": [{ + "execute_command": "chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'", + "inline": [ + "apt-get update", + "apt-get upgrade -y", + + "/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync" + ], + "inline_shebang": "/bin/sh -x", + "type": "shell" + }] +} diff --git a/website/source/docs/builders/azure.html.md b/website/source/docs/builders/azure.html.md index 94e0d0a0c..13b2b46a3 100644 --- a/website/source/docs/builders/azure.html.md +++ b/website/source/docs/builders/azure.html.md @@ -162,16 +162,35 @@ Providing `temp_resource_group_name` or `location` in combination with `build_re `Linux` this configures an SSH authorized key. For `Windows` this configures a WinRM certificate. -- `plan_name` (string) The plan name. This setting (`plan_product`, `plan_publisher`, and `plan_promotion_code`) are - only needed for Marketplace images. Please refer to [Deploy an image with Marketplace terms](https://docs.microsoft.com/en-us/azure/virtual-machines/linux/cli-ps-findimage#deploy-an-image-with-marketplace-terms) for more details. +- `plan_info` (object) - Used for creating images from Marketplace images. Please refer to [Deploy an image with + Marketplace terms](https://aka.ms/azuremarketplaceapideployment) for more details. Not all Marketplace images + support programmatic deployment, and support is controlled by the image publisher. -- `plan_product` (string) The plan product. See `plan_name` for more information. + An example plan_info object is defined below. -- `plan_publisher` (string) Specifies the produce of the image from the Marketplace. This value is the same value as - Offer (`image_offer`). See `plan_name` for more information. + ```json + { + "plan_info": { + "plan_name": "rabbitmq", + "plan_product": "rabbitmq", + "plan_publisher": "bitnami" + } + } + ``` -- `plan_promotion_code` (string) Some Marketplace images use a promotion code. See `plan_name` for more - information. + `plan_name` (string) - The plan name, required. + `plan_product` (string) - The plan product, required. + `plan_publisher` (string) - The plan publisher, required. + `plan_promotion_code` (string) - Some images accept a promotion code, optional. + + Images created from the Marketplace with `plan_info` **must** specify `plan_info` whenever the image is deployed. + The builder automatically adds tags to the image to ensure this information is not lost. The following tags are + added. + + 1. PlanName + 1. PlanProduct + 1. PlanPublisher + 1. PlanPromotionCode - `temp_compute_name` (string) temporary name assigned to the VM. If this value is not set, a random value will be assigned. Knowing the resource group and VM name allows one to execute commands to update the VM during a Packer From 3faf73b5f38e26c73c33e3e9b218a80647bea1f7 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Mon, 5 Mar 2018 14:18:23 -0800 Subject: [PATCH 0573/1216] change backslashes to forward slashes in powershell provisioner; was breaking with cygwin --- provisioner/powershell/provisioner.go | 8 ++++---- provisioner/powershell/provisioner_test.go | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/provisioner/powershell/provisioner.go b/provisioner/powershell/provisioner.go index 92f881314..07b10def9 100644 --- a/provisioner/powershell/provisioner.go +++ b/provisioner/powershell/provisioner.go @@ -397,7 +397,7 @@ func (p *Provisioner) uploadEnvVars(flattenedEnvVars string) (envVarPath string, // Upload all env vars to a powershell script on the target build file system envVarReader := strings.NewReader(flattenedEnvVars) uuid := uuid.TimeOrderedUUID() - envVarPath = fmt.Sprintf(`${env:SYSTEMROOT}\Temp\packer-env-vars-%s.ps1`, uuid) + envVarPath = fmt.Sprintf(`${env:SYSTEMROOT}/Temp/packer-env-vars-%s.ps1`, uuid) log.Printf("Uploading env vars to %s", envVarPath) err = p.communicator.Upload(envVarPath, envVarReader, nil) if err != nil { @@ -482,7 +482,7 @@ func (p *Provisioner) generateElevatedRunner(command string) (uploadedPath strin // Only use %ENVVAR% format for environment variables when setting // the log file path; Do NOT use $env:ENVVAR format as it won't be // expanded correctly in the elevatedTemplate - logFile := `%SYSTEMROOT%\Temp\` + taskName + ".out" + logFile := `%SYSTEMROOT%/Temp/` + taskName + ".out" command += fmt.Sprintf(" > %s 2>&1", logFile) // elevatedTemplate wraps the command in a single quoted XML text @@ -524,7 +524,7 @@ func (p *Provisioner) generateElevatedRunner(command string) (uploadedPath strin return "", err } uuid := uuid.TimeOrderedUUID() - path := fmt.Sprintf(`${env:TEMP}\packer-elevated-shell-%s.ps1`, uuid) + path := fmt.Sprintf(`${env:TEMP}/packer-elevated-shell-%s.ps1`, uuid) log.Printf("Uploading elevated shell wrapper for command [%s] to [%s]", command, path) err = p.communicator.Upload(path, &buffer, nil) if err != nil { @@ -532,6 +532,6 @@ func (p *Provisioner) generateElevatedRunner(command string) (uploadedPath strin } // CMD formatted Path required for this op - path = fmt.Sprintf("%s-%s.ps1", "%TEMP%\\packer-elevated-shell", uuid) + path = fmt.Sprintf("%s-%s.ps1", "%TEMP%/packer-elevated-shell", uuid) return path, err } diff --git a/provisioner/powershell/provisioner_test.go b/provisioner/powershell/provisioner_test.go index 2791601fa..ab68d8f42 100644 --- a/provisioner/powershell/provisioner_test.go +++ b/provisioner/powershell/provisioner_test.go @@ -414,7 +414,7 @@ func TestProvisionerProvision_Inline(t *testing.T) { } cmd := comm.StartCmd.Command - re := regexp.MustCompile(`powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){\$ProgressPreference='SilentlyContinue'};\. \${env:SYSTEMROOT}\\Temp\\packer-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/inlineScript.ps1';exit \$LastExitCode }"`) + re := regexp.MustCompile(`powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){\$ProgressPreference='SilentlyContinue'};\. \${env:SYSTEMROOT}/Temp/packer-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/inlineScript.ps1';exit \$LastExitCode }"`) matched := re.MatchString(cmd) if !matched { t.Fatalf("Got unexpected command: %s", cmd) @@ -434,7 +434,7 @@ func TestProvisionerProvision_Inline(t *testing.T) { } cmd = comm.StartCmd.Command - re = regexp.MustCompile(`powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){\$ProgressPreference='SilentlyContinue'};\. \${env:SYSTEMROOT}\\Temp\\packer-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/inlineScript.ps1';exit \$LastExitCode }"`) + re = regexp.MustCompile(`powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){\$ProgressPreference='SilentlyContinue'};\. \${env:SYSTEMROOT}/Temp/packer-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/inlineScript.ps1';exit \$LastExitCode }"`) matched = re.MatchString(cmd) if !matched { t.Fatalf("Got unexpected command: %s", cmd) @@ -461,7 +461,7 @@ func TestProvisionerProvision_Scripts(t *testing.T) { } cmd := comm.StartCmd.Command - re := regexp.MustCompile(`powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){\$ProgressPreference='SilentlyContinue'};\. \${env:SYSTEMROOT}\\Temp\\packer-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/script.ps1';exit \$LastExitCode }"`) + re := regexp.MustCompile(`powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){\$ProgressPreference='SilentlyContinue'};\. \${env:SYSTEMROOT}/Temp/packer-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/script.ps1';exit \$LastExitCode }"`) matched := re.MatchString(cmd) if !matched { t.Fatalf("Got unexpected command: %s", cmd) @@ -495,7 +495,7 @@ func TestProvisionerProvision_ScriptsWithEnvVars(t *testing.T) { } cmd := comm.StartCmd.Command - re := regexp.MustCompile(`powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){\$ProgressPreference='SilentlyContinue'};\. \${env:SYSTEMROOT}\\Temp\\packer-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/script.ps1';exit \$LastExitCode }"`) + re := regexp.MustCompile(`powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){\$ProgressPreference='SilentlyContinue'};\. \${env:SYSTEMROOT}/Temp/packer-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/script.ps1';exit \$LastExitCode }"`) matched := re.MatchString(cmd) if !matched { t.Fatalf("Got unexpected command: %s", cmd) @@ -612,7 +612,7 @@ func TestProvision_createCommandText(t *testing.T) { // Non-elevated cmd, _ := p.createCommandText() - re := regexp.MustCompile(`powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){\$ProgressPreference='SilentlyContinue'};\. \${env:SYSTEMROOT}\\Temp\\packer-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/script.ps1';exit \$LastExitCode }"`) + re := regexp.MustCompile(`powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){\$ProgressPreference='SilentlyContinue'};\. \${env:SYSTEMROOT}/Temp/packer-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/script.ps1';exit \$LastExitCode }"`) matched := re.MatchString(cmd) if !matched { t.Fatalf("Got unexpected command: %s", cmd) @@ -645,7 +645,7 @@ func TestProvision_uploadEnvVars(t *testing.T) { t.Fatalf("Failed to upload env var file") } - re := regexp.MustCompile(`\${env:SYSTEMROOT}\\Temp\\packer-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1`) + re := regexp.MustCompile(`\${env:SYSTEMROOT}/Temp/packer-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1`) matched := re.MatchString(envVarPath) if !matched { t.Fatalf("Got unexpected path for env var file: %s", envVarPath) From f3a538db469a20a949562540667d2097c9751d3d Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 9 Mar 2018 13:36:47 -0800 Subject: [PATCH 0574/1216] fix tests --- provisioner/powershell/provisioner_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provisioner/powershell/provisioner_test.go b/provisioner/powershell/provisioner_test.go index ab68d8f42..3c758ae4d 100644 --- a/provisioner/powershell/provisioner_test.go +++ b/provisioner/powershell/provisioner_test.go @@ -622,7 +622,7 @@ func TestProvision_createCommandText(t *testing.T) { p.config.ElevatedUser = "vagrant" p.config.ElevatedPassword = "vagrant" cmd, _ = p.createCommandText() - re = regexp.MustCompile(`powershell -executionpolicy bypass -file "%TEMP%\\packer-elevated-shell-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1"`) + re = regexp.MustCompile(`powershell -executionpolicy bypass -file "%TEMP%/packer-elevated-shell-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1"`) matched = re.MatchString(cmd) if !matched { t.Fatalf("Got unexpected elevated command: %s", cmd) From 8c408df53dde76138546665ca620b48770df52aa Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 9 Mar 2018 15:33:28 -0800 Subject: [PATCH 0575/1216] add note about keymap bug in qemu 2.11. See #5769 --- website/source/docs/builders/qemu.html.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/website/source/docs/builders/qemu.html.md b/website/source/docs/builders/qemu.html.md index 2d3c82d9e..ac15772a0 100644 --- a/website/source/docs/builders/qemu.html.md +++ b/website/source/docs/builders/qemu.html.md @@ -456,3 +456,7 @@ seems to be related to having a `common` directory or file in the directory they've run Packer in, like the packer source directory. This appears to be an upstream bug with qemu, and the best solution for now is to remove the file/directory or run in another directory. + +Some users have reported issues with incorrect keymaps using qemu version 2.11. +This is a bug with qemu, and the solution is to upgrade, or downgrade to 2.10.1 +or earlier. From 1d48812b34ed283000d3802ae9225436ed917216 Mon Sep 17 00:00:00 2001 From: Andrew Pennebaker Date: Fri, 9 Mar 2018 20:17:43 -0600 Subject: [PATCH 0576/1216] fix later commands overwriting earlier commands --- .../common/step_type_boot_command.go | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/builder/virtualbox/common/step_type_boot_command.go b/builder/virtualbox/common/step_type_boot_command.go index cb5b43648..d54c5560a 100644 --- a/builder/virtualbox/common/step_type_boot_command.go +++ b/builder/virtualbox/common/step_type_boot_command.go @@ -182,92 +182,92 @@ func scancodes(message string) []string { var scancode []string if strings.HasPrefix(message, "") { - scancode = []string{"38"} + scancode = append(scancode, "38") message = message[len(""):] log.Printf("Special code '' found, replacing with: 38") } if strings.HasPrefix(message, "") { - scancode = []string{"1d"} + scancode = append(scancode, "1d") message = message[len(""):] log.Printf("Special code '' found, replacing with: 1d") } if strings.HasPrefix(message, "") { - scancode = []string{"2a"} + scancode = append(scancode, "2a") message = message[len(""):] log.Printf("Special code '' found, replacing with: 2a") } if strings.HasPrefix(message, "") { - scancode = []string{"b8"} + scancode = append(scancode, "b8") message = message[len(""):] log.Printf("Special code '' found, replacing with: b8") } if strings.HasPrefix(message, "") { - scancode = []string{"9d"} + scancode = append(scancode, "9d") message = message[len(""):] log.Printf("Special code '' found, replacing with: 9d") } if strings.HasPrefix(message, "") { - scancode = []string{"aa"} + scancode = append(scancode, "aa") message = message[len(""):] log.Printf("Special code '' found, replacing with: aa") } if strings.HasPrefix(message, "") { - scancode = []string{"e038"} + scancode = append(scancode, "e038") message = message[len(""):] log.Printf("Special code '' found, replacing with: e038") } if strings.HasPrefix(message, "") { - scancode = []string{"e01d"} + scancode = append(scancode, "e01d") message = message[len(""):] log.Printf("Special code '' found, replacing with: e01d") } if strings.HasPrefix(message, "") { - scancode = []string{"36"} + scancode = append(scancode, "36") message = message[len(""):] log.Printf("Special code '' found, replacing with: 36") } if strings.HasPrefix(message, "") { - scancode = []string{"e0b8"} + scancode = append(scancode, "e0b8") message = message[len(""):] log.Printf("Special code '' found, replacing with: e0b8") } if strings.HasPrefix(message, "") { - scancode = []string{"e09d"} + scancode = append(scancode, "e09d") message = message[len(""):] log.Printf("Special code '' found, replacing with: e09d") } if strings.HasPrefix(message, "") { - scancode = []string{"b6"} + scancode = append(scancode, "b6") message = message[len(""):] log.Printf("Special code '' found, replacing with: b6") } if strings.HasPrefix(message, "") { log.Printf("Special code found, will sleep 1 second at this point.") - scancode = []string{"wait"} + scancode = append(scancode, "wait") message = message[len(""):] } if strings.HasPrefix(message, "") { log.Printf("Special code found, will sleep 5 seconds at this point.") - scancode = []string{"wait5"} + scancode = append(scancode, "wait5") message = message[len(""):] } if strings.HasPrefix(message, "") { log.Printf("Special code found, will sleep 10 seconds at this point.") - scancode = []string{"wait10"} + scancode = append(scancode, "wait10") message = message[len(""):] } @@ -275,7 +275,7 @@ func scancodes(message string) []string { for specialCode, specialValue := range special { if strings.HasPrefix(message, specialCode) { log.Printf("Special code '%s' found, replacing with: %s", specialCode, specialValue) - scancode = specialValue + scancode = append(scancode, specialValue...) message = message[len(specialCode):] break } From 149ce52079dfdeb9e8f23f235c78b9bbb2a297c2 Mon Sep 17 00:00:00 2001 From: Christopher Boumenot Date: Sat, 10 Mar 2018 10:17:38 -0800 Subject: [PATCH 0577/1216] azure: respect -force for managed image deletion --- builder/azure/arm/builder.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/builder/azure/arm/builder.go b/builder/azure/arm/builder.go index f2bc66135..0761b4afe 100644 --- a/builder/azure/arm/builder.go +++ b/builder/azure/arm/builder.go @@ -99,7 +99,16 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe // If a managed image already exists it cannot be overwritten. _, err = azureClient.ImagesClient.Get(b.config.ManagedImageResourceGroupName, b.config.ManagedImageName, "") if err == nil { - return nil, fmt.Errorf("A managed image named %s already exists in the resource group %s.", b.config.ManagedImageName, b.config.ManagedImageResourceGroupName) + if b.config.PackerForce { + _, errChan := azureClient.ImagesClient.Delete(b.config.ManagedImageResourceGroupName, b.config.ManagedImageName, nil) + ui.Say(fmt.Sprintf("the managed image named %s already exists, but deleting it due to -force flag", b.config.ManagedImageName)) + err = <-errChan + if err != nil { + return nil, fmt.Errorf("failed to delete the managed image named %s : %s", b.config.ManagedImageName, azureClient.LastError.Error()) + } + } else { + return nil, fmt.Errorf("the managed image named %s already exists in the resource group %s, use the -force option to automatically delete it.", b.config.ManagedImageName, b.config.ManagedImageResourceGroupName) + } } } From 2a21032964ada0ee655c345ef25e1361e6ba3400 Mon Sep 17 00:00:00 2001 From: Christopher Boumenot Date: Sat, 10 Mar 2018 11:17:43 -0800 Subject: [PATCH 0578/1216] azure: randomize NIC and Public IP names --- ...estVirtualMachineDeployment05.approved.txt | 2 - builder/azure/arm/builder.go | 12 +++--- builder/azure/arm/config.go | 8 ++++ builder/azure/arm/template_factory.go | 13 +++++-- ..._factory_test.TestPlanInfo01.approved.json | 30 ++++++++++----- ..._factory_test.TestPlanInfo02.approved.json | 30 ++++++++++----- ...stVirtualMachineDeployment03.approved.json | 30 ++++++++++----- ...stVirtualMachineDeployment04.approved.json | 38 ++++++++++++------- ...stVirtualMachineDeployment05.approved.json | 20 +++++++--- ...stVirtualMachineDeployment06.approved.json | 30 ++++++++++----- ...stVirtualMachineDeployment07.approved.json | 30 ++++++++++----- ...stVirtualMachineDeployment08.approved.json | 30 ++++++++++----- ...stVirtualMachineDeployment09.approved.json | 30 ++++++++++----- ...stVirtualMachineDeployment10.approved.json | 26 +++++++++---- ...stVirtualMachineDeployment11.approved.json | 30 ++++++++++----- ...stVirtualMachineDeployment12.approved.json | 30 ++++++++++----- builder/azure/arm/tempname.go | 8 ++++ builder/azure/arm/tempname_test.go | 35 +++++++++++++++++ .../azure/common/template/template_builder.go | 30 ++++++++++----- ...uilder_test.TestBuildLinux00.approved.json | 38 ++++++++++++------- ...uilder_test.TestBuildLinux01.approved.json | 38 ++++++++++++------- ...uilder_test.TestBuildLinux02.approved.json | 20 +++++++--- ...lder_test.TestBuildWindows00.approved.json | 38 ++++++++++++------- ...lder_test.TestBuildWindows01.approved.json | 30 ++++++++++----- ...lder_test.TestBuildWindows02.approved.json | 30 ++++++++++----- .../common/template/template_parameters.go | 4 ++ website/source/docs/builders/azure.html.md | 4 ++ 27 files changed, 458 insertions(+), 206 deletions(-) diff --git a/builder/azure/arm/TestVirtualMachineDeployment05.approved.txt b/builder/azure/arm/TestVirtualMachineDeployment05.approved.txt index 906820f27..8a8a5d4d4 100644 --- a/builder/azure/arm/TestVirtualMachineDeployment05.approved.txt +++ b/builder/azure/arm/TestVirtualMachineDeployment05.approved.txt @@ -105,8 +105,6 @@ "addressPrefix": "10.0.0.0/16", "apiVersion": "2015-06-15", "location": "[resourceGroup().location]", - "nicName": "packerNic", - "publicIPAddressName": "packerPublicIP", "publicIPAddressType": "Dynamic", "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", "subnetAddressPrefix": "10.0.0.0/24", diff --git a/builder/azure/arm/builder.go b/builder/azure/arm/builder.go index f2bc66135..cfefa9008 100644 --- a/builder/azure/arm/builder.go +++ b/builder/azure/arm/builder.go @@ -29,11 +29,9 @@ type Builder struct { } const ( - DefaultNicName = "packerNic" - DefaultPublicIPAddressName = "packerPublicIP" - DefaultSasBlobContainer = "system/Microsoft.Compute" - DefaultSasBlobPermission = "r" - DefaultSecretName = "packerKeyVaultSecret" + DefaultSasBlobContainer = "system/Microsoft.Compute" + DefaultSasBlobPermission = "r" + DefaultSecretName = "packerKeyVaultSecret" ) func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { @@ -303,8 +301,8 @@ func (b *Builder) configureStateBag(stateBag multistep.StateBag) { stateBag.Put(constants.ArmKeyVaultDeploymentName, fmt.Sprintf("kv%s", b.config.tmpDeploymentName)) } stateBag.Put(constants.ArmKeyVaultName, b.config.tmpKeyVaultName) - stateBag.Put(constants.ArmNicName, DefaultNicName) - stateBag.Put(constants.ArmPublicIPAddressName, DefaultPublicIPAddressName) + stateBag.Put(constants.ArmNicName, b.config.tmpNicName) + stateBag.Put(constants.ArmPublicIPAddressName, b.config.tmpPublicIPAddressName) if b.config.TempResourceGroupName != "" && b.config.BuildResourceGroupName != "" { stateBag.Put(constants.ArmDoubleResourceGroupNameSet, true) } diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index 5584a15bf..55ed7956d 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -130,9 +130,13 @@ type Config struct { tmpCertificatePassword string tmpResourceGroupName string tmpComputeName string + tmpNicName string + tmpPublicIPAddressName string tmpDeploymentName string tmpKeyVaultName string tmpOSDiskName string + tmpSubnetName string + tmpVirtualNetworkName string tmpWinRMCertificateUrl string useDeviceLogin bool @@ -366,7 +370,11 @@ func setRuntimeValues(c *Config) { } else if c.TempResourceGroupName != "" && c.BuildResourceGroupName == "" { c.tmpResourceGroupName = c.TempResourceGroupName } + c.tmpNicName = tempName.NicName + c.tmpPublicIPAddressName = tempName.PublicIPAddressName c.tmpOSDiskName = tempName.OSDiskName + c.tmpSubnetName = tempName.SubnetName + c.tmpVirtualNetworkName = tempName.VirtualNetworkName c.tmpKeyVaultName = tempName.KeyVaultName } diff --git a/builder/azure/arm/template_factory.go b/builder/azure/arm/template_factory.go index 843580fd2..55ed50b01 100644 --- a/builder/azure/arm/template_factory.go +++ b/builder/azure/arm/template_factory.go @@ -34,13 +34,20 @@ func GetVirtualMachineDeployment(config *Config) (*resources.Deployment, error) AdminUsername: &template.TemplateParameter{Value: config.UserName}, AdminPassword: &template.TemplateParameter{Value: config.Password}, DnsNameForPublicIP: &template.TemplateParameter{Value: config.tmpComputeName}, + NicName: &template.TemplateParameter{Value: config.tmpNicName}, OSDiskName: &template.TemplateParameter{Value: config.tmpOSDiskName}, + PublicIPAddressName: &template.TemplateParameter{Value: config.tmpPublicIPAddressName}, + SubnetName: &template.TemplateParameter{Value: config.tmpSubnetName}, StorageAccountBlobEndpoint: &template.TemplateParameter{Value: config.storageAccountBlobEndpoint}, - VMSize: &template.TemplateParameter{Value: config.VMSize}, - VMName: &template.TemplateParameter{Value: config.tmpComputeName}, + VirtualNetworkName: &template.TemplateParameter{Value: config.tmpVirtualNetworkName}, + VMSize: &template.TemplateParameter{Value: config.VMSize}, + VMName: &template.TemplateParameter{Value: config.tmpComputeName}, } - builder, _ := template.NewTemplateBuilder(template.BasicTemplate) + builder, err := template.NewTemplateBuilder(template.BasicTemplate) + if err != nil { + return nil, err + } osType := compute.Linux switch config.OSType { diff --git a/builder/azure/arm/template_factory_test.TestPlanInfo01.approved.json b/builder/azure/arm/template_factory_test.TestPlanInfo01.approved.json index 74546c490..1d2ef2207 100644 --- a/builder/azure/arm/template_factory_test.TestPlanInfo01.approved.json +++ b/builder/azure/arm/template_factory_test.TestPlanInfo01.approved.json @@ -11,12 +11,24 @@ "dnsNameForPublicIP": { "type": "string" }, + "nicName": { + "type": "string" + }, "osDiskName": { "type": "string" }, + "publicIPAddressName": { + "type": "string" + }, "storageAccountBlobEndpoint": { "type": "string" }, + "subnetName": { + "type": "string" + }, + "virtualNetworkName": { + "type": "string" + }, "vmName": { "type": "string" }, @@ -28,7 +40,7 @@ { "apiVersion": "[variables('publicIPAddressApiVersion')]", "location": "[variables('location')]", - "name": "[variables('publicIPAddressName')]", + "name": "[parameters('publicIPAddressName')]", "properties": { "dnsSettings": { "domainNameLabel": "[parameters('dnsNameForPublicIP')]" @@ -73,11 +85,11 @@ { "apiVersion": "[variables('networkInterfacesApiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]", + "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" ], "location": "[variables('location')]", - "name": "[variables('nicName')]", + "name": "[parameters('nicName')]", "properties": { "ipConfigurations": [ { @@ -85,7 +97,7 @@ "properties": { "privateIPAllocationMethod": "Dynamic", "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]" + "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" }, "subnet": { "id": "[variables('subnetRef')]" @@ -105,7 +117,7 @@ { "apiVersion": "[variables('apiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" + "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" ], "location": "[variables('location')]", "name": "[parameters('vmName')]", @@ -126,7 +138,7 @@ "networkProfile": { "networkInterfaces": [ { - "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" + "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" } ] }, @@ -177,15 +189,13 @@ "location": "[resourceGroup().location]", "managedDiskApiVersion": "2017-03-30", "networkInterfacesApiVersion": "2017-04-01", - "nicName": "packerNic", "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressName": "packerPublicIP", "publicIPAddressType": "Dynamic", "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "packerSubnet", + "subnetName": "[parameters('subnetName')]", "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "packerNetwork", + "virtualNetworkName": "[parameters('virtualNetworkName')]", "virtualNetworkResourceGroup": "[resourceGroup().name]", "virtualNetworksApiVersion": "2017-04-01", "vmStorageAccountContainerName": "images", diff --git a/builder/azure/arm/template_factory_test.TestPlanInfo02.approved.json b/builder/azure/arm/template_factory_test.TestPlanInfo02.approved.json index 8f93bb530..bc3ef83ef 100644 --- a/builder/azure/arm/template_factory_test.TestPlanInfo02.approved.json +++ b/builder/azure/arm/template_factory_test.TestPlanInfo02.approved.json @@ -11,12 +11,24 @@ "dnsNameForPublicIP": { "type": "string" }, + "nicName": { + "type": "string" + }, "osDiskName": { "type": "string" }, + "publicIPAddressName": { + "type": "string" + }, "storageAccountBlobEndpoint": { "type": "string" }, + "subnetName": { + "type": "string" + }, + "virtualNetworkName": { + "type": "string" + }, "vmName": { "type": "string" }, @@ -28,7 +40,7 @@ { "apiVersion": "[variables('publicIPAddressApiVersion')]", "location": "[variables('location')]", - "name": "[variables('publicIPAddressName')]", + "name": "[parameters('publicIPAddressName')]", "properties": { "dnsSettings": { "domainNameLabel": "[parameters('dnsNameForPublicIP')]" @@ -75,11 +87,11 @@ { "apiVersion": "[variables('networkInterfacesApiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]", + "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" ], "location": "[variables('location')]", - "name": "[variables('nicName')]", + "name": "[parameters('nicName')]", "properties": { "ipConfigurations": [ { @@ -87,7 +99,7 @@ "properties": { "privateIPAllocationMethod": "Dynamic", "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]" + "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" }, "subnet": { "id": "[variables('subnetRef')]" @@ -108,7 +120,7 @@ { "apiVersion": "[variables('apiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" + "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" ], "location": "[variables('location')]", "name": "[parameters('vmName')]", @@ -130,7 +142,7 @@ "networkProfile": { "networkInterfaces": [ { - "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" + "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" } ] }, @@ -182,15 +194,13 @@ "location": "[resourceGroup().location]", "managedDiskApiVersion": "2017-03-30", "networkInterfacesApiVersion": "2017-04-01", - "nicName": "packerNic", "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressName": "packerPublicIP", "publicIPAddressType": "Dynamic", "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "packerSubnet", + "subnetName": "[parameters('subnetName')]", "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "packerNetwork", + "virtualNetworkName": "[parameters('virtualNetworkName')]", "virtualNetworkResourceGroup": "[resourceGroup().name]", "virtualNetworksApiVersion": "2017-04-01", "vmStorageAccountContainerName": "images", diff --git a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment03.approved.json b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment03.approved.json index a220d9822..2a9a429e5 100644 --- a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment03.approved.json +++ b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment03.approved.json @@ -11,12 +11,24 @@ "dnsNameForPublicIP": { "type": "string" }, + "nicName": { + "type": "string" + }, "osDiskName": { "type": "string" }, + "publicIPAddressName": { + "type": "string" + }, "storageAccountBlobEndpoint": { "type": "string" }, + "subnetName": { + "type": "string" + }, + "virtualNetworkName": { + "type": "string" + }, "vmName": { "type": "string" }, @@ -28,7 +40,7 @@ { "apiVersion": "[variables('publicIPAddressApiVersion')]", "location": "[variables('location')]", - "name": "[variables('publicIPAddressName')]", + "name": "[parameters('publicIPAddressName')]", "properties": { "dnsSettings": { "domainNameLabel": "[parameters('dnsNameForPublicIP')]" @@ -61,11 +73,11 @@ { "apiVersion": "[variables('networkInterfacesApiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]", + "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" ], "location": "[variables('location')]", - "name": "[variables('nicName')]", + "name": "[parameters('nicName')]", "properties": { "ipConfigurations": [ { @@ -73,7 +85,7 @@ "properties": { "privateIPAllocationMethod": "Dynamic", "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]" + "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" }, "subnet": { "id": "[variables('subnetRef')]" @@ -87,7 +99,7 @@ { "apiVersion": "[variables('apiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" + "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" ], "location": "[variables('location')]", "name": "[parameters('vmName')]", @@ -103,7 +115,7 @@ "networkProfile": { "networkInterfaces": [ { - "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" + "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" } ] }, @@ -148,15 +160,13 @@ "location": "[resourceGroup().location]", "managedDiskApiVersion": "2017-03-30", "networkInterfacesApiVersion": "2017-04-01", - "nicName": "packerNic", "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressName": "packerPublicIP", "publicIPAddressType": "Dynamic", "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "packerSubnet", + "subnetName": "[parameters('subnetName')]", "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "packerNetwork", + "virtualNetworkName": "[parameters('virtualNetworkName')]", "virtualNetworkResourceGroup": "[resourceGroup().name]", "virtualNetworksApiVersion": "2017-04-01", "vmStorageAccountContainerName": "images", diff --git a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment04.approved.json b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment04.approved.json index 0be4bbef4..215acda04 100644 --- a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment04.approved.json +++ b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment04.approved.json @@ -11,12 +11,24 @@ "dnsNameForPublicIP": { "type": "string" }, + "nicName": { + "type": "string" + }, "osDiskName": { "type": "string" }, + "publicIPAddressName": { + "type": "string" + }, "storageAccountBlobEndpoint": { "type": "string" }, + "subnetName": { + "type": "string" + }, + "virtualNetworkName": { + "type": "string" + }, "vmName": { "type": "string" }, @@ -28,7 +40,7 @@ { "apiVersion": "[variables('publicIPAddressApiVersion')]", "location": "[variables('location')]", - "name": "[variables('publicIPAddressName')]", + "name": "[parameters('publicIPAddressName')]", "properties": { "dnsSettings": { "domainNameLabel": "[parameters('dnsNameForPublicIP')]" @@ -61,11 +73,11 @@ { "apiVersion": "[variables('networkInterfacesApiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]", + "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" ], "location": "[variables('location')]", - "name": "[variables('nicName')]", + "name": "[parameters('nicName')]", "properties": { "ipConfigurations": [ { @@ -73,7 +85,7 @@ "properties": { "privateIPAllocationMethod": "Dynamic", "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]" + "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" }, "subnet": { "id": "[variables('subnetRef')]" @@ -87,7 +99,7 @@ { "apiVersion": "[variables('apiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" + "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" ], "location": "[variables('location')]", "name": "[parameters('vmName')]", @@ -103,7 +115,7 @@ "networkProfile": { "networkInterfaces": [ { - "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" + "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" } ] }, @@ -146,18 +158,16 @@ "location": "[resourceGroup().location]", "managedDiskApiVersion": "2017-03-30", "networkInterfacesApiVersion": "2017-04-01", - "nicName": "packerNic", "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressName": "packerPublicIP", "publicIPAddressType": "Dynamic", "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "packerSubnet", + "subnetName": "[parameters('subnetName')]", "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "packerNetwork", - "virtualNetworkResourceGroup": "[resourceGroup().name]", + "virtualNetworkName": "[parameters('virtualNetworkName')]", + "virtualNetworkResourceGroup": "[resourceGroup().name]", "virtualNetworksApiVersion": "2017-04-01", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - } + "vmStorageAccountContainerName": "images", + "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" + } } \ No newline at end of file diff --git a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment05.approved.json b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment05.approved.json index 29c4750c5..c7a73e7ad 100644 --- a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment05.approved.json +++ b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment05.approved.json @@ -11,12 +11,24 @@ "dnsNameForPublicIP": { "type": "string" }, + "nicName": { + "type": "string" + }, "osDiskName": { "type": "string" }, + "publicIPAddressName": { + "type": "string" + }, "storageAccountBlobEndpoint": { "type": "string" }, + "subnetName": { + "type": "string" + }, + "virtualNetworkName": { + "type": "string" + }, "vmName": { "type": "string" }, @@ -29,7 +41,7 @@ "apiVersion": "[variables('networkInterfacesApiVersion')]", "dependsOn": [], "location": "[variables('location')]", - "name": "[variables('nicName')]", + "name": "[parameters('nicName')]", "properties": { "ipConfigurations": [ { @@ -48,7 +60,7 @@ { "apiVersion": "[variables('apiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" + "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" ], "location": "[variables('location')]", "name": "[parameters('vmName')]", @@ -64,7 +76,7 @@ "networkProfile": { "networkInterfaces": [ { - "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" + "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" } ] }, @@ -107,9 +119,7 @@ "location": "[resourceGroup().location]", "managedDiskApiVersion": "2017-03-30", "networkInterfacesApiVersion": "2017-04-01", - "nicName": "packerNic", "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressName": "packerPublicIP", "publicIPAddressType": "Dynamic", "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", "subnetAddressPrefix": "10.0.0.0/24", diff --git a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment06.approved.json b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment06.approved.json index fb5e79b9f..258fd9261 100644 --- a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment06.approved.json +++ b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment06.approved.json @@ -11,12 +11,24 @@ "dnsNameForPublicIP": { "type": "string" }, + "nicName": { + "type": "string" + }, "osDiskName": { "type": "string" }, + "publicIPAddressName": { + "type": "string" + }, "storageAccountBlobEndpoint": { "type": "string" }, + "subnetName": { + "type": "string" + }, + "virtualNetworkName": { + "type": "string" + }, "vmName": { "type": "string" }, @@ -28,7 +40,7 @@ { "apiVersion": "[variables('publicIPAddressApiVersion')]", "location": "[variables('location')]", - "name": "[variables('publicIPAddressName')]", + "name": "[parameters('publicIPAddressName')]", "properties": { "dnsSettings": { "domainNameLabel": "[parameters('dnsNameForPublicIP')]" @@ -71,11 +83,11 @@ { "apiVersion": "[variables('networkInterfacesApiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]", + "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" ], "location": "[variables('location')]", - "name": "[variables('nicName')]", + "name": "[parameters('nicName')]", "properties": { "ipConfigurations": [ { @@ -83,7 +95,7 @@ "properties": { "privateIPAllocationMethod": "Dynamic", "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]" + "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" }, "subnet": { "id": "[variables('subnetRef')]" @@ -102,7 +114,7 @@ { "apiVersion": "[variables('apiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" + "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" ], "location": "[variables('location')]", "name": "[parameters('vmName')]", @@ -118,7 +130,7 @@ "networkProfile": { "networkInterfaces": [ { - "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" + "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" } ] }, @@ -166,15 +178,13 @@ "location": "[resourceGroup().location]", "managedDiskApiVersion": "2017-03-30", "networkInterfacesApiVersion": "2017-04-01", - "nicName": "packerNic", "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressName": "packerPublicIP", "publicIPAddressType": "Dynamic", "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "packerSubnet", + "subnetName": "[parameters('subnetName')]", "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "packerNetwork", + "virtualNetworkName": "[parameters('virtualNetworkName')]", "virtualNetworkResourceGroup": "[resourceGroup().name]", "virtualNetworksApiVersion": "2017-04-01", "vmStorageAccountContainerName": "images", diff --git a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment07.approved.json b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment07.approved.json index 6f722c765..8d7101cfe 100644 --- a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment07.approved.json +++ b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment07.approved.json @@ -11,12 +11,24 @@ "dnsNameForPublicIP": { "type": "string" }, + "nicName": { + "type": "string" + }, "osDiskName": { "type": "string" }, + "publicIPAddressName": { + "type": "string" + }, "storageAccountBlobEndpoint": { "type": "string" }, + "subnetName": { + "type": "string" + }, + "virtualNetworkName": { + "type": "string" + }, "vmName": { "type": "string" }, @@ -28,7 +40,7 @@ { "apiVersion": "[variables('publicIPAddressApiVersion')]", "location": "[variables('location')]", - "name": "[variables('publicIPAddressName')]", + "name": "[parameters('publicIPAddressName')]", "properties": { "dnsSettings": { "domainNameLabel": "[parameters('dnsNameForPublicIP')]" @@ -61,11 +73,11 @@ { "apiVersion": "[variables('networkInterfacesApiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]", + "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" ], "location": "[variables('location')]", - "name": "[variables('nicName')]", + "name": "[parameters('nicName')]", "properties": { "ipConfigurations": [ { @@ -73,7 +85,7 @@ "properties": { "privateIPAllocationMethod": "Dynamic", "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]" + "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" }, "subnet": { "id": "[variables('subnetRef')]" @@ -87,7 +99,7 @@ { "apiVersion": "[variables('apiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" + "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" ], "location": "[variables('location')]", "name": "[parameters('vmName')]", @@ -103,7 +115,7 @@ "networkProfile": { "networkInterfaces": [ { - "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" + "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" } ] }, @@ -147,15 +159,13 @@ "location": "[resourceGroup().location]", "managedDiskApiVersion": "2017-03-30", "networkInterfacesApiVersion": "2017-04-01", - "nicName": "packerNic", "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressName": "packerPublicIP", "publicIPAddressType": "Dynamic", "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "packerSubnet", + "subnetName": "[parameters('subnetName')]", "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "packerNetwork", + "virtualNetworkName": "[parameters('virtualNetworkName')]", "virtualNetworkResourceGroup": "[resourceGroup().name]", "virtualNetworksApiVersion": "2017-04-01", "vmStorageAccountContainerName": "images", diff --git a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment08.approved.json b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment08.approved.json index c567b774c..ae0700880 100644 --- a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment08.approved.json +++ b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment08.approved.json @@ -11,12 +11,24 @@ "dnsNameForPublicIP": { "type": "string" }, + "nicName": { + "type": "string" + }, "osDiskName": { "type": "string" }, + "publicIPAddressName": { + "type": "string" + }, "storageAccountBlobEndpoint": { "type": "string" }, + "subnetName": { + "type": "string" + }, + "virtualNetworkName": { + "type": "string" + }, "vmName": { "type": "string" }, @@ -28,7 +40,7 @@ { "apiVersion": "[variables('publicIPAddressApiVersion')]", "location": "[variables('location')]", - "name": "[variables('publicIPAddressName')]", + "name": "[parameters('publicIPAddressName')]", "properties": { "dnsSettings": { "domainNameLabel": "[parameters('dnsNameForPublicIP')]" @@ -61,11 +73,11 @@ { "apiVersion": "[variables('networkInterfacesApiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]", + "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" ], "location": "[variables('location')]", - "name": "[variables('nicName')]", + "name": "[parameters('nicName')]", "properties": { "ipConfigurations": [ { @@ -73,7 +85,7 @@ "properties": { "privateIPAllocationMethod": "Dynamic", "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]" + "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" }, "subnet": { "id": "[variables('subnetRef')]" @@ -87,7 +99,7 @@ { "apiVersion": "[variables('apiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" + "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" ], "location": "[variables('location')]", "name": "[parameters('vmName')]", @@ -103,7 +115,7 @@ "networkProfile": { "networkInterfaces": [ { - "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" + "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" } ] }, @@ -146,15 +158,13 @@ "location": "[resourceGroup().location]", "managedDiskApiVersion": "2017-03-30", "networkInterfacesApiVersion": "2017-04-01", - "nicName": "packerNic", "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressName": "packerPublicIP", "publicIPAddressType": "Dynamic", "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "packerSubnet", + "subnetName": "[parameters('subnetName')]", "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "packerNetwork", + "virtualNetworkName": "[parameters('virtualNetworkName')]", "virtualNetworkResourceGroup": "[resourceGroup().name]", "virtualNetworksApiVersion": "2017-04-01", "vmStorageAccountContainerName": "images", diff --git a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment09.approved.json b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment09.approved.json index fbbb7384e..a7baa806f 100644 --- a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment09.approved.json +++ b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment09.approved.json @@ -11,12 +11,24 @@ "dnsNameForPublicIP": { "type": "string" }, + "nicName": { + "type": "string" + }, "osDiskName": { "type": "string" }, + "publicIPAddressName": { + "type": "string" + }, "storageAccountBlobEndpoint": { "type": "string" }, + "subnetName": { + "type": "string" + }, + "virtualNetworkName": { + "type": "string" + }, "vmName": { "type": "string" }, @@ -28,7 +40,7 @@ { "apiVersion": "[variables('publicIPAddressApiVersion')]", "location": "[variables('location')]", - "name": "[variables('publicIPAddressName')]", + "name": "[parameters('publicIPAddressName')]", "properties": { "dnsSettings": { "domainNameLabel": "[parameters('dnsNameForPublicIP')]" @@ -61,11 +73,11 @@ { "apiVersion": "[variables('networkInterfacesApiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]", + "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" ], "location": "[variables('location')]", - "name": "[variables('nicName')]", + "name": "[parameters('nicName')]", "properties": { "ipConfigurations": [ { @@ -73,7 +85,7 @@ "properties": { "privateIPAllocationMethod": "Dynamic", "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]" + "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" }, "subnet": { "id": "[variables('subnetRef')]" @@ -87,7 +99,7 @@ { "apiVersion": "[variables('apiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" + "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" ], "location": "[variables('location')]", "name": "[parameters('vmName')]", @@ -103,7 +115,7 @@ "networkProfile": { "networkInterfaces": [ { - "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" + "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" } ] }, @@ -149,15 +161,13 @@ "location": "[resourceGroup().location]", "managedDiskApiVersion": "2017-03-30", "networkInterfacesApiVersion": "2017-04-01", - "nicName": "packerNic", "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressName": "packerPublicIP", "publicIPAddressType": "Dynamic", "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "packerSubnet", + "subnetName": "[parameters('subnetName')]", "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "packerNetwork", + "virtualNetworkName": "[parameters('virtualNetworkName')]", "virtualNetworkResourceGroup": "[resourceGroup().name]", "virtualNetworksApiVersion": "2017-04-01", "vmStorageAccountContainerName": "images", diff --git a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment10.approved.json b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment10.approved.json index 0eb130138..8c27336c5 100644 --- a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment10.approved.json +++ b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment10.approved.json @@ -11,12 +11,24 @@ "dnsNameForPublicIP": { "type": "string" }, + "nicName": { + "type": "string" + }, "osDiskName": { "type": "string" }, + "publicIPAddressName": { + "type": "string" + }, "storageAccountBlobEndpoint": { "type": "string" }, + "subnetName": { + "type": "string" + }, + "virtualNetworkName": { + "type": "string" + }, "vmName": { "type": "string" }, @@ -28,7 +40,7 @@ { "apiVersion": "[variables('publicIPAddressApiVersion')]", "location": "[variables('location')]", - "name": "[variables('publicIPAddressName')]", + "name": "[parameters('publicIPAddressName')]", "properties": { "dnsSettings": { "domainNameLabel": "[parameters('dnsNameForPublicIP')]" @@ -40,10 +52,10 @@ { "apiVersion": "[variables('networkInterfacesApiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]" + "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]" ], "location": "[variables('location')]", - "name": "[variables('nicName')]", + "name": "[parameters('nicName')]", "properties": { "ipConfigurations": [ { @@ -51,7 +63,7 @@ "properties": { "privateIPAllocationMethod": "Dynamic", "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]" + "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" }, "subnet": { "id": "[variables('subnetRef')]" @@ -65,7 +77,7 @@ { "apiVersion": "[variables('apiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" + "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" ], "location": "[variables('location')]", "name": "[parameters('vmName')]", @@ -81,7 +93,7 @@ "networkProfile": { "networkInterfaces": [ { - "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" + "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" } ] }, @@ -127,9 +139,7 @@ "location": "[resourceGroup().location]", "managedDiskApiVersion": "2017-03-30", "networkInterfacesApiVersion": "2017-04-01", - "nicName": "packerNic", "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressName": "packerPublicIP", "publicIPAddressType": "Dynamic", "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", "subnetAddressPrefix": "10.0.0.0/24", diff --git a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment11.approved.json b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment11.approved.json index b5fbfaf2f..4776d5344 100644 --- a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment11.approved.json +++ b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment11.approved.json @@ -11,12 +11,24 @@ "dnsNameForPublicIP": { "type": "string" }, + "nicName": { + "type": "string" + }, "osDiskName": { "type": "string" }, + "publicIPAddressName": { + "type": "string" + }, "storageAccountBlobEndpoint": { "type": "string" }, + "subnetName": { + "type": "string" + }, + "virtualNetworkName": { + "type": "string" + }, "vmName": { "type": "string" }, @@ -28,7 +40,7 @@ { "apiVersion": "[variables('publicIPAddressApiVersion')]", "location": "[variables('location')]", - "name": "[variables('publicIPAddressName')]", + "name": "[parameters('publicIPAddressName')]", "properties": { "dnsSettings": { "domainNameLabel": "[parameters('dnsNameForPublicIP')]" @@ -61,11 +73,11 @@ { "apiVersion": "[variables('networkInterfacesApiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]", + "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" ], "location": "[variables('location')]", - "name": "[variables('nicName')]", + "name": "[parameters('nicName')]", "properties": { "ipConfigurations": [ { @@ -73,7 +85,7 @@ "properties": { "privateIPAllocationMethod": "Dynamic", "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]" + "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" }, "subnet": { "id": "[variables('subnetRef')]" @@ -87,7 +99,7 @@ { "apiVersion": "[variables('apiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" + "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" ], "location": "[variables('location')]", "name": "[parameters('vmName')]", @@ -103,7 +115,7 @@ "networkProfile": { "networkInterfaces": [ { - "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" + "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" } ] }, @@ -160,15 +172,13 @@ "location": "[resourceGroup().location]", "managedDiskApiVersion": "2017-03-30", "networkInterfacesApiVersion": "2017-04-01", - "nicName": "packerNic", "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressName": "packerPublicIP", "publicIPAddressType": "Dynamic", "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "packerSubnet", + "subnetName": "[parameters('subnetName')]", "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "packerNetwork", + "virtualNetworkName": "[parameters('virtualNetworkName')]", "virtualNetworkResourceGroup": "[resourceGroup().name]", "virtualNetworksApiVersion": "2017-04-01", "vmStorageAccountContainerName": "images", diff --git a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment12.approved.json b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment12.approved.json index 531b2654b..25449da24 100644 --- a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment12.approved.json +++ b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment12.approved.json @@ -11,12 +11,24 @@ "dnsNameForPublicIP": { "type": "string" }, + "nicName": { + "type": "string" + }, "osDiskName": { "type": "string" }, + "publicIPAddressName": { + "type": "string" + }, "storageAccountBlobEndpoint": { "type": "string" }, + "subnetName": { + "type": "string" + }, + "virtualNetworkName": { + "type": "string" + }, "vmName": { "type": "string" }, @@ -28,7 +40,7 @@ { "apiVersion": "[variables('publicIPAddressApiVersion')]", "location": "[variables('location')]", - "name": "[variables('publicIPAddressName')]", + "name": "[parameters('publicIPAddressName')]", "properties": { "dnsSettings": { "domainNameLabel": "[parameters('dnsNameForPublicIP')]" @@ -61,11 +73,11 @@ { "apiVersion": "[variables('networkInterfacesApiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]", + "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" ], "location": "[variables('location')]", - "name": "[variables('nicName')]", + "name": "[parameters('nicName')]", "properties": { "ipConfigurations": [ { @@ -73,7 +85,7 @@ "properties": { "privateIPAllocationMethod": "Dynamic", "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]" + "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" }, "subnet": { "id": "[variables('subnetRef')]" @@ -87,7 +99,7 @@ { "apiVersion": "[variables('apiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" + "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" ], "location": "[variables('location')]", "name": "[parameters('vmName')]", @@ -103,7 +115,7 @@ "networkProfile": { "networkInterfaces": [ { - "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" + "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" } ] }, @@ -161,15 +173,13 @@ "location": "[resourceGroup().location]", "managedDiskApiVersion": "2017-03-30", "networkInterfacesApiVersion": "2017-04-01", - "nicName": "packerNic", "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressName": "packerPublicIP", "publicIPAddressType": "Dynamic", "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "packerSubnet", + "subnetName": "[parameters('subnetName')]", "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "packerNetwork", + "virtualNetworkName": "[parameters('virtualNetworkName')]", "virtualNetworkResourceGroup": "[resourceGroup().name]", "virtualNetworksApiVersion": "2017-04-01", "vmStorageAccountContainerName": "images", diff --git a/builder/azure/arm/tempname.go b/builder/azure/arm/tempname.go index 734cd74aa..bf0cb6552 100644 --- a/builder/azure/arm/tempname.go +++ b/builder/azure/arm/tempname.go @@ -19,6 +19,10 @@ type TempName struct { KeyVaultName string ResourceGroupName string OSDiskName string + NicName string + SubnetName string + PublicIPAddressName string + VirtualNetworkName string } func NewTempName() *TempName { @@ -29,6 +33,10 @@ func NewTempName() *TempName { tempName.DeploymentName = fmt.Sprintf("pkrdp%s", suffix) tempName.KeyVaultName = fmt.Sprintf("pkrkv%s", suffix) tempName.OSDiskName = fmt.Sprintf("pkros%s", suffix) + tempName.NicName = fmt.Sprintf("pkrni%s", suffix) + tempName.PublicIPAddressName = fmt.Sprintf("pkrip%s", suffix) + tempName.SubnetName = fmt.Sprintf("pkrsn%s", suffix) + tempName.VirtualNetworkName = fmt.Sprintf("pkrvn%s", suffix) tempName.ResourceGroupName = fmt.Sprintf("packer-Resource-Group-%s", suffix) tempName.AdminPassword = common.RandomString(TempPasswordAlphabet, 32) diff --git a/builder/azure/arm/tempname_test.go b/builder/azure/arm/tempname_test.go index e9a49b429..4c09f8c46 100644 --- a/builder/azure/arm/tempname_test.go +++ b/builder/azure/arm/tempname_test.go @@ -20,15 +20,35 @@ func TestTempNameShouldCreatePrefixedRandomNames(t *testing.T) { t.Errorf("Expected OSDiskName to begin with 'pkros', but got '%s'!", tempName.OSDiskName) } + if strings.Index(tempName.NicName, "pkrni") != 0 { + t.Errorf("Expected NicName to begin with 'pkrni', but got '%s'!", tempName.NicName) + } + + if strings.Index(tempName.PublicIPAddressName, "pkrip") != 0 { + t.Errorf("Expected PublicIPAddressName to begin with 'pkrip', but got '%s'!", tempName.PublicIPAddressName) + } + if strings.Index(tempName.ResourceGroupName, "packer-Resource-Group-") != 0 { t.Errorf("Expected ResourceGroupName to begin with 'packer-Resource-Group-', but got '%s'!", tempName.ResourceGroupName) } + + if strings.Index(tempName.SubnetName, "pkrsn") != 0 { + t.Errorf("Expected SubnetName to begin with 'pkrip', but got '%s'!", tempName.SubnetName) + } + + if strings.Index(tempName.VirtualNetworkName, "pkrvn") != 0 { + t.Errorf("Expected VirtualNetworkName to begin with 'pkrvn', but got '%s'!", tempName.VirtualNetworkName) + } } func TestTempNameShouldHaveSameSuffix(t *testing.T) { tempName := NewTempName() suffix := tempName.ComputeName[5:] + if strings.HasSuffix(tempName.ComputeName, suffix) != true { + t.Errorf("Expected ComputeName to end with '%s', but the value is '%s'!", suffix, tempName.ComputeName) + } + if strings.HasSuffix(tempName.DeploymentName, suffix) != true { t.Errorf("Expected DeploymentName to end with '%s', but the value is '%s'!", suffix, tempName.DeploymentName) } @@ -37,8 +57,23 @@ func TestTempNameShouldHaveSameSuffix(t *testing.T) { t.Errorf("Expected OSDiskName to end with '%s', but the value is '%s'!", suffix, tempName.OSDiskName) } + if strings.HasSuffix(tempName.NicName, suffix) != true { + t.Errorf("Expected NicName to end with '%s', but the value is '%s'!", suffix, tempName.PublicIPAddressName) + } + + if strings.HasSuffix(tempName.PublicIPAddressName, suffix) != true { + t.Errorf("Expected PublicIPAddressName to end with '%s', but the value is '%s'!", suffix, tempName.PublicIPAddressName) + } + if strings.HasSuffix(tempName.ResourceGroupName, suffix) != true { t.Errorf("Expected ResourceGroupName to end with '%s', but the value is '%s'!", suffix, tempName.ResourceGroupName) } + if strings.HasSuffix(tempName.SubnetName, suffix) != true { + t.Errorf("Expected SubnetName to end with '%s', but the value is '%s'!", suffix, tempName.SubnetName) + } + + if strings.HasSuffix(tempName.VirtualNetworkName, suffix) != true { + t.Errorf("Expected VirtualNetworkName to end with '%s', but the value is '%s'!", suffix, tempName.VirtualNetworkName) + } } diff --git a/builder/azure/common/template/template_builder.go b/builder/azure/common/template/template_builder.go index a05b66ab3..b2c15b22f 100644 --- a/builder/azure/common/template/template_builder.go +++ b/builder/azure/common/template/template_builder.go @@ -461,12 +461,24 @@ const BasicTemplate = `{ "dnsNameForPublicIP": { "type": "string" }, + "nicName": { + "type": "string" + }, "osDiskName": { "type": "string" }, + "publicIPAddressName": { + "type": "string" + }, + "subnetName": { + "type": "string" + }, "storageAccountBlobEndpoint": { "type": "string" }, + "virtualNetworkName": { + "type": "string" + }, "vmSize": { "type": "string" }, @@ -482,14 +494,12 @@ const BasicTemplate = `{ "publicIPAddressApiVersion": "2017-04-01", "virtualNetworksApiVersion": "2017-04-01", "location": "[resourceGroup().location]", - "nicName": "packerNic", - "publicIPAddressName": "packerPublicIP", "publicIPAddressType": "Dynamic", "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", - "subnetName": "packerSubnet", + "subnetName": "[parameters('subnetName')]", "subnetAddressPrefix": "10.0.0.0/24", "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "packerNetwork", + "virtualNetworkName": "[parameters('virtualNetworkName')]", "virtualNetworkResourceGroup": "[resourceGroup().name]", "vmStorageAccountContainerName": "images", "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" @@ -498,7 +508,7 @@ const BasicTemplate = `{ { "apiVersion": "[variables('publicIPAddressApiVersion')]", "type": "Microsoft.Network/publicIPAddresses", - "name": "[variables('publicIPAddressName')]", + "name": "[parameters('publicIPAddressName')]", "location": "[variables('location')]", "properties": { "publicIPAllocationMethod": "[variables('publicIPAddressType')]", @@ -531,10 +541,10 @@ const BasicTemplate = `{ { "apiVersion": "[variables('networkInterfacesApiVersion')]", "type": "Microsoft.Network/networkInterfaces", - "name": "[variables('nicName')]", + "name": "[parameters('nicName')]", "location": "[variables('location')]", "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]", + "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" ], "properties": { @@ -544,7 +554,7 @@ const BasicTemplate = `{ "properties": { "privateIPAllocationMethod": "Dynamic", "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]" + "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" }, "subnet": { "id": "[variables('subnetRef')]" @@ -560,7 +570,7 @@ const BasicTemplate = `{ "name": "[parameters('vmName')]", "location": "[variables('location')]", "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" + "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" ], "properties": { "hardwareProfile": { @@ -584,7 +594,7 @@ const BasicTemplate = `{ "networkProfile": { "networkInterfaces": [ { - "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" + "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" } ] }, diff --git a/builder/azure/common/template/template_builder_test.TestBuildLinux00.approved.json b/builder/azure/common/template/template_builder_test.TestBuildLinux00.approved.json index b94fa0628..5a4b69427 100644 --- a/builder/azure/common/template/template_builder_test.TestBuildLinux00.approved.json +++ b/builder/azure/common/template/template_builder_test.TestBuildLinux00.approved.json @@ -11,12 +11,24 @@ "dnsNameForPublicIP": { "type": "string" }, + "nicName": { + "type": "string" + }, "osDiskName": { "type": "string" }, + "publicIPAddressName": { + "type": "string" + }, "storageAccountBlobEndpoint": { "type": "string" }, + "subnetName": { + "type": "string" + }, + "virtualNetworkName": { + "type": "string" + }, "vmName": { "type": "string" }, @@ -28,7 +40,7 @@ { "apiVersion": "[variables('publicIPAddressApiVersion')]", "location": "[variables('location')]", - "name": "[variables('publicIPAddressName')]", + "name": "[parameters('publicIPAddressName')]", "properties": { "dnsSettings": { "domainNameLabel": "[parameters('dnsNameForPublicIP')]" @@ -61,11 +73,11 @@ { "apiVersion": "[variables('networkInterfacesApiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]", + "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" ], "location": "[variables('location')]", - "name": "[variables('nicName')]", + "name": "[parameters('nicName')]", "properties": { "ipConfigurations": [ { @@ -73,7 +85,7 @@ "properties": { "privateIPAllocationMethod": "Dynamic", "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]" + "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" }, "subnet": { "id": "[variables('subnetRef')]" @@ -87,7 +99,7 @@ { "apiVersion": "[variables('apiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" + "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" ], "location": "[variables('location')]", "name": "[parameters('vmName')]", @@ -103,7 +115,7 @@ "networkProfile": { "networkInterfaces": [ { - "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" + "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" } ] }, @@ -148,18 +160,16 @@ "location": "[resourceGroup().location]", "managedDiskApiVersion": "2017-03-30", "networkInterfacesApiVersion": "2017-04-01", - "nicName": "packerNic", "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressName": "packerPublicIP", "publicIPAddressType": "Dynamic", "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "packerSubnet", + "subnetName": "[parameters('subnetName')]", "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "packerNetwork", - "virtualNetworkResourceGroup": "[resourceGroup().name]", + "virtualNetworkName": "[parameters('virtualNetworkName')]", + "virtualNetworkResourceGroup": "[resourceGroup().name]", "virtualNetworksApiVersion": "2017-04-01", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - } + "vmStorageAccountContainerName": "images", + "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" + } } \ No newline at end of file diff --git a/builder/azure/common/template/template_builder_test.TestBuildLinux01.approved.json b/builder/azure/common/template/template_builder_test.TestBuildLinux01.approved.json index 8eac0647d..d23117b48 100644 --- a/builder/azure/common/template/template_builder_test.TestBuildLinux01.approved.json +++ b/builder/azure/common/template/template_builder_test.TestBuildLinux01.approved.json @@ -11,12 +11,24 @@ "dnsNameForPublicIP": { "type": "string" }, + "nicName": { + "type": "string" + }, "osDiskName": { "type": "string" }, + "publicIPAddressName": { + "type": "string" + }, "storageAccountBlobEndpoint": { "type": "string" }, + "subnetName": { + "type": "string" + }, + "virtualNetworkName": { + "type": "string" + }, "vmName": { "type": "string" }, @@ -28,7 +40,7 @@ { "apiVersion": "[variables('publicIPAddressApiVersion')]", "location": "[variables('location')]", - "name": "[variables('publicIPAddressName')]", + "name": "[parameters('publicIPAddressName')]", "properties": { "dnsSettings": { "domainNameLabel": "[parameters('dnsNameForPublicIP')]" @@ -61,11 +73,11 @@ { "apiVersion": "[variables('networkInterfacesApiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]", + "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" ], "location": "[variables('location')]", - "name": "[variables('nicName')]", + "name": "[parameters('nicName')]", "properties": { "ipConfigurations": [ { @@ -73,7 +85,7 @@ "properties": { "privateIPAllocationMethod": "Dynamic", "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]" + "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" }, "subnet": { "id": "[variables('subnetRef')]" @@ -87,7 +99,7 @@ { "apiVersion": "[variables('apiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" + "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" ], "location": "[variables('location')]", "name": "[parameters('vmName')]", @@ -103,7 +115,7 @@ "networkProfile": { "networkInterfaces": [ { - "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" + "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" } ] }, @@ -146,18 +158,16 @@ "location": "[resourceGroup().location]", "managedDiskApiVersion": "2017-03-30", "networkInterfacesApiVersion": "2017-04-01", - "nicName": "packerNic", "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressName": "packerPublicIP", "publicIPAddressType": "Dynamic", "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "packerSubnet", + "subnetName": "[parameters('subnetName')]", "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "packerNetwork", - "virtualNetworkResourceGroup": "[resourceGroup().name]", + "virtualNetworkName": "[parameters('virtualNetworkName')]", + "virtualNetworkResourceGroup": "[resourceGroup().name]", "virtualNetworksApiVersion": "2017-04-01", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - } + "vmStorageAccountContainerName": "images", + "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" + } } \ No newline at end of file diff --git a/builder/azure/common/template/template_builder_test.TestBuildLinux02.approved.json b/builder/azure/common/template/template_builder_test.TestBuildLinux02.approved.json index fffb0d7bb..68d1bc427 100644 --- a/builder/azure/common/template/template_builder_test.TestBuildLinux02.approved.json +++ b/builder/azure/common/template/template_builder_test.TestBuildLinux02.approved.json @@ -11,12 +11,24 @@ "dnsNameForPublicIP": { "type": "string" }, + "nicName": { + "type": "string" + }, "osDiskName": { "type": "string" }, + "publicIPAddressName": { + "type": "string" + }, "storageAccountBlobEndpoint": { "type": "string" }, + "subnetName": { + "type": "string" + }, + "virtualNetworkName": { + "type": "string" + }, "vmName": { "type": "string" }, @@ -29,7 +41,7 @@ "apiVersion": "[variables('networkInterfacesApiVersion')]", "dependsOn": [], "location": "[variables('location')]", - "name": "[variables('nicName')]", + "name": "[parameters('nicName')]", "properties": { "ipConfigurations": [ { @@ -48,7 +60,7 @@ { "apiVersion": "[variables('apiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" + "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" ], "location": "[variables('location')]", "name": "[parameters('vmName')]", @@ -64,7 +76,7 @@ "networkProfile": { "networkInterfaces": [ { - "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" + "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" } ] }, @@ -108,9 +120,7 @@ "location": "[resourceGroup().location]", "managedDiskApiVersion": "2017-03-30", "networkInterfacesApiVersion": "2017-04-01", - "nicName": "packerNic", "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressName": "packerPublicIP", "publicIPAddressType": "Dynamic", "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", "subnetAddressPrefix": "10.0.0.0/24", diff --git a/builder/azure/common/template/template_builder_test.TestBuildWindows00.approved.json b/builder/azure/common/template/template_builder_test.TestBuildWindows00.approved.json index 1c3138deb..cbf46d3e8 100644 --- a/builder/azure/common/template/template_builder_test.TestBuildWindows00.approved.json +++ b/builder/azure/common/template/template_builder_test.TestBuildWindows00.approved.json @@ -11,12 +11,24 @@ "dnsNameForPublicIP": { "type": "string" }, + "nicName": { + "type": "string" + }, "osDiskName": { "type": "string" }, + "publicIPAddressName": { + "type": "string" + }, "storageAccountBlobEndpoint": { "type": "string" }, + "subnetName": { + "type": "string" + }, + "virtualNetworkName": { + "type": "string" + }, "vmName": { "type": "string" }, @@ -28,7 +40,7 @@ { "apiVersion": "[variables('publicIPAddressApiVersion')]", "location": "[variables('location')]", - "name": "[variables('publicIPAddressName')]", + "name": "[parameters('publicIPAddressName')]", "properties": { "dnsSettings": { "domainNameLabel": "[parameters('dnsNameForPublicIP')]" @@ -61,11 +73,11 @@ { "apiVersion": "[variables('networkInterfacesApiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]", + "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" ], "location": "[variables('location')]", - "name": "[variables('nicName')]", + "name": "[parameters('nicName')]", "properties": { "ipConfigurations": [ { @@ -73,7 +85,7 @@ "properties": { "privateIPAllocationMethod": "Dynamic", "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]" + "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" }, "subnet": { "id": "[variables('subnetRef')]" @@ -87,7 +99,7 @@ { "apiVersion": "[variables('apiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" + "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" ], "location": "[variables('location')]", "name": "[parameters('vmName')]", @@ -103,7 +115,7 @@ "networkProfile": { "networkInterfaces": [ { - "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" + "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" } ] }, @@ -162,18 +174,16 @@ "location": "[resourceGroup().location]", "managedDiskApiVersion": "2017-03-30", "networkInterfacesApiVersion": "2017-04-01", - "nicName": "packerNic", "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressName": "packerPublicIP", "publicIPAddressType": "Dynamic", "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "packerSubnet", + "subnetName": "[parameters('subnetName')]", "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "packerNetwork", - "virtualNetworkResourceGroup": "[resourceGroup().name]", + "virtualNetworkName": "[parameters('virtualNetworkName')]", + "virtualNetworkResourceGroup": "[resourceGroup().name]", "virtualNetworksApiVersion": "2017-04-01", - "vmStorageAccountContainerName": "images", - "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" - } + "vmStorageAccountContainerName": "images", + "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" + } } \ No newline at end of file diff --git a/builder/azure/common/template/template_builder_test.TestBuildWindows01.approved.json b/builder/azure/common/template/template_builder_test.TestBuildWindows01.approved.json index b7a145df9..c660dcd75 100644 --- a/builder/azure/common/template/template_builder_test.TestBuildWindows01.approved.json +++ b/builder/azure/common/template/template_builder_test.TestBuildWindows01.approved.json @@ -11,12 +11,24 @@ "dnsNameForPublicIP": { "type": "string" }, + "nicName": { + "type": "string" + }, "osDiskName": { "type": "string" }, + "publicIPAddressName": { + "type": "string" + }, "storageAccountBlobEndpoint": { "type": "string" }, + "subnetName": { + "type": "string" + }, + "virtualNetworkName": { + "type": "string" + }, "vmName": { "type": "string" }, @@ -28,7 +40,7 @@ { "apiVersion": "[variables('publicIPAddressApiVersion')]", "location": "[variables('location')]", - "name": "[variables('publicIPAddressName')]", + "name": "[parameters('publicIPAddressName')]", "properties": { "dnsSettings": { "domainNameLabel": "[parameters('dnsNameForPublicIP')]" @@ -61,11 +73,11 @@ { "apiVersion": "[variables('networkInterfacesApiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]", + "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" ], "location": "[variables('location')]", - "name": "[variables('nicName')]", + "name": "[parameters('nicName')]", "properties": { "ipConfigurations": [ { @@ -73,7 +85,7 @@ "properties": { "privateIPAllocationMethod": "Dynamic", "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]" + "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" }, "subnet": { "id": "[variables('subnetRef')]" @@ -87,7 +99,7 @@ { "apiVersion": "[variables('apiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" + "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" ], "location": "[variables('location')]", "name": "[parameters('vmName')]", @@ -103,7 +115,7 @@ "networkProfile": { "networkInterfaces": [ { - "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" + "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" } ] }, @@ -185,15 +197,13 @@ "location": "[resourceGroup().location]", "managedDiskApiVersion": "2017-03-30", "networkInterfacesApiVersion": "2017-04-01", - "nicName": "packerNic", "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressName": "packerPublicIP", "publicIPAddressType": "Dynamic", "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "packerSubnet", + "subnetName": "[parameters('subnetName')]", "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "packerNetwork", + "virtualNetworkName": "[parameters('virtualNetworkName')]", "virtualNetworkResourceGroup": "[resourceGroup().name]", "virtualNetworksApiVersion": "2017-04-01", "vmStorageAccountContainerName": "images", diff --git a/builder/azure/common/template/template_builder_test.TestBuildWindows02.approved.json b/builder/azure/common/template/template_builder_test.TestBuildWindows02.approved.json index 61b05a125..0507771ee 100644 --- a/builder/azure/common/template/template_builder_test.TestBuildWindows02.approved.json +++ b/builder/azure/common/template/template_builder_test.TestBuildWindows02.approved.json @@ -11,12 +11,24 @@ "dnsNameForPublicIP": { "type": "string" }, + "nicName": { + "type": "string" + }, "osDiskName": { "type": "string" }, + "publicIPAddressName": { + "type": "string" + }, "storageAccountBlobEndpoint": { "type": "string" }, + "subnetName": { + "type": "string" + }, + "virtualNetworkName": { + "type": "string" + }, "vmName": { "type": "string" }, @@ -28,7 +40,7 @@ { "apiVersion": "[variables('publicIPAddressApiVersion')]", "location": "[variables('location')]", - "name": "[variables('publicIPAddressName')]", + "name": "[parameters('publicIPAddressName')]", "properties": { "dnsSettings": { "domainNameLabel": "[parameters('dnsNameForPublicIP')]" @@ -61,11 +73,11 @@ { "apiVersion": "[variables('networkInterfacesApiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]", + "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" ], "location": "[variables('location')]", - "name": "[variables('nicName')]", + "name": "[parameters('nicName')]", "properties": { "ipConfigurations": [ { @@ -73,7 +85,7 @@ "properties": { "privateIPAllocationMethod": "Dynamic", "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]" + "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" }, "subnet": { "id": "[variables('subnetRef')]" @@ -87,7 +99,7 @@ { "apiVersion": "[variables('apiVersion')]", "dependsOn": [ - "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" + "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" ], "location": "[variables('location')]", "name": "[parameters('vmName')]", @@ -103,7 +115,7 @@ "networkProfile": { "networkInterfaces": [ { - "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" + "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" } ] }, @@ -178,15 +190,13 @@ "location": "[resourceGroup().location]", "managedDiskApiVersion": "2017-03-30", "networkInterfacesApiVersion": "2017-04-01", - "nicName": "packerNic", "publicIPAddressApiVersion": "2017-04-01", - "publicIPAddressName": "packerPublicIP", "publicIPAddressType": "Dynamic", "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", "subnetAddressPrefix": "10.0.0.0/24", - "subnetName": "packerSubnet", + "subnetName": "[parameters('subnetName')]", "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", - "virtualNetworkName": "packerNetwork", + "virtualNetworkName": "[parameters('virtualNetworkName')]", "virtualNetworkResourceGroup": "[resourceGroup().name]", "virtualNetworksApiVersion": "2017-04-01", "vmStorageAccountContainerName": "images", diff --git a/builder/azure/common/template/template_parameters.go b/builder/azure/common/template/template_parameters.go index 424a30c89..65bc55e75 100644 --- a/builder/azure/common/template/template_parameters.go +++ b/builder/azure/common/template/template_parameters.go @@ -24,9 +24,13 @@ type TemplateParameters struct { KeyVaultName *TemplateParameter `json:"keyVaultName,omitempty"` KeyVaultSecretValue *TemplateParameter `json:"keyVaultSecretValue,omitempty"` ObjectId *TemplateParameter `json:"objectId,omitempty"` + NicName *TemplateParameter `json:"nicName,omitempty"` OSDiskName *TemplateParameter `json:"osDiskName,omitempty"` + PublicIPAddressName *TemplateParameter `json:"publicIPAddressName,omitempty"` StorageAccountBlobEndpoint *TemplateParameter `json:"storageAccountBlobEndpoint,omitempty"` + SubnetName *TemplateParameter `json:"subnetName,omitempty"` TenantId *TemplateParameter `json:"tenantId,omitempty"` + VirtualNetworkName *TemplateParameter `json:"virtualNetworkName,omitempty"` VMSize *TemplateParameter `json:"vmSize,omitempty"` VMName *TemplateParameter `json:"vmName,omitempty"` } diff --git a/website/source/docs/builders/azure.html.md b/website/source/docs/builders/azure.html.md index 13b2b46a3..9019e035e 100644 --- a/website/source/docs/builders/azure.html.md +++ b/website/source/docs/builders/azure.html.md @@ -374,9 +374,13 @@ The Azure builder creates the following random values at runtime. - Compute Name: a random 15-character name prefixed with pkrvm; the name of the VM. - Deployment Name: a random 15-character name prefixed with pkfdp; the name of the deployment. - KeyVault Name: a random 15-character name prefixed with pkrkv. +- NIC Name: a random 15-character name prefixed with pkrni. +- Public IP Name: a random 15-character name prefixed with pkrip. - OS Disk Name: a random 15-character name prefixed with pkros. - Resource Group Name: a random 33-character name prefixed with packer-Resource-Group-. +- Subnet Name: a random 15-character name prefixed with pkrsn. - SSH Key Pair: a 2,048-bit asymmetric key pair; can be overridden by the user. +- Virtual Network Name: a random 15-character name prefixed with pkrvn. The default alphabet used for random values is **0123456789bcdfghjklmnpqrstvwxyz**. The alphabet was reduced (no vowels) to prevent running afoul of Azure decency controls. From 2a88672cd96ff29c69ef9b343a8bb64c691554df Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Mon, 12 Mar 2018 15:08:19 -0700 Subject: [PATCH 0579/1216] add test for modifyer keypress commands --- .../common/step_type_boot_command_test.go | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 builder/virtualbox/common/step_type_boot_command_test.go diff --git a/builder/virtualbox/common/step_type_boot_command_test.go b/builder/virtualbox/common/step_type_boot_command_test.go new file mode 100644 index 000000000..23a5d9077 --- /dev/null +++ b/builder/virtualbox/common/step_type_boot_command_test.go @@ -0,0 +1,43 @@ +package common + +import ( + "testing" +) + +func TestScancodes(t *testing.T) { + var bootcommand = []string{ + "1234567890-=", + "!@#$%^&*()_+", + "qwertyuiop[]", + "QWERTYUIOP{}", + "asdfghjkl;'`", + `ASDFGHJKL:"~`, + "\\zxcvbnm,./", + "|ZXCVBNM<>?", + "", + "", + "", + } + + var expected = [][]string{ + {"02", "82", "03", "83", "04", "84", "05", "85", "06", "86", "07", "87", "08", "88", "09", "89", "0a", "8a", "0b", "8b", "0c", "8c", "0d", "8d", "1c", "9c", "wait"}, + {"2a", "02", "aa", "82", "2a", "03", "aa", "83", "2a", "04", "aa", "84", "2a", "05", "aa", "85", "2a", "06", "aa", "86", "2a", "07", "aa", "87", "2a", "08", "aa", "88", "2a", "09", "aa", "89", "2a", "0a", "aa", "8a", "2a", "0b", "aa", "8b", "2a", "0c", "aa", "8c", "2a", "0d", "aa", "8d", "1c", "9c"}, + {"10", "90", "11", "91", "12", "92", "13", "93", "14", "94", "15", "95", "16", "96", "17", "97", "18", "98", "19", "99", "1a", "9a", "1b", "9b", "1c", "9c"}, + {"2a", "10", "aa", "90", "2a", "11", "aa", "91", "2a", "12", "aa", "92", "2a", "13", "aa", "93", "2a", "14", "aa", "94", "2a", "15", "aa", "95", "2a", "16", "aa", "96", "2a", "17", "aa", "97", "2a", "18", "aa", "98", "2a", "19", "aa", "99", "2a", "1a", "aa", "9a", "2a", "1b", "aa", "9b", "1c", "9c"}, + {"1e", "9e", "1f", "9f", "20", "a0", "21", "a1", "22", "a2", "23", "a3", "24", "a4", "25", "a5", "26", "a6", "27", "a7", "28", "a8", "29", "a9", "1c", "9c"}, + {"2a", "1e", "aa", "9e", "2a", "1f", "aa", "9f", "2a", "20", "aa", "a0", "2a", "21", "aa", "a1", "2a", "22", "aa", "a2", "2a", "23", "aa", "a3", "2a", "24", "aa", "a4", "2a", "25", "aa", "a5", "2a", "26", "aa", "a6", "2a", "27", "aa", "a7", "2a", "28", "aa", "a8", "2a", "29", "aa", "a9", "1c", "9c"}, + {"2b", "ab", "2c", "ac", "2d", "ad", "2e", "ae", "2f", "af", "30", "b0", "31", "b1", "32", "b2", "33", "b3", "34", "b4", "35", "b5", "1c", "9c"}, + {"2a", "2b", "aa", "ab", "2a", "2c", "aa", "ac", "2a", "2d", "aa", "ad", "2a", "2e", "aa", "ae", "2a", "2f", "aa", "af", "2a", "30", "aa", "b0", "2a", "31", "aa", "b1", "2a", "32", "aa", "b2", "2a", "33", "aa", "b3", "2a", "34", "aa", "b4", "2a", "35", "aa", "b5", "1c", "9c"}, + {"1c", "9c"}, + {"38", "01", "81", "b8", "wait5"}, + {"0f", "8f", "0f", "8f", "0f", "8f", "0f", "8f", "0f", "8f", "39", "b9", "wait5"}, + } + + for i, command := range bootcommand { + for j, code := range scancodes(command) { + if code != expected[i][j] { + t.Fatalf("%#v should have become %#v", scancodes(command), expected[i]) + } + } + } +} From f6b1349bf32e505b80f3e695a326d2d6098063dd Mon Sep 17 00:00:00 2001 From: Hariharan Jayaraman Date: Mon, 12 Mar 2018 21:09:53 -0700 Subject: [PATCH 0580/1216] fixing marketplace sample --- examples/azure/marketplace_plan_info.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/azure/marketplace_plan_info.json b/examples/azure/marketplace_plan_info.json index 4891a9e6b..710dda8ee 100644 --- a/examples/azure/marketplace_plan_info.json +++ b/examples/azure/marketplace_plan_info.json @@ -19,9 +19,9 @@ "capture_name_prefix": "packer", "os_type": "Linux", - "image_publisher": "Canonical", - "image_offer": "UbuntuServer", - "image_sku": "16.04-LTS", + "image_publisher": "bitnami", + "image_offer": "rabbitmq", + "image_sku": "rabbitmq", "azure_tags": { "dept": "engineering", From 5c0191828f6f3ba26cf666ef5c1ba5eb01f8586c Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:05:56 +0000 Subject: [PATCH 0581/1216] spelling: account --- post-processor/alicloud-import/post-processor.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/post-processor/alicloud-import/post-processor.go b/post-processor/alicloud-import/post-processor.go index 2606e5fdf..4421b170d 100644 --- a/post-processor/alicloud-import/post-processor.go +++ b/post-processor/alicloud-import/post-processor.go @@ -214,7 +214,7 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac if err != nil { e, _ := err.(*packercommon.Error) - if e.Code == "NoSetRoletoECSServiceAcount" { + if e.Code == "NoSetRoletoECSServiceAccount" { ramClient := ram.NewClient(p.config.AlicloudAccessKey, p.config.AlicloudSecretKey) roleResponse, err := ramClient.GetRole(ram.RoleQueryRequest{ RoleName: "AliyunECSImageImportDefaultRole", @@ -282,7 +282,7 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac imageId, err = ecsClient.ImportImage(imageImageArgs) if err != nil { e, _ = err.(*packercommon.Error) - if e.Code == "NoSetRoletoECSServiceAcount" { + if e.Code == "NoSetRoletoECSServiceAccount" { time.Sleep(5 * time.Second) continue } else if e.Code == "ImageIsImporting" || From 5e167e3b6d55c0b198bd49fe6af3097827eaa235 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:03:51 +0000 Subject: [PATCH 0582/1216] spelling: accumulates --- helper/multistep/multistep_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helper/multistep/multistep_test.go b/helper/multistep/multistep_test.go index 25bbeef9b..3ced60d83 100644 --- a/helper/multistep/multistep_test.go +++ b/helper/multistep/multistep_test.go @@ -2,7 +2,7 @@ package multistep import "context" -// A step for testing that accumuluates data into a string slice in the +// A step for testing that accumulates data into a string slice in the // the state bag. It always uses the "data" key in the state bag, and will // initialize it. type TestStepAcc struct { From fe98bcc5a109cf2629c209ca51c2264e7d2fc0b9 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:07:10 +0000 Subject: [PATCH 0583/1216] spelling: address --- builder/alicloud/ecs/step_config_public_ip.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builder/alicloud/ecs/step_config_public_ip.go b/builder/alicloud/ecs/step_config_public_ip.go index 7e0a246e0..594f85603 100644 --- a/builder/alicloud/ecs/step_config_public_ip.go +++ b/builder/alicloud/ecs/step_config_public_ip.go @@ -10,8 +10,8 @@ import ( ) type stepConfigAlicloudPublicIP struct { - publicIPAdress string - RegionId string + publicIPAddress string + RegionId string } func (s *stepConfigAlicloudPublicIP) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { @@ -25,7 +25,7 @@ func (s *stepConfigAlicloudPublicIP) Run(_ context.Context, state multistep.Stat ui.Say(fmt.Sprintf("Error allocating public ip: %s", err)) return multistep.ActionHalt } - s.publicIPAdress = ipaddress + s.publicIPAddress = ipaddress ui.Say(fmt.Sprintf("Allocated public ip address %s.", ipaddress)) state.Put("ipaddress", ipaddress) return multistep.ActionContinue From c38cdd043982f409074aa6bfd3f9becbde1e9e8c Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:06:52 +0000 Subject: [PATCH 0584/1216] spelling: administrator --- website/source/docs/provisioners/converge.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/provisioners/converge.html.md b/website/source/docs/provisioners/converge.html.md index b76652abe..1c06678ed 100644 --- a/website/source/docs/provisioners/converge.html.md +++ b/website/source/docs/provisioners/converge.html.md @@ -59,7 +59,7 @@ Optional parameters: various [configuration template variables](/docs/templates/engine.html) available. -- `prevent_sudo` (boolean) - stop Converge from running with adminstrator +- `prevent_sudo` (boolean) - stop Converge from running with administrator privileges via sudo - `bootstrap_command` (string) - the command used to bootstrap Converge. This From 75a7ceec48062171e5b282cd2a7942638210e072 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:09:12 +0000 Subject: [PATCH 0585/1216] spelling: alicloud --- builder/alicloud/ecs/run_config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/alicloud/ecs/run_config.go b/builder/alicloud/ecs/run_config.go index b6098ce5c..2c52bb37b 100644 --- a/builder/alicloud/ecs/run_config.go +++ b/builder/alicloud/ecs/run_config.go @@ -57,7 +57,7 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { } if c.InstanceType == "" { - errs = append(errs, errors.New("An aliclod_instance_type must be specified")) + errs = append(errs, errors.New("An alicloud_instance_type must be specified")) } if c.UserData != "" && c.UserDataFile != "" { From e7a30b4ba2f88cfa40086cb333996adec32878c7 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:11:42 +0000 Subject: [PATCH 0586/1216] spelling: attempt --- command/fix.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/fix.go b/command/fix.go index 1a94f5786..e7cfac9ea 100644 --- a/command/fix.go +++ b/command/fix.go @@ -85,7 +85,7 @@ func (c *FixCommand) Run(args []string) int { c.Ui.Say(result) if flagValidate { - // Attemot to parse and validate the template + // Attempt to parse and validate the template tpl, err := template.Parse(strings.NewReader(result)) if err != nil { c.Ui.Error(fmt.Sprintf( From 7895051962b62e40fb01b15b4b701e4a6fd9e935 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:12:16 +0000 Subject: [PATCH 0587/1216] spelling: attribute --- builder/alicloud/ecs/step_run_instance.go | 4 ++-- builder/oracle/classic/config.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/builder/alicloud/ecs/step_run_instance.go b/builder/alicloud/ecs/step_run_instance.go index 72491dd1d..57a799183 100644 --- a/builder/alicloud/ecs/step_run_instance.go +++ b/builder/alicloud/ecs/step_run_instance.go @@ -43,8 +43,8 @@ func (s *stepRunAlicloudInstance) Cleanup(state multistep.StateBag) { ui := state.Get("ui").(packer.Ui) client := state.Get("client").(*ecs.Client) instance := state.Get("instance").(*ecs.InstanceAttributesType) - instanceAttrubite, _ := client.DescribeInstanceAttribute(instance.InstanceId) - if instanceAttrubite.Status == ecs.Starting || instanceAttrubite.Status == ecs.Running { + instanceAttribute, _ := client.DescribeInstanceAttribute(instance.InstanceId) + if instanceAttribute.Status == ecs.Starting || instanceAttribute.Status == ecs.Running { if err := client.StopInstance(instance.InstanceId, true); err != nil { ui.Say(fmt.Sprintf("Error stopping instance %s, it may still be around %s", instance.InstanceId, err)) return diff --git a/builder/oracle/classic/config.go b/builder/oracle/classic/config.go index b9f5d808f..a483c8e50 100644 --- a/builder/oracle/classic/config.go +++ b/builder/oracle/classic/config.go @@ -33,7 +33,7 @@ type Config struct { SourceImageList string `mapstructure:"source_image_list"` SnapshotTimeout time.Duration `mapstructure:"snapshot_timeout"` DestImageList string `mapstructure:"dest_image_list"` - // Attributes and Atributes file are both optional and mutually exclusive. + // Attributes and Attributes file are both optional and mutually exclusive. Attributes string `mapstructure:"attributes"` AttributesFile string `mapstructure:"attributes_file"` // Optional; if you don't enter anything, the image list description @@ -129,7 +129,7 @@ func NewConfig(raws ...interface{}) (*Config, error) { err = json.Unmarshal(fidata, &data) c.attribs = data if err != nil { - err = fmt.Errorf("Problem parsing json from attrinutes_file: %s", err) + err = fmt.Errorf("Problem parsing json from attributes_file: %s", err) packer.MultiErrorAppend(errs, err) } c.attribs = data From c312493c387fe7522f631ee8322fc625d8fb569a Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:13:24 +0000 Subject: [PATCH 0588/1216] spelling: available --- examples/alicloud/chef/chef.sh | 2 +- post-processor/vagrant/post-processor.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/alicloud/chef/chef.sh b/examples/alicloud/chef/chef.sh index 6d678b7cc..3e3144561 100644 --- a/examples/alicloud/chef/chef.sh +++ b/examples/alicloud/chef/chef.sh @@ -1,5 +1,5 @@ #!/bin/sh -#if the related deb pkg not found, please replace with it other avaiable repository url +#if the related deb pkg not found, please replace with it other available repository url HOSTNAME=`ifconfig eth1|grep 'inet addr'|cut -d ":" -f2|cut -d " " -f1` if [ not $HOSTNAME ] ; then HOSTNAME=`ifconfig eth0|grep 'inet addr'|cut -d ":" -f2|cut -d " " -f1` diff --git a/post-processor/vagrant/post-processor.go b/post-processor/vagrant/post-processor.go index de93628c0..d4b587316 100644 --- a/post-processor/vagrant/post-processor.go +++ b/post-processor/vagrant/post-processor.go @@ -246,7 +246,7 @@ func providerForName(name string) Provider { } } -// OutputPathTemplate is the structure that is availalable within the +// OutputPathTemplate is the structure that is available within the // OutputPath variables. type outputPathTemplate struct { ArtifactId string From f362789174140601fc5d07fd5ed788eabb51308f Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:15:08 +0000 Subject: [PATCH 0589/1216] spelling: because --- builder/oracle/classic/step_security.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/oracle/classic/step_security.go b/builder/oracle/classic/step_security.go index 892695f83..a4b547d27 100644 --- a/builder/oracle/classic/step_security.go +++ b/builder/oracle/classic/step_security.go @@ -53,7 +53,7 @@ func (s *stepSecurity) Run(_ context.Context, state multistep.StateBag) multiste application = "/oracle/public/ssh" } else if commType == "WINRM" { // Check to see whether a winRM security protocol is already defined; - // don't need to do this for SSH becasue it is built into the Oracle API. + // don't need to do this for SSH because it is built into the Oracle API. protocolClient := client.SecurityProtocols() winrmProtocol := fmt.Sprintf("WINRM_%s", runUUID) input := compute.CreateSecurityProtocolInput{ From 9434b0fed852323907b4bf3dc4fc249aba12e77f Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:19:04 +0000 Subject: [PATCH 0590/1216] spelling: builder --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cc52892f..f7bc2926d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1321,7 +1321,7 @@ * builder/parallels: Support Parallels Desktop 11. [GH-2199] * builder/openstack: Add `rackconnect_wait` for Rackspace customers to wait for RackConnect data to appear -* buidler/openstack: Add `ssh_interface` option for rackconnect for users that +* builder/openstack: Add `ssh_interface` option for rackconnect for users that have prohibitive firewalls * builder/openstack: Flavor names can be used as well as refs * builder/openstack: Add `availability_zone` [GH-2016] From 5010bfda39811c46e27c09fe9e03b230a66942e9 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:20:55 +0000 Subject: [PATCH 0591/1216] spelling: comes --- builder/openstack/step_key_pair_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/openstack/step_key_pair_test.go b/builder/openstack/step_key_pair_test.go index 0ce45d6a7..891ad3cdc 100644 --- a/builder/openstack/step_key_pair_test.go +++ b/builder/openstack/step_key_pair_test.go @@ -81,7 +81,7 @@ func TestBerToDer(t *testing.T) { Writer: msg, } - // Test - a DER encoded key commes back unchanged. + // Test - a DER encoded key comes back unchanged. newKey := berToDer(der_encoded_key, ui) if newKey != der_encoded_key { t.Errorf("Trying to convert a DER encoded key should return the same key.") From 47a4bbd9f592222db03f10a16c33398758522c6c Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:22:09 +0000 Subject: [PATCH 0592/1216] spelling: compaction --- builder/parallels/common/driver_9.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/parallels/common/driver_9.go b/builder/parallels/common/driver_9.go index b6757d891..cfdee4a5d 100644 --- a/builder/parallels/common/driver_9.go +++ b/builder/parallels/common/driver_9.go @@ -119,7 +119,7 @@ func getAppPath(bundleID string) (string, error) { return pathOutput, nil } -// CompactDisk performs the compation of the specified virtual disk image. +// CompactDisk performs the compaction of the specified virtual disk image. func (d *Parallels9Driver) CompactDisk(diskPath string) error { prlDiskToolPath, err := exec.LookPath("prl_disk_tool") if err != nil { From f6745897c5100fc536e2a36e2b75d76ad66b9e4c Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:21:31 +0000 Subject: [PATCH 0593/1216] spelling: compute --- post-processor/checksum/post-processor_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/post-processor/checksum/post-processor_test.go b/post-processor/checksum/post-processor_test.go index fb4d0faf1..6b4420d39 100644 --- a/post-processor/checksum/post-processor_test.go +++ b/post-processor/checksum/post-processor_test.go @@ -35,7 +35,7 @@ func TestChecksumSHA1(t *testing.T) { t.Errorf("Unable to read checksum file: %s", err) } if buf, _ := ioutil.ReadAll(f); !bytes.Equal(buf, []byte("d3486ae9136e7856bc42212385ea797094475802\tpackage.txt\n")) { - t.Errorf("Failed to compate checksum: %s\n%s", buf, "d3486ae9136e7856bc42212385ea797094475802 package.txt") + t.Errorf("Failed to compute checksum: %s\n%s", buf, "d3486ae9136e7856bc42212385ea797094475802 package.txt") } defer f.Close() From 935c8e9a2827b429a4c38ff729f35293f81a55b2 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 08:19:16 +0000 Subject: [PATCH 0594/1216] spelling: configuration --- builder/parallels/common/driver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/parallels/common/driver.go b/builder/parallels/common/driver.go index b485d5b67..ac23a7880 100644 --- a/builder/parallels/common/driver.go +++ b/builder/parallels/common/driver.go @@ -51,7 +51,7 @@ type Driver interface { // Send scancodes to the vm using the prltype python script. SendKeyScanCodes(string, ...string) error - // Apply default сonfiguration settings to the virtual machine + // Apply default configuration settings to the virtual machine SetDefaultConfiguration(string) error // Finds the MAC address of the NIC nic0 From faf7928a4105676d1127859201c59aaca1d5ad77 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:22:23 +0000 Subject: [PATCH 0595/1216] spelling: conservative --- contrib/azure-setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/azure-setup.sh b/contrib/azure-setup.sh index ef855b353..8ccd0cbc0 100755 --- a/contrib/azure-setup.sh +++ b/contrib/azure-setup.sh @@ -182,7 +182,7 @@ createServicePrincipal() { createPermissions() { echo "==> Creating permissions" az role assignment create --assignee $azure_object_id --role "Owner" --scope /subscriptions/$azure_subscription_id - # If the user wants to use a more conserative scope, she can. She must + # If the user wants to use a more conservative scope, she can. She must # configure the Azure builder to use build_resource_group_name. The # easiest solution is subscription wide permission. # az role assignment create --spn http://$meta_name -g $azure_group_name -o "API Management Service Contributor" From efb525f03ddd92f768d7946280b7097367ef1a42 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:22:37 +0000 Subject: [PATCH 0596/1216] spelling: creating --- builder/hyperv/common/step_create_external_switch.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/hyperv/common/step_create_external_switch.go b/builder/hyperv/common/step_create_external_switch.go index 56e6fa6e8..52626afce 100644 --- a/builder/hyperv/common/step_create_external_switch.go +++ b/builder/hyperv/common/step_create_external_switch.go @@ -23,7 +23,7 @@ func (s *StepCreateExternalSwitch) Run(_ context.Context, state multistep.StateB ui := state.Get("ui").(packer.Ui) vmName := state.Get("vmName").(string) - errorMsg := "Error createing external switch: %s" + errorMsg := "Error creating external switch: %s" var err error ui.Say("Creating external switch...") From 684a3f5d8a80e2c8da6a55d39e3076771571e1dc Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:26:33 +0000 Subject: [PATCH 0597/1216] spelling: custom --- provisioner/salt-masterless/provisioner_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provisioner/salt-masterless/provisioner_test.go b/provisioner/salt-masterless/provisioner_test.go index bad83b3f6..166e17f96 100644 --- a/provisioner/salt-masterless/provisioner_test.go +++ b/provisioner/salt-masterless/provisioner_test.go @@ -49,7 +49,7 @@ func TestProvisionerPrepare_InvalidKey(t *testing.T) { } } -func TestProvisionerPrepare_CustomeState(t *testing.T) { +func TestProvisionerPrepare_CustomState(t *testing.T) { var p Provisioner config := testConfig() From 0cff0f2f848c981c77e613d30c9ec68ac46c34ae Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:35:21 +0000 Subject: [PATCH 0598/1216] spelling: default --- website/source/docs/builders/hyperv-iso.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/builders/hyperv-iso.html.md b/website/source/docs/builders/hyperv-iso.html.md index e1454fea7..094e46bcb 100644 --- a/website/source/docs/builders/hyperv-iso.html.md +++ b/website/source/docs/builders/hyperv-iso.html.md @@ -101,7 +101,7 @@ can be configured for this builder. source is a vhd/vhdx. This defaults to false. - `skip_export` (boolean) - If true skips VM export. If you are interested only in the vhd/vhdx files, you can enable this option. This will create - inline disks which improves the build performance. There will not be any copying of source vhds to temp directory. This defauls to false. + inline disks which improves the build performance. There will not be any copying of source vhds to temp directory. This defaults to false. - `enable_dynamic_memory` (boolean) - If true enable dynamic memory for virtual machine. This defaults to false. From dfcd5742ccfc308e3451bfaa2d6480a1aec6eac9 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:35:31 +0000 Subject: [PATCH 0599/1216] spelling: defined --- website/source/docs/builders/profitbricks.html.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/source/docs/builders/profitbricks.html.md b/website/source/docs/builders/profitbricks.html.md index bbb691bab..2b4bc4bc5 100644 --- a/website/source/docs/builders/profitbricks.html.md +++ b/website/source/docs/builders/profitbricks.html.md @@ -25,9 +25,9 @@ builder. - `image` (string) - ProfitBricks volume image. Only Linux public images are supported. To obtain full list of available images you can use [ProfitBricks CLI](https://github.com/profitbricks/profitbricks-cli#image). -- `password` (string) - ProfitBricks password. This can be specified via environment variable \`PROFITBRICKS\_PASSWORD', if provided. The value definded in the config has precedence over environemnt variable. +- `password` (string) - ProfitBricks password. This can be specified via environment variable \`PROFITBRICKS\_PASSWORD', if provided. The value defined in the config has precedence over environemnt variable. -- `username` (string) - ProfitBricks username. This can be specified via environment variable \`PROFITBRICKS\_USERNAME', if provided. The value definded in the config has precedence over environemnt variable. +- `username` (string) - ProfitBricks username. This can be specified via environment variable \`PROFITBRICKS\_USERNAME', if provided. The value defined in the config has precedence over environemnt variable. ### Optional From 939e7d5587fc28b4f0f2b7a9f50c97e21fb147e9 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:36:38 +0000 Subject: [PATCH 0600/1216] spelling: delete --- builder/alicloud/ecs/builder.go | 6 +++--- builder/alicloud/ecs/image_config.go | 4 ++-- builder/alicloud/ecs/step_delete_images_snapshots.go | 8 ++++---- post-processor/alicloud-import/post-processor.go | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/builder/alicloud/ecs/builder.go b/builder/alicloud/ecs/builder.go index 08a7dad08..7269bd3f0 100644 --- a/builder/alicloud/ecs/builder.go +++ b/builder/alicloud/ecs/builder.go @@ -89,7 +89,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe steps = []multistep.Step{ &stepPreValidate{ AlicloudDestImageName: b.config.AlicloudImageName, - ForceDelete: b.config.AlicloudImageForceDetele, + ForceDelete: b.config.AlicloudImageForceDelete, }, &stepCheckAlicloudSourceImage{ SourceECSImageId: b.config.AlicloudSourceImage, @@ -165,8 +165,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe ForceStop: b.config.ForceStopInstance, }, &stepDeleteAlicloudImageSnapshots{ - AlicloudImageForceDeteleSnapshots: b.config.AlicloudImageForceDeteleSnapshots, - AlicloudImageForceDetele: b.config.AlicloudImageForceDetele, + AlicloudImageForceDeleteSnapshots: b.config.AlicloudImageForceDeleteSnapshots, + AlicloudImageForceDelete: b.config.AlicloudImageForceDelete, AlicloudImageName: b.config.AlicloudImageName, }, &stepCreateAlicloudImage{}, diff --git a/builder/alicloud/ecs/image_config.go b/builder/alicloud/ecs/image_config.go index b987558f9..d747009ef 100644 --- a/builder/alicloud/ecs/image_config.go +++ b/builder/alicloud/ecs/image_config.go @@ -32,8 +32,8 @@ type AlicloudImageConfig struct { AlicloudImageUNShareAccounts []string `mapstructure:"image_unshare_account"` AlicloudImageDestinationRegions []string `mapstructure:"image_copy_regions"` AlicloudImageDestinationNames []string `mapstructure:"image_copy_names"` - AlicloudImageForceDetele bool `mapstructure:"image_force_delete"` - AlicloudImageForceDeteleSnapshots bool `mapstructure:"image_force_delete_snapshots"` + AlicloudImageForceDelete bool `mapstructure:"image_force_delete"` + AlicloudImageForceDeleteSnapshots bool `mapstructure:"image_force_delete_snapshots"` AlicloudImageForceDeleteInstances bool `mapstructure:"image_force_delete_instances"` AlicloudImageSkipRegionValidation bool `mapstructure:"skip_region_validation"` AlicloudDiskDevices `mapstructure:",squash"` diff --git a/builder/alicloud/ecs/step_delete_images_snapshots.go b/builder/alicloud/ecs/step_delete_images_snapshots.go index d3f6139f0..9e294c45f 100644 --- a/builder/alicloud/ecs/step_delete_images_snapshots.go +++ b/builder/alicloud/ecs/step_delete_images_snapshots.go @@ -12,8 +12,8 @@ import ( ) type stepDeleteAlicloudImageSnapshots struct { - AlicloudImageForceDetele bool - AlicloudImageForceDeteleSnapshots bool + AlicloudImageForceDelete bool + AlicloudImageForceDeleteSnapshots bool AlicloudImageName string } @@ -23,7 +23,7 @@ func (s *stepDeleteAlicloudImageSnapshots) Run(_ context.Context, state multiste config := state.Get("config").(Config) ui.Say("Deleting image snapshots.") // Check for force delete - if s.AlicloudImageForceDetele { + if s.AlicloudImageForceDelete { images, _, err := client.DescribeImages(&ecs.DescribeImagesArgs{ RegionId: common.Region(config.AlicloudRegion), ImageName: s.AlicloudImageName, @@ -43,7 +43,7 @@ func (s *stepDeleteAlicloudImageSnapshots) Run(_ context.Context, state multiste ui.Error(err.Error()) return multistep.ActionHalt } - if s.AlicloudImageForceDeteleSnapshots { + if s.AlicloudImageForceDeleteSnapshots { for _, diskDevice := range image.DiskDeviceMappings.DiskDeviceMapping { if err := client.DeleteSnapshot(diskDevice.SnapshotId); err != nil { err := fmt.Errorf("Deleting ECS snapshot failed: %s", err) diff --git a/post-processor/alicloud-import/post-processor.go b/post-processor/alicloud-import/post-processor.go index 4421b170d..853e97ef1 100644 --- a/post-processor/alicloud-import/post-processor.go +++ b/post-processor/alicloud-import/post-processor.go @@ -60,7 +60,7 @@ type Config struct { Architecture string `mapstructure:"image_architecture"` Size string `mapstructure:"image_system_size"` Format string `mapstructure:"format"` - AlicloudImageForceDetele bool `mapstructure:"image_force_delete"` + AlicloudImageForceDelete bool `mapstructure:"image_force_delete"` ctx interpolate.Context } @@ -160,7 +160,7 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac getEndPonit(p.config.OSSBucket), p.config.OSSKey, err) } - if len(images) > 0 && !p.config.AlicloudImageForceDetele { + if len(images) > 0 && !p.config.AlicloudImageForceDelete { return nil, false, fmt.Errorf("Duplicated image exists, please delete the existing images " + "or set the 'image_force_delete' value as true") } @@ -185,7 +185,7 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac if err != nil { return nil, false, fmt.Errorf("Failed to upload image %s: %s", source, err) } - if len(images) > 0 && p.config.AlicloudImageForceDetele { + if len(images) > 0 && p.config.AlicloudImageForceDelete { if err = ecsClient.DeleteImage(packercommon.Region(p.config.AlicloudRegion), images[0].ImageId); err != nil { return nil, false, fmt.Errorf("Delete duplicated image %s failed", images[0].ImageName) From e4c56e3cbd0568bd4bb6824fad3208d30c4bdf5a Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:35:45 +0000 Subject: [PATCH 0601/1216] spelling: delimiters --- website/source/docs/builders/hyperv-iso.html.md | 2 +- website/source/docs/builders/hyperv-vmcx.html.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/website/source/docs/builders/hyperv-iso.html.md b/website/source/docs/builders/hyperv-iso.html.md index 094e46bcb..8e481bcad 100644 --- a/website/source/docs/builders/hyperv-iso.html.md +++ b/website/source/docs/builders/hyperv-iso.html.md @@ -221,7 +221,7 @@ can be configured for this builder. - `mac_address` (string) - This allows a specific MAC address to be used on the default virtual network card. The MAC address must be a string with no - delimeters, for example "0000deadbeef". + delimiters, for example "0000deadbeef". - `vm_name` (string) - This is the name of the virtual machine for the new virtual machine, without the file extension. By default this is "packer-BUILDNAME", diff --git a/website/source/docs/builders/hyperv-vmcx.html.md b/website/source/docs/builders/hyperv-vmcx.html.md index a7afe93ce..f43372c65 100644 --- a/website/source/docs/builders/hyperv-vmcx.html.md +++ b/website/source/docs/builders/hyperv-vmcx.html.md @@ -227,7 +227,7 @@ can be configured for this builder. - `mac_address` (string) - This allows a specific MAC address to be used on the default virtual network card. The MAC address must be a string with no - delimeters, for example "0000deadbeef". + delimiters, for example "0000deadbeef". - `vm_name` (string) - This is the name of the virtual machine for the new virtual machine, without the file extension. By default this is From 9d26e6dd8670ebede82129ee3f59e55213de06a4 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:36:15 +0000 Subject: [PATCH 0602/1216] spelling: descriptive --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7bc2926d..93421f139 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1598,7 +1598,7 @@ manager certs. [GH-1137] * builder/amazon/all: `delete_on_termination` set to false will work. * builder/amazon/all: Fix race condition on setting tags. [GH-1367] -* builder/amazon/all: More desctriptive error messages if Amazon only +* builder/amazon/all: More descriptive error messages if Amazon only sends an error code. [GH-1189] * builder/docker: Error if `DOCKER_HOST` is set. * builder/docker: Remove the container during cleanup. [GH-1206] From 60ef3c3374487ff5f0ec5a293505d327bbb73d12 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:37:32 +0000 Subject: [PATCH 0603/1216] spelling: directories --- builder/hyperv/common/step_create_tempdir.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/hyperv/common/step_create_tempdir.go b/builder/hyperv/common/step_create_tempdir.go index 1a9767fe5..b445a4702 100644 --- a/builder/hyperv/common/step_create_tempdir.go +++ b/builder/hyperv/common/step_create_tempdir.go @@ -11,7 +11,7 @@ import ( ) type StepCreateTempDir struct { - // The user-supplied root directores into which we create subdirectories. + // The user-supplied root directories into which we create subdirectories. TempPath string VhdTempPath string // The subdirectories with the randomly generated name. From 8dfafa6a5eaa14bd0ef2b0fea66f65dc716f3d55 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:38:01 +0000 Subject: [PATCH 0604/1216] spelling: dispatchable --- packer/build_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packer/build_test.go b/packer/build_test.go index 8fe7bc5af..dd78bf4e7 100644 --- a/packer/build_test.go +++ b/packer/build_test.go @@ -191,7 +191,7 @@ func TestBuild_Run(t *testing.T) { t.Fatal("should be called") } - // Verify hooks are disapatchable + // Verify hooks are dispatchable dispatchHook := builder.RunHook dispatchHook.Run("foo", nil, nil, 42) From 76a7258bb46def460bef316d874ea6311f8d99b4 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:39:36 +0000 Subject: [PATCH 0605/1216] spelling: documentation --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93421f139..c99b822be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2425,7 +2425,7 @@ * Amazon EBS builder can now optionally use a pre-made security group instead of randomly generating one. * DigitalOcean API key and client IDs can now be passed in as - environmental variables. See the documentatin for more details. + environmental variables. See the documentation for more details. * VirtualBox and VMware can now have `floppy_files` specified to attach floppy disks when booting. This allows for unattended Windows installs. * `packer build` has a new `-force` flag that forces the removal of From 83471c8399aa6a9ec2b8e862e1f4129b416dec2f Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:39:58 +0000 Subject: [PATCH 0606/1216] spelling: doesn't --- builder/lxd/step_lxd_launch.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/lxd/step_lxd_launch.go b/builder/lxd/step_lxd_launch.go index de34a5cb4..7549a408d 100644 --- a/builder/lxd/step_lxd_launch.go +++ b/builder/lxd/step_lxd_launch.go @@ -31,7 +31,7 @@ func (s *stepLxdLaunch) Run(_ context.Context, state multistep.StateBag) multist return multistep.ActionHalt } // TODO: Should we check `lxc info ` for "Running"? - // We have to do this so /tmp doens't get cleared and lose our provisioner scripts. + // We have to do this so /tmp doesn't get cleared and lose our provisioner scripts. time.Sleep(1 * time.Second) return multistep.ActionContinue From e2f279dbaa047bacaccc992b396526357a1925d1 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:40:16 +0000 Subject: [PATCH 0607/1216] spelling: download --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c99b822be..b584f16a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2274,11 +2274,11 @@ * builder/amazon-instance: send IAM instance profile data. [GH-294] * builder/digitalocean: API request parameters are properly URL encoded. [GH-281] -* builder/virtualbox: dowload progress won't be shown until download +* builder/virtualbox: download progress won't be shown until download actually starts. [GH-288] * builder/virtualbox: floppy files names of 13 characters are now properly written to the FAT12 filesystem. [GH-285] -* builder/vmware: dowload progress won't be shown until download +* builder/vmware: download progress won't be shown until download actually starts. [GH-288] * builder/vmware: interrupt works while typing commands over VNC. * builder/virtualbox: floppy files names of 13 characters are now properly From 785b42368d234eed985b87834eadd79de477ab97 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:40:52 +0000 Subject: [PATCH 0608/1216] spelling: easily --- packer/ui_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packer/ui_test.go b/packer/ui_test.go index 76d0cd5b5..ef14985ec 100644 --- a/packer/ui_test.go +++ b/packer/ui_test.go @@ -215,7 +215,7 @@ func TestBasicUi_Ask(t *testing.T) { } for _, testCase := range testCases { - // Because of the internal bufio we can't eaily reset the input, so create a new one each time + // Because of the internal bufio we can't easily reset the input, so create a new one each time bufferUi := testUi() writeReader(bufferUi, testCase.Input) From 9b9ab4c492c417e835917a6c78b26871031e3684 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:41:47 +0000 Subject: [PATCH 0609/1216] spelling: encrypt --- builder/oracle/oci/client/config_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builder/oracle/oci/client/config_test.go b/builder/oracle/oci/client/config_test.go index 154fe1ec2..1be34c5f0 100644 --- a/builder/oracle/oci/client/config_test.go +++ b/builder/oracle/oci/client/config_test.go @@ -144,7 +144,7 @@ func TestParseEncryptedPrivateKeyValidPassword(t *testing.T) { password, cipherType) if err != nil { - t.Fatalf("Unexpected error encryting PEM block: %+v", err) + t.Fatalf("Unexpected error encrypting PEM block: %+v", err) } // Parse private key @@ -195,7 +195,7 @@ func TestParseEncryptedPrivateKeyPKCS8(t *testing.T) { password, cipherType) if err != nil { - t.Fatalf("Unexpected error encryting PEM block: %+v", err) + t.Fatalf("Unexpected error encrypting PEM block: %+v", err) } // Parse private key From 04d6bfc696c5fd2b9e474efe977064a535f983c6 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:41:21 +0000 Subject: [PATCH 0610/1216] spelling: environment --- CHANGELOG.md | 2 +- builder/azure/arm/config.go | 2 +- examples/alicloud/jenkins/jenkins.sh | 2 +- provisioner/powershell/provisioner.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b584f16a8..9707c60cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2228,7 +2228,7 @@ * core: All HTTP downloads across Packer now support the standard proxy environmental variables (`HTTP_PROXY`, `NO_PROXY`, etc.) [GH-252] * builder/amazon: API requests will use HTTP proxy if specified by - enviromental variables. + environmental variables. * builder/digitalocean: API requests will use HTTP proxy if specified by environmental variables. diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index 5584a15bf..65c276a68 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -410,7 +410,7 @@ func setCloudEnvironment(c *Config) error { name := strings.ToUpper(c.CloudEnvironmentName) envName, ok := lookup[name] if !ok { - return fmt.Errorf("There is no cloud envionment matching the name '%s'!", c.CloudEnvironmentName) + return fmt.Errorf("There is no cloud environment matching the name '%s'!", c.CloudEnvironmentName) } env, err := azure.EnvironmentFromName(envName) diff --git a/examples/alicloud/jenkins/jenkins.sh b/examples/alicloud/jenkins/jenkins.sh index af3eb51a3..72972c24e 100644 --- a/examples/alicloud/jenkins/jenkins.sh +++ b/examples/alicloud/jenkins/jenkins.sh @@ -32,7 +32,7 @@ mv $TOMCAT_NAME /opt wget $JENKINS_URL mv jenkins.war $TOMCAT_PATH/webapps/ -#set emvironment +#set environment echo "TOMCAT_PATH=\"$TOMCAT_PATH\"">>/etc/profile echo "JENKINS_HOME=\"$TOMCAT_PATH/webapps/jenkins\"">>/etc/profile echo PATH="\"\$PATH:\$TOMCAT_PATH:\$JENKINS_HOME\"">>/etc/profile diff --git a/provisioner/powershell/provisioner.go b/provisioner/powershell/provisioner.go index 07b10def9..9655f6fed 100644 --- a/provisioner/powershell/provisioner.go +++ b/provisioner/powershell/provisioner.go @@ -338,7 +338,7 @@ func (p *Provisioner) retryable(f func() error) error { } } -// Enviroment variables required within the remote environment are uploaded within a PS script and +// Environment variables required within the remote environment are uploaded within a PS script and // then enabled by 'dot sourcing' the script immediately prior to execution of the main command func (p *Provisioner) prepareEnvVars(elevated bool) (envVarPath string, err error) { // Collate all required env vars into a plain string with required formatting applied From fc99dc25ae710b81ff0f353eefc9ae2fea6ba26f Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:45:13 +0000 Subject: [PATCH 0611/1216] spelling: error --- communicator/ssh/communicator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/communicator/ssh/communicator.go b/communicator/ssh/communicator.go index b5b67633c..0e198cb0d 100644 --- a/communicator/ssh/communicator.go +++ b/communicator/ssh/communicator.go @@ -748,7 +748,7 @@ func (c *comm) scpSession(scpCommand string, f func(io.Writer, *bufio.Reader) er err = session.Wait() if err != nil { if exitErr, ok := err.(*ssh.ExitError); ok { - // Otherwise, we have an ExitErorr, meaning we can just read + // Otherwise, we have an ExitError, meaning we can just read // the exit status log.Printf("non-zero exit status: %d", exitErr.ExitStatus()) stdoutB, err := ioutil.ReadAll(stdoutR) From 33bf6de9216503ba9af890604a7609e628481b73 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:46:49 +0000 Subject: [PATCH 0612/1216] spelling: exceeds --- builder/cloudstack/step_create_instance.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/cloudstack/step_create_instance.go b/builder/cloudstack/step_create_instance.go index 19f6a3c28..343d36c35 100644 --- a/builder/cloudstack/step_create_instance.go +++ b/builder/cloudstack/step_create_instance.go @@ -226,7 +226,7 @@ func (s *stepCreateInstance) generateUserData(userData string, httpGETOnly bool) if len(ud) > maxUD { return "", fmt.Errorf( "The supplied user_data contains %d bytes after encoding, "+ - "this exeeds the limit of %d bytes", len(ud), maxUD) + "this exceeds the limit of %d bytes", len(ud), maxUD) } return ud, nil From 041a115f6598cbb5d7c0a72136ab85df729d33ef Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 08:15:56 +0000 Subject: [PATCH 0613/1216] spelling: existent --- CHANGELOG.md | 2 +- builder/parallels/iso/builder_test.go | 2 +- builder/parallels/pvm/config_test.go | 2 +- builder/virtualbox/ovf/config_test.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9707c60cb..15368d2c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -658,7 +658,7 @@ * builder/amazon: Crashes when new EBS vols are used. [GH-4308] * builder/amazon: Fix crash in amazon-instance. [GH-4372] * builder/amazon: fix run volume tagging [GH-4420] -* builder/amazon: fix when using non-existant security\_group\_id. [GH-4425] +* builder/amazon: fix when using non-existent security\_group\_id. [GH-4425] * builder/amazon: Properly error if we don't have the ec2:DescribeSecurityGroups permission. [GH-4304] * builder/amazon: Properly wait for security group to exist. [GH-4369] diff --git a/builder/parallels/iso/builder_test.go b/builder/parallels/iso/builder_test.go index b4b9eb917..88a28144e 100644 --- a/builder/parallels/iso/builder_test.go +++ b/builder/parallels/iso/builder_test.go @@ -86,7 +86,7 @@ func TestBuilderPrepare_FloppyFiles(t *testing.T) { func TestBuilderPrepare_InvalidFloppies(t *testing.T) { var b Builder config := testConfig() - config["floppy_files"] = []string{"nonexistant.bat", "nonexistant.ps1"} + config["floppy_files"] = []string{"nonexistent.bat", "nonexistent.ps1"} b = Builder{} _, errs := b.Prepare(config) if errs == nil { diff --git a/builder/parallels/pvm/config_test.go b/builder/parallels/pvm/config_test.go index c6c36a0b4..8c869c9fd 100644 --- a/builder/parallels/pvm/config_test.go +++ b/builder/parallels/pvm/config_test.go @@ -84,7 +84,7 @@ func TestNewConfig_FloppyFiles(t *testing.T) { func TestNewConfig_InvalidFloppies(t *testing.T) { c := testConfig(t) - c["floppy_files"] = []string{"nonexistant.bat", "nonexistant.ps1"} + c["floppy_files"] = []string{"nonexistent.bat", "nonexistent.ps1"} _, _, errs := NewConfig(c) if errs == nil { t.Fatalf("Nonexistent floppies should trigger multierror") diff --git a/builder/virtualbox/ovf/config_test.go b/builder/virtualbox/ovf/config_test.go index 51412a0c5..e36c44fcf 100644 --- a/builder/virtualbox/ovf/config_test.go +++ b/builder/virtualbox/ovf/config_test.go @@ -73,7 +73,7 @@ func TestNewConfig_sourcePath(t *testing.T) { t.Fatalf("bad: %#v", warns) } if err == nil { - t.Fatalf("Nonexistant file should throw a validation error!") + t.Fatalf("Nonexistent file should throw a validation error!") } // Bad From 466f0d2be415c1c6fe0f957adbb8877d2258a37c Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:49:46 +0000 Subject: [PATCH 0614/1216] spelling: firewall --- builder/cloudstack/step_configure_networking.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/cloudstack/step_configure_networking.go b/builder/cloudstack/step_configure_networking.go index ff37a540a..4dba63ef7 100644 --- a/builder/cloudstack/step_configure_networking.go +++ b/builder/cloudstack/step_configure_networking.go @@ -187,7 +187,7 @@ func (s *stepSetupNetworking) Cleanup(state multistep.StateBag) { // Create a new parameter struct. p := client.Firewall.NewDeleteFirewallRuleParams(fwRuleID) - ui.Message("Deleting firewal rule...") + ui.Message("Deleting firewall rule...") if _, err := client.Firewall.DeleteFirewallRule(p); err != nil { // This is a very poor way to be told the ID does no longer exist :( if !strings.Contains(err.Error(), fmt.Sprintf( From dc942a0d8ade51a81c64e543d558dacb2cb0b529 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:50:00 +0000 Subject: [PATCH 0615/1216] spelling: first --- builder/parallels/common/driver_9.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builder/parallels/common/driver_9.go b/builder/parallels/common/driver_9.go index cfdee4a5d..b8b697ba1 100644 --- a/builder/parallels/common/driver_9.go +++ b/builder/parallels/common/driver_9.go @@ -43,7 +43,7 @@ func (d *Parallels9Driver) Import(name, srcPath, dstDir string, reassignMAC bool srcMAC := "auto" if !reassignMAC { - srcMAC, err = getFirtsMACAddress(srcPath) + srcMAC, err = getFirstMACAddress(srcPath) if err != nil { return err } @@ -70,7 +70,7 @@ func getVMID(path string) (string, error) { return getConfigValueFromXpath(path, "/ParallelsVirtualMachine/Identification/VmUuid") } -func getFirtsMACAddress(path string) (string, error) { +func getFirstMACAddress(path string) (string, error) { return getConfigValueFromXpath(path, "/ParallelsVirtualMachine/Hardware/NetworkAdapter[@id='0']/MAC") } From b545c6f87e0ec1b617e2d7083d4a5cd586b846be Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:50:25 +0000 Subject: [PATCH 0616/1216] spelling: flattened --- provisioner/windows-shell/provisioner.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/provisioner/windows-shell/provisioner.go b/provisioner/windows-shell/provisioner.go index 585fa437c..babc9b5ec 100644 --- a/provisioner/windows-shell/provisioner.go +++ b/provisioner/windows-shell/provisioner.go @@ -203,11 +203,11 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { defer f.Close() // Create environment variables to set before executing the command - flattendVars := p.createFlattenedEnvVars() + flattenedVars := p.createFlattenedEnvVars() // Compile the command p.config.ctx.Data = &ExecuteCommandTemplate{ - Vars: flattendVars, + Vars: flattenedVars, Path: p.config.RemotePath, } command, err := interpolate.Render(p.config.ExecuteCommand, &p.config.ctx) From 24b4c36fc9a23b9ded5be57605e3a1dd23d2e481 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:52:02 +0000 Subject: [PATCH 0617/1216] spelling: function --- builder/ncloud/step_validate_template.go | 4 ++-- provisioner/powershell/provisioner_test.go | 4 ++-- provisioner/windows-shell/provisioner_test.go | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/builder/ncloud/step_validate_template.go b/builder/ncloud/step_validate_template.go index c63c5f04b..f00525e5c 100644 --- a/builder/ncloud/step_validate_template.go +++ b/builder/ncloud/step_validate_template.go @@ -25,7 +25,7 @@ type StepValidateTemplate struct { FeeSystemTypeCode string } -// NewStepValidateTemplate : funciton for Validation a tempalte +// NewStepValidateTemplate : function for Validation a tempalte func NewStepValidateTemplate(conn *ncloud.Conn, ui packer.Ui, config *Config) *StepValidateTemplate { var step = &StepValidateTemplate{ Conn: conn, @@ -249,7 +249,7 @@ func (s *StepValidateTemplate) validateTemplate() error { return s.validateServerProductCode() } -// Run : main funciton for validation a template +// Run : main function for validation a template func (s *StepValidateTemplate) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { s.Say("Validating deployment template ...") diff --git a/provisioner/powershell/provisioner_test.go b/provisioner/powershell/provisioner_test.go index 3c758ae4d..9c8237eb8 100644 --- a/provisioner/powershell/provisioner_test.go +++ b/provisioner/powershell/provisioner_test.go @@ -694,7 +694,7 @@ func TestRetryable(t *testing.T) { err := p.Prepare(config) err = p.retryable(retryMe) if err != nil { - t.Fatalf("should not have error retrying funuction") + t.Fatalf("should not have error retrying function") } count = 0 @@ -702,7 +702,7 @@ func TestRetryable(t *testing.T) { err = p.Prepare(config) err = p.retryable(retryMe) if err == nil { - t.Fatalf("should have error retrying funuction") + t.Fatalf("should have error retrying function") } } diff --git a/provisioner/windows-shell/provisioner_test.go b/provisioner/windows-shell/provisioner_test.go index 1f2ee2cab..e0731da10 100644 --- a/provisioner/windows-shell/provisioner_test.go +++ b/provisioner/windows-shell/provisioner_test.go @@ -434,7 +434,7 @@ func TestRetryable(t *testing.T) { err := p.Prepare(config) err = p.retryable(retryMe) if err != nil { - t.Fatalf("should not have error retrying funuction") + t.Fatalf("should not have error retrying function") } count = 0 @@ -442,7 +442,7 @@ func TestRetryable(t *testing.T) { err = p.Prepare(config) err = p.retryable(retryMe) if err == nil { - t.Fatalf("should have error retrying funuction") + t.Fatalf("should have error retrying function") } } From 210f8e8312a396ccf67e349127335ee0a4eda0d4 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 07:59:00 +0000 Subject: [PATCH 0618/1216] spelling: hyphen --- builder/azure/arm/artifact_test.go | 2 +- builder/azure/arm/config_test.go | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/builder/azure/arm/artifact_test.go b/builder/azure/arm/artifact_test.go index 4aff7cee6..a86a6353e 100644 --- a/builder/azure/arm/artifact_test.go +++ b/builder/azure/arm/artifact_test.go @@ -235,7 +235,7 @@ func TestAdditionalDiskArtifactProperties(t *testing.T) { } } -func TestArtifactOverHypenatedCaptureUri(t *testing.T) { +func TestArtifactOverHyphenatedCaptureUri(t *testing.T) { template := CaptureTemplate{ Resources: []CaptureResources{ { diff --git a/builder/azure/arm/config_test.go b/builder/azure/arm/config_test.go index c7758fb91..71f3c352d 100644 --- a/builder/azure/arm/config_test.go +++ b/builder/azure/arm/config_test.go @@ -499,7 +499,7 @@ func TestConfigShouldRejectMalformedCaptureNamePrefix(t *testing.T) { wellFormedCaptureNamePrefix := []string{ "packer", "AbcdefghijklmnopqrstuvwX", - "hypen-hypen", + "hyphen-hyphen", "0leading-number", "v1.core.local", } @@ -514,8 +514,8 @@ func TestConfigShouldRejectMalformedCaptureNamePrefix(t *testing.T) { } malformedCaptureNamePrefix := []string{ - "-leading-hypen", - "trailing-hypen-", + "-leading-hyphen", + "trailing-hyphen-", "trailing-period.", "_leading-underscore", "punc-!@#$%^&*()_+-=-punc", @@ -550,7 +550,7 @@ func TestConfigShouldRejectMalformedCaptureContainerName(t *testing.T) { wellFormedCaptureContainerName := []string{ "0leading", "aleading", - "hype-hypen", + "hype-hyphen", "abcdefghijklmnopqrstuvwxyz0123456789-abcdefghijklmnopqrstuvwxyz", // 63 characters } @@ -565,9 +565,9 @@ func TestConfigShouldRejectMalformedCaptureContainerName(t *testing.T) { malformedCaptureContainerName := []string{ "No-Capitals", - "double--hypens", - "-leading-hypen", - "trailing-hypen-", + "double--hyphens", + "-leading-hyphen", + "trailing-hyphen-", "punc-!@#$%^&*()_+-=-punc", "there-are-over-63-characters-in-this-string-and-that-is-a-bad-container-name", } From 57c0e9e4a7f361cd6b38cc6336c47300344d05d8 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 08:02:43 +0000 Subject: [PATCH 0619/1216] spelling: illegal --- builder/virtualbox/common/export_config_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/virtualbox/common/export_config_test.go b/builder/virtualbox/common/export_config_test.go index 612bfc01d..6848a9a66 100644 --- a/builder/virtualbox/common/export_config_test.go +++ b/builder/virtualbox/common/export_config_test.go @@ -10,7 +10,7 @@ func TestExportConfigPrepare_BootWait(t *testing.T) { // Bad c = new(ExportConfig) - c.Format = "illega" + c.Format = "illegal" errs = c.Prepare(testConfigTemplate(t)) if len(errs) == 0 { t.Fatalf("bad: %#v", errs) From 3a31baae4f012f0b241669d2dbbf239bb94ae1d5 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 08:00:05 +0000 Subject: [PATCH 0620/1216] spelling: incorrect --- builder/alicloud/ecs/step_config_vswitch.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/alicloud/ecs/step_config_vswitch.go b/builder/alicloud/ecs/step_config_vswitch.go index c1494e9d0..4bbb19773 100644 --- a/builder/alicloud/ecs/step_config_vswitch.go +++ b/builder/alicloud/ecs/step_config_vswitch.go @@ -137,7 +137,7 @@ func (s *stepConfigAlicloudVSwitch) Cleanup(state multistep.StateBag) { e, _ := err.(*common.Error) if (e.Code == "IncorrectVSwitchStatus" || e.Code == "DependencyViolation" || e.Code == "DependencyViolation.HaVip" || - e.Code == "IncorretRouteEntryStatus") && time.Now().Before(timeoutPoint) { + e.Code == "IncorrectRouteEntryStatus") && time.Now().Before(timeoutPoint) { time.Sleep(1 * time.Second) continue } From 1e99dce12bda11197a27a75aba4ab407d7c985b5 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 08:05:14 +0000 Subject: [PATCH 0621/1216] spelling: inertness --- builder/azure/arm/authenticate_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/azure/arm/authenticate_test.go b/builder/azure/arm/authenticate_test.go index 37d7b0e1b..12791138c 100644 --- a/builder/azure/arm/authenticate_test.go +++ b/builder/azure/arm/authenticate_test.go @@ -8,7 +8,7 @@ import ( // Behavior is the most important thing to assert for ServicePrincipalToken, but // that cannot be done in a unit test because it involves network access. Instead, -// I assert the expected intertness of this class. +// I assert the expected inertness of this class. func TestNewAuthenticate(t *testing.T) { testSubject := NewAuthenticate(azure.PublicCloud, "clientID", "clientString", "tenantID") spn, err := testSubject.getServicePrincipalToken() From 074b04522fd3882580dbf06e65a21d8806c56206 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 08:04:26 +0000 Subject: [PATCH 0622/1216] spelling: initializing --- website/source/docs/other/debugging.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/other/debugging.html.md b/website/source/docs/other/debugging.html.md index b34f1485b..f71e8b5e4 100644 --- a/website/source/docs/other/debugging.html.md +++ b/website/source/docs/other/debugging.html.md @@ -28,7 +28,7 @@ ephemeral key will be deleted at the end of the packer run during cleanup. For a local builder, the SSH session initiated will be visible in the detail provided when `PACKER_LOG=1` environment variable is set prior to a build, and you can connect to the local machine using the userid and password defined -in the kickstart or preseed associated with initialzing the local VM. +in the kickstart or preseed associated with initializing the local VM. ### Windows From 62f59662a34768b83e79116343bcff70b8f0b65d Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 08:04:40 +0000 Subject: [PATCH 0623/1216] spelling: installation --- builder/hyperv/common/step_polling_installation.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builder/hyperv/common/step_polling_installation.go b/builder/hyperv/common/step_polling_installation.go index b9d2984f6..133418ee7 100644 --- a/builder/hyperv/common/step_polling_installation.go +++ b/builder/hyperv/common/step_polling_installation.go @@ -15,10 +15,10 @@ import ( const port string = "13000" -type StepPollingInstalation struct { +type StepPollingInstallation struct { } -func (s *StepPollingInstalation) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { +func (s *StepPollingInstallation) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) errorMsg := "Error polling VM: %s" @@ -75,6 +75,6 @@ func (s *StepPollingInstalation) Run(_ context.Context, state multistep.StateBag return multistep.ActionContinue } -func (s *StepPollingInstalation) Cleanup(state multistep.StateBag) { +func (s *StepPollingInstallation) Cleanup(state multistep.StateBag) { } From 933ac20e68a9d4d72983889e6df4a45d0075fb79 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 08:04:50 +0000 Subject: [PATCH 0624/1216] spelling: instance --- builder/alicloud/ecs/builder.go | 2 +- builder/alicloud/ecs/step_create_instance.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/builder/alicloud/ecs/builder.go b/builder/alicloud/ecs/builder.go index 7269bd3f0..e0d7c0d7c 100644 --- a/builder/alicloud/ecs/builder.go +++ b/builder/alicloud/ecs/builder.go @@ -132,7 +132,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe RegionId: b.config.AlicloudRegion, InternetChargeType: b.config.InternetChargeType, InternetMaxBandwidthOut: b.config.InternetMaxBandwidthOut, - InstnaceName: b.config.InstanceName, + InstanceName: b.config.InstanceName, ZoneId: b.config.ZoneId, }) if b.chooseNetworkType() == VpcNet { diff --git a/builder/alicloud/ecs/step_create_instance.go b/builder/alicloud/ecs/step_create_instance.go index 5001fe4df..ff41a3df6 100644 --- a/builder/alicloud/ecs/step_create_instance.go +++ b/builder/alicloud/ecs/step_create_instance.go @@ -21,7 +21,7 @@ type stepCreateAlicloudInstance struct { RegionId string InternetChargeType string InternetMaxBandwidthOut int - InstnaceName string + InstanceName string ZoneId string instance *ecs.InstanceAttributesType } @@ -63,7 +63,7 @@ func (s *stepCreateAlicloudInstance) Run(_ context.Context, state multistep.Stat IoOptimized: ioOptimized, VSwitchId: vswitchId, SecurityGroupId: securityGroupId, - InstanceName: s.InstnaceName, + InstanceName: s.InstanceName, Password: password, ZoneId: s.ZoneId, DataDisk: diskDeviceToDiskType(config.AlicloudImageConfig.ECSImagesDiskMappings), @@ -89,7 +89,7 @@ func (s *stepCreateAlicloudInstance) Run(_ context.Context, state multistep.Stat InternetMaxBandwidthOut: s.InternetMaxBandwidthOut, IoOptimized: ioOptimized, SecurityGroupId: securityGroupId, - InstanceName: s.InstnaceName, + InstanceName: s.InstanceName, Password: password, ZoneId: s.ZoneId, DataDisk: diskDeviceToDiskType(config.AlicloudImageConfig.ECSImagesDiskMappings), From eebe2365872d4553321f045ee320d7ee5e1ea857 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 08:07:31 +0000 Subject: [PATCH 0625/1216] spelling: keyboard --- communicator/ssh/password_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/communicator/ssh/password_test.go b/communicator/ssh/password_test.go index e513716d0..47a4c7782 100644 --- a/communicator/ssh/password_test.go +++ b/communicator/ssh/password_test.go @@ -15,7 +15,7 @@ func TestPasswordKeyboardInteractive_Impl(t *testing.T) { } } -func TestPasswordKeybardInteractive_Challenge(t *testing.T) { +func TestPasswordKeyboardInteractive_Challenge(t *testing.T) { p := PasswordKeyboardInteractive("foo") result, err := p("foo", "bar", []string{"one", "two"}, nil) if err != nil { From ae4abedfa290875eaacf686cf39c2999415ecb16 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 08:10:59 +0000 Subject: [PATCH 0626/1216] spelling: mandatory --- builder/ncloud/config_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/ncloud/config_test.go b/builder/ncloud/config_test.go index 7e2591594..fb7886dbc 100644 --- a/builder/ncloud/config_test.go +++ b/builder/ncloud/config_test.go @@ -113,7 +113,7 @@ func TestEmptyConfig(t *testing.T) { _, _, err := NewConfig(raw) if err == nil { - t.Error("Expected Config to require 'access_key', 'secret_key' and some mendatory fields, but it did not") + t.Error("Expected Config to require 'access_key', 'secret_key' and some mandatory fields, but it did not") } if !strings.Contains(err.Error(), "access_key is required") { From 5c0d0322f0a9972c999e64b371e5f1a34e044b06 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 08:09:00 +0000 Subject: [PATCH 0627/1216] spelling: manifest --- website/source/docs/post-processors/manifest.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/post-processors/manifest.html.md b/website/source/docs/post-processors/manifest.html.md index 8ad53f451..4d5aaa398 100644 --- a/website/source/docs/post-processors/manifest.html.md +++ b/website/source/docs/post-processors/manifest.html.md @@ -67,7 +67,7 @@ An example manifest file looks like: If the build is run again, the new build artifacts will be added to the manifest file rather than replacing it. It is possible to grab specific build artifacts from the manifest by using `packer_run_uuid`. -The above mainfest was generated with this packer.json: +The above manifest was generated with this packer.json: ```json { From 964d5dd55e099a0192f4d9c1804f549398221633 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 08:09:41 +0000 Subject: [PATCH 0628/1216] spelling: mapstructure --- builder/virtualbox/common/export_config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/virtualbox/common/export_config.go b/builder/virtualbox/common/export_config.go index 08dfb267c..b55941943 100644 --- a/builder/virtualbox/common/export_config.go +++ b/builder/virtualbox/common/export_config.go @@ -7,7 +7,7 @@ import ( ) type ExportConfig struct { - Format string `mapstruture:"format"` + Format string `mapstructure:"format"` } func (c *ExportConfig) Prepare(ctx *interpolate.Context) []error { From 73d3d65b84ea426a61d8e9b6cbf686a4527abf6a Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 08:10:01 +0000 Subject: [PATCH 0629/1216] spelling: marshal --- builder/azure/pkcs12/pkcs12.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/azure/pkcs12/pkcs12.go b/builder/azure/pkcs12/pkcs12.go index 8279cb572..806246daa 100644 --- a/builder/azure/pkcs12/pkcs12.go +++ b/builder/azure/pkcs12/pkcs12.go @@ -398,7 +398,7 @@ func Encode(derBytes []byte, privateKey interface{}, password string) (pfxBytes return nil, err } - // Marhsal []contentInfo so we can re-constitute the byte stream that will + // Marshal []contentInfo so we can re-constitute the byte stream that will // be suitable for computing the MAC bytes, err := asn1.Marshal(contentInfos) if err != nil { From cab8b6ed68ac08f4e506e2816ebf1bdac7530a6b Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 08:10:20 +0000 Subject: [PATCH 0630/1216] spelling: maximum --- builder/hyperv/iso/builder.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/hyperv/iso/builder.go b/builder/hyperv/iso/builder.go index b92e779df..b912ab316 100644 --- a/builder/hyperv/iso/builder.go +++ b/builder/hyperv/iso/builder.go @@ -172,7 +172,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } if len(b.config.AdditionalDiskSize) > 64 { - err = errors.New("VM's currently support a maximun of 64 additional SCSI attached disks.") + err = errors.New("VM's currently support a maximum of 64 additional SCSI attached disks.") errs = packer.MultiErrorAppend(errs, err) } From abb2faf677d472fe66422c8621914c366f36eae4 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 08:10:53 +0000 Subject: [PATCH 0631/1216] spelling: membership --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15368d2c3..363e39dce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -411,7 +411,7 @@ * builder/cloudstack: Properly report back errors. [GH-5103] [GH-5123] * builder/docker: Fix windows filepath in docker-toolbox call [GH-4887] * builder/docker: Fix windows filepath in docker-toolbox call. [GH-4887] -* builder/hyperv: Use SID to verify membersip in Admin group, fixing for non- +* builder/hyperv: Use SID to verify membership in Admin group, fixing for non- english users. [GH-5022] * builder/hyperv: Verify membership in the group Hyper-V Administrators by SID not name. [GH-5022] From d987fcefc34bf7d38304828d4ff13067c20c3fbb Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 08:12:53 +0000 Subject: [PATCH 0632/1216] spelling: multiples --- builder/vmware/common/vmx.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builder/vmware/common/vmx.go b/builder/vmware/common/vmx.go index 1fcdfa35e..631e0106d 100644 --- a/builder/vmware/common/vmx.go +++ b/builder/vmware/common/vmx.go @@ -44,14 +44,14 @@ func EncodeVMX(contents map[string]string) string { } // a list of VMX key fragments that the value must not be quoted - // fragments are used to cover multliples (i.e. multiple disks) + // fragments are used to cover multiples (i.e. multiple disks) // keys are still lowercase at this point, use lower fragments noQuotes := []string{ ".virtualssd", } // a list of VMX key fragments that are case sensitive - // fragments are used to cover multliples (i.e. multiple disks) + // fragments are used to cover multiples (i.e. multiple disks) caseSensitive := []string{ ".virtualSSD", } From e43b8de3b1834a47ceebccca7dd8238d20a5085c Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 08:14:30 +0000 Subject: [PATCH 0633/1216] spelling: network --- builder/azure/arm/template_factory.go | 2 +- builder/azure/common/template/template_builder.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/builder/azure/arm/template_factory.go b/builder/azure/arm/template_factory.go index 843580fd2..62b25dc26 100644 --- a/builder/azure/arm/template_factory.go +++ b/builder/azure/arm/template_factory.go @@ -86,7 +86,7 @@ func GetVirtualMachineDeployment(config *Config) (*resources.Deployment, error) } if config.VirtualNetworkName != "" && DefaultPrivateVirtualNetworkWithPublicIp != config.PrivateVirtualNetworkWithPublicIp { - builder.SetPrivateVirtualNetworWithPublicIp( + builder.SetPrivateVirtualNetworkWithPublicIp( config.VirtualNetworkResourceGroupName, config.VirtualNetworkName, config.VirtualNetworkSubnetName) diff --git a/builder/azure/common/template/template_builder.go b/builder/azure/common/template/template_builder.go index a05b66ab3..93de89374 100644 --- a/builder/azure/common/template/template_builder.go +++ b/builder/azure/common/template/template_builder.go @@ -274,7 +274,7 @@ func (s *TemplateBuilder) SetVirtualNetwork(virtualNetworkResourceGroup, virtual return nil } -func (s *TemplateBuilder) SetPrivateVirtualNetworWithPublicIp(virtualNetworkResourceGroup, virtualNetworkName, subnetName string) error { +func (s *TemplateBuilder) SetPrivateVirtualNetworkWithPublicIp(virtualNetworkResourceGroup, virtualNetworkName, subnetName string) error { s.setVariable("virtualNetworkResourceGroup", virtualNetworkResourceGroup) s.setVariable("virtualNetworkName", virtualNetworkName) s.setVariable("subnetName", subnetName) From 64aae1c7816e54857a8c19ae7e5983b2f3d9c1c1 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 08:16:48 +0000 Subject: [PATCH 0634/1216] spelling: occurring --- helper/multistep/debug_runner.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helper/multistep/debug_runner.go b/helper/multistep/debug_runner.go index 88af01c54..a6760c5f1 100644 --- a/helper/multistep/debug_runner.go +++ b/helper/multistep/debug_runner.go @@ -7,7 +7,7 @@ import ( "sync" ) -// DebugLocation is the location where the pause is occuring when debugging +// DebugLocation is the location where the pause is occurring when debugging // a step sequence. "DebugLocationAfterRun" is after the run of the named // step. "DebugLocationBeforeCleanup" is before the cleanup of the named // step. From 35353faa625303a57c7562ed8ec9749953a43249 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 08:17:22 +0000 Subject: [PATCH 0635/1216] spelling: omitted --- website/source/docs/builders/openstack.html.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/source/docs/builders/openstack.html.md b/website/source/docs/builders/openstack.html.md index 709d39246..0809b6545 100755 --- a/website/source/docs/builders/openstack.html.md +++ b/website/source/docs/builders/openstack.html.md @@ -81,7 +81,7 @@ builder. cluster will be used. This may be required for some OpenStack clusters. - `cacert` (string) - Custom CA certificate file path. - If ommited the OS\_CACERT environment variable can be used. + If omitted the OS\_CACERT environment variable can be used. - `config_drive` (boolean) - Whether or not nova should use ConfigDrive for cloud-init metadata. @@ -113,7 +113,7 @@ builder. done over an insecure connection. By default this is false. - `key` (string) - Client private key file path for SSL client authentication. - If ommited the OS\_KEY environment variable can be used. + If omitted the OS\_KEY environment variable can be used. - `metadata` (object of key/value strings) - Glance metadata that will be applied to the image. From 61030c0d85b62fd1bda77ec394d18ab43918ca7c Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 08:27:26 +0000 Subject: [PATCH 0636/1216] spelling: output --- builder/vmware/common/output_dir_local_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/vmware/common/output_dir_local_test.go b/builder/vmware/common/output_dir_local_test.go index c3197117e..3026c6c66 100644 --- a/builder/vmware/common/output_dir_local_test.go +++ b/builder/vmware/common/output_dir_local_test.go @@ -4,6 +4,6 @@ import ( "testing" ) -func TestLocalOuputDir_impl(t *testing.T) { +func TestLocalOutputDir_impl(t *testing.T) { var _ OutputDir = new(LocalOutputDir) } From 0503e429c38ab5ef3dc58d59b5a17c436e0c3ca8 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 08:28:26 +0000 Subject: [PATCH 0637/1216] spelling: override --- fix/fixer_pp_vagrant_override.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fix/fixer_pp_vagrant_override.go b/fix/fixer_pp_vagrant_override.go index 36aadfa46..a0c530728 100644 --- a/fix/fixer_pp_vagrant_override.go +++ b/fix/fixer_pp_vagrant_override.go @@ -4,7 +4,7 @@ import ( "github.com/mitchellh/mapstructure" ) -// FixerVagrantPPOvveride is a Fixer that replaces the provider-specific +// FixerVagrantPPOverride is a Fixer that replaces the provider-specific // overrides for the Vagrant post-processor with the new style introduced // as part of Packer 0.5.0. type FixerVagrantPPOverride struct{} From 6a2ad49feb5b97d62e24aa2b3e193a6d5dbf3fc7 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 08:30:28 +0000 Subject: [PATCH 0638/1216] spelling: parameter --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 363e39dce..420424e40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1352,7 +1352,7 @@ * core: Fix potential panic for post-processor plugin exits. [GH-2098] * core: `PACKER_CONFIG` may point to a non-existent file. [GH-2226] * builder/amazon: Allow spaces in AMI names when using `clean_ami_name` [GH-2182] -* builder/amazon: Remove deprecated ec2-upload-bundle paramger. [GH-1931] +* builder/amazon: Remove deprecated ec2-upload-bundle parameter. [GH-1931] * builder/amazon: Use IAM Profile to upload bundle if provided. [GH-1985] * builder/amazon: Use correct exit code after SSH authentication failed. [GH-2004] * builder/amazon: Retry finding created instance for eventual From 18f51e7338373bfed6176fc7d96891b17954526b Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 08:29:17 +0000 Subject: [PATCH 0639/1216] spelling: parameters --- builder/oracle/oci/client/image.go | 2 +- builder/oracle/oci/client/instance.go | 2 +- builder/oracle/oci/client/vnic.go | 2 +- builder/oracle/oci/client/vnic_attachment.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/builder/oracle/oci/client/image.go b/builder/oracle/oci/client/image.go index 802b4650c..67ada02d5 100644 --- a/builder/oracle/oci/client/image.go +++ b/builder/oracle/oci/client/image.go @@ -56,7 +56,7 @@ type Image struct { TimeCreated time.Time `json:"timeCreated"` } -// GetImageParams are the paramaters available when communicating with the +// GetImageParams are the parameters available when communicating with the // GetImage API endpoint. type GetImageParams struct { ID string `url:"imageId"` diff --git a/builder/oracle/oci/client/instance.go b/builder/oracle/oci/client/instance.go index 30cdbd50b..e81ad3a44 100644 --- a/builder/oracle/oci/client/instance.go +++ b/builder/oracle/oci/client/instance.go @@ -60,7 +60,7 @@ type Instance struct { TimeCreated time.Time `json:"timeCreated"` } -// GetInstanceParams are the paramaters available when communicating with the +// GetInstanceParams are the parameters available when communicating with the // GetInstance API endpoint. type GetInstanceParams struct { ID string `url:"instanceId,omitempty"` diff --git a/builder/oracle/oci/client/vnic.go b/builder/oracle/oci/client/vnic.go index 31b37ec3e..9922d34aa 100644 --- a/builder/oracle/oci/client/vnic.go +++ b/builder/oracle/oci/client/vnic.go @@ -29,7 +29,7 @@ type VNIC struct { TimeCreated time.Time `json:"timeCreated"` } -// GetVNICParams are the paramaters available when communicating with the +// GetVNICParams are the parameters available when communicating with the // ListVNICs API endpoint. type GetVNICParams struct { ID string `url:"vnicId"` diff --git a/builder/oracle/oci/client/vnic_attachment.go b/builder/oracle/oci/client/vnic_attachment.go index 3458f7da1..1e42a0573 100644 --- a/builder/oracle/oci/client/vnic_attachment.go +++ b/builder/oracle/oci/client/vnic_attachment.go @@ -31,7 +31,7 @@ type VNICAttachment struct { VNICID string `json:"vnicId"` } -// ListVnicAttachmentsParams are the paramaters available when communicating +// ListVnicAttachmentsParams are the parameters available when communicating // with the ListVnicAttachments API endpoint. type ListVnicAttachmentsParams struct { AvailabilityDomain string `url:"availabilityDomain,omitempty"` From c14d6af5aaef2e2671e22b78980fb19e0e165062 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 13 Mar 2018 09:05:56 +0000 Subject: [PATCH 0640/1216] spelling: persistent --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 420424e40..c43badcb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1435,7 +1435,7 @@ * builder/googlecompute: Support for ubuntu-os-cloud project * builder/googlecompute: Support for OAuth2 to avoid client secrets file -* builder/googlecompute: GCE image from persistant disk instead of tarball +* builder/googlecompute: GCE image from persistent disk instead of tarball * builder/qemu: Checksum type "none" can be used * provisioner/chef: Generate a node name if none available * provisioner/chef: Added ssl_verify_mode configuration From f5156a7efbd42fc100c22d7c8e7b9c38b0d93d4a Mon Sep 17 00:00:00 2001 From: Andre Pearce <15135665+andrepearce@users.noreply.github.com> Date: Tue, 13 Mar 2018 20:11:52 +1100 Subject: [PATCH 0641/1216] Nest the additional field of `ami_root_device` --- website/source/docs/builders/amazon-ebssurrogate.html.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/website/source/docs/builders/amazon-ebssurrogate.html.md b/website/source/docs/builders/amazon-ebssurrogate.html.md index d40ec04e1..f9ab61473 100644 --- a/website/source/docs/builders/amazon-ebssurrogate.html.md +++ b/website/source/docs/builders/amazon-ebssurrogate.html.md @@ -55,9 +55,9 @@ builder. the root device of the AMI. This looks like the mappings in `ami_block_device_mapping`, except with an additional field: -- `source_device_name` (string) - The device name of the block device on the - source instance to be used as the root device for the AMI. This must correspond - to a block device in `launch_block_device_mapping`. + - `source_device_name` (string) - The device name of the block device on the + source instance to be used as the root device for the AMI. This must correspond + to a block device in `launch_block_device_mapping`. ### Optional: From e31ffab30cdd6d5186793f21b68fc16814b37be4 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Tue, 13 Mar 2018 10:25:47 -0700 Subject: [PATCH 0642/1216] update middleman 0.3.30 --- website/Makefile | 2 +- website/packer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/website/Makefile b/website/Makefile index ccc3ae634..f8e465b43 100644 --- a/website/Makefile +++ b/website/Makefile @@ -1,4 +1,4 @@ -VERSION?="0.3.29" +VERSION?="0.3.30" build: @echo "==> Starting build in Docker..." diff --git a/website/packer.json b/website/packer.json index cbfb5c565..91f87c2e9 100644 --- a/website/packer.json +++ b/website/packer.json @@ -8,7 +8,7 @@ "builders": [ { "type": "docker", - "image": "hashicorp/middleman-hashicorp:0.3.29", + "image": "hashicorp/middleman-hashicorp:0.3.30", "discard": "true", "volumes": { "{{ pwd }}": "/website" From dfa0b0cdb1ea2b9c136e169c2588f4c68a64fda8 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Tue, 13 Mar 2018 10:42:08 -0700 Subject: [PATCH 0643/1216] update middleman 0.3.32 --- website/Gemfile | 2 +- website/Gemfile.lock | 6 +++--- website/Makefile | 2 +- website/packer.json | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/website/Gemfile b/website/Gemfile index d21d35e45..a7edfed49 100644 --- a/website/Gemfile +++ b/website/Gemfile @@ -1,3 +1,3 @@ source "https://rubygems.org" -gem "middleman-hashicorp", "0.3.30" +gem "middleman-hashicorp", "0.3.32" diff --git a/website/Gemfile.lock b/website/Gemfile.lock index e567bf2ed..913b76417 100644 --- a/website/Gemfile.lock +++ b/website/Gemfile.lock @@ -6,7 +6,7 @@ GEM minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) - autoprefixer-rails (8.1.0) + autoprefixer-rails (8.1.0.1) execjs bootstrap-sass (3.3.7) autoprefixer-rails (>= 5.2.1) @@ -78,7 +78,7 @@ GEM rack (>= 1.4.5, < 2.0) thor (>= 0.15.2, < 2.0) tilt (~> 1.4.1, < 2.0) - middleman-hashicorp (0.3.30) + middleman-hashicorp (0.3.32) bootstrap-sass (~> 3.3) builder (~> 3.2) middleman (~> 3.4) @@ -153,7 +153,7 @@ PLATFORMS ruby DEPENDENCIES - middleman-hashicorp (= 0.3.30) + middleman-hashicorp (= 0.3.32) BUNDLED WITH 1.16.1 diff --git a/website/Makefile b/website/Makefile index f8e465b43..82b2cedc1 100644 --- a/website/Makefile +++ b/website/Makefile @@ -1,4 +1,4 @@ -VERSION?="0.3.30" +VERSION?="0.3.32" build: @echo "==> Starting build in Docker..." diff --git a/website/packer.json b/website/packer.json index 91f87c2e9..aeaa162e0 100644 --- a/website/packer.json +++ b/website/packer.json @@ -8,7 +8,7 @@ "builders": [ { "type": "docker", - "image": "hashicorp/middleman-hashicorp:0.3.30", + "image": "hashicorp/middleman-hashicorp:0.3.32", "discard": "true", "volumes": { "{{ pwd }}": "/website" From 7eda44d28c5fe40819446520c22388b3763c2da4 Mon Sep 17 00:00:00 2001 From: Evan Brown Date: Tue, 13 Mar 2018 12:36:14 -0700 Subject: [PATCH 0644/1216] builder/googlecompute: disambiguate disable_default_service_account This change requires 'disable_default_service_account=false' in order to set 'service_account_email'. This is a guard against an incorrect assumption that disabling the default service account would mean that no service account would be used. --- builder/googlecompute/config.go | 5 +++ builder/googlecompute/config_test.go | 49 ++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/builder/googlecompute/config.go b/builder/googlecompute/config.go index 537fc7422..11e8ac47f 100644 --- a/builder/googlecompute/config.go +++ b/builder/googlecompute/config.go @@ -225,6 +225,11 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { errs = packer.MultiErrorAppend(fmt.Errorf("'on_host_maintenance' must be set to 'TERMINATE' when 'accelerator_count' is more than 0")) } + // If DisableDefaultServiceAccount is provided, don't allow a value for ServiceAccountEmail + if c.DisableDefaultServiceAccount && c.ServiceAccountEmail != "" { + errs = packer.MultiErrorAppend(fmt.Errorf("you may not specify a 'service_account_email' when 'disable_default_service_account' is true")) + } + // Check for any errors. if errs != nil && len(errs.Errors) > 0 { return nil, nil, errs diff --git a/builder/googlecompute/config_test.go b/builder/googlecompute/config_test.go index 508b3c528..d3ed84506 100644 --- a/builder/googlecompute/config_test.go +++ b/builder/googlecompute/config_test.go @@ -276,6 +276,55 @@ func TestConfigPrepareAccelerator(t *testing.T) { } } +func TestConfigPrepareServiceAccount(t *testing.T) { + cases := []struct { + Keys []string + Values []interface{} + Err bool + }{ + { + []string{"disable_default_service_account", "service_account_email"}, + []interface{}{true, "service@account.email.com"}, + true, + }, + { + []string{"disable_default_service_account", "service_account_email"}, + []interface{}{false, "service@account.email.com"}, + false, + }, + { + []string{"disable_default_service_account", "service_account_email"}, + []interface{}{true, ""}, + false, + }, + } + + for _, tc := range cases { + raw := testConfig(t) + + errStr := "" + for k := range tc.Keys { + + // Create the string for error reporting + // convert value to string if it can be converted + errStr += fmt.Sprintf("%s:%v, ", tc.Keys[k], tc.Values[k]) + if tc.Values[k] == nil { + delete(raw, tc.Keys[k]) + } else { + raw[tc.Keys[k]] = tc.Values[k] + } + } + + _, warns, errs := NewConfig(raw) + + if tc.Err { + testConfigErr(t, warns, errs, strings.TrimRight(errStr, ", ")) + } else { + testConfigOk(t, warns, errs) + } + } +} + func TestConfigDefaults(t *testing.T) { cases := []struct { Read func(c *Config) interface{} From b33d6ce82eb443a0cd6d1c0aa2a5be0f5449f6a5 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Tue, 13 Mar 2018 16:11:11 -0700 Subject: [PATCH 0645/1216] fix salt provisioner on linux --- provisioner/salt-masterless/provisioner.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/provisioner/salt-masterless/provisioner.go b/provisioner/salt-masterless/provisioner.go index f1ae1fe84..76d2ff2d3 100644 --- a/provisioner/salt-masterless/provisioner.go +++ b/provisioner/salt-masterless/provisioner.go @@ -89,9 +89,9 @@ type guestOSTypeConfig struct { var guestOSTypeConfigs = map[string]guestOSTypeConfig{ provisioner.UnixOSType: { configDir: "/etc/salt", - tempDir: "/tmp/salt/", - stateRoot: "/srv/salt/", - pillarRoot: "/srv/pillar/", + 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", bootstrapRunCmd: "sh /tmp/install_salt.sh", }, @@ -129,7 +129,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return fmt.Errorf("Invalid guest_os_type: \"%s\"", p.config.GuestOSType) } - p.guestCommands, err = provisioner.NewGuestCommands(p.config.GuestOSType, !p.config.DisableSudo) + p.guestCommands, err = provisioner.NewGuestCommands(p.config.GuestOSType, false) if err != nil { return fmt.Errorf("Invalid guest_os_type: \"%s\"", p.config.GuestOSType) } @@ -413,7 +413,7 @@ func (p *Provisioner) uploadFile(ui packer.Ui, comm packer.Communicator, dst, sr func (p *Provisioner) moveFile(ui packer.Ui, comm packer.Communicator, dst string, src string) error { ui.Message(fmt.Sprintf("Moving %s to %s", src, dst)) cmd := &packer.RemoteCmd{ - Command: p.guestCommands.MovePath(src, dst), + Command: p.sudo(p.guestCommands.MovePath(src, dst)), } if err := cmd.StartWithUi(comm, ui); err != nil || cmd.ExitStatus != 0 { if err == nil { From 8c72bba160088b95acd25794968c5ad9d8d84c76 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 14 Mar 2018 02:14:18 +0000 Subject: [PATCH 0646/1216] spelling: possible --- builder/lxd/communicator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/lxd/communicator.go b/builder/lxd/communicator.go index 8ce455268..d016b0990 100644 --- a/builder/lxd/communicator.go +++ b/builder/lxd/communicator.go @@ -90,7 +90,7 @@ func (c *Communicator) UploadDir(dst string, src string, exclude []string) error // Don't use 'z' flag as compressing may take longer and the transfer is likely local. // If this isn't the case, it is possible for the user to compress in another step then transfer. - // It wouldn't be possibe to disable compression, without exposing this option. + // It wouldn't be possible to disable compression, without exposing this option. tar, err := c.CmdWrapper(fmt.Sprintf("tar -cf - -C %s .", src)) if err != nil { return err From 8e36178e5ba3ad113a9b49f5f0b79e9093e3fd93 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 14 Mar 2018 02:14:31 +0000 Subject: [PATCH 0647/1216] spelling: preemptible --- website/source/docs/builders/googlecompute.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/builders/googlecompute.html.md b/website/source/docs/builders/googlecompute.html.md index fb006c7ae..d5fd09bce 100644 --- a/website/source/docs/builders/googlecompute.html.md +++ b/website/source/docs/builders/googlecompute.html.md @@ -263,7 +263,7 @@ builder. If preemptible is true this can only be `TERMINATE`. If preemptible is false, it defaults to `MIGRATE` -- `preemptible` (boolean) - If true, launch a preembtible instance. +- `preemptible` (boolean) - If true, launch a preemptible instance. - `region` (string) - The region in which to launch the instance. Defaults to to the region hosting the specified `zone`. From 91ecc80ede958ed26e8c61510c36dfd0a725281f Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 14 Mar 2018 02:14:59 +0000 Subject: [PATCH 0648/1216] spelling: privileges --- website/source/docs/builders/oracle-classic.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/builders/oracle-classic.html.md b/website/source/docs/builders/oracle-classic.html.md index 1ea03dc46..30f5bfd5e 100644 --- a/website/source/docs/builders/oracle-classic.html.md +++ b/website/source/docs/builders/oracle-classic.html.md @@ -78,7 +78,7 @@ This builder currently only works with the SSH communicator. - `ssh_username` (string) - The username that Packer will use to SSH into the instance; defaults to `opc`, the default oracle user, which has sudo - priveliges. If you have already configured users on your machine, you may + privileges. If you have already configured users on your machine, you may prompt Packer to use one of those instead. For more detail, see the [documentation](https://docs.oracle.com/en/cloud/iaas/compute-iaas-cloud/stcsg/accessing-oracle-linux-instance-using-ssh.html). From fa36e1d9613c7fd2c025db03f77c9b5ab1dc70e6 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 14 Mar 2018 02:17:22 +0000 Subject: [PATCH 0649/1216] spelling: processing --- builder/vmware/iso/step_create_vmx.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/builder/vmware/iso/step_create_vmx.go b/builder/vmware/iso/step_create_vmx.go index ffeb724ec..46e5da43a 100644 --- a/builder/vmware/iso/step_create_vmx.go +++ b/builder/vmware/iso/step_create_vmx.go @@ -446,7 +446,7 @@ func (s *stepCreateVMX) Run(_ context.Context, state multistep.StateBag) multist templateData.SCSI_Present = "TRUE" templateData.CDROMType = "scsi" default: - err := fmt.Errorf("Error procesing VMX template: %s", cdromAdapterType) + err := fmt.Errorf("Error processing VMX template: %s", cdromAdapterType) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt @@ -505,7 +505,7 @@ func (s *stepCreateVMX) Run(_ context.Context, state multistep.StateBag) multist } else { serial, err := unformat_serial(config.Serial) if err != nil { - err := fmt.Errorf("Error procesing VMX template: %s", err) + err := fmt.Errorf("Error processing VMX template: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt @@ -541,7 +541,7 @@ func (s *stepCreateVMX) Run(_ context.Context, state multistep.StateBag) multist break default: - err := fmt.Errorf("Error procesing VMX template: %v", serial) + err := fmt.Errorf("Error processing VMX template: %v", serial) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt @@ -554,7 +554,7 @@ func (s *stepCreateVMX) Run(_ context.Context, state multistep.StateBag) multist } else { parallel, err := unformat_parallel(config.Parallel) if err != nil { - err := fmt.Errorf("Error procesing VMX template: %s", err) + err := fmt.Errorf("Error processing VMX template: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt @@ -578,7 +578,7 @@ func (s *stepCreateVMX) Run(_ context.Context, state multistep.StateBag) multist break default: - err := fmt.Errorf("Error procesing VMX template: %v", parallel) + err := fmt.Errorf("Error processing VMX template: %v", parallel) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt @@ -590,7 +590,7 @@ func (s *stepCreateVMX) Run(_ context.Context, state multistep.StateBag) multist /// render the .vmx template vmxContents, err := interpolate.Render(vmxTemplate, &ctx) if err != nil { - err := fmt.Errorf("Error procesing VMX template: %s", err) + err := fmt.Errorf("Error processing VMX template: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt From 06dcad2c33bce7e2c6292d110e493cff7b330d4f Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 14 Mar 2018 02:17:47 +0000 Subject: [PATCH 0650/1216] spelling: propagating --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c43badcb1..746909496 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2400,7 +2400,7 @@ ### BUG FIXES: * builder/amazon/all: Gracefully handle when AMI appears to not exist - while AWS state is propogating. [GH-207] + while AWS state is propagating. [GH-207] * builder/virtualbox: Trim carriage returns for Windows to properly detect VM state on Windows. [GH-218] * core: build names no longer cause invalid config errors. [GH-197] From 3c60f637384941854f2ffc89afd5c8e9c3157ebc Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 14 Mar 2018 02:17:37 +0000 Subject: [PATCH 0651/1216] spelling: property --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 746909496..44a7fc8e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2482,7 +2482,7 @@ * core: Non-200 response codes on downloads now show proper errors. [GH-141] * amazon-ebs: SSH handshake is retried. [GH-130] -* vagrant: The `BuildName` template propery works properly in +* vagrant: The `BuildName` template property works properly in the output path. * vagrant: Properly configure the provider-specific post-processors so things like `vagrantfile_template` work. [GH-129] From d3dc5ba6d3208f3a49901fcd9b2962c600f5fd27 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 14 Mar 2018 02:15:21 +0000 Subject: [PATCH 0652/1216] spelling: provisioner --- packer/provisioner.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packer/provisioner.go b/packer/provisioner.go index a754f7544..93411808d 100644 --- a/packer/provisioner.go +++ b/packer/provisioner.go @@ -82,7 +82,7 @@ func (h *ProvisionHook) Run(name string, ui Ui, comm Communicator, data interfac return nil } -// Cancels the privisioners that are still running. +// Cancels the provisioners that are still running. func (h *ProvisionHook) Cancel() { h.lock.Lock() defer h.lock.Unlock() From 8294c8bc66add7d8f589782411d1bdbdc00d42b6 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 14 Mar 2018 02:19:01 +0000 Subject: [PATCH 0653/1216] spelling: receive --- builder/oracle/oci/client/base_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/oracle/oci/client/base_client.go b/builder/oracle/oci/client/base_client.go index b2fd31d9b..01d66215a 100644 --- a/builder/oracle/oci/client/base_client.go +++ b/builder/oracle/oci/client/base_client.go @@ -184,7 +184,7 @@ func (c *baseClient) Request() (*http.Request, error) { return req, nil } -// Recieve creates a http request from the client and executes it returning the +// Receive creates a http request from the client and executes it returning the // response. func (c *baseClient) Receive(successV, failureV interface{}) (*http.Response, error) { req, err := c.Request() From 5178dd36e8cf8041f9d9e14951076802b4072207 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 14 Mar 2018 02:19:10 +0000 Subject: [PATCH 0654/1216] spelling: regular --- common/step_download.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/step_download.go b/common/step_download.go index ac62fa698..5f5bb7dfe 100644 --- a/common/step_download.go +++ b/common/step_download.go @@ -63,7 +63,7 @@ func (s *StepDownload) Run(_ context.Context, state multistep.StateBag) multiste ui.Say(fmt.Sprintf("Downloading or copying %s", s.Description)) // First try to use any already downloaded file - // If it fails, proceed to regualar download logic + // If it fails, proceed to regular download logic var downloadConfigs = make([]*DownloadConfig, len(s.Url)) var finalPath string From 50f097b03d1fde8c338a28c42ce6cc3f4446f91a Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 14 Mar 2018 02:19:20 +0000 Subject: [PATCH 0655/1216] spelling: repeatedly --- website/source/docs/builders/docker.html.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/source/docs/builders/docker.html.md b/website/source/docs/builders/docker.html.md index 23080e3ce..1105e8cf0 100644 --- a/website/source/docs/builders/docker.html.md +++ b/website/source/docs/builders/docker.html.md @@ -357,14 +357,14 @@ shown below: This builder allows you to build Docker images *without* Dockerfiles. -With this builder, you can repeatably create Docker images without the use of a +With this builder, you can repeatedly create Docker images without the use of a Dockerfile. You don't need to know the syntax or semantics of Dockerfiles. Instead, you can just provide shell scripts, Chef recipes, Puppet manifests, etc. to provision your Docker container just like you would a regular virtualized or dedicated machine. While Docker has many features, Packer views Docker simply as an container -runner. To that end, Packer is able to repeatably build these containers +runner. To that end, Packer is able to repeatedly build these containers using portable provisioning scripts. ## Overriding the host directory From aae5b50dabc7902a24802aa2bd4d6a79383c29f5 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 14 Mar 2018 02:19:26 +0000 Subject: [PATCH 0656/1216] spelling: response --- .../azure/arm/azure_error_response_test.go | 20 +++++++++---------- builder/oracle/oci/client/transport_test.go | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/builder/azure/arm/azure_error_response_test.go b/builder/azure/arm/azure_error_response_test.go index faa429ad1..2e4f7f1ac 100644 --- a/builder/azure/arm/azure_error_response_test.go +++ b/builder/azure/arm/azure_error_response_test.go @@ -12,16 +12,16 @@ const AzureErrorSimple = `{"error":{"code":"ResourceNotFound","message":"The Res const AzureErrorNested = `{"status":"Failed","error":{"code":"DeploymentFailed","message":"At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/arm-debug for usage details.","details":[{"code":"BadRequest","message":"{\r\n \"error\": {\r\n \"code\": \"InvalidRequestFormat\",\r\n \"message\": \"Cannot parse the request.\",\r\n \"details\": [\r\n {\r\n \"code\": \"InvalidJson\",\r\n \"message\": \"Error converting value \\\"playground\\\" to type 'Microsoft.WindowsAzure.Networking.Nrp.Frontend.Contract.Csm.Public.IpAllocationMethod'. Path 'properties.publicIPAllocationMethod', line 1, position 130.\"\r\n }\r\n ]\r\n }\r\n}"}]}}` func TestAzureErrorSimpleShouldUnmarshal(t *testing.T) { - var azureErrorReponse azureErrorResponse - err := json.Unmarshal([]byte(AzureErrorSimple), &azureErrorReponse) + var azureErrorResponse azureErrorResponse + err := json.Unmarshal([]byte(AzureErrorSimple), &azureErrorResponse) if err != nil { t.Fatal(err) } - if azureErrorReponse.ErrorDetails.Code != "ResourceNotFound" { + if azureErrorResponse.ErrorDetails.Code != "ResourceNotFound" { t.Errorf("Error.Code") } - if azureErrorReponse.ErrorDetails.Message != "The Resource 'Microsoft.Compute/images/PackerUbuntuImage' under resource group 'packer-test00' was not found." { + if azureErrorResponse.ErrorDetails.Message != "The Resource 'Microsoft.Compute/images/PackerUbuntuImage' under resource group 'packer-test00' was not found." { t.Errorf("Error.Message") } } @@ -51,26 +51,26 @@ func TestAzureErrorEmptyShouldFormat(t *testing.T) { } func TestAzureErrorSimpleShouldFormat(t *testing.T) { - var azureErrorReponse azureErrorResponse - err := json.Unmarshal([]byte(AzureErrorSimple), &azureErrorReponse) + var azureErrorResponse azureErrorResponse + err := json.Unmarshal([]byte(AzureErrorSimple), &azureErrorResponse) if err != nil { t.Fatal(err) } - err = approvaltests.VerifyString(t, azureErrorReponse.Error()) + err = approvaltests.VerifyString(t, azureErrorResponse.Error()) if err != nil { t.Fatal(err) } } func TestAzureErrorNestedShouldFormat(t *testing.T) { - var azureErrorReponse azureErrorResponse - err := json.Unmarshal([]byte(AzureErrorNested), &azureErrorReponse) + var azureErrorResponse azureErrorResponse + err := json.Unmarshal([]byte(AzureErrorNested), &azureErrorResponse) if err != nil { t.Fatal(err) } - err = approvaltests.VerifyString(t, azureErrorReponse.Error()) + err = approvaltests.VerifyString(t, azureErrorResponse.Error()) if err != nil { t.Fatal(err) } diff --git a/builder/oracle/oci/client/transport_test.go b/builder/oracle/oci/client/transport_test.go index 2299f3a0e..ebf02040b 100644 --- a/builder/oracle/oci/client/transport_test.go +++ b/builder/oracle/oci/client/transport_test.go @@ -148,7 +148,7 @@ func TestKnownGoodRequests(t *testing.T) { for header, val := range tt.expectedHeaders { if sentReq.Header.Get(header) != val { - t.Fatalf("%s: Header mismatch in responnse,\n\t expecting \"%s\"\n\t got \"%s\"", tt.name, val, sentReq.Header.Get(header)) + t.Fatalf("%s: Header mismatch in response,\n\t expecting \"%s\"\n\t got \"%s\"", tt.name, val, sentReq.Header.Get(header)) } } } From 007930ec2d3f4d50ba1d0c1e1af4276c5dd05bb4 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 14 Mar 2018 03:20:51 +0000 Subject: [PATCH 0657/1216] spelling: restricted --- builder/amazon/common/access_config_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/amazon/common/access_config_test.go b/builder/amazon/common/access_config_test.go index e1c44cb89..9c543816a 100644 --- a/builder/amazon/common/access_config_test.go +++ b/builder/amazon/common/access_config_test.go @@ -42,7 +42,7 @@ func TestAccessConfigPrepare_Region(t *testing.T) { } -func TestAccessConfigPrepare_RegionRestrictd(t *testing.T) { +func TestAccessConfigPrepare_RegionRestricted(t *testing.T) { c := testAccessConfig() // Create a Session with a custom region From 6c1654d421ec899f1a172e6a2e680a3793bd2c6f Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 14 Mar 2018 03:21:38 +0000 Subject: [PATCH 0658/1216] spelling: separators --- communicator/ssh/communicator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/communicator/ssh/communicator.go b/communicator/ssh/communicator.go index 0e198cb0d..d81ab750a 100644 --- a/communicator/ssh/communicator.go +++ b/communicator/ssh/communicator.go @@ -591,7 +591,7 @@ func (c *comm) scpUploadSession(path string, input io.Reader, fi *os.FileInfo) e target_file = filepath.Base((*fi).Name()) } - // On windows, filepath.Dir uses backslash seperators (ie. "\tmp"). + // On windows, filepath.Dir uses backslash separators (ie. "\tmp"). // This does not work when the target host is unix. Switch to forward slash // which works for unix and windows target_dir = filepath.ToSlash(target_dir) From 795116ef4ff95777cc0bc270032ad41ad8f6d507 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 14 Mar 2018 03:21:44 +0000 Subject: [PATCH 0659/1216] spelling: session --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44a7fc8e3..1343ea956 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2012,7 +2012,7 @@ * builder/digitalocean: scrub API keys from config debug output. [GH-516] * builder/virtualbox: error if VirtualBox version cant be detected. [GH-488] * builder/virtualbox: detect if vboxdrv isn't properly setup. [GH-488] -* builder/virtualbox: sleep a bit before export to ensure the sesssion +* builder/virtualbox: sleep a bit before export to ensure the session is unlocked. [GH-512] * builder/virtualbox: create SATA drives properly on VirtualBox 4.3. [GH-547] * builder/virtualbox: support user templates in SSH key path. [GH-539] From 5dd9be653aca7650216b413b214ac90ff9f4d895 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 14 Mar 2018 03:22:32 +0000 Subject: [PATCH 0660/1216] spelling: should --- packer/core.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packer/core.go b/packer/core.go index 7da3e4510..c8ac2cfb7 100644 --- a/packer/core.go +++ b/packer/core.go @@ -67,7 +67,7 @@ func NewCore(c *CoreConfig) (*Core, error) { return nil, err } - // Go through and interpolate all the build names. We shuld be able + // Go through and interpolate all the build names. We should be able // to do this at this point with the variables. result.builds = make(map[string]*template.Builder) for _, b := range c.Template.Builders { From c7c8ed04f56d1ec2574f56d9675715bc3bd923c8 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 14 Mar 2018 03:23:02 +0000 Subject: [PATCH 0661/1216] spelling: specify --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1343ea956..fe2f21796 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2169,7 +2169,7 @@ * builder/virtualbox,vmware: Support SHA512 as a checksum type. [GH-356] * builder/vmware: The root hard drive type can now be specified with "disk_type_id" for advanced users. [GH-328] -* provisioner/salt-masterless: Ability to specfy a minion config. [GH-264] +* provisioner/salt-masterless: Ability to specify a minion config. [GH-264] * provisioner/salt-masterless: Ability to upload pillars. [GH-353] ### IMPROVEMENTS: From c563ef0797347d2402dfd9a0292f1b183996ffdd Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 14 Mar 2018 03:23:56 +0000 Subject: [PATCH 0662/1216] spelling: structure --- builder/googlecompute/driver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/googlecompute/driver.go b/builder/googlecompute/driver.go index 4c07a06cf..11c4eadb9 100644 --- a/builder/googlecompute/driver.go +++ b/builder/googlecompute/driver.go @@ -82,7 +82,7 @@ type InstanceConfig struct { Zone string } -// WindowsPasswordConfig is the data structue that GCE needs to encrypt the created +// WindowsPasswordConfig is the data structure that GCE needs to encrypt the created // windows password. type WindowsPasswordConfig struct { key *rsa.PrivateKey From c49c40ac41e617099044fc4e085c7538a7e982a1 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 14 Mar 2018 03:24:39 +0000 Subject: [PATCH 0663/1216] spelling: targeted --- packer/ui_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packer/ui_test.go b/packer/ui_test.go index ef14985ec..a1bb0193c 100644 --- a/packer/ui_test.go +++ b/packer/ui_test.go @@ -99,34 +99,34 @@ func TestColoredUi_noColorEnv(t *testing.T) { func TestTargetedUI(t *testing.T) { bufferUi := testUi() - targettedUi := &TargetedUI{ + targetedUi := &TargetedUI{ Target: "foo", Ui: bufferUi, } var actual, expected string - targettedUi.Say("foo") + targetedUi.Say("foo") actual = readWriter(bufferUi) expected = "==> foo: foo\n" if actual != expected { t.Fatalf("bad: %#v", actual) } - targettedUi.Message("foo") + targetedUi.Message("foo") actual = readWriter(bufferUi) expected = " foo: foo\n" if actual != expected { t.Fatalf("bad: %#v", actual) } - targettedUi.Error("bar") + targetedUi.Error("bar") actual = readErrorWriter(bufferUi) expected = "==> foo: bar\n" if actual != expected { t.Fatalf("bad: %#v", actual) } - targettedUi.Say("foo\nbar") + targetedUi.Say("foo\nbar") actual = readWriter(bufferUi) expected = "==> foo: foo\n==> foo: bar\n" if actual != expected { From 5a393d4fac5703762bd426cc2cf537a5f56a7166 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 14 Mar 2018 03:24:51 +0000 Subject: [PATCH 0664/1216] spelling: template --- builder/ncloud/step_validate_template.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builder/ncloud/step_validate_template.go b/builder/ncloud/step_validate_template.go index f00525e5c..873cd4723 100644 --- a/builder/ncloud/step_validate_template.go +++ b/builder/ncloud/step_validate_template.go @@ -13,7 +13,7 @@ import ( "github.com/olekukonko/tablewriter" ) -//StepValidateTemplate : struct for Validation a tempalte +//StepValidateTemplate : struct for Validation a template type StepValidateTemplate struct { Conn *ncloud.Conn Validate func() error @@ -25,7 +25,7 @@ type StepValidateTemplate struct { FeeSystemTypeCode string } -// NewStepValidateTemplate : function for Validation a tempalte +// NewStepValidateTemplate : function for Validation a template func NewStepValidateTemplate(conn *ncloud.Conn, ui packer.Ui, config *Config) *StepValidateTemplate { var step = &StepValidateTemplate{ Conn: conn, From bf3bf79b8194f2efccaa3eeb785b48235908305e Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 14 Mar 2018 03:25:12 +0000 Subject: [PATCH 0665/1216] spelling: timeoutms --- website/source/docs/builders/hyperv-iso.html.md | 2 +- website/source/docs/builders/hyperv-vmcx.html.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/website/source/docs/builders/hyperv-iso.html.md b/website/source/docs/builders/hyperv-iso.html.md index 8e481bcad..3e919c925 100644 --- a/website/source/docs/builders/hyperv-iso.html.md +++ b/website/source/docs/builders/hyperv-iso.html.md @@ -656,7 +656,7 @@ Finish Setup cache proxy during installation --> cmd.exe /c winrm set winrm/config @{MaxTimeoutms="1800000"} - Win RM MaxTimoutms + Win RM MaxTimeoutms 5 true diff --git a/website/source/docs/builders/hyperv-vmcx.html.md b/website/source/docs/builders/hyperv-vmcx.html.md index f43372c65..5ff7b604d 100644 --- a/website/source/docs/builders/hyperv-vmcx.html.md +++ b/website/source/docs/builders/hyperv-vmcx.html.md @@ -651,7 +651,7 @@ Finish Setup cache proxy during installation --> cmd.exe /c winrm set winrm/config @{MaxTimeoutms="1800000"} - Win RM MaxTimoutms + Win RM MaxTimeoutms 5 true From 3b694feabc792db0e5b26cc45ce8dff249dc415d Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 14 Mar 2018 03:25:35 +0000 Subject: [PATCH 0666/1216] spelling: transfer --- builder/digitalocean/wait.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builder/digitalocean/wait.go b/builder/digitalocean/wait.go index 2e4bd280c..684955853 100644 --- a/builder/digitalocean/wait.go +++ b/builder/digitalocean/wait.go @@ -198,12 +198,12 @@ func waitForImageState( } }() - log.Printf("Waiting for up to %d seconds for image transter to become %s", timeout/time.Second, desiredState) + log.Printf("Waiting for up to %d seconds for image transfer to become %s", timeout/time.Second, desiredState) select { case err := <-result: return err case <-time.After(timeout): - err := fmt.Errorf("Timeout while waiting to for image transter to become '%s'", desiredState) + err := fmt.Errorf("Timeout while waiting to for image transfer to become '%s'", desiredState) return err } } From 33f90d7783d4fb726536c016a2a1a0545a2dc348 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 14 Mar 2018 03:27:22 +0000 Subject: [PATCH 0667/1216] spelling: unmarshalling --- builder/azure/pkcs12/pkcs12.go | 2 +- builder/azure/pkcs12/safebags.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/builder/azure/pkcs12/pkcs12.go b/builder/azure/pkcs12/pkcs12.go index 806246daa..c5ce90f0d 100644 --- a/builder/azure/pkcs12/pkcs12.go +++ b/builder/azure/pkcs12/pkcs12.go @@ -92,7 +92,7 @@ const ( ) // unmarshal calls asn1.Unmarshal, but also returns an error if there is any -// trailing data after unmarshaling. +// trailing data after unmarshalling. func unmarshal(in []byte, out interface{}) error { trailing, err := asn1.Unmarshal(in, out) if err != nil { diff --git a/builder/azure/pkcs12/safebags.go b/builder/azure/pkcs12/safebags.go index bbd1526b8..836236da3 100644 --- a/builder/azure/pkcs12/safebags.go +++ b/builder/azure/pkcs12/safebags.go @@ -83,7 +83,7 @@ func decodePkcs8ShroudedKeyBag(asn1Data, password []byte) (privateKey interface{ ret := new(asn1.RawValue) if err = unmarshal(pkData, ret); err != nil { - return nil, errors.New("pkcs12: error unmarshaling decrypted private key: " + err.Error()) + return nil, errors.New("pkcs12: error unmarshalling decrypted private key: " + err.Error()) } if privateKey, err = x509.ParsePKCS8PrivateKey(pkData); err != nil { From b81672c906da1d0c5d9aaf0247ad4435a46227ee Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 14 Mar 2018 03:28:00 +0000 Subject: [PATCH 0668/1216] spelling: valid --- builder/googlecompute/private_key.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/googlecompute/private_key.go b/builder/googlecompute/private_key.go index 6bf22a309..cf81cba06 100644 --- a/builder/googlecompute/private_key.go +++ b/builder/googlecompute/private_key.go @@ -19,7 +19,7 @@ func processPrivateKeyFile(privateKeyFile, passphrase string) ([]byte, error) { PEMBlock, _ := pem.Decode(rawPrivateKeyBytes) if PEMBlock == nil { return nil, fmt.Errorf( - "%s does not contain a vaild private key", privateKeyFile) + "%s does not contain a valid private key", privateKeyFile) } if x509.IsEncryptedPEMBlock(PEMBlock) { From ecc3d12340509065d04699d9b265b02248d4505d Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 14 Mar 2018 03:28:29 +0000 Subject: [PATCH 0669/1216] spelling: validate --- website/source/docs/templates/engine.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/templates/engine.html.md b/website/source/docs/templates/engine.html.md index 065e6001b..dd5c9e0fe 100644 --- a/website/source/docs/templates/engine.html.md +++ b/website/source/docs/templates/engine.html.md @@ -67,7 +67,7 @@ Here is a full list of the available functions for reference. This engine does not guarantee that the final image name will match the regex; it will not truncate your name if it exceeds 63 characters, and it - will not valiate that the beginning and end of the engine's output are + will not validate that the beginning and end of the engine's output are valid. For example, `"image_name": {{isotime | clean_image_name}}"` will cause your build to fail because the image name will start with a number, which is why in the From 0171dfc890e4bc7edd2434244aa309b514595ee5 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 14 Mar 2018 03:28:19 +0000 Subject: [PATCH 0670/1216] spelling: validating --- builder/amazon/common/run_config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/amazon/common/run_config.go b/builder/amazon/common/run_config.go index 289a461b9..f647182aa 100644 --- a/builder/amazon/common/run_config.go +++ b/builder/amazon/common/run_config.go @@ -78,7 +78,7 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { // Validation errs := c.Comm.Prepare(ctx) - // Valadating ssh_interface + // Validating ssh_interface if c.SSHInterface != "public_ip" && c.SSHInterface != "private_ip" && c.SSHInterface != "public_dns" && From aa52f68fda11ddba7f0f9906de3a29e7312873a5 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 14 Mar 2018 03:28:07 +0000 Subject: [PATCH 0671/1216] spelling: variable --- website/source/docs/builders/alicloud-ecs.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/builders/alicloud-ecs.html.md b/website/source/docs/builders/alicloud-ecs.html.md index 5c4330778..df7abbeff 100644 --- a/website/source/docs/builders/alicloud-ecs.html.md +++ b/website/source/docs/builders/alicloud-ecs.html.md @@ -159,7 +159,7 @@ builder. `_` or `-`. It cannot begin with `http://` or `https://`. - `security_token` (string) - STS access token, can be set through template or by exporting - as environment vairalbe such "export SecurityToken=value". + as environment variable such "export SecurityToken=value". - `skip_region_validation` (boolean) - The region validation can be skipped if this value is true, the default value is false. From 27dfe92f1cd5aebf889296a632abfc46e41bd56a Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 14 Mar 2018 03:29:14 +0000 Subject: [PATCH 0672/1216] spelling: virtualization --- builder/file/builder.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/file/builder.go b/builder/file/builder.go index f8b52e609..407bfcee5 100644 --- a/builder/file/builder.go +++ b/builder/file/builder.go @@ -2,7 +2,7 @@ package file /* The File builder creates an artifact from a file. Because it does not require -any virutalization or network resources, it's very fast and useful for testing. +any virtualization or network resources, it's very fast and useful for testing. */ import ( From c5482b3e7e77f045480099aa55af6907f42137b3 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 14 Mar 2018 03:29:50 +0000 Subject: [PATCH 0673/1216] spelling: warnings --- packer/build_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packer/build_test.go b/packer/build_test.go index dd78bf4e7..a903c9803 100644 --- a/packer/build_test.go +++ b/packer/build_test.go @@ -102,7 +102,7 @@ func TestBuild_Prepare_Twice(t *testing.T) { build.Prepare() } -func TestBuildPrepare_BuilderWarniings(t *testing.T) { +func TestBuildPrepare_BuilderWarnings(t *testing.T) { expected := []string{"foo"} build := testBuild() From 38ffc65c4ec7d9d5df6cbb3fda9560574105d907 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 14 Mar 2018 03:30:07 +0000 Subject: [PATCH 0674/1216] spelling: whether --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe2f21796..8d922192d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1569,7 +1569,7 @@ * builder/docker: Can now specify login credentials to pull images. * builder/docker: Support mounting additional volumes. [GH-1430] * builder/parallels/all: Path to tools ISO is calculated automatically. [GH-1455] -* builder/parallels-pvm: `reassign_mac` option to choose wehther or not +* builder/parallels-pvm: `reassign_mac` option to choose whether or not to generate a new MAC address. [GH-1461] * builder/qemu: Can specify "none" acceleration type. [GH-1395] * builder/qemu: Can specify "tcg" acceleration type. [GH-1395] From b32a8cab4b18cb9ed18d7bc563c669a5d860e91d Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Wed, 14 Mar 2018 09:05:52 -0700 Subject: [PATCH 0675/1216] add note that packer doesn't support windows containers --- website/source/docs/builders/docker.html.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/website/source/docs/builders/docker.html.md b/website/source/docs/builders/docker.html.md index 23080e3ce..beab7dd28 100644 --- a/website/source/docs/builders/docker.html.md +++ b/website/source/docs/builders/docker.html.md @@ -30,6 +30,10 @@ support running on a Docker remote host_. You can learn about what [platforms Docker supports and how to install onto them](https://docs.docker.com/engine/installation/) in the Docker documentation. + + Please note: Packer does not yet have support for Windows containers. + + ## Basic Example: Export Below is a fully functioning example. It doesn't do anything useful, since no From b0937ff94d90e856238cb89571fa50fa0071dabd Mon Sep 17 00:00:00 2001 From: Jeff Escalante Date: Thu, 8 Mar 2018 15:43:41 -0500 Subject: [PATCH 0676/1216] switch analytics from ga to segment --- website/Makefile | 2 +- website/config.rb | 10 ++++++++++ website/source/assets/javascripts/analytics.js | 9 +++++++++ .../source/assets/javascripts/application.js | 3 +++ website/source/layouts/layout.erb | 18 ++++++++++-------- 5 files changed, 33 insertions(+), 9 deletions(-) create mode 100644 website/source/assets/javascripts/analytics.js diff --git a/website/Makefile b/website/Makefile index 82b2cedc1..934ed941e 100644 --- a/website/Makefile +++ b/website/Makefile @@ -8,7 +8,7 @@ build: --tty \ --volume "$(shell pwd):/website" \ hashicorp/middleman-hashicorp:${VERSION} \ - bundle exec middleman build --verbose --clean + ENV=production bundle exec middleman build --verbose --clean website: @echo "==> Starting website in Docker..." diff --git a/website/config.rb b/website/config.rb index 63b2de323..38ef7eb6f 100644 --- a/website/config.rb +++ b/website/config.rb @@ -8,6 +8,16 @@ activate :hashicorp do |h| end helpers do + # Returns a segment tracking ID such that local development is not + # tracked to production systems. + def segmentId() + if (ENV['ENV'] == 'production') + 'AjXdfmTTk1I9q9dfyePuDFHBrz1tCO3l' + else + '0EXTgkNx0Ydje2PGXVbRhpKKoe5wtzcE' + end + end + # Returns the FQDN of the image URL. # # @param [String] path diff --git a/website/source/assets/javascripts/analytics.js b/website/source/assets/javascripts/analytics.js new file mode 100644 index 000000000..17ca06dd9 --- /dev/null +++ b/website/source/assets/javascripts/analytics.js @@ -0,0 +1,9 @@ +document.addEventListener('DOMContentLoaded', () => { + track('.downloads .download a', el => { + return { + event: 'Download', + category: 'Button', + label: `Packer | v${el.href.match(/\/(\d+\.\d+\.\d+)\//)[1]}` + } + }) +}) diff --git a/website/source/assets/javascripts/application.js b/website/source/assets/javascripts/application.js index 5d2365e14..ae70e571d 100644 --- a/website/source/assets/javascripts/application.js +++ b/website/source/assets/javascripts/application.js @@ -3,3 +3,6 @@ //= require hashicorp/mega-nav //= require hashicorp/sidebar +//= require hashicorp/analytics + +//= require analytics diff --git a/website/source/layouts/layout.erb b/website/source/layouts/layout.erb index ab0f53361..aa476c059 100644 --- a/website/source/layouts/layout.erb +++ b/website/source/layouts/layout.erb @@ -119,14 +119,16 @@ - + - From 5b7e058d0a8278e4a2c76d98034de76266dcd120 Mon Sep 17 00:00:00 2001 From: Anthony Dong Date: Mon, 2 Apr 2018 19:32:14 +0200 Subject: [PATCH 0751/1216] Add documentation for template variables available for tagging --- .../docs/builders/amazon-chroot.html.md | 27 +++++++------ .../source/docs/builders/amazon-ebs.html.md | 38 +++++++++++-------- .../docs/builders/amazon-ebssurrogate.html.md | 35 +++++++++-------- .../docs/builders/amazon-ebsvolume.html.md | 21 ++++++---- .../docs/builders/amazon-instance.html.md | 24 +++++++----- 5 files changed, 86 insertions(+), 59 deletions(-) diff --git a/website/source/docs/builders/amazon-chroot.html.md b/website/source/docs/builders/amazon-chroot.html.md index c9612d3b8..3738d4e7d 100644 --- a/website/source/docs/builders/amazon-chroot.html.md +++ b/website/source/docs/builders/amazon-chroot.html.md @@ -77,10 +77,8 @@ each category, the available configuration keys are alphabetized. - `ami_description` (string) - The description to set for the resulting AMI(s). By default this description is empty. This is a - [template engine](/docs/templates/engine.html) - where the `SourceAMI` variable is replaced with the source AMI ID and - `BuildRegion` variable is replaced with name of the region where this - is built. + [template engine](/docs/templates/engine.html), + see [Build template data](#build-template-data) for more information. - `ami_groups` (array of strings) - A list of groups that have access to launch the resulting AMI(s). By default no groups have permission to launch @@ -251,10 +249,8 @@ each category, the available configuration keys are alphabetized. - `snapshot_tags` (object of key/value strings) - Tags to apply to snapshot. They will override AMI tags if already applied to snapshot. This is a - [template engine](/docs/templates/engine.html) - where the `SourceAMI` variable is replaced with the source AMI ID and - `BuildRegion` variable is replaced with name of the region where this - is built. + [template engine](/docs/templates/engine.html), + see [Build template data](#build-template-data) for more information. - `snapshot_groups` (array of strings) - A list of groups that have access to create volumes from the snapshot(s). By default no groups have permission to create @@ -307,10 +303,8 @@ each category, the available configuration keys are alphabetized. Default `false`. - `tags` (object of key/value strings) - Tags applied to the AMI. This is a - [template engine](/docs/templates/engine.html) - where the `SourceAMI` variable is replaced with the source AMI ID and - `BuildRegion` variable is replaced with name of the region where this - is built. + [template engine](/docs/templates/engine.html), + see [Build template data](#build-template-data) for more information. ## Basic Example @@ -432,3 +426,12 @@ provisioning commands to install the os and bootloader. ] } ``` + +## Build template data + +The available variables are: + +- `BuildRegion` - The region (for example `eu-central-1`) where Packer is building the AMI. +- `SourceAMI` - The source AMI ID (for example `ami-a2412fcd`) used to build the AMI. +- `SourceAMIName` - The source AMI Name (for example `ubuntu/images/ebs-ssd/ubuntu-xenial-16.04-amd64-server-20180306`) used to build the AMI. +- `SourceAMITags` - The source AMI Tags, as a `map[string]string` object. diff --git a/website/source/docs/builders/amazon-ebs.html.md b/website/source/docs/builders/amazon-ebs.html.md index 400eb7ecb..fce12b678 100644 --- a/website/source/docs/builders/amazon-ebs.html.md +++ b/website/source/docs/builders/amazon-ebs.html.md @@ -114,9 +114,8 @@ builder. - `ami_description` (string) - The description to set for the resulting AMI(s). By default this description is empty. This is a - [template engine](/docs/templates/engine.html) - where the `SourceAMI` variable is replaced with the source AMI ID and - `BuildRegion` variable is replaced with the value of `region`. + [template engine](/docs/templates/engine.html), + see [Build template data](#build-template-data) for more information. - `ami_groups` (array of strings) - A list of groups that have access to launch the resulting AMI(s). By default no groups have permission to launch @@ -221,16 +220,14 @@ builder. - `run_tags` (object of key/value strings) - Tags to apply to the instance that is *launched* to create the AMI. These tags are *not* applied to the resulting AMI unless they're duplicated in `tags`. This is a - [template engine](/docs/templates/engine.html) - where the `SourceAMI` variable is replaced with the source AMI ID and - `BuildRegion` variable is replaced with the value of `region`. + [template engine](/docs/templates/engine.html), + see [Build template data](#build-template-data) for more information. - `run_volume_tags` (object of key/value strings) - Tags to apply to the volumes that are *launched* to create the AMI. These tags are *not* applied to the resulting AMI unless they're duplicated in `tags`. This is a - [template engine](/docs/templates/engine.html) - where the `SourceAMI` variable is replaced with the source AMI ID and - `BuildRegion` variable is replaced with the value of `region`. + [template engine](/docs/templates/engine.html), + see [Build template data](#build-template-data) for more information. - `security_group_id` (string) - The ID (*not* the name) of the security group to assign to the instance. By default this is not set and Packer will @@ -264,9 +261,8 @@ builder. - `snapshot_tags` (object of key/value strings) - Tags to apply to snapshot. They will override AMI tags if already applied to snapshot. This is a - [template engine](/docs/templates/engine.html) - where the `SourceAMI` variable is replaced with the source AMI ID and - `BuildRegion` variable is replaced with the value of `region`. + [template engine](/docs/templates/engine.html), + see [Build template data](#build-template-data) for more information. - `source_ami_filter` (object) - Filters used to populate the `source_ami` field. Example: @@ -361,9 +357,8 @@ builder. - `tags` (object of key/value strings) - Tags applied to the AMI and relevant snapshots. This is a - [template engine](/docs/templates/engine.html) - where the `SourceAMI` variable is replaced with the source AMI ID and - `BuildRegion` variable is replaced with the value of `region`. + [template engine](/docs/templates/engine.html), + see [Build template data](#build-template-data) for more information. - `temporary_key_pair_name` (string) - The name of the temporary key pair to generate. By default, Packer generates a name that looks like @@ -462,6 +457,15 @@ configuration of `launch_block_device_mappings` will expand the root volume } ``` +## Build template data + +The available variables are: + +- `BuildRegion` - The region (for example `eu-central-1`) where Packer is building the AMI. +- `SourceAMI` - The source AMI ID (for example `ami-a2412fcd`) used to build the AMI. +- `SourceAMIName` - The source AMI Name (for example `ubuntu/images/ebs-ssd/ubuntu-xenial-16.04-amd64-server-20180306`) used to build the AMI. +- `SourceAMITags` - The source AMI Tags, as a `map[string]string` object. + ## Tag Example Here is an example using the optional AMI tags. This will add the tags @@ -481,7 +485,9 @@ images exist when this template is run: "ami_name": "packer-quick-start {{timestamp}}", "tags": { "OS_Version": "Ubuntu", - "Release": "Latest" + "Release": "Latest", + "Base_AMI_Name": "{{ .SourceAMIName }}", + "Extra": "{{ .SourceAMITags.TagName }}" } } ``` diff --git a/website/source/docs/builders/amazon-ebssurrogate.html.md b/website/source/docs/builders/amazon-ebssurrogate.html.md index 1d3724533..98781a94d 100644 --- a/website/source/docs/builders/amazon-ebssurrogate.html.md +++ b/website/source/docs/builders/amazon-ebssurrogate.html.md @@ -107,9 +107,8 @@ builder. - `ami_description` (string) - The description to set for the resulting AMI(s). By default this description is empty. This is a - [template engine](/docs/templates/engine.html) - where the `SourceAMI` variable is replaced with the source AMI ID and - `BuildRegion` variable is replaced with the value of `region`. + [template engine](/docs/templates/engine.html), + see [Build template data](#build-template-data) for more information. - `ami_groups` (array of strings) - A list of groups that have access to launch the resulting AMI(s). By default no groups have permission to launch @@ -214,16 +213,14 @@ builder. - `run_tags` (object of key/value strings) - Tags to apply to the instance that is *launched* to create the AMI. These tags are *not* applied to the resulting AMI unless they're duplicated in `tags`. This is a - [template engine](/docs/templates/engine.html) - where the `SourceAMI` variable is replaced with the source AMI ID and - `BuildRegion` variable is replaced with the value of `region`. + [template engine](/docs/templates/engine.html), + see [Build template data](#build-template-data) for more information. - `run_volume_tags` (object of key/value strings) - Tags to apply to the volumes that are *launched* to create the AMI. These tags are *not* applied to the resulting AMI unless they're duplicated in `tags`. This is a - [template engine](/docs/templates/engine.html) - where the `SourceAMI` variable is replaced with the source AMI ID and - `BuildRegion` variable is replaced with the value of `region`. + [template engine](/docs/templates/engine.html), + see [Build template data](#build-template-data) for more information. - `security_group_id` (string) - The ID (*not* the name) of the security group to assign to the instance. By default this is not set and Packer will @@ -257,9 +254,8 @@ builder. - `snapshot_tags` (object of key/value strings) - Tags to apply to snapshot. They will override AMI tags if already applied to snapshot. This is a - [template engine](/docs/templates/engine.html) - where the `SourceAMI` variable is replaced with the source AMI ID and - `BuildRegion` variable is replaced with the value of `region`. + [template engine](/docs/templates/engine.html), + see [Build template data](#build-template-data) for more information. - `source_ami_filter` (object) - Filters used to populate the `source_ami` field. Example: @@ -354,9 +350,8 @@ builder. - `tags` (object of key/value strings) - Tags applied to the AMI and relevant snapshots. This is a - [template engine](/docs/templates/engine.html) - where the `SourceAMI` variable is replaced with the source AMI ID and - `BuildRegion` variable is replaced with the value of `region`. + [template engine](/docs/templates/engine.html), + see [Build template data](#build-template-data) for more information. - `temporary_key_pair_name` (string) - The name of the temporary keypair to generate. By default, Packer generates a name with a UUID. @@ -426,6 +421,16 @@ with the `-debug` flag. In debug mode, the Amazon builder will save the private key in the current directory and will output the DNS or IP information as well. You can use this information to access the instance as it is running. +## Build template data + +The available variables are: + +- `BuildRegion` - The region (for example `eu-central-1`) where Packer is building the AMI. +- `SourceAMI` - The source AMI ID (for example `ami-a2412fcd`) used to build the AMI. +- `SourceAMIName` - The source AMI Name (for example `ubuntu/images/ebs-ssd/ubuntu-xenial-16.04-amd64-server-20180306`) used to build the AMI. +- `SourceAMITags` - The source AMI Tags, as a `map[string]string` object. + + -> **Note:** Packer uses pre-built AMIs as the source for building images. These source AMIs may include volumes that are not flagged to be destroyed on termination of the instance building the new image. In addition to those volumes diff --git a/website/source/docs/builders/amazon-ebsvolume.html.md b/website/source/docs/builders/amazon-ebsvolume.html.md index 5a3ee9830..3c277920f 100644 --- a/website/source/docs/builders/amazon-ebsvolume.html.md +++ b/website/source/docs/builders/amazon-ebsvolume.html.md @@ -86,10 +86,9 @@ builder. volumes, `io1` for Provisioned IOPS (SSD) volumes, and `standard` for Magnetic volumes - `tags` (map) - Tags to apply to the volume. These are retained after the - builder completes. This is a \[template engine\] - (/docs/templates/engine.html) where the `SourceAMI` - variable is replaced with the source AMI ID and `BuildRegion` variable - is replaced with the value of `region`. + builder completes. This is a + [template engine](/docs/templates/engine.html), + see [Build template data](#build-template-data) for more information. - `associate_public_ip_address` (boolean) - If using a non-default VPC, public IP addresses are not provided by default. If this is toggled, your new @@ -126,9 +125,8 @@ builder. - `run_tags` (object of key/value strings) - Tags to apply to the instance that is *launched* to create the AMI. These tags are *not* applied to the resulting AMI unless they're duplicated in `tags`. This is a - [template engine](/docs/templates/engine.html) - where the `SourceAMI` variable is replaced with the source AMI ID and - `BuildRegion` variable is replaced with the value of `region`. + [template engine](/docs/templates/engine.html), + see [Build template data](#build-template-data) for more information. - `security_group_id` (string) - The ID (*not* the name) of the security group to assign to the instance. By default this is not set and Packer will @@ -328,6 +326,15 @@ with the `-debug` flag. In debug mode, the Amazon builder will save the private key in the current directory and will output the DNS or IP information as well. You can use this information to access the instance as it is running. +## Build template data + +The available variables are: + +- `BuildRegion` - The region (for example `eu-central-1`) where Packer is building the AMI. +- `SourceAMI` - The source AMI ID (for example `ami-a2412fcd`) used to build the AMI. +- `SourceAMIName` - The source AMI Name (for example `ubuntu/images/ebs-ssd/ubuntu-xenial-16.04-amd64-server-20180306`) used to build the AMI. +- `SourceAMITags` - The source AMI Tags, as a `map[string]string` object. + -> **Note:** Packer uses pre-built AMIs as the source for building images. These source AMIs may include volumes that are not flagged to be destroyed on termination of the instance building the new image. In addition to those volumes diff --git a/website/source/docs/builders/amazon-instance.html.md b/website/source/docs/builders/amazon-instance.html.md index 58ac7088b..e6e40535c 100644 --- a/website/source/docs/builders/amazon-instance.html.md +++ b/website/source/docs/builders/amazon-instance.html.md @@ -136,9 +136,8 @@ builder. - `ami_description` (string) - The description to set for the resulting AMI(s). By default this description is empty. This is a - [template engine](/docs/templates/engine.html) - where the `SourceAMI` variable is replaced with the source AMI ID and - `BuildRegion` variable is replaced with the value of `region`. + [template engine](/docs/templates/engine.html), + see [Build template data](#build-template-data) for more information. - `ami_groups` (array of strings) - A list of groups that have access to launch the resulting AMI(s). By default no groups have permission to launch @@ -236,9 +235,8 @@ builder. - `run_tags` (object of key/value strings) - Tags to apply to the instance that is *launched* to create the AMI. These tags are *not* applied to the resulting AMI unless they're duplicated in `tags`. This is a - [template engine](/docs/templates/engine.html) - where the `SourceAMI` variable is replaced with the source AMI ID and - `BuildRegion` variable is replaced with the value of `region`. + [template engine](/docs/templates/engine.html), + see [Build template data](#build-template-data) for more information. - `security_group_id` (string) - The ID (*not* the name) of the security group to assign to the instance. By default this is not set and Packer will @@ -361,9 +359,8 @@ builder. required if you are using an non-default VPC. - `tags` (object of key/value strings) - Tags applied to the AMI. This is a - [template engine](/docs/templates/engine.html) - where the `SourceAMI` variable is replaced with the source AMI ID and - `BuildRegion` variable is replaced with the value of `region`. + [template engine](/docs/templates/engine.html), + see [Build template data](#build-template-data) for more information. - `temporary_key_pair_name` (string) - The name of the temporary key pair to generate. By default, Packer generates a name that looks like @@ -425,6 +422,15 @@ with the `-debug` flag. In debug mode, the Amazon builder will save the private key in the current directory and will output the DNS or IP information as well. You can use this information to access the instance as it is running. +## Build template data + +The available variables are: + +- `BuildRegion` - The region (for example `eu-central-1`) where Packer is building the AMI. +- `SourceAMI` - The source AMI ID (for example `ami-a2412fcd`) used to build the AMI. +- `SourceAMIName` - The source AMI Name (for example `ubuntu/images/ebs-ssd/ubuntu-xenial-16.04-amd64-server-20180306`) used to build the AMI. +- `SourceAMITags` - The source AMI Tags, as a `map[string]string` object. + ## Custom Bundle Commands A lot of the process required for creating an instance-store backed AMI involves From c0bd4fdafe2ddd7822d0fdcb1b1cc7e996737020 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Mon, 2 Apr 2018 10:52:05 -0700 Subject: [PATCH 0752/1216] Add PR lifecycle to contributing doc. --- CONTRIBUTING.md | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2ae5b39fd..e0c51bc78 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -91,9 +91,10 @@ export PATH=$PATH:$GOPATH/bin ### Opening an Pull Request -When you are ready to open a pull-request, you will need to -[fork Packer](https://github.com/hashicorp/packer#fork-destination-box), push -your changes to your fork, and then open a pull-request. +Thank you for contributing! When you are ready to open a pull-request, you will +need to [fork +Packer](https://github.com/hashicorp/packer#fork-destination-box), push your +changes to your fork, and then open a pull-request. For example, my github username is `cbednarski`, so I would do the following: @@ -109,6 +110,29 @@ From there, open your fork in your browser to open a new pull-request. will break if you `git clone` your fork instead of using `go get` on the main Packer project. +### Pull Request Lifecycle + +1. You are welcome to submit your pull request for commentary or review before + it is fully completed. Please prefix the title of your pull request with + "[WIP]" to indicate this. It's also a good idea to include specific questions + or items you'd like feedback on. + +1. Once you believe your pull request is ready to be merged, you can remove any + "[WIP]" prefix from the title and a core team member will review. + +1. One of Packer's core team members will look over your contribution and + either provide comments letting you know if there is anything left to do. We + do our best to provide feedback in a timely manner, but it may take some time + for us to respond. + +1. Once all outstanding comments and checklist items have been addressed, your + contribution will be merged! Merged PRs will be included in the next + Packer release. The core team takes care of updating the CHANGELOG as they + merge. + +1. In rare cases, we might decide that a PR should be closed. We'll make sure to + provide clear reasoning when this happens. + ### Tips for Working on Packer #### Working on forks From 6f7044c0f663e2d7c7a9cb13dd1a71bebc37b346 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Mon, 2 Apr 2018 10:52:27 -0700 Subject: [PATCH 0753/1216] move contributing doc out of root --- CONTRIBUTING.md => .github/CONTRIBUTING.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename CONTRIBUTING.md => .github/CONTRIBUTING.md (100%) diff --git a/CONTRIBUTING.md b/.github/CONTRIBUTING.md similarity index 100% rename from CONTRIBUTING.md rename to .github/CONTRIBUTING.md From ce0447693120ff159897b8529b4cd4b29b7e813f Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Mon, 2 Apr 2018 10:55:13 -0700 Subject: [PATCH 0754/1216] update contributing link --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d6b01c47a..d7b2186c9 100644 --- a/README.md +++ b/README.md @@ -92,4 +92,7 @@ https://www.packer.io/docs ## Developing Packer -See [CONTRIBUTING.md](https://github.com/hashicorp/packer/blob/master/CONTRIBUTING.md) for best practices and instructions on setting up your development environment to work on Packer. +See +[CONTRIBUTING.md](https://github.com/hashicorp/packer/blob/master/.github/CONTRIBUTING.md) +for best practices and instructions on setting up your development environment +to work on Packer. From 70426af4ebccb4e7066e264f0c5126fd916962e0 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Mon, 2 Apr 2018 10:59:59 -0700 Subject: [PATCH 0755/1216] update contributing link --- Makefile | 2 +- README.md | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 3a68f0ba5..54852ce26 100644 --- a/Makefile +++ b/Makefile @@ -91,7 +91,7 @@ testrace: deps ## Test for race conditions @go test -race $(TEST) $(TESTARGS) -timeout=2m updatedeps: - @echo "INFO: Packer deps are managed by govendor. See CONTRIBUTING.md" + @echo "INFO: Packer deps are managed by govendor. See .github/CONTRIBUTING.md" help: @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/README.md b/README.md index d7b2186c9..dcb0af7f4 100644 --- a/README.md +++ b/README.md @@ -43,8 +43,10 @@ for those with a bit more patience. Otherwise, the quick start below will get you up and running quickly, at the sacrifice of not explaining some key points. -First, [download a pre-built Packer binary](https://www.packer.io/downloads.html) -for your operating system or [compile Packer yourself](CONTRIBUTING.md#setting-up-go-to-work-on-packer). +First, [download a pre-built Packer +binary](https://www.packer.io/downloads.html) for your operating system or +[compile Packer +yourself](https://github.com/hashicorp/packer/blob/master/.github/CONTRIBUTING.md#setting-up-go-to-work-on-packer). After Packer is installed, create your first template, which tells Packer what platforms to build images for and how you want to build them. In our From 52f69cd91a749bc90ac9fc817c6e7a3cf4eb7a25 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Mon, 2 Apr 2018 11:12:07 -0700 Subject: [PATCH 0756/1216] Validate image name. --- builder/oracle/classic/config.go | 13 +++++++++++-- builder/oracle/classic/config_test.go | 18 ++++++++++-------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/builder/oracle/classic/config.go b/builder/oracle/classic/config.go index 78fb42085..da3a76460 100644 --- a/builder/oracle/classic/config.go +++ b/builder/oracle/classic/config.go @@ -97,8 +97,17 @@ func NewConfig(raws ...interface{}) (*Config, error) { // Object names can contain only alphanumeric characters, hyphens, underscores, and periods reValidObject := regexp.MustCompile("^[a-zA-Z0-9-._/]+$") - if !reValidObject.MatchString(c.DestImageList) { - errs = packer.MultiErrorAppend(errs, fmt.Errorf("dest_image_list can contain only alphanumeric characters, hyphens, underscores, and periods.")) + var objectValidation = []struct { + name string + value string + }{ + {"dest_image_list", c.DestImageList}, + {"image_name", c.ImageName}, + } + for _, ov := range objectValidation { + if !reValidObject.MatchString(ov.value) { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("%s can contain only alphanumeric characters, hyphens, underscores, and periods.", ov.name)) + } } if c.Attributes != "" && c.AttributesFile != "" { diff --git a/builder/oracle/classic/config_test.go b/builder/oracle/classic/config_test.go index 7351af0d3..ff6bf2a00 100644 --- a/builder/oracle/classic/config_test.go +++ b/builder/oracle/classic/config_test.go @@ -72,14 +72,16 @@ func TestConfigValidatesObjects(t *testing.T) { {"Matt...?", false}, {"/Config-thing/myuser/myimage", true}, } - for _, tt := range objectTests { - tc := testConfig() - tc["dest_image_list"] = tt.object - _, err := NewConfig(tc) - if tt.valid { - assert.NoError(t, err, tt.object) - } else { - assert.Error(t, err, tt.object) + for _, s := range []string{"dest_image_list", "image_name"} { + for _, tt := range objectTests { + tc := testConfig() + tc[s] = tt.object + _, err := NewConfig(tc) + if tt.valid { + assert.NoError(t, err, tt.object) + } else { + assert.Error(t, err, tt.object) + } } } } From 7e13b5c62aad42cb4976bfe1032c78744b0097eb Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Mon, 2 Apr 2018 11:56:11 -0700 Subject: [PATCH 0757/1216] prevent panics when cleaning up resources that haven't been created. --- .../oracle/classic/step_create_instance.go | 8 ++++-- .../classic/step_create_ip_reservation.go | 8 ++++-- builder/oracle/classic/step_security.go | 28 +++++++++++++------ builder/oracle/classic/step_snapshot.go | 11 ++++---- 4 files changed, 37 insertions(+), 18 deletions(-) diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go index d9d26b3e3..d52500e09 100644 --- a/builder/oracle/classic/step_create_instance.go +++ b/builder/oracle/classic/step_create_instance.go @@ -57,18 +57,22 @@ func (s *stepCreateInstance) Run(_ context.Context, state multistep.StateBag) mu } func (s *stepCreateInstance) Cleanup(state multistep.StateBag) { + instanceID, ok := state.GetOk("instance_id") + if !ok { + return + } + // terminate instance ui := state.Get("ui").(packer.Ui) client := state.Get("client").(*compute.ComputeClient) config := state.Get("config").(*Config) - imID := state.Get("instance_id").(string) ui.Say("Terminating source instance...") instanceClient := client.Instances() input := &compute.DeleteInstanceInput{ Name: config.ImageName, - ID: imID, + ID: instanceID.(string), } err := instanceClient.DeleteInstance(input) diff --git a/builder/oracle/classic/step_create_ip_reservation.go b/builder/oracle/classic/step_create_ip_reservation.go index 74466a39f..5d400e746 100644 --- a/builder/oracle/classic/step_create_ip_reservation.go +++ b/builder/oracle/classic/step_create_ip_reservation.go @@ -42,12 +42,16 @@ func (s *stepCreateIPReservation) Run(_ context.Context, state multistep.StateBa } func (s *stepCreateIPReservation) Cleanup(state multistep.StateBag) { + ipResName, ok := state.GetOk("ipres_name") + if !ok { + return + } + ui := state.Get("ui").(packer.Ui) ui.Say("Cleaning up IP reservations...") client := state.Get("client").(*compute.ComputeClient) - ipResName := state.Get("ipres_name").(string) - input := compute.DeleteIPReservationInput{Name: ipResName} + input := compute.DeleteIPReservationInput{Name: ipResName.(string)} ipClient := client.IPReservations() err := ipClient.DeleteIPReservation(&input) if err != nil { diff --git a/builder/oracle/classic/step_security.go b/builder/oracle/classic/step_security.go index 34b0d6544..a5b04418b 100644 --- a/builder/oracle/classic/step_security.go +++ b/builder/oracle/classic/step_security.go @@ -97,6 +97,15 @@ func (s *stepSecurity) Run(_ context.Context, state multistep.StateBag) multiste } func (s *stepSecurity) Cleanup(state multistep.StateBag) { + secRuleName, ok := state.GetOk("security_rule_name") + if !ok { + return + } + secListName, ok := state.GetOk("security_list") + if !ok { + return + } + client := state.Get("client").(*compute.ComputeClient) ui := state.Get("ui").(packer.Ui) config := state.Get("config").(*Config) @@ -105,37 +114,38 @@ func (s *stepSecurity) Cleanup(state multistep.StateBag) { namePrefix := fmt.Sprintf("/Compute-%s/%s/", config.IdentityDomain, config.Username) // delete security rules that Packer generated - secRuleName := state.Get("security_rule_name").(string) secRulesClient := client.SecRules() - ruleInput := compute.DeleteSecRuleInput{Name: namePrefix + secRuleName} + ruleInput := compute.DeleteSecRuleInput{Name: namePrefix + secRuleName.(string)} err := secRulesClient.DeleteSecRule(&ruleInput) if err != nil { ui.Say(fmt.Sprintf("Error deleting the packer-generated security rule %s; "+ - "please delete manually. (error: %s)", secRuleName, err.Error())) + "please delete manually. (error: %s)", secRuleName.(string), err.Error())) } // delete security list that Packer generated - secListName := state.Get("security_list").(string) secListClient := client.SecurityLists() - input := compute.DeleteSecurityListInput{Name: namePrefix + secListName} + input := compute.DeleteSecurityListInput{Name: namePrefix + secListName.(string)} err = secListClient.DeleteSecurityList(&input) if err != nil { ui.Say(fmt.Sprintf("Error deleting the packer-generated security list %s; "+ - "please delete manually. (error : %s)", secListName, err.Error())) + "please delete manually. (error : %s)", secListName.(string), err.Error())) } // Some extra cleanup if we used the winRM communicator if config.Comm.Type == "winrm" { // Delete the packer-generated application - application := state.Get("winrm_application").(string) + application, ok := state.GetOk("winrm_application") + if !ok { + return + } applicationClient := client.SecurityApplications() deleteApplicationInput := compute.DeleteSecurityApplicationInput{ - Name: namePrefix + application, + Name: namePrefix + application.(string), } err = applicationClient.DeleteSecurityApplication(&deleteApplicationInput) if err != nil { ui.Say(fmt.Sprintf("Error deleting the packer-generated winrm security application %s; "+ - "please delete manually. (error : %s)", application, err.Error())) + "please delete manually. (error : %s)", application.(string), err.Error())) } } diff --git a/builder/oracle/classic/step_snapshot.go b/builder/oracle/classic/step_snapshot.go index e0ebf3091..64058966d 100644 --- a/builder/oracle/classic/step_snapshot.go +++ b/builder/oracle/classic/step_snapshot.go @@ -15,7 +15,6 @@ type stepSnapshot struct { func (s *stepSnapshot) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { // get variables from state - s.cleanupSnap = false ui := state.Get("ui").(packer.Ui) ui.Say("Creating Snapshot...") config := state.Get("config").(*Config) @@ -39,7 +38,6 @@ func (s *stepSnapshot) Run(_ context.Context, state multistep.StateBag) multiste state.Put("error", err) return multistep.ActionHalt } - s.cleanupSnap = true state.Put("snapshot", snap) ui.Message(fmt.Sprintf("Created snapshot: %s.", snap.Name)) return multistep.ActionContinue @@ -47,13 +45,16 @@ func (s *stepSnapshot) Run(_ context.Context, state multistep.StateBag) multiste func (s *stepSnapshot) Cleanup(state multistep.StateBag) { // Delete the snapshot - ui := state.Get("ui").(packer.Ui) - if !s.cleanupSnap { + var snap *compute.Snapshot + if snapshot, ok := state.GetOk("snapshot"); ok { + snap = snapshot.(*compute.Snapshot) + } else { return } + + ui := state.Get("ui").(packer.Ui) ui.Say("Deleting Snapshot...") client := state.Get("client").(*compute.ComputeClient) - snap := state.Get("snapshot").(*compute.Snapshot) snapClient := client.Snapshots() snapInput := compute.DeleteSnapshotInput{ Snapshot: snap.Name, From bc5cf25d69bf74623d87a6520a7165c460d036ca Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Mon, 2 Apr 2018 20:09:32 -0700 Subject: [PATCH 0758/1216] update github link to point to hashicorp --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index dcb0af7f4..b031d542e 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,10 @@ [travis]: https://travis-ci.org/hashicorp/packer [appveyor-badge]: https://ci.appveyor.com/api/projects/status/miavlgnp989e5obc/branch/master?svg=true [appveyor]: https://ci.appveyor.com/project/hashicorp/packer -[godoc-badge]: https://godoc.org/github.com/mitchellh/packer?status.svg -[godoc]: https://godoc.org/github.com/mitchellh/packer -[report-badge]: https://goreportcard.com/badge/github.com/mitchellh/packer -[report]: https://goreportcard.com/report/github.com/mitchellh/packer +[godoc-badge]: https://godoc.org/github.com/hashicorp/packer?status.svg +[godoc]: https://godoc.org/github.com/hashicorp/packer +[report-badge]: https://goreportcard.com/badge/github.com/hashicorp/packer +[report]: https://goreportcard.com/report/github.com/hashicorp/packer * Website: https://www.packer.io * IRC: `#packer-tool` on Freenode From 5206427a47a7d638fed6cef3aeeeefba0c2d21e8 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Mon, 2 Apr 2018 20:13:07 -0700 Subject: [PATCH 0759/1216] Update go-fs. Closes #6083 --- .../github.com/mitchellh/go-fs/fat/boot_sector.go | 13 +++++++------ .../github.com/mitchellh/go-fs/fat/cluster_chain.go | 3 ++- vendor/github.com/mitchellh/go-fs/fat/directory.go | 9 +++++---- .../mitchellh/go-fs/fat/directory_cluster.go | 7 ++++--- vendor/github.com/mitchellh/go-fs/fat/fat.go | 3 ++- vendor/github.com/mitchellh/go-fs/fat/short_name.go | 9 ++++++--- .../github.com/mitchellh/go-fs/fat/super_floppy.go | 3 ++- vendor/vendor.json | 10 +++++----- 8 files changed, 33 insertions(+), 24 deletions(-) diff --git a/vendor/github.com/mitchellh/go-fs/fat/boot_sector.go b/vendor/github.com/mitchellh/go-fs/fat/boot_sector.go index 689a6b75c..f62df9283 100644 --- a/vendor/github.com/mitchellh/go-fs/fat/boot_sector.go +++ b/vendor/github.com/mitchellh/go-fs/fat/boot_sector.go @@ -4,8 +4,9 @@ import ( "encoding/binary" "errors" "fmt" - "github.com/mitchellh/go-fs" "unicode" + + "github.com/mitchellh/go-fs" ) type MediaType uint8 @@ -99,7 +100,7 @@ func (b *BootSectorCommon) Bytes() ([]byte, error) { for i, r := range b.OEMName { if r > unicode.MaxASCII { - return nil, fmt.Errorf("'%s' in OEM name not a valid ASCII char. Must be ASCII.", r) + return nil, fmt.Errorf("%#U in OEM name not a valid ASCII char. Must be ASCII.", r) } sector[0x3+i] = byte(r) @@ -245,7 +246,7 @@ func (b *BootSectorFat16) Bytes() ([]byte, error) { for i, r := range b.VolumeLabel { if r > unicode.MaxASCII { - return nil, fmt.Errorf("'%s' in VolumeLabel not a valid ASCII char. Must be ASCII.", r) + return nil, fmt.Errorf("%#U in VolumeLabel not a valid ASCII char. Must be ASCII.", r) } sector[43+i] = byte(r) @@ -258,7 +259,7 @@ func (b *BootSectorFat16) Bytes() ([]byte, error) { for i, r := range b.FileSystemTypeLabel { if r > unicode.MaxASCII { - return nil, fmt.Errorf("'%s' in FileSystemTypeLabel not a valid ASCII char. Must be ASCII.", r) + return nil, fmt.Errorf("%#U in FileSystemTypeLabel not a valid ASCII char. Must be ASCII.", r) } sector[54+i] = byte(r) @@ -324,7 +325,7 @@ func (b *BootSectorFat32) Bytes() ([]byte, error) { for i, r := range b.VolumeLabel { if r > unicode.MaxASCII { - return nil, fmt.Errorf("'%s' in VolumeLabel not a valid ASCII char. Must be ASCII.", r) + return nil, fmt.Errorf("%#U in VolumeLabel not a valid ASCII char. Must be ASCII.", r) } sector[71+i] = byte(r) @@ -337,7 +338,7 @@ func (b *BootSectorFat32) Bytes() ([]byte, error) { for i, r := range b.FileSystemTypeLabel { if r > unicode.MaxASCII { - return nil, fmt.Errorf("'%s' in FileSystemTypeLabel not a valid ASCII char. Must be ASCII.", r) + return nil, fmt.Errorf("%#U in FileSystemTypeLabel not a valid ASCII char. Must be ASCII.", r) } sector[82+i] = byte(r) diff --git a/vendor/github.com/mitchellh/go-fs/fat/cluster_chain.go b/vendor/github.com/mitchellh/go-fs/fat/cluster_chain.go index a1f9161d7..344f12e7c 100644 --- a/vendor/github.com/mitchellh/go-fs/fat/cluster_chain.go +++ b/vendor/github.com/mitchellh/go-fs/fat/cluster_chain.go @@ -1,9 +1,10 @@ package fat import ( - "github.com/mitchellh/go-fs" "io" "math" + + "github.com/mitchellh/go-fs" ) type ClusterChain struct { diff --git a/vendor/github.com/mitchellh/go-fs/fat/directory.go b/vendor/github.com/mitchellh/go-fs/fat/directory.go index ff62319fd..2dec26ad2 100644 --- a/vendor/github.com/mitchellh/go-fs/fat/directory.go +++ b/vendor/github.com/mitchellh/go-fs/fat/directory.go @@ -2,9 +2,10 @@ package fat import ( "fmt" - "github.com/mitchellh/go-fs" "strings" "time" + + "github.com/mitchellh/go-fs" ) // Directory implements fs.Directory and is used to interface with @@ -16,9 +17,9 @@ type Directory struct { } // DirectoryEntry implements fs.DirectoryEntry and represents a single -// file/folder within a directory in a FAT filesystem. Note that the -// underlying directory entry data structures on the disk may be more -// than one to accomodate for long filenames. +// file/folder within a directory in a FAT filesystem. Note that there may be +// more than one underlying directory entry data structure on the disk to +// account for long filenames. type DirectoryEntry struct { dir *Directory lfnEntries []*DirectoryClusterEntry diff --git a/vendor/github.com/mitchellh/go-fs/fat/directory_cluster.go b/vendor/github.com/mitchellh/go-fs/fat/directory_cluster.go index b822e82db..691710636 100644 --- a/vendor/github.com/mitchellh/go-fs/fat/directory_cluster.go +++ b/vendor/github.com/mitchellh/go-fs/fat/directory_cluster.go @@ -5,11 +5,12 @@ import ( "encoding/binary" "errors" "fmt" - "github.com/mitchellh/go-fs" "math" "strings" "time" "unicode/utf16" + + "github.com/mitchellh/go-fs" ) type DirectoryAttr uint8 @@ -126,7 +127,7 @@ func NewDirectoryCluster(start uint32, parent uint32, t time.Time) *DirectoryClu // Create the "." and ".." entries cluster.entries = []*DirectoryClusterEntry{ - &DirectoryClusterEntry{ + { accessTime: t, attr: AttrDirectory, cluster: start, @@ -134,7 +135,7 @@ func NewDirectoryCluster(start uint32, parent uint32, t time.Time) *DirectoryClu name: ".", writeTime: t, }, - &DirectoryClusterEntry{ + { accessTime: t, attr: AttrDirectory, cluster: parent, diff --git a/vendor/github.com/mitchellh/go-fs/fat/fat.go b/vendor/github.com/mitchellh/go-fs/fat/fat.go index 8597986f6..3b6ceb16a 100644 --- a/vendor/github.com/mitchellh/go-fs/fat/fat.go +++ b/vendor/github.com/mitchellh/go-fs/fat/fat.go @@ -3,8 +3,9 @@ package fat import ( "errors" "fmt" - "github.com/mitchellh/go-fs" "math" + + "github.com/mitchellh/go-fs" ) // The first cluster that can really hold user data is always 2 diff --git a/vendor/github.com/mitchellh/go-fs/fat/short_name.go b/vendor/github.com/mitchellh/go-fs/fat/short_name.go index df03a2aaf..c91c161f5 100644 --- a/vendor/github.com/mitchellh/go-fs/fat/short_name.go +++ b/vendor/github.com/mitchellh/go-fs/fat/short_name.go @@ -30,11 +30,11 @@ func generateShortName(longName string, used []string) (string, error) { if dotIdx == -1 { dotIdx = len(longName) } else { - ext = longName[dotIdx+1 : len(longName)] + ext = longName[dotIdx+1:] } ext = cleanShortString(ext) - ext = ext[0:len(ext)] + ext = ext[0:] rawName := longName[0:dotIdx] name := cleanShortString(rawName) simpleName := fmt.Sprintf("%s.%s", name, ext) @@ -42,7 +42,7 @@ func generateShortName(longName string, used []string) (string, error) { simpleName = simpleName[0 : len(simpleName)-1] } - doSuffix := name != rawName || len(name) > 8 + doSuffix := name != rawName || len(name) > 8 || len(ext) > 3 if !doSuffix { for _, usedSingle := range used { if strings.ToUpper(usedSingle) == simpleName { @@ -53,6 +53,9 @@ func generateShortName(longName string, used []string) (string, error) { } if doSuffix { + if len(ext) > 3 { + ext = ext[:3] + } found := false for i := 1; i < 99999; i++ { serial := fmt.Sprintf("~%d", i) diff --git a/vendor/github.com/mitchellh/go-fs/fat/super_floppy.go b/vendor/github.com/mitchellh/go-fs/fat/super_floppy.go index 16ad2caeb..ceaf4926a 100644 --- a/vendor/github.com/mitchellh/go-fs/fat/super_floppy.go +++ b/vendor/github.com/mitchellh/go-fs/fat/super_floppy.go @@ -3,8 +3,9 @@ package fat import ( "errors" "fmt" - "github.com/mitchellh/go-fs" "time" + + "github.com/mitchellh/go-fs" ) // SuperFloppyConfig is the configuration for various properties of diff --git a/vendor/vendor.json b/vendor/vendor.json index b48f00557..3f24598df 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1022,14 +1022,14 @@ { "checksumSHA1": "mVqDwKcibat0IKAdzAhfGIHPwI8=", "path": "github.com/mitchellh/go-fs", - "revision": "7bae45d9a684750e82b97ff320c82556614e621b", - "revisionTime": "2016-11-08T19:11:23Z" + "revision": "7b48fa161ea73ce54566b233d6fba8750b2faf47", + "revisionTime": "2018-04-02T23:40:41Z" }, { - "checksumSHA1": "B5dy+Lg6jFPgHYhozztz88z3b4A=", + "checksumSHA1": "i+3acspzTbQODW3dWX2JKydHVAo=", "path": "github.com/mitchellh/go-fs/fat", - "revision": "7bae45d9a684750e82b97ff320c82556614e621b", - "revisionTime": "2016-11-08T19:11:23Z" + "revision": "7b48fa161ea73ce54566b233d6fba8750b2faf47", + "revisionTime": "2018-04-02T23:40:41Z" }, { "checksumSHA1": "z235fRXw4+SW4xWgLTYc8SwkM2M=", From 8fdd20ec2d390ba1e72261ccd957317b479525b5 Mon Sep 17 00:00:00 2001 From: vkatsikaros Date: Tue, 3 Apr 2018 16:29:51 +0300 Subject: [PATCH 0760/1216] Add simpler example for chef client in local mode The existing documentation example looks intimidating as it includes: * variable interpolation * configuration of the chef provisioner * and it misses the `config_template` file In the issue comments, [simpler and complete example](https://github.com/hashicorp/packer/issues/3355#issuecomment-198134727) exists already and works out of the box. This change adds the simpler and complete example, while leaving the more complicated example intact. --- .../docs/provisioners/chef-client.html.md | 60 ++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/website/source/docs/provisioners/chef-client.html.md b/website/source/docs/provisioners/chef-client.html.md index c40f41034..d634b5c6f 100644 --- a/website/source/docs/provisioners/chef-client.html.md +++ b/website/source/docs/provisioners/chef-client.html.md @@ -286,7 +286,65 @@ directories, append a shell provisioner after Chef to modify them. ## Examples -### Chef Client Local Mode +### Chef Client Local Mode - Simple + +The following example shows how to run the `chef-client` provisioner in local +mode. + +**Packer variables** + +Set the necessary Packer variables using environment variables or provide a [var +file](/docs/templates/user-variables.html). + +``` json +"variables": { + "chef_dir": "/tmp/packer-chef-client" +} +``` + +**Setup the** `chef-client` **provisioner** + +Make sure we have the correct directories and permissions for the `chef-client` +provisioner. You will need to bootstrap the Chef run by providing the necessary +cookbooks using Berkshelf or some other means. + +``` json + "provisioners": [ + ... + { "type": "shell", "inline": [ "mkdir -p {{user `chef_dir`}}" ] }, + { "type": "file", "source": "./roles", "destination": "{{user `chef_dir`}}" }, + { "type": "file", "source": "./cookbooks", "destination": "{{user `chef_dir`}}" }, + { "type": "file", "source": "./data_bags", "destination": "{{user `chef_dir`}}" }, + { "type": "file", "source": "./environments", "destination": "{{user `chef_dir`}}" }, + { "type": "file", "source": "./scripts/install_chef.sh", "destination": "{{user `chef_dir`}}/install_chef.sh" }, + { + "type": "chef-client", + "install_command": "sudo bash {{user `chef_dir`}}/install_chef.sh", + "server_url": "http://localhost:8889", + "config_template": "./config/client.rb.template", + "run_list": [ "role[testing]" ], + "skip_clean_node": true, + "skip_clean_client": true + } + ... + ] +``` + +And ./config/client.rb.template referenced by the above configuration: + +```ruby +log_level :info +log_location STDOUT +local_mode true +chef_zero.enabled true +ssl_verify_mode "verify_peer" +role_path "{{user `chef_dir`}}/roles" +data_bag_path "{{user `chef_dir`}}/data_bags" +environment_path "{{user `chef_dir`}}/environments" +cookbook_path [ "{{user `chef_dir`}}/cookbooks" ] +``` + +### Chef Client Local Mode - Passing variables The following example shows how to run the `chef-client` provisioner in local mode, while passing a `run_list` using a variable. From c0719a359087b3c91d823dd52c91fb87718da663 Mon Sep 17 00:00:00 2001 From: Omer Katz Date: Thu, 5 Apr 2018 12:56:07 +0300 Subject: [PATCH 0761/1216] Allow using a custom inventory file. --- provisioner/ansible/provisioner.go | 10 +++++----- website/source/docs/provisioners/ansible.html.md | 7 ++++++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/provisioner/ansible/provisioner.go b/provisioner/ansible/provisioner.go index bc24044b3..d6e785c70 100644 --- a/provisioner/ansible/provisioner.go +++ b/provisioner/ansible/provisioner.go @@ -56,7 +56,7 @@ type Config struct { SkipVersionCheck bool `mapstructure:"skip_version_check"` UseSFTP bool `mapstructure:"use_sftp"` InventoryDirectory string `mapstructure:"inventory_directory"` - inventoryFile string + InventoryFile string `mapstructure:"inventory_file"` } type Provisioner struct { @@ -266,7 +266,7 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { go p.adapter.Serve() - if len(p.config.inventoryFile) == 0 { + if len(p.config.InventoryFile) == 0 { tf, err := ioutil.TempFile(p.config.InventoryDirectory, "packer-provisioner-ansible") if err != nil { return fmt.Errorf("Error preparing inventory file: %s", err) @@ -295,9 +295,9 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { return fmt.Errorf("Error preparing inventory file: %s", err) } tf.Close() - p.config.inventoryFile = tf.Name() + p.config.InventoryFile = tf.Name() defer func() { - p.config.inventoryFile = "" + p.config.InventoryFile = "" }() } @@ -320,7 +320,7 @@ func (p *Provisioner) Cancel() { func (p *Provisioner) executeAnsible(ui packer.Ui, comm packer.Communicator, privKeyFile string) error { playbook, _ := filepath.Abs(p.config.PlaybookFile) - inventory := p.config.inventoryFile + inventory := p.config.InventoryFile if len(p.config.InventoryDirectory) > 0 { inventory = p.config.InventoryDirectory } diff --git a/website/source/docs/provisioners/ansible.html.md b/website/source/docs/provisioners/ansible.html.md index 5c9f1c31c..0f9aa5908 100644 --- a/website/source/docs/provisioners/ansible.html.md +++ b/website/source/docs/provisioners/ansible.html.md @@ -81,8 +81,13 @@ Optional Parameters: should be placed. When unspecified, the host is not associated with any groups. +- `inventory_file` (string) - The inventory file to use during provisioning. + When unspecified, Packer will create a temporary inventory file and will + use the `host_alias`. + - `host_alias` (string) - The alias by which the Ansible host should be known. - Defaults to `default`. + Defaults to `default`. This setting is ignored when using a custom inventory + file. - `inventory_directory` (string) - The directory in which to place the temporary generated Ansible inventory file. By default, this is the From b17b333e298e0fadc7d42b5370c725ce2be69144 Mon Sep 17 00:00:00 2001 From: Seth Vargo Date: Wed, 4 Apr 2018 19:24:21 -0700 Subject: [PATCH 0762/1216] Add a common package for specifying useragent and adopt that everywhere There were 5 different formats for the Packer useragent string. This fixes that and unifies it into a helper package. I did not touch oracle's user-agent, because it looked kinda special. --- builder/azure/arm/azure_client.go | 33 ++++++++++++----------------- builder/azure/common/devicelogin.go | 6 ++---- builder/googlecompute/driver_gce.go | 11 ++++------ builder/scaleway/config.go | 3 ++- common/step_download.go | 3 ++- helper/useragent/useragent.go | 29 +++++++++++++++++++++++++ helper/useragent/useragent_test.go | 18 ++++++++++++++++ 7 files changed, 71 insertions(+), 32 deletions(-) create mode 100644 helper/useragent/useragent.go create mode 100644 helper/useragent/useragent_test.go diff --git a/builder/azure/arm/azure_client.go b/builder/azure/arm/azure_client.go index c4d8b544d..faa0a4766 100644 --- a/builder/azure/arm/azure_client.go +++ b/builder/azure/arm/azure_client.go @@ -2,7 +2,6 @@ package arm import ( "encoding/json" - "fmt" "math" "net/http" "net/url" @@ -19,17 +18,13 @@ import ( "github.com/Azure/go-autorest/autorest/adal" "github.com/Azure/go-autorest/autorest/azure" "github.com/hashicorp/packer/builder/azure/common" - "github.com/hashicorp/packer/version" + "github.com/hashicorp/packer/helper/useragent" ) const ( EnvPackerLogAzureMaxLen = "PACKER_LOG_AZURE_MAXLEN" ) -var ( - packerUserAgent = fmt.Sprintf(";packer/%s", version.FormattedVersion()) -) - type AzureClient struct { storage.BlobStorageClient resources.DeploymentsClient @@ -137,67 +132,67 @@ func NewAzureClient(subscriptionID, resourceGroupName, storageAccountName string azureClient.DeploymentsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) azureClient.DeploymentsClient.RequestInspector = withInspection(maxlen) azureClient.DeploymentsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.DeploymentsClient.UserAgent += packerUserAgent + azureClient.DeploymentsClient.UserAgent = useragent.String() azureClient.DeploymentOperationsClient = resources.NewDeploymentOperationsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) azureClient.DeploymentOperationsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) azureClient.DeploymentOperationsClient.RequestInspector = withInspection(maxlen) azureClient.DeploymentOperationsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.DeploymentOperationsClient.UserAgent += packerUserAgent + azureClient.DeploymentOperationsClient.UserAgent = useragent.String() azureClient.DisksClient = disk.NewDisksClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) azureClient.DisksClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) azureClient.DisksClient.RequestInspector = withInspection(maxlen) azureClient.DisksClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.DisksClient.UserAgent += packerUserAgent + azureClient.DisksClient.UserAgent = useragent.String() azureClient.GroupsClient = resources.NewGroupsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) azureClient.GroupsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) azureClient.GroupsClient.RequestInspector = withInspection(maxlen) azureClient.GroupsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.GroupsClient.UserAgent += packerUserAgent + azureClient.GroupsClient.UserAgent = useragent.String() azureClient.ImagesClient = compute.NewImagesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) azureClient.ImagesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) azureClient.ImagesClient.RequestInspector = withInspection(maxlen) azureClient.ImagesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.ImagesClient.UserAgent += packerUserAgent + azureClient.ImagesClient.UserAgent = useragent.String() azureClient.InterfacesClient = network.NewInterfacesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) azureClient.InterfacesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) azureClient.InterfacesClient.RequestInspector = withInspection(maxlen) azureClient.InterfacesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.InterfacesClient.UserAgent += packerUserAgent + azureClient.InterfacesClient.UserAgent = useragent.String() azureClient.SubnetsClient = network.NewSubnetsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) azureClient.SubnetsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) azureClient.SubnetsClient.RequestInspector = withInspection(maxlen) azureClient.SubnetsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.SubnetsClient.UserAgent += packerUserAgent + azureClient.SubnetsClient.UserAgent = useragent.String() azureClient.VirtualNetworksClient = network.NewVirtualNetworksClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) azureClient.VirtualNetworksClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) azureClient.VirtualNetworksClient.RequestInspector = withInspection(maxlen) azureClient.VirtualNetworksClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.VirtualNetworksClient.UserAgent += packerUserAgent + azureClient.VirtualNetworksClient.UserAgent = useragent.String() azureClient.PublicIPAddressesClient = network.NewPublicIPAddressesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) azureClient.PublicIPAddressesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) azureClient.PublicIPAddressesClient.RequestInspector = withInspection(maxlen) azureClient.PublicIPAddressesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.PublicIPAddressesClient.UserAgent += packerUserAgent + azureClient.PublicIPAddressesClient.UserAgent = useragent.String() azureClient.VirtualMachinesClient = compute.NewVirtualMachinesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) azureClient.VirtualMachinesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) azureClient.VirtualMachinesClient.RequestInspector = withInspection(maxlen) azureClient.VirtualMachinesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), templateCapture(azureClient), errorCapture(azureClient)) - azureClient.VirtualMachinesClient.UserAgent += packerUserAgent + azureClient.VirtualMachinesClient.UserAgent = useragent.String() azureClient.AccountsClient = armStorage.NewAccountsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) azureClient.AccountsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) azureClient.AccountsClient.RequestInspector = withInspection(maxlen) azureClient.AccountsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.AccountsClient.UserAgent += packerUserAgent + azureClient.AccountsClient.UserAgent = useragent.String() keyVaultURL, err := url.Parse(cloud.KeyVaultEndpoint) if err != nil { @@ -208,7 +203,7 @@ func NewAzureClient(subscriptionID, resourceGroupName, storageAccountName string azureClient.VaultClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalTokenVault) azureClient.VaultClient.RequestInspector = withInspection(maxlen) azureClient.VaultClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.VaultClient.UserAgent += packerUserAgent + azureClient.VaultClient.UserAgent = useragent.String() // TODO(boumenot) - SDK still does not have a full KeyVault client. // There are two ways that KeyVault has to be accessed, and each one has their own SPN. An authenticated SPN @@ -222,7 +217,7 @@ func NewAzureClient(subscriptionID, resourceGroupName, storageAccountName string azureClient.VaultClientDelete.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) azureClient.VaultClientDelete.RequestInspector = withInspection(maxlen) azureClient.VaultClientDelete.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.VaultClientDelete.UserAgent += packerUserAgent + azureClient.VaultClientDelete.UserAgent = useragent.String() // If this is a managed disk build, this should be ignored. if resourceGroupName != "" && storageAccountName != "" { diff --git a/builder/azure/common/devicelogin.go b/builder/azure/common/devicelogin.go index 904bd157f..8cf689600 100644 --- a/builder/azure/common/devicelogin.go +++ b/builder/azure/common/devicelogin.go @@ -12,7 +12,7 @@ import ( "github.com/Azure/go-autorest/autorest/adal" "github.com/Azure/go-autorest/autorest/azure" "github.com/Azure/go-autorest/autorest/to" - "github.com/hashicorp/packer/version" + "github.com/hashicorp/packer/helper/useragent" "github.com/mitchellh/go-homedir" ) @@ -21,8 +21,6 @@ var ( clientIDs = map[string]string{ azure.PublicCloud.Name: "04cc58ec-51ab-4833-ac0d-ce3a7912414b", } - - userAgent = fmt.Sprintf("packer/%s", version.FormattedVersion()) ) // NOTE(ahmetalpbalkan): Azure Active Directory implements OAuth 2.0 Device Flow @@ -138,7 +136,7 @@ func tokenFromFile(say func(string), oauthCfg adal.OAuthConfig, tokenPath, clien // endpoint is polled until user gives consent, denies or the flow times out. // Returned token must be saved. func tokenFromDeviceFlow(say func(string), oauthCfg adal.OAuthConfig, clientID, resource string) (*adal.ServicePrincipalToken, error) { - cl := autorest.NewClientWithUserAgent(userAgent) + cl := autorest.NewClientWithUserAgent(useragent.String()) deviceCode, err := adal.InitiateDeviceAuth(&cl, oauthCfg, clientID, resource) if err != nil { return nil, fmt.Errorf("Failed to start device auth: %v", err) diff --git a/builder/googlecompute/driver_gce.go b/builder/googlecompute/driver_gce.go index 8adbd833e..91f20a58a 100644 --- a/builder/googlecompute/driver_gce.go +++ b/builder/googlecompute/driver_gce.go @@ -10,15 +10,14 @@ import ( "fmt" "log" "net/http" - "runtime" "strings" "time" "google.golang.org/api/compute/v1" "github.com/hashicorp/packer/common" + "github.com/hashicorp/packer/helper/useragent" "github.com/hashicorp/packer/packer" - "github.com/hashicorp/packer/version" "golang.org/x/oauth2" "golang.org/x/oauth2/google" @@ -81,15 +80,13 @@ func NewDriverGCE(ui packer.Ui, p string, a *AccountFile) (Driver, error) { log.Printf("[INFO] Instantiating GCE client...") service, err := compute.New(client) - // Set UserAgent - versionString := version.FormattedVersion() - service.UserAgent = fmt.Sprintf( - "(%s %s) Packer/%s", runtime.GOOS, runtime.GOARCH, versionString) - if err != nil { return nil, err } + // Set UserAgent + service.UserAgent = useragent.String() + return &driverGCE{ projectId: p, service: service, diff --git a/builder/scaleway/config.go b/builder/scaleway/config.go index f965e1ce0..d658e1dad 100644 --- a/builder/scaleway/config.go +++ b/builder/scaleway/config.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/packer/common/uuid" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/config" + "github.com/hashicorp/packer/helper/useragent" "github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/template/interpolate" "github.com/mitchellh/mapstructure" @@ -51,7 +52,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { return nil, nil, err } - c.UserAgent = "Packer - Scaleway builder" + c.UserAgent = useragent.String() if c.Organization == "" { c.Organization = os.Getenv("SCALEWAY_API_ACCESS_KEY") diff --git a/common/step_download.go b/common/step_download.go index 5f5bb7dfe..03340b974 100644 --- a/common/step_download.go +++ b/common/step_download.go @@ -9,6 +9,7 @@ import ( "time" "github.com/hashicorp/packer/helper/multistep" + "github.com/hashicorp/packer/helper/useragent" "github.com/hashicorp/packer/packer" ) @@ -91,7 +92,7 @@ func (s *StepDownload) Run(_ context.Context, state multistep.StateBag) multiste CopyFile: false, Hash: HashForType(s.ChecksumType), Checksum: checksum, - UserAgent: "Packer", + UserAgent: useragent.String(), } downloadConfigs[i] = config diff --git a/helper/useragent/useragent.go b/helper/useragent/useragent.go new file mode 100644 index 000000000..bd433d0cb --- /dev/null +++ b/helper/useragent/useragent.go @@ -0,0 +1,29 @@ +package useragent + +import ( + "fmt" + "runtime" + + "github.com/hashicorp/packer/version" +) + +var ( + // projectURL is the project URL. + projectURL = "https://www.packer.io/" + + // rt is the runtime - variable for tests. + rt = runtime.Version() + + // versionFunc is the func that returns the current version. This is a + // function to take into account the different build processes and distinguish + // between enterprise and oss builds. + versionFunc = func() string { + return version.FormattedVersion() + } +) + +// String returns the consistent user-agent string for Packer. +func String() string { + return fmt.Sprintf("Packer/%s (+%s; %s)", + versionFunc(), projectURL, rt) +} diff --git a/helper/useragent/useragent_test.go b/helper/useragent/useragent_test.go new file mode 100644 index 000000000..b193f77eb --- /dev/null +++ b/helper/useragent/useragent_test.go @@ -0,0 +1,18 @@ +package useragent + +import ( + "testing" +) + +func TestUserAgent(t *testing.T) { + projectURL = "https://packer-test.com" + rt = "go5.0" + versionFunc = func() string { return "1.2.3" } + + act := String() + + exp := "Packer/1.2.3 (+https://packer-test.com; go5.0)" + if exp != act { + t.Errorf("expected %q to be %q", act, exp) + } +} From b193b96f76818fc431cef346c6e8c056823d451c Mon Sep 17 00:00:00 2001 From: Seth Vargo Date: Thu, 5 Apr 2018 14:25:11 -0400 Subject: [PATCH 0763/1216] Include arch and os --- helper/useragent/useragent.go | 10 ++++++++-- helper/useragent/useragent_test.go | 4 +++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/helper/useragent/useragent.go b/helper/useragent/useragent.go index bd433d0cb..575d77aba 100644 --- a/helper/useragent/useragent.go +++ b/helper/useragent/useragent.go @@ -14,6 +14,12 @@ var ( // rt is the runtime - variable for tests. rt = runtime.Version() + // goos is the os - variable for tests. + goos = runtime.GOOS + + // goarch is the architecture - variable for tests. + goarch = runtime.GOARCH + // versionFunc is the func that returns the current version. This is a // function to take into account the different build processes and distinguish // between enterprise and oss builds. @@ -24,6 +30,6 @@ var ( // String returns the consistent user-agent string for Packer. func String() string { - return fmt.Sprintf("Packer/%s (+%s; %s)", - versionFunc(), projectURL, rt) + return fmt.Sprintf("Packer/%s (+%s; %s; %s/%s)", + versionFunc(), projectURL, rt, goos, goarch) } diff --git a/helper/useragent/useragent_test.go b/helper/useragent/useragent_test.go index b193f77eb..b792753c6 100644 --- a/helper/useragent/useragent_test.go +++ b/helper/useragent/useragent_test.go @@ -7,11 +7,13 @@ import ( func TestUserAgent(t *testing.T) { projectURL = "https://packer-test.com" rt = "go5.0" + goos = "linux" + goarch = "amd64" versionFunc = func() string { return "1.2.3" } act := String() - exp := "Packer/1.2.3 (+https://packer-test.com; go5.0)" + exp := "Packer/1.2.3 (+https://packer-test.com; go5.0; linux/amd64)" if exp != act { t.Errorf("expected %q to be %q", act, exp) } From 5eeac07b6327121ea3210b6520346b9bc067aac8 Mon Sep 17 00:00:00 2001 From: Seth Vargo Date: Thu, 5 Apr 2018 14:28:46 -0400 Subject: [PATCH 0764/1216] Include existing azure user agent --- builder/azure/arm/azure_client.go | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/builder/azure/arm/azure_client.go b/builder/azure/arm/azure_client.go index faa0a4766..bb272435e 100644 --- a/builder/azure/arm/azure_client.go +++ b/builder/azure/arm/azure_client.go @@ -2,6 +2,7 @@ package arm import ( "encoding/json" + "fmt" "math" "net/http" "net/url" @@ -132,67 +133,67 @@ func NewAzureClient(subscriptionID, resourceGroupName, storageAccountName string azureClient.DeploymentsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) azureClient.DeploymentsClient.RequestInspector = withInspection(maxlen) azureClient.DeploymentsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.DeploymentsClient.UserAgent = useragent.String() + azureClient.DeploymentsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.DeploymentsClient.UserAgent) azureClient.DeploymentOperationsClient = resources.NewDeploymentOperationsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) azureClient.DeploymentOperationsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) azureClient.DeploymentOperationsClient.RequestInspector = withInspection(maxlen) azureClient.DeploymentOperationsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.DeploymentOperationsClient.UserAgent = useragent.String() + azureClient.DeploymentOperationsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.DeploymentOperationsClient.UserAgent) azureClient.DisksClient = disk.NewDisksClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) azureClient.DisksClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) azureClient.DisksClient.RequestInspector = withInspection(maxlen) azureClient.DisksClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.DisksClient.UserAgent = useragent.String() + azureClient.DisksClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.DisksClient.UserAgent) azureClient.GroupsClient = resources.NewGroupsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) azureClient.GroupsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) azureClient.GroupsClient.RequestInspector = withInspection(maxlen) azureClient.GroupsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.GroupsClient.UserAgent = useragent.String() + azureClient.GroupsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.GroupsClient.UserAgent) azureClient.ImagesClient = compute.NewImagesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) azureClient.ImagesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) azureClient.ImagesClient.RequestInspector = withInspection(maxlen) azureClient.ImagesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.ImagesClient.UserAgent = useragent.String() + azureClient.ImagesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.ImagesClient.UserAgent) azureClient.InterfacesClient = network.NewInterfacesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) azureClient.InterfacesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) azureClient.InterfacesClient.RequestInspector = withInspection(maxlen) azureClient.InterfacesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.InterfacesClient.UserAgent = useragent.String() + azureClient.InterfacesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.InterfacesClient.UserAgent) azureClient.SubnetsClient = network.NewSubnetsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) azureClient.SubnetsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) azureClient.SubnetsClient.RequestInspector = withInspection(maxlen) azureClient.SubnetsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.SubnetsClient.UserAgent = useragent.String() + azureClient.SubnetsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.SubnetsClient.UserAgent) azureClient.VirtualNetworksClient = network.NewVirtualNetworksClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) azureClient.VirtualNetworksClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) azureClient.VirtualNetworksClient.RequestInspector = withInspection(maxlen) azureClient.VirtualNetworksClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.VirtualNetworksClient.UserAgent = useragent.String() + azureClient.VirtualNetworksClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.VirtualNetworksClient.UserAgent) azureClient.PublicIPAddressesClient = network.NewPublicIPAddressesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) azureClient.PublicIPAddressesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) azureClient.PublicIPAddressesClient.RequestInspector = withInspection(maxlen) azureClient.PublicIPAddressesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.PublicIPAddressesClient.UserAgent = useragent.String() + azureClient.PublicIPAddressesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.PublicIPAddressesClient.UserAgent) azureClient.VirtualMachinesClient = compute.NewVirtualMachinesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) azureClient.VirtualMachinesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) azureClient.VirtualMachinesClient.RequestInspector = withInspection(maxlen) azureClient.VirtualMachinesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), templateCapture(azureClient), errorCapture(azureClient)) - azureClient.VirtualMachinesClient.UserAgent = useragent.String() + azureClient.VirtualMachinesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.VirtualMachinesClient.UserAgent) azureClient.AccountsClient = armStorage.NewAccountsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) azureClient.AccountsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) azureClient.AccountsClient.RequestInspector = withInspection(maxlen) azureClient.AccountsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.AccountsClient.UserAgent = useragent.String() + azureClient.AccountsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.AccountsClient.UserAgent) keyVaultURL, err := url.Parse(cloud.KeyVaultEndpoint) if err != nil { @@ -203,7 +204,7 @@ func NewAzureClient(subscriptionID, resourceGroupName, storageAccountName string azureClient.VaultClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalTokenVault) azureClient.VaultClient.RequestInspector = withInspection(maxlen) azureClient.VaultClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.VaultClient.UserAgent = useragent.String() + azureClient.VaultClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.VaultClient.UserAgent) // TODO(boumenot) - SDK still does not have a full KeyVault client. // There are two ways that KeyVault has to be accessed, and each one has their own SPN. An authenticated SPN @@ -217,7 +218,7 @@ func NewAzureClient(subscriptionID, resourceGroupName, storageAccountName string azureClient.VaultClientDelete.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) azureClient.VaultClientDelete.RequestInspector = withInspection(maxlen) azureClient.VaultClientDelete.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) - azureClient.VaultClientDelete.UserAgent = useragent.String() + azureClient.VaultClientDelete.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.VaultClientDelete.UserAgent) // If this is a managed disk build, this should be ignored. if resourceGroupName != "" && storageAccountName != "" { From 302b1988a5a4e7c90589c898f1120b06875384e8 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 5 Apr 2018 13:29:32 -0700 Subject: [PATCH 0765/1216] Restrict deregistered AMIs to those owned by self. --- builder/amazon/common/step_deregister_ami.go | 1 + 1 file changed, 1 insertion(+) diff --git a/builder/amazon/common/step_deregister_ami.go b/builder/amazon/common/step_deregister_ami.go index 9c176d3c7..eb8b957b5 100644 --- a/builder/amazon/common/step_deregister_ami.go +++ b/builder/amazon/common/step_deregister_ami.go @@ -41,6 +41,7 @@ func (s *StepDeregisterAMI) Run(_ context.Context, state multistep.StateBag) mul )) resp, err := regionconn.DescribeImages(&ec2.DescribeImagesInput{ + Owners: aws.StringSlice([]string{"self"}), Filters: []*ec2.Filter{{ Name: aws.String("name"), Values: []*string{aws.String(s.AMIName)}, From d236f26439f22df68c4aecb46c260c88387e6018 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 5 Apr 2018 15:24:54 -0700 Subject: [PATCH 0766/1216] allow users to access winrm password in powershell and elevated powershell provisioners --- builder/azure/arm/builder.go | 3 +++ builder/azure/arm/config.go | 4 ++++ builder/azure/arm/step_save_winrm_password.go | 22 +++++++++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 builder/azure/arm/step_save_winrm_password.go diff --git a/builder/azure/arm/builder.go b/builder/azure/arm/builder.go index b81ccf94f..8fb4389ad 100644 --- a/builder/azure/arm/builder.go +++ b/builder/azure/arm/builder.go @@ -176,6 +176,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe NewStepValidateTemplate(azureClient, ui, b.config, GetVirtualMachineDeployment), NewStepDeployTemplate(azureClient, ui, b.config, deploymentName, GetVirtualMachineDeployment), NewStepGetIPAddress(azureClient, ui, endpointConnectType), + &StepSaveWinRMPassword{ + Password: b.config.tmpAdminPassword, + }, &communicator.StepConnectWinRM{ Config: &b.config.Comm, Host: func(stateBag multistep.StateBag) (string, error) { diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index 8f795f353..5d789708d 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -22,6 +22,7 @@ import ( "github.com/hashicorp/packer/builder/azure/common/constants" "github.com/hashicorp/packer/builder/azure/pkcs12" "github.com/hashicorp/packer/common" + commonhelper "github.com/hashicorp/packer/helper/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" @@ -357,6 +358,9 @@ func setRuntimeValues(c *Config) { var tempName = NewTempName() c.tmpAdminPassword = tempName.AdminPassword + // store so that we can access this later during provisioning + commonhelper.SetSharedState("winrm_password", c.tmpAdminPassword) + c.tmpCertificatePassword = tempName.CertificatePassword if c.TempComputeName == "" { c.tmpComputeName = tempName.ComputeName diff --git a/builder/azure/arm/step_save_winrm_password.go b/builder/azure/arm/step_save_winrm_password.go new file mode 100644 index 000000000..e28fcd771 --- /dev/null +++ b/builder/azure/arm/step_save_winrm_password.go @@ -0,0 +1,22 @@ +package arm + +import ( + "context" + + commonhelper "github.com/hashicorp/packer/helper/common" + "github.com/hashicorp/packer/helper/multistep" +) + +type StepSaveWinRMPassword struct { + Password string +} + +func (s *StepSaveWinRMPassword) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { + // store so that we can access this later during provisioning + commonhelper.SetSharedState("winrm_password", s.Password) + return multistep.ActionContinue +} + +func (s *StepSaveWinRMPassword) Cleanup(multistep.StateBag) { + commonhelper.RemoveSharedStateFile("winrm_password") +} From bb53d5f6ccb6e6a4e762876415e88b1e0715dc84 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 5 Apr 2018 15:26:19 -0700 Subject: [PATCH 0767/1216] update docs to include Azure. --- website/source/docs/provisioners/powershell.html.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/website/source/docs/provisioners/powershell.html.md b/website/source/docs/provisioners/powershell.html.md index 2051efff3..0a97d41ac 100644 --- a/website/source/docs/provisioners/powershell.html.md +++ b/website/source/docs/provisioners/powershell.html.md @@ -72,10 +72,9 @@ Optional parameters: - `environment_vars` (array of strings) - An array of key/value pairs to inject prior to the execute\_command. The format should be `key=value`. Packer injects some environmental variables by default into the - environment, as well, which are covered in the section below. If you are - using AWS and would like to use the randomly-generated unique - If you are running on AWS and would like to access the AWS-generated - Administrator password that Packer uses to connect to the instance via + environment, as well, which are covered in the section below. + If you are running on AWS or Azure and would like to access the generated + password that Packer uses to connect to the instance via WinRM, you can use the template variable `{{.WinRMPassword}}` to set this as an environment variable. For example: From 6da6ea74de37e1be37fd3ef99424ef6c79c41d92 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 5 Apr 2018 16:12:20 -0700 Subject: [PATCH 0768/1216] add warning to generated feature in file provisioner --- website/source/docs/provisioners/file.html.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/website/source/docs/provisioners/file.html.md b/website/source/docs/provisioners/file.html.md index 0dc2e1d01..d2baa868b 100644 --- a/website/source/docs/provisioners/file.html.md +++ b/website/source/docs/provisioners/file.html.md @@ -48,8 +48,13 @@ The available configuration options are listed below. All elements are required. "upload". If it is set to "download" then the file "source" in the machine will be downloaded locally to "destination" -- `generated` (boolean) - If true, check the file existence only before uploading. - This allows to upload files created on-the-fly. This defaults to false. +- `generated` (boolean) - For advanced users only. If true, check the file + existence only before uploading, rather than upon pre-build validation. + This allows to upload files created on-the-fly. This defaults to false. We + don't recommend using this feature, since it can cause Packer to become + dependent on system state. We would prefer you generate your files before + the Packer run, but realize that there are situations where this may be + unavoidable. ## Directory Uploads From f094b3be8506ba98dd00811ce50dc99774e83f1b Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 5 Apr 2018 16:32:03 -0700 Subject: [PATCH 0769/1216] update changelog --- CHANGELOG.md | 7 +++++++ builder/amazon/common/step_deregister_ami.go | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6aabaee88..1d20a9f6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,15 @@ ## (UNRELEASED) ### BUG FIXES: + * post-processor/vagrant: Large VMDKs should no longer show a 0-byte size on OS X. [GH-6084] +### IMPROVEMENTS: + +* builder/amazon: Setting `force_delete` will only delete AMIs owned by the + user. This should prevent failures where we try to delete an AMI with + a matching name, but owned by someone else. [GH-6111] + ## 1.2.2 (March 26, 2018) ### BUG FIXES: diff --git a/builder/amazon/common/step_deregister_ami.go b/builder/amazon/common/step_deregister_ami.go index eb8b957b5..8a82d7c7e 100644 --- a/builder/amazon/common/step_deregister_ami.go +++ b/builder/amazon/common/step_deregister_ami.go @@ -44,7 +44,7 @@ func (s *StepDeregisterAMI) Run(_ context.Context, state multistep.StateBag) mul Owners: aws.StringSlice([]string{"self"}), Filters: []*ec2.Filter{{ Name: aws.String("name"), - Values: []*string{aws.String(s.AMIName)}, + Values: aws.StringSlice([]string{s.AMIName}), }}}) if err != nil { From 6855216387594566f37672a0962b6e7d18b192d9 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 5 Apr 2018 10:04:12 -0700 Subject: [PATCH 0770/1216] force QueryEscape to escape spaces as %20 instead of as + for ovftool. --- post-processor/vsphere/post-processor.go | 13 ++++++-- post-processor/vsphere/post-processor_test.go | 31 +++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/post-processor/vsphere/post-processor.go b/post-processor/vsphere/post-processor.go index b9702fe82..fcf209ea7 100644 --- a/post-processor/vsphere/post-processor.go +++ b/post-processor/vsphere/post-processor.go @@ -110,9 +110,9 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac return nil, false, fmt.Errorf("VMX, OVF or OVA file not found") } - password := url.QueryEscape(p.config.Password) + password := escapeWithSpaces(p.config.Password) ovftool_uri := fmt.Sprintf("vi://%s:%s@%s/%s/host/%s", - url.QueryEscape(p.config.Username), + escapeWithSpaces(p.config.Username), password, p.config.Host, p.config.Datacenter, @@ -146,7 +146,7 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac } func (p *PostProcessor) filterLog(s string) string { - password := url.QueryEscape(p.config.Password) + password := escapeWithSpaces(p.config.Password) return strings.Replace(s, password, "", -1) } @@ -186,3 +186,10 @@ func (p *PostProcessor) BuildArgs(source, ovftool_uri string) ([]string, error) return args, nil } + +// Encode everything except for + signs +func escapeWithSpaces(stringToEscape string) string { + escapedString := url.QueryEscape(stringToEscape) + escapedString = strings.Replace(escapedString, "+", `%20`, -1) + return escapedString +} diff --git a/post-processor/vsphere/post-processor_test.go b/post-processor/vsphere/post-processor_test.go index 61b58789b..65b99f9a7 100644 --- a/post-processor/vsphere/post-processor_test.go +++ b/post-processor/vsphere/post-processor_test.go @@ -40,3 +40,34 @@ func TestArgs(t *testing.T) { t.Logf("ovftool %s", strings.Join(args, " ")) } + +func TestEscaping(t *testing.T) { + type escapeCases struct { + Input string + Expected string + } + + cases := []escapeCases{ + {`this has spaces`, `this%20has%20spaces`}, + {`exclaimation_!`, `exclaimation_%21`}, + {`hash_#_dollar_$`, `hash_%23_dollar_%24`}, + {`ampersand_&awesome`, `ampersand_%26awesome`}, + {`single_quote_'_and_another_'`, `single_quote_%27_and_another_%27`}, + {`open_paren_(_close_paren_)`, `open_paren_%28_close_paren_%29`}, + {`asterisk_*_plus_+`, `asterisk_%2A_plus_%2B`}, + {`comma_,slash_/`, `comma_%2Cslash_%2F`}, + {`colon_:semicolon_;`, `colon_%3Asemicolon_%3B`}, + {`equal_=question_?`, `equal_%3Dquestion_%3F`}, + {`at_@`, `at_%40`}, + {`open_bracket_[closed_bracket]`, `open_bracket_%5Bclosed_bracket%5D`}, + {`user:password with $paces@host/name.foo`, `user%3Apassword%20with%20%24paces%40host%2Fname.foo`}, + } + for _, escapeCase := range cases { + received := escapeWithSpaces(escapeCase.Input) + + if escapeCase.Expected != received { + t.Errorf("Error escaping URL; expected %s got %s", escapeCase.Expected, received) + } + } + +} From 049e1bbf73bcfdc22a2e2b79714aaa93dbdb756f Mon Sep 17 00:00:00 2001 From: Matthew Patton Date: Wed, 1 Feb 2017 22:57:36 -0500 Subject: [PATCH 0771/1216] too many files for shell during Make, convert .go and .sh to EOL=lf --- .gitattributes | 6 ++++-- Makefile | 9 ++++++--- scripts/gofmtcheck.sh | 14 ++++---------- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/.gitattributes b/.gitattributes index 7230f36f4..fba3b1303 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,4 @@ - -common/test-fixtures/root/* eol=lf \ No newline at end of file +* text=auto +*.go text eol=lf +*.sh text eol=lf +common/test-fixtures/root/* eol=lf diff --git a/Makefile b/Makefile index 54852ce26..cb48d7e2c 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ VET?=$(shell ls -d */ | grep -v vendor | grep -v website) GITSHA:=$(shell git rev-parse HEAD) # Get the current local branch name from git (if we can, this may be blank) GITBRANCH:=$(shell git symbolic-ref --short HEAD 2>/dev/null) -GOFMT_FILES?=$$(find . -not -path "./vendor/*" -name "*.go") +GOFMT_FILES?=find . -path ./vendor -prune -o -name "*.go" GOOS=$(shell go env GOOS) GOARCH=$(shell go env GOARCH) GOPATH=$(shell go env GOPATH) @@ -57,10 +57,13 @@ dev: deps ## Build and install a development build @cp $(GOPATH)/bin/packer pkg/$(GOOS)_$(GOARCH) fmt: ## Format Go code - @gofmt -w -s $(GOFMT_FILES) + @$(GOFMT_FILES) | xargs gofmt -w -s fmt-check: ## Check go code formatting - $(CURDIR)/scripts/gofmtcheck.sh $(GOFMT_FILES) + @echo "==> Checking that code complies with gofmt requirements..." + @echo "You can use the command: \`make fmt\` to reformat code." + @$(GOFMT_FILES) | xargs $(CURDIR)/scripts/gofmtcheck.sh + @echo "Check complete." fmt-docs: @find ./website/source/docs -name "*.md" -exec pandoc --wrap auto --columns 79 --atx-headers -s -f "markdown_github+yaml_metadata_block" -t "markdown_github+yaml_metadata_block" {} -o {} \; diff --git a/scripts/gofmtcheck.sh b/scripts/gofmtcheck.sh index 5b99bcdc4..cc6deb81e 100755 --- a/scripts/gofmtcheck.sh +++ b/scripts/gofmtcheck.sh @@ -1,14 +1,8 @@ #!/usr/bin/env bash -# Check gofmt -echo "==> Checking that code complies with gofmt requirements..." -gofmt_files=$(gofmt -s -l ${@}) -if [[ -n ${gofmt_files} ]]; then - echo 'gofmt needs running on the following files:' - echo "${gofmt_files}" - echo "You can use the command: \`make fmt\` to reformat code." - exit 1 -fi -echo "Check passed." +for f in $@; do + [ -n "`dos2unix 2>/dev/null < $f | gofmt -s -d`" ] && echo $f +done +# always return success or else 'make' will abort exit 0 From 1d0cf3d9094126b0a278674fe8b758e1df967869 Mon Sep 17 00:00:00 2001 From: Matthew Patton Date: Thu, 2 Feb 2017 02:05:09 -0500 Subject: [PATCH 0772/1216] handle missing GOPATH and cygwin considerations --- scripts/build.sh | 37 +++++++++++++------------------------ 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/scripts/build.sh b/scripts/build.sh index 84bd7f67d..4c02cedb3 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -21,44 +21,33 @@ rm -f bin/* rm -rf pkg/* mkdir -p bin/ +OLDIFS=$IFS +IFS=: +case $(uname) in + MINGW*|MSYS*) + IFS=";" + ;; +esac + # Build! echo "==> Building..." set +e -gox \ +${GOX:-$GOPATH/bin/gox} \ -os="${XC_OS}" \ -arch="${XC_ARCH}" \ -osarch="!darwin/arm !darwin/arm64" \ -ldflags "${GOLDFLAGS}" \ -output "pkg/{{.OS}}_{{.Arch}}/packer" \ . -set -e -# Move all the compiled things to the $GOPATH/bin -GOPATH=${GOPATH:-$(go env GOPATH)} -case $(uname) in - CYGWIN*) - GOPATH="$(cygpath $GOPATH)" - ;; -esac -OLDIFS=$IFS -IFS=: -case $(uname) in - MINGW*) - IFS=";" - ;; - MSYS*) - IFS=";" - ;; -esac -MAIN_GOPATH=($GOPATH) IFS=$OLDIFS +set -e # Copy our OS/Arch to the bin/ directory echo "==> Copying binaries for this platform..." -DEV_PLATFORM="./pkg/$(go env GOOS)_$(go env GOARCH)" -for F in $(find ${DEV_PLATFORM} -mindepth 1 -maxdepth 1 -type f); do - cp ${F} bin/ - cp ${F} ${MAIN_GOPATH}/bin/ +for F in $(find pkg/$(go env GOOS)_$(go env GOARCH) -mindepth 1 -maxdepth 1 -type f); do + cp -v ${F} bin/ + cp -v ${F} ${GOPATH}/bin/ done # Done! From 9a2d5885cedd9eec42d9e95abdd334da0c1de18e Mon Sep 17 00:00:00 2001 From: Matthew Patton Date: Fri, 29 Dec 2017 17:46:14 -0500 Subject: [PATCH 0773/1216] ignore Eclipse project files --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 6ab3cdfb9..0f310de91 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,5 @@ packer-test*.log .idea/ *.iml Thumbs.db -/packer.exe \ No newline at end of file +/packer.exe +.project From 6a85f5aed7776ea562a67a589971a56385a81e09 Mon Sep 17 00:00:00 2001 From: Matthew Patton Date: Thu, 2 Feb 2017 02:05:09 -0500 Subject: [PATCH 0774/1216] handle missing GOPATH and cygwin considerations --- scripts/build.sh | 79 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 66 insertions(+), 13 deletions(-) diff --git a/scripts/build.sh b/scripts/build.sh index 4c02cedb3..5533cf939 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -1,6 +1,10 @@ #!/usr/bin/env bash -# + # This script builds the application from source for multiple platforms. +# Determine the arch/os combos we're building for +ALL_XC_ARCH="386 amd64 arm arm64 ppc64le" +ALL_XC_OS="linux darwin windows freebsd openbsd solaris" + set -e # Get the parent directory of where this script is. @@ -21,33 +25,82 @@ rm -f bin/* rm -rf pkg/* mkdir -p bin/ -OLDIFS=$IFS -IFS=: -case $(uname) in - MINGW*|MSYS*) - IFS=";" +# helpers for Cygwin-hosted builds +: ${OSTYPE:=`uname`} + +case $OSTYPE in + MINGW*|MSYS*|cygwin|CYGWIN*) + # cygwin only translates ';' to ':' on select environment variables + PATHSEP=';' ;; + *) PATHSEP=':' esac +function convert_path() { + local flag + [ "${1:0:1}" = '-' ] && { flag="$1"; shift; } + + [ -n "$1" ] || return 0 + case ${OSTYPE:-`uname`} in + cygwin|CYGWIN*) + cygpath $flag -- "$1" + ;; + *) echo "$1" + esac +} + +# XXX works in MINGW? +which go &>/dev/null || PATH+=":`convert_path "${GOROOT:?}"`/bin" + +OLDIFS="$IFS" + +# make sure GOPATH is consistent - Windows binaries can't handle Cygwin-style paths +IFS="$PATHSEP" +for d in ${GOPATH:-$(go env GOPATH)}; do + _GOPATH+="${_GOPATH:+$PATHSEP}$(convert_path --windows "$d")" +done +GOPATH="$_GOPATH" + +# locate 'gox' and traverse GOPATH if needed +which "${GOX:=gox}" &>/dev/null || { + for d in $GOPATH; do + GOX="$(convert_path --unix "$d")/bin/gox" + [ -x "$GOX" ] && break || unset GOX + done +} +IFS="$OLDIFS" + # Build! echo "==> Building..." + +# If in dev mode, only build for ourself +if [ -n "${PACKER_DEV+x}" ]; then + XC_OS=$(go env GOOS) + XC_ARCH=$(go env GOARCH) +fi + set +e -${GOX:-$GOPATH/bin/gox} \ - -os="${XC_OS}" \ - -arch="${XC_ARCH}" \ +${GOX:?command not found} \ + -os="${XC_OS:-$ALL_XC_OS}" \ + -arch="${XC_ARCH:-$ALL_XC_ARCH}" \ -osarch="!darwin/arm !darwin/arm64" \ -ldflags "${GOLDFLAGS}" \ -output "pkg/{{.OS}}_{{.Arch}}/packer" \ . - -IFS=$OLDIFS set -e +# trim GOPATH to first element +IFS="$PATHSEP" +MAIN_GOPATH=($GOPATH) +MAIN_GOPATH="$(convert_path --unix "$MAIN_GOPATH")" +IFS=$OLDIFS + # Copy our OS/Arch to the bin/ directory echo "==> Copying binaries for this platform..." -for F in $(find pkg/$(go env GOOS)_$(go env GOARCH) -mindepth 1 -maxdepth 1 -type f); do +DEV_PLATFORM="./pkg/$(go env GOOS)_$(go env GOARCH)" +for F in $(find ${DEV_PLATFORM} -mindepth 1 -maxdepth 1 -type f); do cp -v ${F} bin/ - cp -v ${F} ${GOPATH}/bin/ + cp -v ${F} ${MAIN_GOPATH}/bin/ done # Done! From d5bf9277cebb6d334e0db93a601301d9f4ee38ea Mon Sep 17 00:00:00 2001 From: Matthew Patton Date: Sat, 7 Apr 2018 05:33:46 -0400 Subject: [PATCH 0775/1216] remove rebase duplicate --- scripts/build.sh | 4 ---- 1 file changed, 4 deletions(-) diff --git a/scripts/build.sh b/scripts/build.sh index 5533cf939..19ed1ad09 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -15,10 +15,6 @@ DIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )" # Change into that directory cd $DIR -# Determine the arch/os combos we're building for -XC_ARCH=${XC_ARCH:-"386 amd64 arm arm64 ppc64le"} -XC_OS=${XC_OS:-linux darwin windows freebsd openbsd solaris} - # Delete the old dir echo "==> Removing old directory..." rm -f bin/* From 6bb173045a3c42059aa5609bf8021b198857a69e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Fortunato?= Date: Sat, 7 Apr 2018 22:07:49 +0200 Subject: [PATCH 0776/1216] fix: documentation scaleway - update commercial type name --- website/source/docs/builders/scaleway.html.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/website/source/docs/builders/scaleway.html.md b/website/source/docs/builders/scaleway.html.md index b1864bdb0..c712ce2c6 100644 --- a/website/source/docs/builders/scaleway.html.md +++ b/website/source/docs/builders/scaleway.html.md @@ -56,10 +56,10 @@ builder. or `ams1`). Consequently, this is the region where the snapshot will be available. -- `commercial_type` (string) - The name of the server commercial type: `C1`, - `C2S`, `C2M`, `C2L`, `X64-2GB`, `X64-4GB`, `X64-8GB`, `X64-15GB`, - `X64-30GB`, `X64-60GB`, `X64-120GB`, `ARM64-2GB`, `ARM64-4GB`, `ARM64-8GB`, - `ARM64-16GB`, `ARM64-32GB`, `ARM64-64GB`, `ARM64-128GB` +- `commercial_type` (string) - The name of the server commercial type: + `ARM64-128GB`,`ARM64-16GB`,`ARM64-2GB`,`ARM64-32GB`,`ARM64-4GB`, `ARM64-64GB`, + `ARM64-8GB`,`C1`,`C2L`,`C2M`,`C2S`,`VC1L`,`VC1M`,`VC1S`, + `X64-120GB`,`X64-15GB`,`X64-30GB`,`X64-60GB` ### Optional: @@ -84,7 +84,7 @@ access tokens: "api_token": "YOUR TOKEN", "image": "UUID OF THE BASE IMAGE", "region": "par1", - "commercial_type": "X64-2GB", + "commercial_type": "VC1S", "ssh_username": "root", "ssh_private_key_file": "~/.ssh/id_rsa" } From 554b2b4a5dc88296032179ff795a0bd5872bf63b Mon Sep 17 00:00:00 2001 From: Matthew Patton Date: Mon, 9 Apr 2018 19:47:41 -0400 Subject: [PATCH 0777/1216] ignore errors during Find --- scripts/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build.sh b/scripts/build.sh index 19ed1ad09..4f55b9893 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -94,7 +94,7 @@ IFS=$OLDIFS # Copy our OS/Arch to the bin/ directory echo "==> Copying binaries for this platform..." DEV_PLATFORM="./pkg/$(go env GOOS)_$(go env GOARCH)" -for F in $(find ${DEV_PLATFORM} -mindepth 1 -maxdepth 1 -type f); do +for F in $(find ${DEV_PLATFORM} -mindepth 1 -maxdepth 1 -type f 2>/dev/null); do cp -v ${F} bin/ cp -v ${F} ${MAIN_GOPATH}/bin/ done From ee1ff3132d22035faeefefb81797f61421b963bd Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Tue, 10 Apr 2018 08:13:06 -0700 Subject: [PATCH 0778/1216] remove attempt to discover whether destination is a directory from upload function in various communicators --- builder/docker/communicator.go | 16 ----------- communicator/ssh/communicator.go | 49 -------------------------------- 2 files changed, 65 deletions(-) diff --git a/builder/docker/communicator.go b/builder/docker/communicator.go index 7b0ecae07..e898ecd1c 100644 --- a/builder/docker/communicator.go +++ b/builder/docker/communicator.go @@ -105,22 +105,6 @@ func (c *Communicator) uploadReader(dst string, src io.Reader) error { // uploadFile uses docker cp to copy the file from the host to the container func (c *Communicator) uploadFile(dst string, src io.Reader, fi *os.FileInfo) error { - // find out if it's a directory - testDirectoryCommand := fmt.Sprintf(`test -d "%s"`, dst) - cmd := &packer.RemoteCmd{Command: testDirectoryCommand} - - err := c.Start(cmd) - - if err != nil { - log.Printf("Unable to check whether remote path is a dir: %s", err) - return err - } - cmd.Wait() - if cmd.ExitStatus == 0 { - log.Printf("path is a directory; copying file into directory.") - dst = filepath.Join(dst, filepath.Base((*fi).Name())) - } - // command format: docker cp /path/to/infile containerid:/path/to/outfile log.Printf("Copying to %s on container %s.", dst, c.ContainerID) diff --git a/communicator/ssh/communicator.go b/communicator/ssh/communicator.go index 218c11be6..6545d1d5c 100644 --- a/communicator/ssh/communicator.go +++ b/communicator/ssh/communicator.go @@ -410,27 +410,6 @@ func (c *comm) sftpUploadSession(path string, input io.Reader, fi *os.FileInfo) func (c *comm) sftpUploadFile(path string, input io.Reader, client *sftp.Client, fi *os.FileInfo) error { log.Printf("[DEBUG] sftp: uploading %s", path) - - // find out if destination is a directory (this is to replicate rsync behavior) - testDirectoryCommand := fmt.Sprintf(`test -d "%s"`, path) - - cmd := &packer.RemoteCmd{ - Command: testDirectoryCommand, - } - - err := c.Start(cmd) - - if err != nil { - log.Printf("[ERROR] Unable to check whether remote path is a dir: %s", err) - return err - } - cmd.Wait() - if cmd.ExitStatus == 0 { - return fmt.Errorf( - "Destination path (%s) is a directory that already exists. "+ - "Please ensure the destination is writable.", path) - } - f, err := client.Create(path) if err != nil { return err @@ -586,34 +565,6 @@ func (c *comm) scpUploadSession(path string, input io.Reader, fi *os.FileInfo) e target_dir := filepath.Dir(path) target_file := filepath.Base(path) - // find out if destination is a directory (this is to replicate rsync behavior) - testDirectoryCommand := fmt.Sprintf(`test -d "%s"`, path) - var stdout, stderr bytes.Buffer - cmd := &packer.RemoteCmd{ - Command: testDirectoryCommand, - Stdout: &stdout, - Stderr: &stderr, - } - - err := c.Start(cmd) - - if err != nil { - log.Printf("[ERROR] Unable to check whether remote path is a dir: %s", err) - return err - } - cmd.Wait() - if stdout.Len() > 0 { - return fmt.Errorf("%s", stdout.Bytes()) - } - if stderr.Len() > 0 { - return fmt.Errorf("%s", stderr.Bytes()) - } - if cmd.ExitStatus == 0 { - return fmt.Errorf( - "Destination path (%s) is a directory that already exists. "+ - "Please ensure the destination is writable.", path) - } - // On windows, filepath.Dir uses backslash separators (ie. "\tmp"). // This does not work when the target host is unix. Switch to forward slash // which works for unix and windows From 545b2c4e0f251545a94708289c1d66cbd709e20e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loi=CC=88c=20Carr?= Date: Thu, 12 Apr 2018 09:35:46 +0200 Subject: [PATCH 0779/1216] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d20a9f6d..9e5e744e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### BUG FIXES: * post-processor/vagrant: Large VMDKs should no longer show a 0-byte size on OS X. [GH-6084] +* builder/scaleway: Fix compilation issues on solaris/amd64. [GH-6069] ### IMPROVEMENTS: From 0f0fc1b99c29cae5f12ef0c56cadb8fe8d59ae52 Mon Sep 17 00:00:00 2001 From: andrew-best-diaxion Date: Fri, 13 Apr 2018 14:02:55 +1000 Subject: [PATCH 0780/1216] azure-cli output config check added Adds a check to contrib/azure-setup.sh which ensure the users azure-cli is configured to output json. --- contrib/azure-setup.sh | 394 +++++++++++++++++++++-------------------- 1 file changed, 202 insertions(+), 192 deletions(-) diff --git a/contrib/azure-setup.sh b/contrib/azure-setup.sh index 6149b38d0..40ad49697 100755 --- a/contrib/azure-setup.sh +++ b/contrib/azure-setup.sh @@ -12,257 +12,267 @@ azure_tenant_id= # Derived from the account after login location= azure_object_id= azureversion= +azurecliconfig= create_sleep=10 showhelp() { - echo "azure-setup" - echo "" - echo " azure-setup helps you generate packer credentials for azure" - echo "" - echo " The script creates a resource group, storage account, application" - echo " (client), service principal, and permissions and displays a snippet" - echo " for use in your packer templates." - echo "" - echo " For simplicity we make a lot of assumptions and choose reasonable" - echo " defaults. If you want more control over what happens, please use" - echo " the azure-cli directly." - echo "" - echo " Note that you must already have an Azure account, username," - echo " password, and subscription. You can create those here:" - echo "" - echo " - https://account.windowsazure.com/" - echo "" - echo "REQUIREMENTS" - echo "" - echo " - azure-cli" - echo " - jq" - echo "" - echo " Use the requirements command (below) for more info." - echo "" - echo "USAGE" - echo "" - echo " ./azure-setup.sh requirements" - echo " ./azure-setup.sh setup" - echo "" + echo "azure-setup" + echo "" + echo " azure-setup helps you generate packer credentials for azure" + echo "" + echo " The script creates a resource group, storage account, application" + echo " (client), service principal, and permissions and displays a snippet" + echo " for use in your packer templates." + echo "" + echo " For simplicity we make a lot of assumptions and choose reasonable" + echo " defaults. If you want more control over what happens, please use" + echo " the azure-cli directly." + echo "" + echo " Note that you must already have an Azure account, username," + echo " password, and subscription. You can create those here:" + echo "" + echo " - https://account.windowsazure.com/" + echo "" + echo "REQUIREMENTS" + echo "" + echo " - azure-cli" + echo " - jq" + echo "" + echo " Use the requirements command (below) for more info." + echo "" + echo "USAGE" + echo "" + echo " ./azure-setup.sh requirements" + echo " ./azure-setup.sh setup" + echo "" } requirements() { - found=0 + found=0 - azureversion=$(az --version) - if [ $? -eq 0 ]; then - found=$((found + 1)) - echo "Found azure-cli version: $azureversion" - else - echo "azure-cli is missing. Please install azure-cli from" - echo "https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest" - echo "Alternatively, you can use the Cloud Shell https://docs.microsoft.com/en-us/azure/cloud-shell/overview right from the Azure Portal or even VS Code." - fi + azureversion=$(az --version) + if [ $? -eq 0 ]; then + found=$((found + 1)) + echo "Found azure-cli version: $azureversion" + else + echo "azure-cli is missing. Please install azure-cli from" + echo "https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest" + echo "Alternatively, you can use the Cloud Shell https://docs.microsoft.com/en-us/azure/cloud-shell/overview right from the Azure Portal or even VS Code." + fi - jqversion=$(jq --version) - if [ $? -eq 0 ]; then - found=$((found + 1)) - echo "Found jq version: $jqversion" - else - echo "jq is missing. Please install jq from" - echo "https://stedolan.github.io/jq/" - fi + azurecliconfig=$(cat $HOME/.azure/config | grep output | awk -F'[ ]' '{print $3}') + if [ "$azurecliconfig" == "json" ]; then + found=$((found +1)) + echo "Found correct azure-cli output configuration: output = $azurecliconfig." + else + echo "azure-cli is configured for $azurecliconfig output." + echo "Please reconfigure your azure-cli client to output using json." + fi - if [ $found -lt 2 ]; then - exit 1 - fi + jqversion=$(jq --version) + if [ $? -eq 0 ]; then + found=$((found + 1)) + echo "Found jq version: $jqversion" + else + echo "jq is missing. Please install jq from" + echo "https://stedolan.github.io/jq/" + fi + + if [ $found -lt 3 ]; then + exit 1 + fi } askSubscription() { - az account list -otable - echo "" - echo "Please enter the Id of the account you wish to use. If you do not see" - echo "a valid account in the list press Ctrl+C to abort and create one." - echo "If you leave this blank we will use the Current account." - echo -n "> " - read azure_subscription_id + az account list -otable + echo "" + echo "Please enter the Id of the account you wish to use. If you do not see" + echo "a valid account in the list press Ctrl+C to abort and create one." + echo "If you leave this blank we will use the Current account." + echo -n "> " + read azure_subscription_id - if [ "$azure_subscription_id" != "" ]; then - az account set --subscription $azure_subscription_id - else - azure_subscription_id=$(az account list | jq -r '.[] | select(.isDefault==true) | .id') - fi - azure_tenant_id=$(az account list | jq -r '.[] | select(.id=="'$azure_subscription_id'") | .tenantId') - echo "Using subscription_id: $azure_subscription_id" - echo "Using tenant_id: $azure_tenant_id" + if [ "$azure_subscription_id" != "" ]; then + az account set --subscription $azure_subscription_id + else + azure_subscription_id=$(az account list | jq -r '.[] | select(.isDefault==true) | .id') + fi + azure_tenant_id=$(az account list | jq -r '.[] | select(.id=="'$azure_subscription_id'") | .tenantId') + echo "Using subscription_id: $azure_subscription_id" + echo "Using tenant_id: $azure_tenant_id" } askName() { - echo "" - echo "Choose a name for your resource group, storage account and client" - echo "client. This is arbitrary, but it must not already be in use by" - echo "any of those resources. ALPHANUMERIC ONLY. Ex: mypackerbuild" - echo -n "> " - read meta_name + echo "" + echo "Choose a name for your resource group, storage account and client" + echo "client. This is arbitrary, but it must not already be in use by" + echo "any of those resources. ALPHANUMERIC ONLY. Ex: mypackerbuild" + echo -n "> " + read meta_name } askSecret() { - echo "" - echo "Enter a secret for your application. We recommend generating one with" - echo "openssl rand -base64 24. If you leave this blank we will attempt to" - echo "generate one for you using openssl. THIS WILL BE SHOWN IN PLAINTEXT." - echo "Ex: mypackersecret8734" - echo -n "> " - read azure_client_secret - if [ "$azure_client_secret" = "" ]; then - azure_client_secret=$(openssl rand -base64 24) - if [ $? -ne 0 ]; then - echo "Error generating secret" - exit 1 - fi - echo "Generated client_secret: $azure_client_secret" + echo "" + echo "Enter a secret for your application. We recommend generating one with" + echo "openssl rand -base64 24. If you leave this blank we will attempt to" + echo "generate one for you using openssl. THIS WILL BE SHOWN IN PLAINTEXT." + echo "Ex: mypackersecret8734" + echo -n "> " + read azure_client_secret + if [ "$azure_client_secret" = "" ]; then + azure_client_secret=$(openssl rand -base64 24) + if [ $? -ne 0 ]; then + echo "Error generating secret" + exit 1 fi + echo "Generated client_secret: $azure_client_secret" + fi } askLocation() { - az account list-locations -otable - echo "" - echo "Choose which region your resource group and storage account will be created. example: westus" - echo -n "> " - read location + az account list-locations -otable + echo "" + echo "Choose which region your resource group and storage account will be created. example: westus" + echo -n "> " + read location } createResourceGroup() { - echo "==> Creating resource group" - az group create -n $meta_name -l $location - if [ $? -eq 0 ]; then - azure_group_name=$meta_name - else - echo "Error creating resource group: $meta_name" - return 1 - fi + echo "==> Creating resource group" + az group create -n $meta_name -l $location + if [ $? -eq 0 ]; then + azure_group_name=$meta_name + else + echo "Error creating resource group: $meta_name" + return 1 + fi } createStorageAccount() { - echo "==> Creating storage account" - az storage account create --name $meta_name --resource-group $meta_name --location $location --kind Storage --sku Standard_LRS - if [ $? -eq 0 ]; then - azure_storage_name=$meta_name - else - echo "Error creating storage account: $meta_name" - return 1 - fi + echo "==> Creating storage account" + az storage account create --name $meta_name --resource-group $meta_name --location $location --kind Storage --sku Standard_LRS + if [ $? -eq 0 ]; then + azure_storage_name=$meta_name + else + echo "Error creating storage account: $meta_name" + return 1 + fi } createApplication() { - echo "==> Creating application" - echo "==> Does application exist?" - azure_client_id=$(az ad app list | jq -r '.[] | select(.displayName | contains("'$meta_name'")) ') - - if [ "$azure_client_id" != "" ]; then - echo "==> application already exist, grab appId" - azure_client_id=$(az ad app list | jq -r '.[] | select(.displayName | contains("'$meta_name'")) .appId') - else - echo "==> application does not exist" - azure_client_id=$(az ad app create --display-name $meta_name --identifier-uris http://$meta_name --homepage http://$meta_name --password $azure_client_secret | jq -r .appId) - fi + echo "==> Creating application" + echo "==> Does application exist?" + azure_client_id=$(az ad app list | jq -r '.[] | select(.displayName | contains("'$meta_name'")) ') - if [ $? -ne 0 ]; then - echo "Error creating application: $meta_name @ http://$meta_name" - return 1 - fi + if [ "$azure_client_id" != "" ]; then + echo "==> application already exist, grab appId" + azure_client_id=$(az ad app list | jq -r '.[] | select(.displayName | contains("'$meta_name'")) .appId') + else + echo "==> application does not exist" + azure_client_id=$(az ad app create --display-name $meta_name --identifier-uris http://$meta_name --homepage http://$meta_name --password $azure_client_secret | jq -r .appId) + fi + + if [ $? -ne 0 ]; then + echo "Error creating application: $meta_name @ http://$meta_name" + return 1 + fi } createServicePrincipal() { - echo "==> Creating service principal" - azure_object_id=$(az ad sp create --id $azure_client_id | jq -r .objectId) - echo $azure_object_id "was selected." + echo "==> Creating service principal" + azure_object_id=$(az ad sp create --id $azure_client_id | jq -r .objectId) + echo $azure_object_id "was selected." - if [ $? -ne 0 ]; then - echo "Error creating service principal: $azure_client_id" - return 1 - fi + if [ $? -ne 0 ]; then + echo "Error creating service principal: $azure_client_id" + return 1 + fi } createPermissions() { - echo "==> Creating permissions" - az role assignment create --assignee $azure_object_id --role "Owner" --scope /subscriptions/$azure_subscription_id - # If the user wants to use a more conservative scope, she can. She must - # configure the Azure builder to use build_resource_group_name. The - # easiest solution is subscription wide permission. - # az role assignment create --spn http://$meta_name -g $azure_group_name -o "API Management Service Contributor" - if [ $? -ne 0 ]; then - echo "Error creating permissions for: http://$meta_name" - return 1 - fi + echo "==> Creating permissions" + az role assignment create --assignee $azure_object_id --role "Owner" --scope /subscriptions/$azure_subscription_id + # If the user wants to use a more conservative scope, she can. She must + # configure the Azure builder to use build_resource_group_name. The + # easiest solution is subscription wide permission. + # az role assignment create --spn http://$meta_name -g $azure_group_name -o "API Management Service Contributor" + if [ $? -ne 0 ]; then + echo "Error creating permissions for: http://$meta_name" + return 1 + fi } showConfigs() { - echo "" - echo "Use the following configuration for your packer template:" - echo "" - echo "{" - echo " \"client_id\": \"$azure_client_id\"," - echo " \"client_secret\": \"$azure_client_secret\"," - echo " \"object_id\": \"$azure_object_id\"," - echo " \"subscription_id\": \"$azure_subscription_id\"," - echo " \"tenant_id\": \"$azure_tenant_id\"," - echo " \"resource_group_name\": \"$azure_group_name\"," - echo " \"storage_account\": \"$azure_storage_name\"," - echo "}" - echo "" + echo "" + echo "Use the following configuration for your packer template:" + echo "" + echo "{" + echo " \"client_id\": \"$azure_client_id\"," + echo " \"client_secret\": \"$azure_client_secret\"," + echo " \"object_id\": \"$azure_object_id\"," + echo " \"subscription_id\": \"$azure_subscription_id\"," + echo " \"tenant_id\": \"$azure_tenant_id\"," + echo " \"resource_group_name\": \"$azure_group_name\"," + echo " \"storage_account\": \"$azure_storage_name\"," + echo "}" + echo "" } doSleep() { - local sleep_time=${PACKER_SLEEP_TIME-$create_sleep} - echo "" - echo "Sleeping for ${sleep_time} seconds to wait for resources to be " - echo "created. If you get an error about a resource not existing, you can " - echo "try increasing the amount of time we wait after creating resources " - echo "by setting PACKER_SLEEP_TIME to something higher than the default." - echo "" - sleep $sleep_time + local sleep_time=${PACKER_SLEEP_TIME-$create_sleep} + echo "" + echo "Sleeping for ${sleep_time} seconds to wait for resources to be " + echo "created. If you get an error about a resource not existing, you can " + echo "try increasing the amount of time we wait after creating resources " + echo "by setting PACKER_SLEEP_TIME to something higher than the default." + echo "" + sleep $sleep_time } retryable() { - n=0 - until [ $n -ge $1 ] - do - $2 && return 0 - echo "$2 failed. Retrying..." - n=$[$n+1] - doSleep - done - echo "$2 failed after $1 tries. Exiting." - exit 1 + n=0 + until [ $n -ge $1 ] + do + $2 && return 0 + echo "$2 failed. Retrying..." + n=$[$n+1] + doSleep + done + echo "$2 failed after $1 tries. Exiting." + exit 1 } setup() { - requirements + requirements - az login + az login - askSubscription - askName - askSecret - askLocation + askSubscription + askName + askSecret + askLocation - # Some of the resources take a while to converge in the API. To make the - # script more reliable we'll add a sleep after we create each resource. + # Some of the resources take a while to converge in the API. To make the + # script more reliable we'll add a sleep after we create each resource. - retryable 3 createResourceGroup - retryable 3 createStorageAccount - retryable 3 createApplication - retryable 3 createServicePrincipal - retryable 3 createPermissions + retryable 3 createResourceGroup + retryable 3 createStorageAccount + retryable 3 createApplication + retryable 3 createServicePrincipal + retryable 3 createPermissions - showConfigs + showConfigs } case "$1" in - requirements) - requirements - ;; - setup) - setup - ;; - *) - showhelp - ;; + requirements) + requirements + ;; + setup) + setup + ;; + *) + showhelp + ;; esac From 5e22942f3002c423267cfbe186721d6698e7acaf Mon Sep 17 00:00:00 2001 From: Jeff Escalante Date: Fri, 13 Apr 2018 17:59:14 -0400 Subject: [PATCH 0781/1216] analytics script adjustments --- website/source/layouts/layout.erb | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/website/source/layouts/layout.erb b/website/source/layouts/layout.erb index 768ea0a83..a6c615b72 100644 --- a/website/source/layouts/layout.erb +++ b/website/source/layouts/layout.erb @@ -112,23 +112,24 @@ - + + + <%= yield_content :head %> + + @@ -112,11 +120,6 @@ <%= yield_content :head %> - - @@ -119,20 +106,22 @@ + + <%= javascript_include_tag "application" %> + - - - - + + + <%= javascript_include_tag "application", defer: true %> + + + <%= yield_content :head %> @@ -106,22 +113,6 @@ - - <%= javascript_include_tag "application" %> - -