Compare commits

..

94 Commits

Author SHA1 Message Date
Matthew Hooker 9698d7283f Cut version 1.1.3 2017-12-08 15:33:50 -08:00
Matthew Hooker 4a864d59d7 Prepare for 1.1.3 2017-12-08 15:26:17 -08:00
Matthew Hooker cad5ccfa64 update changelog 2017-12-08 15:24:37 -08:00
Matthew Hooker 3710c34eb5 Revert "update changelog"
This reverts commit 98420fc9f5.
2017-12-08 15:17:32 -08:00
Matthew Hooker c4bc69af71 Merge pull request #5679 from hashicorp/spotinstanceenasriovno
remove sriov/ena support for spot instances
2017-12-08 15:15:29 -08:00
Matthew Hooker f216330ba3 spot instance ena/sriov checking for all other builders 2017-12-08 14:56:19 -08:00
Matthew Hooker 4b1d8e3fe8 don't allow enhanced networking flags for spot instances. 2017-12-08 13:38:49 -08:00
Matthew Hooker 73b98b2a04 use waiter to wait for ebs instances to stop 2017-12-08 13:38:48 -08:00
Matthew Hooker dada63801b also use waiter code for spot instances 2017-12-08 13:38:48 -08:00
Matthew Hooker 9e61005f7c Merge pull request #5660 from hashicorp/fix_5625
modify validation to allow user to have the original region in the am…
2017-12-08 13:36:10 -08:00
Megan Marsh 98420fc9f5 update changelog 2017-12-08 13:23:42 -08:00
SwampDragons 24ed2b7081 Merge pull request #5684 from BGnoinski/master
Update amazon run_config.go absent ssh_private_key_file error
2017-12-08 09:40:36 -08:00
Ben Gnoinski 5df2e040d0 Update amazon run_config.go absent ssh_private_key_file error 2017-12-08 08:51:27 -08:00
Matthew Hooker 2c1c7b9659 Merge pull request #5630 from johndaviesco/winrm_no_proxy
Winrm no proxy
2017-12-07 15:41:16 -08:00
SwampDragons ea55db6d99 Merge pull request #5678 from hashicorp/fix5627
Wait until source instance OK before continuing
2017-12-07 14:47:10 -08:00
Matthew Hooker a90c45d9bb Wait until source instance OK before continuing 2017-12-07 12:31:50 -08:00
Matthew Hooker da4d6f76be Merge pull request #5676 from hashicorp/fix5669
Correctly set aws region if given in template along with a profile.
2017-12-07 11:13:44 -08:00
Matthew Hooker 7b5c0900ef Correctly set aws region if given in template along with a profile. 2017-12-07 11:12:57 -08:00
SwampDragons ca26ab5d5a Merge pull request #5672 from hashicorp/fix_5622
try using UI again
2017-12-07 10:18:53 -08:00
Megan Marsh 19c997cb0e revert to using UI becuase the remote command syntax breaks things on linux with vmware fusion. 2017-12-07 09:08:12 -08:00
John Davies-Colley 76ac755ed9 fixing wording for proxy usage 👌 2017-12-06 17:13:02 +13:00
John Davies-Colley 92d1bdbdab docs change for deprecation and proxy usage ✔ 2017-12-06 16:50:54 +13:00
Christopher Boumenot 1202bdd080 Merge pull request #5661 from jamielennox/no-location-for-existing-rg
Azure: Don't provide location for build_resource_group_name
2017-12-05 09:52:45 -08:00
SwampDragons 0fbbdc2ea3 Merge pull request #5664 from kwilczynski/refactor-commands-version
Re-factor version command to use version.FormattedVersion() function.
2017-12-04 12:02:50 -08:00
Matthew Hooker 8970722845 use latest go for travis tests 2017-12-04 10:43:38 -08:00
Jamie Lennox d043c37ad4 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
2017-12-04 11:22:54 +11:00
Krzysztof Wilczynski 0e08640fff 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 <kw@linux.com>
2017-12-03 23:07:16 +01:00
SwampDragons 8a30b3db80 Merge pull request #5456 from GoSecure/vmware-iso-improved-network-logging
vmware-iso builder: Logging on network errors on connection refused
2017-11-30 16:31:30 -08:00
Matthew Hooker c28a50af16 try to fix transient test failure 2017-11-30 16:30:37 -08:00
SwampDragons 753d3cf216 Merge pull request #5618 from kwilczynski/ansible-local-clean-staging-directory
ansible-local: Add ability to clean staging directory.
2017-11-30 14:27:59 -08:00
Megan Marsh 1c681fc096 tests 2017-11-30 14:08:48 -08:00
Megan Marsh 556da47d35 modify validation to allow user to have the original region in the ami_regions list 2017-11-30 13:51:33 -08:00
Christopher Boumenot 7e8517473d Merge pull request #5656 from boumenot/pr-azure-delete-keyvault
azure: delete keyvault deployment
2017-11-30 12:03:03 -08:00
SwampDragons 37d22160a8 Merge pull request #5619 from kwilczynski/add-packer_version-function
Add new `packer_version` function.
2017-11-30 09:43:25 -08:00
Christopher Boumenot da0c13f622 azure: delete keyvault deployment 2017-11-30 01:19:08 -08:00
SwampDragons aa2bbf2aec Merge pull request #5649 from casey-robertson/chef-client-provisioner
Changes Linux install URL to omnitruck.  Changes powershell install t…
2017-11-29 14:40:35 -08:00
SwampDragons 72afc2eab3 Merge pull request #5632 from VladRassokhin/do-not-donwload-twice
Do not re-download iso multiple times from different urls
2017-11-29 11:58:14 -08:00
SwampDragons 03f9e12d04 Merge pull request #5650 from harijayms/azuredocsupdate
Updates to Docs to ensure permission issues are clearer
2017-11-29 09:52:28 -08:00
Hariharan Jayaraman b66426e668 Updates to Docs to ensure permission issues are clearer 2017-11-28 16:23:49 -08:00
Casey Robertson 8f02150178 Changes Linux install URL to omnitruck. Changes powershell install to use omnitruck rather than hard-coded 32-bit url 2017-11-28 14:23:12 -08:00
zhuzhih2017 2f7b9640d9 Update CHANGELOG.md 2017-11-28 11:16:14 +08:00
zhuzhih2017 bdc1fa0527 Merge pull request #5641 from zhuzhih2017/master
Add security token supported and TLS handshake timeout support
2017-11-28 11:15:56 +08:00
zhuzhih2017 de0017c37a Add security token supported and TLS handshake timeout support 2017-11-28 10:32:17 +08:00
John Davies-Colley 10aaa49beb fixing tests and funky logic 💦 2017-11-28 14:42:14 +13:00
Matthew Hooker ff3efb4641 remove trailing spaces 2017-11-27 17:26:03 -08:00
Matthew Hooker be3fe340c8 add a section about uploading files that don't exist. 2017-11-27 17:23:31 -08:00
SwampDragons e990e49711 Merge pull request #5638 from hashicorp/docs_5623
Docs 5623
2017-11-27 16:58:24 -08:00
SwampDragons c9072083d1 Merge pull request #5624 from ilons/master
Should state that file provisioner requires source at execution time
2017-11-27 16:47:06 -08:00
Megan Marsh a325780aa5 make description match 2017-11-27 15:18:58 -08:00
Megan Marsh df492d35bf remove confusing use of local. dang. 2017-11-27 15:17:54 -08:00
Megan Marsh 150bf1f6da clarify shell-local location 2017-11-27 15:15:52 -08:00
Megan Marsh 52f5f2b895 clarify where ansible-local runs 2017-11-27 15:10:29 -08:00
John Davies-Colley 0c787ec9df Valadating early 2017-11-28 11:46:32 +13:00
SwampDragons a51885be9c Merge pull request #5458 from vijayinvites/DifferentialDisk
Hyper-V builder: Add support for differencing disk
2017-11-27 11:26:07 -08:00
Vladislav Rassokhin af8a0c46c5 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.
2017-11-26 00:10:34 +03:00
Vijaya Bhaskar Reddy Kondreddi 3d5303c60d Fix tests 2017-11-24 13:24:44 +05:30
Vijaya Bhaskar Reddy Kondreddi b7e3f37b44 Add support for differential disk 2017-11-24 12:44:53 +05:30
Ilon Sjögren 0dae5bc8bd Should state that file provisioner requires source at execution time 2017-11-22 09:13:47 +01:00
John Davies-Colley d4f37ab5f6 changing config varibles name in docs to match names in code 2017-11-22 17:15:46 +13:00
John Davies-Colley 75320440ec adding missing quote 🙏 2017-11-22 15:59:15 +13:00
Henry Muru Paenga 7b03613649 Update docs with ssh_interface 2017-11-22 15:52:22 +13:00
John Davies-Colley 707ec675b2 ssh interface for amazon builders 🔨 2017-11-22 15:49:38 +13:00
Krzysztof Wilczynski 7d80e37c14 Add new packer_version function.
Signed-off-by: Krzysztof Wilczynski <kw@linux.com>
2017-11-21 22:04:33 +01:00
Krzysztof Wilczynski 10370adbab ansible-local: Add ability to clean staging directory.
Signed-off-by: Krzysztof Wilczynski <kw@linux.com>
2017-11-21 21:09:01 +01:00
Christopher Boumenot baacb7e173 Merge pull request #5599 from boumenot/pr-azure-validate-rgn
azure: sanity check resource group names
2017-11-20 15:22:04 -08:00
Christopher Boumenot a3c9485062 azure: sanity check resource group names 2017-11-20 15:19:12 -08:00
SwampDragons 61d8274096 Merge pull request #5604 from carlosonunez/patch-1
Add instructions for the Python client.
2017-11-20 09:57:44 -08:00
Carlos Nunez a8ff095059 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.
2017-11-18 15:44:10 -06:00
Carlos Nunez e9a63d8653 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.
2017-11-18 15:27:54 -06:00
Christopher Boumenot 247da61ad1 Merge pull request #5593 from boumenot/pr-azure-keep-resource-group
Azure: Keep temporary resource group.
2017-11-16 17:39:33 -08:00
Christopher Boumenot 8985bd45ba azure: go fmt 2017-11-16 17:32:35 -08:00
Christopher Boumenot ee767e55d1 azure: change literal to constant 2017-11-16 16:36:42 -08:00
Christopher Boumenot 2684153cb2 azure: reject bad configuration faster 2017-11-16 16:34:18 -08:00
Christopher Boumenot 787f08f39b azure: add user message to indicate what is being deleted 2017-11-16 16:31:05 -08:00
SwampDragons 76e3167b6e Merge pull request #5591 from hashicorp/fix_5589
fix regression :(
2017-11-16 14:35:19 -08:00
Megan Marsh 3c20176dbb runtime imported but not used 2017-11-16 11:12:23 -08:00
Megan Marsh 4fb8a27879 remove the actual offending code 2017-11-16 11:03:10 -08:00
Megan Marsh 74a4cc04fe fix regression :( 2017-11-16 10:22:12 -08:00
Megan Marsh 20390ff1ec fix vetting for test fail print statements 2017-11-15 17:01:53 -08:00
SwampDragons ab542c07ff Merge pull request #5547 from egraff/hyperv-patch
builder/hyperv/vmcx: missing InterpolateContext
2017-11-15 17:00:55 -08:00
Matthew Hooker 284ffa2562 spell fix 2017-11-15 14:19:28 -08:00
Matthew Hooker 9ee1385283 update changelog 2017-11-15 14:17:00 -08:00
Matthew Hooker f974d3552c Merge pull request #5588 from ammaritiz/align-Qemu-disk-size
Align virtual disk size for qemu builder
2017-11-15 14:15:16 -08:00
Matthew Hooker 097296792a prepare for next version 2017-11-15 14:08:58 -08:00
Ammar Ansari b8bd66d10d 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
2017-11-15 15:00:13 -05:00
Arjen Schwarz f43f3155d4 Remove breaking debug statement 2017-11-10 11:04:31 +11:00
Arjen Schwarz 0e706320ad 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
2017-11-09 22:20:09 +11:00
Arjen Schwarz 0377140c39 Azure docs: Add documentation concerning build_resource_group_name
Part of #5045
2017-11-09 22:07:43 +11:00
Arjen Schwarz f4600a208f 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.
2017-11-06 16:16:58 +11:00
Erlend Graff 27fc72c137 fix formatting errors 2017-11-05 15:16:47 +01:00
Erlend Graff ae6987c74b hyper-v: add test for hashicorp/packer#5184 2017-11-05 14:58:08 +01:00
Erlend Graff eeeee3ec35 hyper-v/vmcx: add missing InterpolateContext 2017-11-05 14:55:56 +01:00
Erlend Graff d890051940 hyper-v: implement driver mock 2017-11-05 14:51:18 +01:00
Olivier Bilodeau 820811675d vmware-iso builder: Logging on network errors on connection refused 2017-10-13 19:00:48 -04:00
95 changed files with 4746 additions and 514 deletions
+1 -1
View File
@@ -8,7 +8,7 @@ language: go
go:
- 1.7.4
- 1.8.3
- 1.9
- 1.x
install:
- make deps
+41 -2
View File
@@ -1,6 +1,45 @@
## 1.1.3 (December 8, 2017)
### IMPROVEMENTS:
* 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/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)
### IMRPOVEMENTS:
### IMPROVEMENTS:
* builder/amazon: Correctly deregister AMIs when `force_deregister` is set.
[GH-5525]
@@ -259,7 +298,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]
+7 -1
View File
@@ -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
+3 -1
View File
@@ -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)
+17 -22
View File
@@ -2,6 +2,7 @@ package common
import (
"fmt"
"log"
"github.com/hashicorp/packer/template/interpolate"
)
@@ -41,22 +42,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 +83,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.
log.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"))
+15
View File
@@ -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) {
+25 -2
View File
@@ -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 {
@@ -77,11 +78,29 @@ 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" &&
c.SSHInterface != "private_ip" &&
c.SSHInterface != "public_dns" &&
c.SSHInterface != "private_dns" &&
c.SSHInterface != "" {
errs = append(errs, errors.New(fmt.Sprintf("Unknown interface type: %s", c.SSHInterface)))
}
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."))
}
}
@@ -133,3 +152,7 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
return errs
}
func (c *RunConfig) IsSpotInstance() bool {
return c.SpotPrice != "" && c.SpotPrice != "0"
}
+25 -6
View File
@@ -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:
panic(fmt.Sprintf("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")
}
}
+36 -24
View File
@@ -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{
@@ -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 != "" {
@@ -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) {
@@ -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)
+42 -36
View File
@@ -69,6 +69,13 @@ 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.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
}
@@ -110,47 +117,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,
}
}
@@ -193,7 +199,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,
@@ -201,7 +207,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{
+42 -36
View File
@@ -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,
}
}
@@ -204,7 +210,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,
@@ -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{
+40 -35
View File
@@ -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,
}
}
@@ -181,7 +186,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,
@@ -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{
+28 -21
View File
@@ -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,
}
}
@@ -268,7 +275,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,
+30
View File
@@ -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,10 +43,12 @@ type AzureClient struct {
compute.VirtualMachinesClient
common.VaultClient
armStorage.AccountsClient
disk.DisksClient
InspectorMaxLength int
Template *CaptureTemplate
LastError azureErrorResponse
VaultClientDelete common.VaultClient
}
func getCaptureResponse(body string) *CaptureTemplate {
@@ -135,6 +139,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)
@@ -194,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)
+29 -5
View File
@@ -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 {
@@ -127,11 +136,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 +157,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,11 +295,22 @@ 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)
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())
@@ -297,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)
}
-1
View File
@@ -20,7 +20,6 @@ func TestStateBagShouldBePopulatedExpectedValues(t *testing.T) {
constants.ArmTags,
constants.ArmComputeName,
constants.ArmDeploymentName,
constants.ArmLocation,
constants.ArmNicName,
constants.ArmResourceGroupName,
constants.ArmStorageAccountName,
+77 -7
View File
@@ -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}(?<![-.])$
//
// This is not an exhaustive match, but it should be extremely close.
validResourceGroupNameRe = "^[^_\\W][\\w-._\\(\\)]{0,63}$"
validManagedDiskName = "^[^_\\W][\\w-._)]{0,79}$"
)
var (
reCaptureContainerName = regexp.MustCompile("^[a-z0-9][a-z0-9\\-]{2,62}$")
reCaptureNamePrefix = regexp.MustCompile("^[A-Za-z0-9][A-Za-z0-9_\\-\\.]{0,23}$")
reManagedDiskName = regexp.MustCompile(validManagedDiskName)
reResourceGroupName = regexp.MustCompile(validResourceGroupNameRe)
)
type Config struct {
@@ -83,6 +97,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 +144,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 +349,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
@@ -511,6 +533,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 {
@@ -564,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 {
@@ -578,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"))
@@ -587,6 +613,30 @@ func assertRequiredParametersSet(c *Config, errs *packer.MultiError) {
}
}
if c.TempResourceGroupName != "" {
if ok, err := assertResourceGroupName(c.TempResourceGroupName, "temp_resource_group_name"); !ok {
errs = packer.MultiErrorAppend(errs, err)
}
}
if c.BuildResourceGroupName != "" {
if ok, err := assertResourceGroupName(c.BuildResourceGroupName, "build_resource_group_name"); !ok {
errs = packer.MultiErrorAppend(errs, err)
}
}
if c.ManagedImageResourceGroupName != "" {
if ok, err := assertResourceGroupName(c.ManagedImageResourceGroupName, "managed_image_resource_group_name"); !ok {
errs = packer.MultiErrorAppend(errs, err)
}
}
if c.ManagedImageName != "" {
if ok, err := assertManagedImageName(c.ManagedImageName, "managed_image_name"); !ok {
errs = packer.MultiErrorAppend(errs, err)
}
}
if c.VirtualNetworkName == "" && c.VirtualNetworkResourceGroupName != "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("If virtual_network_resource_group_name is specified, so must virtual_network_name"))
}
@@ -615,3 +665,23 @@ func assertRequiredParametersSet(c *Config, errs *packer.MultiError) {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("The managed_image_storage_account_type %q is invalid", c.ManagedImageStorageAccountType))
}
}
func assertManagedImageName(name, setting string) (bool, error) {
if !isValidAzureName(reManagedDiskName, name) {
return false, fmt.Errorf("The setting %s must match the regular expression %q, and not end with a '-' or '.'.", setting, validManagedDiskName)
}
return true, nil
}
func assertResourceGroupName(rgn, setting string) (bool, error) {
if !isValidAzureName(reResourceGroupName, rgn) {
return false, fmt.Errorf("The setting %s must match the regular expression %q, and not end with a '-' or '.'.", setting, validResourceGroupNameRe)
}
return true, nil
}
func isValidAzureName(re *regexp.Regexp, rgn string) bool {
return re.Match([]byte(rgn)) &&
!strings.HasSuffix(rgn, ".") &&
!strings.HasSuffix(rgn, "-")
}
+158
View File
@@ -899,6 +899,164 @@ 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 TestConfigShouldRejectInvalidResourceGroupNames(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",
"os_type": "linux",
}
tests := []struct {
name string
ok bool
}{
// The Good
{"packer-Resource-Group-jt2j3fc", true},
{"My", true},
{"My-(with-parens)-Resource-Group", true},
// The Bad
{"My Resource Group", false},
{"My-Resource-Group-", false},
{"My.Resource.Group.", false},
// The Ugly
{"My!@#!@#%$%yM", false},
{" ", false},
{"My10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", false},
}
settings := []string{"temp_resource_group_name", "build_resource_group_name"}
for _, x := range settings {
for _, y := range tests {
config[x] = y.name
_, _, err := newConfig(config, getPackerConfiguration())
if !y.ok && err == nil {
t.Errorf("expected config to reject %q for setting %q", y.name, x)
} else if y.ok && err != nil {
t.Errorf("expected config to accept %q for setting %q", y.name, x)
}
}
delete(config, "location") // not valid for build_resource_group_name
delete(config, x)
}
}
func TestConfigShouldRejectManagedDiskNames(t *testing.T) {
config := map[string]interface{}{
"image_offer": "ignore",
"image_publisher": "ignore",
"image_sku": "ignore",
"location": "ignore",
"subscription_id": "ignore",
"communicator": "none",
"os_type": "linux",
"managed_image_name": "ignore",
"managed_image_resource_group_name": "ignore",
}
testsResourceGroupNames := []struct {
name string
ok bool
}{
// The Good
{"packer-Resource-Group-jt2j3fc", true},
{"My", true},
{"My-(with-parens)-Resource-Group", true},
// The Bad
{"My Resource Group", false},
{"My-Resource-Group-", false},
{"My.Resource.Group.", false},
// The Ugly
{"My!@#!@#%$%yM", false},
{" ", false},
{"My10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", false},
}
settingUnderTest := "managed_image_resource_group_name"
for _, y := range testsResourceGroupNames {
config[settingUnderTest] = y.name
_, _, err := newConfig(config, getPackerConfiguration())
if !y.ok && err == nil {
t.Errorf("expected config to reject %q for setting %q", y.name, settingUnderTest)
} else if y.ok && err != nil {
t.Errorf("expected config to accept %q for setting %q", y.name, settingUnderTest)
}
}
config["managed_image_resource_group_name"] = "ignored"
testNames := []struct {
name string
ok bool
}{
// The Good
{"ManagedDiskName", true},
{"Managed-Disk-Name", true},
{"My33", true},
// The Bad
{"Managed Disk Name", false},
{"Managed-Disk-Name-", false},
{"Managed.Disk.Name.", false},
// The Ugly
{"My!@#!@#%$%yM", false},
{" ", false},
{"My10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", false},
}
settingUnderTest = "managed_image_name"
for _, y := range testNames {
config[settingUnderTest] = y.name
_, _, err := newConfig(config, getPackerConfiguration())
if !y.ok && err == nil {
t.Logf("expected config to reject %q for setting %q", y.name, settingUnderTest)
} else if y.ok && err != nil {
t.Logf("expected config to accept %q for setting %q", y.name, settingUnderTest)
}
}
}
func getArmBuilderConfiguration() map[string]string {
m := make(map[string]string)
for _, v := range requiredConfigValues {
+62 -18
View File
@@ -1,6 +1,7 @@
package arm
import (
"errors"
"fmt"
"github.com/Azure/azure-sdk-for-go/arm/resources/resources"
@@ -14,6 +15,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 +26,7 @@ func NewStepCreateResourceGroup(client *AzureClient, ui packer.Ui) *StepCreateRe
}
step.create = step.createResourceGroup
step.exists = step.doesResourceGroupExist
return step
}
@@ -39,22 +42,58 @@ 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 ...")
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 {
return processStepResult(err, s.error, state)
}
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)
}
err := s.create(resourceGroupName, location, tags)
if err == nil {
// 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 {
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)
}
@@ -68,17 +107,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.")
}
@@ -1,6 +1,7 @@
package arm
import (
"errors"
"fmt"
"testing"
@@ -8,11 +9,59 @@ 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 !!") },
say: func(message string) {},
error: func(e error) {},
exists: func(string) (bool, error) { return false, nil },
}
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 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()
@@ -32,6 +81,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 +108,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,11 +142,68 @@ func TestStepCreateResourceGroupShouldTakeStepArgumentsFromStateBag(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) {},
error: func(e error) {},
exists: func(string) (bool, error) { return false, nil },
}
stateBag := createTestExistingStateBagStepCreateResourceGroup()
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 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) {},
error: func(e error) {},
exists: func(string) (bool, error) { return true, nil },
}
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 createTestStateBagStepCreateResourceGroup() multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
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{
@@ -103,6 +211,5 @@ func createTestStateBagStepCreateResourceGroup() multistep.StateBag {
}
stateBag.Put(constants.ArmTags, &tags)
return stateBag
}
+27 -6
View File
@@ -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,23 +41,42 @@ func (s *StepDeleteOSDisk) deleteBlob(storageContainerName string, blobName stri
return err
}
func (s *StepDeleteOSDisk) 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 *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 processStepResult(err, s.error, state)
}
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, "/")
+78 -3
View File
@@ -1,6 +1,7 @@
package arm
import (
"errors"
"fmt"
"testing"
@@ -10,9 +11,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")
@@ -102,10 +104,83 @@ 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)
stateBag.Put(constants.ArmIsManagedImage, false)
stateBag.Put(constants.ArmIsExistingResourceGroup, false)
stateBag.Put(constants.ArmResourceGroupName, "testgroup")
return stateBag
}
@@ -3,15 +3,20 @@ 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"
"github.com/mitchellh/multistep"
)
const (
maxResourcesToDelete = 50
)
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 +32,89 @@ 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)
err = s.deleteDeploymentResources(deploymentName, resourceGroupName)
if err != nil {
return err
}
err := <-errChan
if err != nil {
s.say(s.client.LastError.Error())
if keyVaultDeploymentName, ok := state.GetOk(constants.ArmKeyVaultDeploymentName); ok {
err = s.deleteDeploymentResources(keyVaultDeploymentName.(string), resourceGroupName)
if err != nil {
return err
}
}
return nil
} else {
s.say("\nThe resource group was created by Packer, deleting ...")
_, errChan := s.client.GroupsClient.Delete(resourceGroupName, cancelCh)
err = <-errChan
if err != nil {
s.say(s.client.LastError.Error())
}
return err
}
}
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)
}
return err
}
func (s *StepDeleteResourceGroup) Run(state multistep.StateBag) multistep.StepAction {
@@ -45,8 +125,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)
@@ -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) {},
+149 -11
View File
@@ -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,24 +13,32 @@ 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
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
step.delete = step.deleteOperationResource
step.disk = step.getImageDetails
step.deleteDisk = step.deleteImage
return step
}
@@ -50,20 +61,147 @@ 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)
},
)
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.KeyVault/vaults":
_, err := s.client.VaultClientDelete.Delete(resourceGroupName, resourceName)
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 = s.name
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))
}
}
}
@@ -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.")
}
@@ -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"
@@ -23,9 +24,11 @@ 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"
ArmIsExistingResourceGroup string = "arm.IsExistingResourceGroup"
ArmIsManagedImage string = "arm.IsManagedImage"
ArmManagedImageResourceGroupName string = "arm.ManagedImageResourceGroupName"
+77 -1
View File
@@ -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)
}
+1 -1
View File
@@ -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
+536
View File
@@ -0,0 +1,536 @@
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_DifferentialDisk bool
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, diffDisks bool) 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
d.CreateVirtualMachine_DifferentialDisk = diffDisks
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
}
+2 -2
View File
@@ -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 {
+2 -1
View File
@@ -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)
+4
View File
@@ -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{},
+45
View File
@@ -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: %#v", ret)
}
}
+5 -1
View File
@@ -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"`
@@ -97,7 +100,8 @@ 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{
"boot_command",
+53 -1
View File
@@ -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: %#v", ret)
}
}
+1 -1
View File
@@ -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 == "" {
+1 -1
View File
@@ -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)
}
+3
View File
@@ -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 {
+8 -20
View File
@@ -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.
+2 -6
View File
@@ -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
},
-5
View File
@@ -15,7 +15,6 @@ import (
"net/http"
"net/url"
"os"
"runtime"
)
// DownloadConfig is the configuration given to instantiate a new
@@ -130,10 +129,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 {
+16 -7
View File
@@ -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
}
+31 -14
View File
@@ -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)
+1 -1
View File
@@ -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",
@@ -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",
+1 -1
View File
@@ -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()
+30 -2
View File
@@ -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
}
@@ -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")
}
}
+2 -2
View File
@@ -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",
},
+7 -11
View File
@@ -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
}
+17 -9
View File
@@ -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()
}
}
+21
View File
@@ -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)
}
}
+53
View File
@@ -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,
}
}
+728
View File
@@ -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
}
+278
View File
@@ -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"`
}
+733
View File
@@ -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
}
+29
View File
@@ -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"
}
+147 -12
View File
@@ -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)
+2 -1
View File
@@ -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,
}
+5 -1
View File
@@ -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 {
+58 -17
View File
@@ -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
}
+25
View File
@@ -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
+62 -4
View File
@@ -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
}
+2 -2
View File
@@ -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 {
+1
View File
@@ -102,6 +102,7 @@ type NextHopType string
const (
NextHopIntance = NextHopType("Instance") //Default
NextHopTunnel = NextHopType("Tunnel")
NextHopTunnelRouterInterface = NextHopType("RouterInterface")
)
type CreateRouteEntryArgs struct {
+227
View File
@@ -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
}
+18 -3
View File
@@ -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
}
+2
View File
@@ -31,6 +31,8 @@ type GroupResponse struct {
type GroupListResponse struct {
RamCommonResponse
IsTruncated bool
Marker string
Groups struct {
Group []Group
}
+11 -3
View File
@@ -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
}
}
+8 -4
View File
@@ -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:
+20 -14
View File
@@ -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",
@@ -566,34 +572,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=",
+1 -1
View File
@@ -9,7 +9,7 @@ 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
+1 -1
View File
@@ -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
@@ -183,6 +183,12 @@ builder.
generate. By default, Packer generates a name that looks like `packer_<UUID>`,
where `<UUID>` 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.
@@ -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,19 @@ 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) - *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,
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.
Where Packer is configured for an outbound proxy but WinRM traffic should be direct
`ssh_interface` must be set to `private_dns` and `<region>.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
@@ -321,8 +321,19 @@ 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) - *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,
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.
Where Packer is configured for an outbound proxy but WinRM traffic should be direct
`ssh_interface` must be set to `private_dns` and `<region>.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
@@ -225,8 +225,19 @@ 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.
- `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,
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.
Where Packer is configured for an outbound proxy but WinRM traffic should be direct
`ssh_interface` must be set to `private_dns` and `<region>.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
@@ -329,8 +329,19 @@ 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) - *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,
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.
Where Packer is configured for an outbound proxy but WinRM traffic should be direct
`ssh_interface` must be set to `private_dns` and `<region>.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
@@ -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')"
```
-&gt; 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
```
-&gt; `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
```
-&gt; `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
@@ -175,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 <<EOF
> {
> "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
@@ -189,6 +277,7 @@ $ azure ad app list --json \
| 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.
+47 -23
View File
@@ -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://<storage_account>.blob.core.windows.net/system/Microsoft.Compute/Images/<capture_container_name>/<capture_name_prefix>.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.
@@ -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
@@ -55,10 +51,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://<storage_account>.blob.core.windows.net/system/Microsoft.Compute/Images/<capture_container_name>/<capture_name_prefix>.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,15 +64,45 @@ 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.
#### 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
@@ -86,9 +112,9 @@ When creating a managed image the following two options are required.
- `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 +136,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,13 +154,10 @@ 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.
- `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 +293,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
+1 -1
View File
@@ -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.
@@ -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:
@@ -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:
- `<leftAltOn>` `<rightAltOn>` - Simulates pressing and holding the alt key.
- `<leftCtrlOn>` `<rightCtrlOn>` - Simulates pressing and holding the ctrl key.
- `<leftCtrlOn>` `<rightCtrlOn>` - Simulates pressing and holding the ctrl key.
- `<leftShiftOn>` `<rightShiftOn>` - 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 `<leftCtrlOn>c<leftCtrlOff>`.
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 `<leftCtrlOn>c<leftCtrlOff>`.
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:
<Order>3</Order>
<Size>128</Size>
<Type>MSR</Type>
</CreatePartition>
</CreatePartition>
<CreatePartition wcm:action="add">
<Order>4</Order>
<Extend>true</Extend>
<Extend>true</Extend>
<Type>Primary</Type>
</CreatePartition>
</CreatePartitions>
@@ -609,8 +609,8 @@ autounattend.xml:
<POLICYProxySettingsPerUser>0</POLICYProxySettingsPerUser>
<HKLMProxyEnable>true</HKLMProxyEnable>
<HKLMProxyServer>cache-proxy:3142</HKLMProxyServer>
</component>
Finish Setup cache proxy during installation -->
</component>
Finish Setup cache proxy during installation -->
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AutoLogon>
<Password>
@@ -842,13 +842,13 @@ sysprep-unattend.xml:
</component>
</settings>
<settings pass="oobeSystem">
<!-- Setup proxy after sysprep
<!-- Setup proxy after sysprep
<component name="Microsoft-Windows-IE-ClientNetworkProtocolImplementation" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<POLICYProxySettingsPerUser>1</POLICYProxySettingsPerUser>
<HKLMProxyEnable>false</HKLMProxyEnable>
<HKLMProxyServer>cache-proxy:3142</HKLMProxyServer>
</component>
Finish proxy after sysprep -->
Finish proxy after sysprep -->
<component language="neutral" name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<InputLocale>0809:00000809</InputLocale>
<SystemLocale>en-GB</SystemLocale>
+2 -2
View File
@@ -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.
+1 -1
View File
@@ -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
@@ -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
@@ -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 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'
@@ -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
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.
-&gt; **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.
@@ -114,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
@@ -140,6 +144,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
@@ -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
+15 -1
View File
@@ -75,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
@@ -90,6 +101,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 +111,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/",
@@ -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
+1
View File
@@ -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: