Compare commits
94 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9698d7283f | |||
| 4a864d59d7 | |||
| cad5ccfa64 | |||
| 3710c34eb5 | |||
| c4bc69af71 | |||
| f216330ba3 | |||
| 4b1d8e3fe8 | |||
| 73b98b2a04 | |||
| dada63801b | |||
| 9e61005f7c | |||
| 98420fc9f5 | |||
| 24ed2b7081 | |||
| 5df2e040d0 | |||
| 2c1c7b9659 | |||
| ea55db6d99 | |||
| a90c45d9bb | |||
| da4d6f76be | |||
| 7b5c0900ef | |||
| ca26ab5d5a | |||
| 19c997cb0e | |||
| 76ac755ed9 | |||
| 92d1bdbdab | |||
| 1202bdd080 | |||
| 0fbbdc2ea3 | |||
| 8970722845 | |||
| d043c37ad4 | |||
| 0e08640fff | |||
| 8a30b3db80 | |||
| c28a50af16 | |||
| 753d3cf216 | |||
| 1c681fc096 | |||
| 556da47d35 | |||
| 7e8517473d | |||
| 37d22160a8 | |||
| da0c13f622 | |||
| aa2bbf2aec | |||
| 72afc2eab3 | |||
| 03f9e12d04 | |||
| b66426e668 | |||
| 8f02150178 | |||
| 2f7b9640d9 | |||
| bdc1fa0527 | |||
| de0017c37a | |||
| 10aaa49beb | |||
| ff3efb4641 | |||
| be3fe340c8 | |||
| e990e49711 | |||
| c9072083d1 | |||
| a325780aa5 | |||
| df492d35bf | |||
| 150bf1f6da | |||
| 52f5f2b895 | |||
| 0c787ec9df | |||
| a51885be9c | |||
| af8a0c46c5 | |||
| 3d5303c60d | |||
| b7e3f37b44 | |||
| 0dae5bc8bd | |||
| d4f37ab5f6 | |||
| 75320440ec | |||
| 7b03613649 | |||
| 707ec675b2 | |||
| 7d80e37c14 | |||
| 10370adbab | |||
| baacb7e173 | |||
| a3c9485062 | |||
| 61d8274096 | |||
| a8ff095059 | |||
| e9a63d8653 | |||
| 247da61ad1 | |||
| 8985bd45ba | |||
| ee767e55d1 | |||
| 2684153cb2 | |||
| 787f08f39b | |||
| 76e3167b6e | |||
| 3c20176dbb | |||
| 4fb8a27879 | |||
| 74a4cc04fe | |||
| 20390ff1ec | |||
| ab542c07ff | |||
| 284ffa2562 | |||
| 9ee1385283 | |||
| f974d3552c | |||
| 097296792a | |||
| b8bd66d10d | |||
| f43f3155d4 | |||
| 0e706320ad | |||
| 0377140c39 | |||
| f4600a208f | |||
| 27fc72c137 | |||
| ae6987c74b | |||
| eeeee3ec35 | |||
| d890051940 | |||
| 820811675d |
+1
-1
@@ -8,7 +8,7 @@ language: go
|
||||
go:
|
||||
- 1.7.4
|
||||
- 1.8.3
|
||||
- 1.9
|
||||
- 1.x
|
||||
|
||||
install:
|
||||
- make deps
|
||||
|
||||
+41
-2
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ func TestStateBagShouldBePopulatedExpectedValues(t *testing.T) {
|
||||
constants.ArmTags,
|
||||
constants.ArmComputeName,
|
||||
constants.ArmDeploymentName,
|
||||
constants.ArmLocation,
|
||||
constants.ArmNicName,
|
||||
constants.ArmResourceGroupName,
|
||||
constants.ArmStorageAccountName,
|
||||
|
||||
@@ -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, "-")
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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, "/")
|
||||
|
||||
@@ -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) {},
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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{},
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 == "" {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
},
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -102,6 +102,7 @@ type NextHopType string
|
||||
const (
|
||||
NextHopIntance = NextHopType("Instance") //Default
|
||||
NextHopTunnel = NextHopType("Tunnel")
|
||||
NextHopTunnelRouterInterface = NextHopType("RouterInterface")
|
||||
)
|
||||
|
||||
type CreateRouteEntryArgs struct {
|
||||
|
||||
+227
@@ -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
@@ -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
@@ -31,6 +31,8 @@ type GroupResponse struct {
|
||||
|
||||
type GroupListResponse struct {
|
||||
RamCommonResponse
|
||||
IsTruncated bool
|
||||
Marker string
|
||||
Groups struct {
|
||||
Group []Group
|
||||
}
|
||||
|
||||
+11
-3
@@ -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
@@ -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:
|
||||
|
||||
Vendored
+20
-14
@@ -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
@@ -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
@@ -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')"
|
||||
```
|
||||
|
||||
-> Throughout this document when you see a command pipe to `jq` you may instead omit `--json` and everything after it, but the output will be more verbose. For example you can simply run `azure account list` instead.
|
||||
|
||||
This will print out one line that look like this:
|
||||
@@ -107,6 +146,12 @@ $ azure location list
|
||||
$ azure group create -n GROUPNAME -l LOCATION
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
```shell
|
||||
$ az group create -n GROUPNAME -l LOCATION
|
||||
```
|
||||
|
||||
Your storage account (below) will need to use the same `GROUPNAME` and `LOCATION`.
|
||||
|
||||
### Create a Storage Account
|
||||
@@ -121,9 +166,15 @@ $ azure storage account create \
|
||||
--kind storage STORAGENAME
|
||||
```
|
||||
|
||||
-> `LRS` is meant as a literal "LRS" and not as a variable.
|
||||
Python:
|
||||
|
||||
Make sure that `GROUPNAME` and `LOCATION` are the same as above.
|
||||
```shell
|
||||
$ az storage account create -n STORAGENAME -g GROUPNAME -l LOCATION --sku Standard_LRS
|
||||
```
|
||||
|
||||
-> `LRS` and `Standard_LRS` are meant as literal "LRS" or "Standard_LRS" and not as variables.
|
||||
|
||||
Make sure that `GROUPNAME` and `LOCATION` are the same as above. Also, ensure that `GROUPNAME` is less than 24 characters long and contains only lowercase letters and numbers.
|
||||
|
||||
### Create an Application
|
||||
|
||||
@@ -137,6 +188,12 @@ $ azure ad app create \
|
||||
-p PASSWORD
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
```shell
|
||||
az ad app create --display-name APPNAME --identifier-uris APPURL --homepage APPURL --password PASSWORD
|
||||
```
|
||||
|
||||
Password is your `client_secret` and can be anything you like. I recommend using `openssl rand -base64 24`.
|
||||
|
||||
### Create a Service Principal
|
||||
@@ -153,6 +210,13 @@ $ azure ad app list --json \
|
||||
$ azure ad sp create --applicationId APPID
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
```shell
|
||||
$ id=$(az ad app list | jq -r '.[] | select(.displayName == "Packer") | .appId')
|
||||
$ az ad sp create --appid "$id"
|
||||
```
|
||||
|
||||
### Grant Permissions to Your Application
|
||||
|
||||
Finally, we will associate the proper permissions with our application's service principal. We're going to assign the `Owner` role to our Packer application and change the scope to manage our whole subscription. (The `Owner` role can be scoped to a specific resource group to further reduce the scope of the account.) This allows Packer to create temporary resource groups for each build.
|
||||
@@ -164,6 +228,13 @@ $ azure role assignment create \
|
||||
-c /subscriptions/SUBSCRIPTIONID
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
```shell
|
||||
# NOTE: Trying to assign the role to the service principal by name directly yields a HTTP 400 error. See: https://github.com/Azure/azure-cli/issues/4911
|
||||
$ az role assignment create --assignee "$(az ad sp list | jq -r '.[] | select(.displayName == "APPNAME") | .objectId')" --role Owner
|
||||
```
|
||||
|
||||
There are a lot of pre-defined roles and you can define your own with more granular permissions, though this is out of scope. You can see a list of pre-configured roles via:
|
||||
|
||||
``` shell
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
|
||||
-> **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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user