Remove extra calls from CreateAzureVM

Also rename GetDeployment & DeleteDeployment and
give async control to the caller
This commit is contained in:
Paul Meyer 2015-03-18 17:17:15 -07:00
Родитель de1dcac65f
Коммит 588a3bb281
2 изменённых файлов: 314 добавлений и 425 удалений

Просмотреть файл

@ -18,22 +18,17 @@ import (
"unicode"
"github.com/MSOpenTech/azure-sdk-for-go/management"
hostedserviceclient "github.com/MSOpenTech/azure-sdk-for-go/management/hostedservice"
locationclient "github.com/MSOpenTech/azure-sdk-for-go/management/location"
storageserviceclient "github.com/MSOpenTech/azure-sdk-for-go/management/storageservice"
)
const (
azureDeploymentListURL = "services/hostedservices/%s/deployments"
azureHostedServiceListURL = "services/hostedservices"
deleteAzureHostedServiceURL = "services/hostedservices/%s?comp=media"
azureHostedServiceAvailabilityURL = "services/hostedservices/operations/isavailable/%s"
azureDeploymentURL = "services/hostedservices/%s/deployments/%s"
deleteAzureDeploymentURL = "services/hostedservices/%s/deployments/%s?comp=media"
azureRoleURL = "services/hostedservices/%s/deployments/%s/roles/%s"
azureOperationsURL = "services/hostedservices/%s/deployments/%s/roleinstances/%s/Operations"
azureCertificatListURL = "services/hostedservices/%s/certificates"
azureRoleSizeListURL = "rolesizes"
azureDeploymentListURL = "services/hostedservices/%s/deployments"
azureDeploymentURL = "services/hostedservices/%s/deployments/%s"
deleteAzureDeploymentURL = "services/hostedservices/%s/deployments/%s?comp=media"
azureRoleURL = "services/hostedservices/%s/deployments/%s/roles/%s"
azureOperationsURL = "services/hostedservices/%s/deployments/%s/roleinstances/%s/Operations"
azureRoleSizeListURL = "rolesizes"
osLinux = "Linux"
osWindows = "Windows"
@ -47,7 +42,6 @@ const (
errInvalidPassword = "Password must have at least one upper case, lower case and numeric character."
errInvalidRoleSize = "Invalid role size: %s. Available role sizes: %s"
errInvalidRoleSizeInLocation = "Role size: %s not available in location: %s."
errInvalidDnsLength = "The DNS name must be between 3 and 25 characters."
)
//NewClient is used to instantiate a new VmClient from an Azure client
@ -55,51 +49,19 @@ func NewClient(client management.Client) VirtualMachineClient {
return VirtualMachineClient{client: client}
}
func (self VirtualMachineClient) CreateAzureVM(azureVMConfiguration *Role, dnsName, location string) error {
if azureVMConfiguration == nil {
return fmt.Errorf(errParamNotSpecified, "azureVMConfiguration")
}
if dnsName == "" {
return fmt.Errorf(errParamNotSpecified, "dnsName")
}
if location == "" {
return fmt.Errorf(errParamNotSpecified, "location")
func (self VirtualMachineClient) CreateDeployment(role *Role, cloudserviceName string) (requestId string, err error) {
if role == nil {
return "", fmt.Errorf(errParamNotSpecified, "role")
}
hostedServiceClient := hostedserviceclient.NewClient(self.client)
requestId, err := hostedServiceClient.CreateHostedService(dnsName, location, "", dnsName, "")
if err != nil {
return err
}
vMDeploymentBytes, err := xml.Marshal(DeploymentRequest{
Name: role.RoleName,
DeploymentSlot: "Production",
Label: role.RoleName,
RoleList: []*Role{role}})
err = self.client.WaitAsyncOperation(requestId)
if err != nil {
return err
}
if azureVMConfiguration.UseCertAuth {
err = self.uploadServiceCert(dnsName, azureVMConfiguration.CertPath)
if err != nil {
hostedServiceClient.DeleteHostedService(dnsName)
return err
}
}
vMDeployment := self.createVMDeploymentConfig(azureVMConfiguration)
vMDeploymentBytes, err := xml.Marshal(vMDeployment)
if err != nil {
hostedServiceClient.DeleteHostedService(dnsName)
return err
}
requestURL := fmt.Sprintf(azureDeploymentListURL, azureVMConfiguration.RoleName)
requestId, err = self.client.SendAzurePostRequest(requestURL, vMDeploymentBytes)
if err != nil {
hostedServiceClient.DeleteHostedService(dnsName)
return err
}
return self.client.WaitAsyncOperation(requestId)
requestURL := fmt.Sprintf(azureDeploymentListURL, cloudserviceName)
return self.client.SendAzurePostRequest(requestURL, vMDeploymentBytes)
}
func (self VirtualMachineClient) CreateAzureVMConfiguration(dnsName, instanceSize, imageName, location string) (*Role, error) {
@ -242,7 +204,7 @@ func (self VirtualMachineClient) SetAzureDockerVMExtension(azureVMConfiguration
return azureVMConfiguration, nil
}
func (self VirtualMachineClient) GetVMDeployment(cloudserviceName, deploymentName string) (*DeploymentResponse, error) {
func (self VirtualMachineClient) GetDeployment(cloudserviceName, deploymentName string) (*DeploymentResponse, error) {
if cloudserviceName == "" {
return nil, fmt.Errorf(errParamNotSpecified, "cloudserviceName")
}
@ -266,26 +228,16 @@ func (self VirtualMachineClient) GetVMDeployment(cloudserviceName, deploymentNam
return deployment, nil
}
func (self VirtualMachineClient) DeleteVMDeployment(cloudserviceName, deploymentName string) error {
func (self VirtualMachineClient) DeleteDeployment(cloudserviceName, deploymentName string) (requestId string, err error) {
if cloudserviceName == "" {
return fmt.Errorf(errParamNotSpecified, "cloudserviceName")
return "", fmt.Errorf(errParamNotSpecified, "cloudserviceName")
}
if deploymentName == "" {
return fmt.Errorf(errParamNotSpecified, "deploymentName")
return "", fmt.Errorf(errParamNotSpecified, "deploymentName")
}
requestURL := fmt.Sprintf(deleteAzureDeploymentURL, cloudserviceName, deploymentName)
requestId, err := self.client.SendAzureDeleteRequest(requestURL)
if err != nil {
return err
}
err = self.client.WaitAsyncOperation(requestId)
if err != nil {
return err
}
return nil
return self.client.SendAzureDeleteRequest(requestURL)
}
func (self VirtualMachineClient) GetRole(cloudserviceName, deploymentName, roleName string) (*Role, error) {
@ -326,9 +278,9 @@ func (self VirtualMachineClient) StartRole(cloudserviceName, deploymentName, rol
return fmt.Errorf(errParamNotSpecified, "roleName")
}
startRoleOperation := self.createStartRoleOperation()
startRoleOperationBytes, err := xml.Marshal(startRoleOperation)
startRoleOperationBytes, err := xml.Marshal(StartRoleOperation{
OperationType: "StartRoleOperation",
})
if err != nil {
return err
}
@ -353,9 +305,9 @@ func (self VirtualMachineClient) ShutdownRole(cloudserviceName, deploymentName,
return fmt.Errorf(errParamNotSpecified, "roleName")
}
shutdownRoleOperation := self.createShutdowRoleOperation()
shutdownRoleOperationBytes, err := xml.Marshal(shutdownRoleOperation)
shutdownRoleOperationBytes, err := xml.Marshal(ShutdownRoleOperation{
OperationType: "ShutdownRoleOperation",
})
if err != nil {
return err
}
@ -380,9 +332,9 @@ func (self VirtualMachineClient) RestartRole(cloudserviceName, deploymentName, r
return fmt.Errorf(errParamNotSpecified, "roleName")
}
restartRoleOperation := self.createRestartRoleOperation()
restartRoleOperationBytes, err := xml.Marshal(restartRoleOperation)
restartRoleOperationBytes, err := xml.Marshal(RestartRoleOperation{
OperationType: "RestartRoleOperation",
})
if err != nil {
return err
}
@ -458,24 +410,6 @@ func (self VirtualMachineClient) ResolveRoleSize(roleSizeName string) error {
return errors.New(fmt.Sprintf(errInvalidRoleSize, roleSizeName, strings.Trim(availableSizes.String(), ", ")))
}
func (self VirtualMachineClient) createStartRoleOperation() StartRoleOperation {
return StartRoleOperation{
OperationType: "StartRoleOperation",
}
}
func (self VirtualMachineClient) createShutdowRoleOperation() ShutdownRoleOperation {
return ShutdownRoleOperation{
OperationType: "ShutdownRoleOperation",
}
}
func (self VirtualMachineClient) createRestartRoleOperation() RestartRoleOperation {
return RestartRoleOperation{
OperationType: "RestartRoleOperation",
}
}
func (self VirtualMachineClient) createDockerPublicConfig(dockerPort int) (string, error) {
config := dockerPublicConfig{DockerPort: dockerPort, Version: dockerPublicConfigVersion}
configJson, err := json.Marshal(config)
@ -502,16 +436,6 @@ func (self VirtualMachineClient) addDockerPort(configurationSets []Configuration
return nil
}
func (self VirtualMachineClient) createVMDeploymentConfig(role *Role) DeploymentRequest {
return DeploymentRequest{
Name: role.RoleName,
DeploymentSlot: "Production",
Label: role.RoleName,
RoleList: []*Role{role},
}
}
func (self VirtualMachineClient) createAzureVMRole(name, instanceSize, imageName, location string) (*Role, error) {
vhd, err := self.createOSVirtualHardDisk(name, imageName, location)
if err != nil {
@ -608,41 +532,6 @@ func (self VirtualMachineClient) createLinuxProvisioningConfig(dnsName, userName
return provisioningConfig, nil
}
func (self VirtualMachineClient) uploadServiceCert(dnsName, certPath string) error {
certificateConfig, err := self.createServiceCertDeploymentConf(certPath)
if err != nil {
return err
}
certificateConfigBytes, err := xml.Marshal(certificateConfig)
if err != nil {
return err
}
requestURL := fmt.Sprintf(azureCertificatListURL, dnsName)
requestId, azureErr := self.client.SendAzurePostRequest(requestURL, certificateConfigBytes)
if azureErr != nil {
return azureErr
}
return self.client.WaitAsyncOperation(requestId)
}
func (self VirtualMachineClient) createServiceCertDeploymentConf(certPath string) (ServiceCertificate, error) {
certConfig := ServiceCertificate{}
data, err := ioutil.ReadFile(certPath)
if err != nil {
return certConfig, err
}
certData := base64.StdEncoding.EncodeToString(data)
certConfig.Data = certData
certConfig.CertificateFormat = "pfx"
return certConfig, nil
}
func (self VirtualMachineClient) createSshConfig(certPath, userName string) (SSH, error) {
sshConfig := SSH{}
publicKey := PublicKey{}

Просмотреть файл

@ -1,284 +1,284 @@
package virtualmachine
import (
"encoding/xml"
"testing"
)
func TestDocumentedDeploymentRequest(t *testing.T) {
// xml based on https://msdn.microsoft.com/en-us/library/azure/jj157194.aspx
// fixed typos, replaced strongly typed fields with values of correct type
xmlString := `<Deployment xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Name>name-of-deployment</Name>
<DeploymentSlot>deployment-environment</DeploymentSlot>
<Label>identifier-of-deployment</Label>
<RoleList>
<Role>
<RoleName>name-of-the-virtual-machine</RoleName>
<RoleType>PersistentVMRole</RoleType>
<ConfigurationSets>
<ConfigurationSet i:type="WindowsProvisioningConfigurationSet">
<ConfigurationSetType>WindowsProvisioningConfiguration</ConfigurationSetType>
<ComputerName>name-of-computer</ComputerName>
<AdminPassword>administrator-password</AdminPassword>
<EnableAutomaticUpdates>true</EnableAutomaticUpdates>
<TimeZone>time-zone</TimeZone>
<DomainJoin>
<Credentials>
<Domain>domain-to-join</Domain>
<Username>user-name-in-the-domain</Username>
<Password>password-for-the-user-name</Password>
</Credentials>
<JoinDomain>domain-to-join</JoinDomain>
<MachineObjectOU>distinguished-name-of-the-ou</MachineObjectOU>
</DomainJoin>
<StoredCertificateSettings>
<CertificateSetting>
<StoreLocation>LocalMachine</StoreLocation>
<StoreName>name-of-store-on-the-machine</StoreName>
<Thumbprint>certificate-thumbprint</Thumbprint>
</CertificateSetting>
</StoredCertificateSettings>
<WinRM>
<Listeners>
<Listener>
<Protocol>listener-protocol</Protocol>
</Listener>
<Listener>
<CertificateThumbprint>certificate-thumbprint</CertificateThumbprint>
<Protocol>listener-protocol</Protocol>
</Listener>
</Listeners>
</WinRM>
<AdminUsername>name-of-administrator-account</AdminUsername>
<CustomData>base-64-encoded-data</CustomData>
<AdditionalUnattendContent>
<Passes>
<UnattendPass>
<PassName>name-of-pass</PassName>
<Components>
<UnattendComponent>
<ComponentName>name-of-component</ComponentName>
<ComponentSettings>
<ComponentSetting>
<SettingName>name-of-setting</SettingName>
<Content>base-64-encoded-XML-content</Content>
</ComponentSetting>
</ComponentSettings>
</UnattendComponent>
</Components>
</UnattendPass>
</Passes>
</AdditionalUnattendContent>
</ConfigurationSet>
<ConfigurationSet i:type="LinuxProvisioningConfigurationSet">
<ConfigurationSetType>LinuxProvisioningConfiguration</ConfigurationSetType>
<HostName>host-name-for-the-virtual-machine</HostName>
<UserName>new-user-name</UserName>
<UserPassword>password-for-the-new-user</UserPassword>
<DisableSshPasswordAuthentication>true</DisableSshPasswordAuthentication>
<SSH>
<PublicKeys>
<PublicKey>
<FingerPrint>certificate-fingerprint</FingerPrint>
<Path>SSH-public-key-storage-location</Path>
</PublicKey>
</PublicKeys>
<KeyPairs>
<KeyPair>
<FingerPrint>certificate-fingerprint</FingerPrint>
<Path>SSH-public-key-storage-location</Path>
</KeyPair>
</KeyPairs>
</SSH>
<CustomData>base-64-encoded-data</CustomData>
</ConfigurationSet>
<ConfigurationSet>
<ConfigurationSetType>NetworkConfiguration</ConfigurationSetType>
<InputEndpoints>
<InputEndpoint>
<LoadBalancedEndpointSetName>name-of-load-balanced-set</LoadBalancedEndpointSetName>
<LocalPort>22</LocalPort>
<Name>ZZH</Name>
<Port>33</Port>
<LoadBalancerProbe>
<Path>/probe/me</Path>
<Port>80</Port>
<Protocol>http</Protocol>
<IntervalInSeconds>30</IntervalInSeconds>
<TimeoutInSeconds>5</TimeoutInSeconds>
</LoadBalancerProbe>
<Protocol>endpoint-protocol</Protocol>
<EnableDirectServerReturn>enable-direct-server-return</EnableDirectServerReturn>
<EndpointACL>
<Rules>
<Rule>
<Order>priority-of-the-rule</Order>
<Action>permit-rule</Action>
<RemoteSubnet>subnet-of-the-rule</RemoteSubnet>
<Description>description-of-the-rule</Description>
</Rule>
</Rules>
</EndpointACL>
<LoadBalancerName>name-of-internal-loadbalancer</LoadBalancerName>
<IdleTimeoutInMinutes>9</IdleTimeoutInMinutes>
</InputEndpoint>
</InputEndpoints>
<SubnetNames>
<SubnetName>name-of-subnet</SubnetName>
</SubnetNames>
<StaticVirtualNetworkIPAddress>ip-address</StaticVirtualNetworkIPAddress>
<PublicIPs>
<PublicIP>
<Name>name-of-public-ip</Name>
<IdleTimeoutInMinutes>11</IdleTimeoutInMinutes>
</PublicIP>
</PublicIPs>
</ConfigurationSet>
</ConfigurationSets>
<ResourceExtensionReferences>
<ResourceExtensionReference>
<ReferenceName>name-of-reference</ReferenceName>
<Publisher>name-of-publisher</Publisher>
<Name>name-of-extension</Name>
<Version>version-of-extension</Version>
<ResourceExtensionParameterValues>
<ResourceExtensionParameterValue>
<Key>name-of-parameter-key</Key>
<Value>parameter-value</Value>
<Type>type-of-parameter</Type>
</ResourceExtensionParameterValue>
</ResourceExtensionParameterValues>
<State>state-of-resource</State>
<Certificates>
<Certificate>
<Thumbprint>certificate-thumbprint</Thumbprint>
<ThumbprintAlgorithm>certificate-algorithm</ThumbprintAlgorithm>
</Certificate>
</Certificates>
</ResourceExtensionReference>
</ResourceExtensionReferences>
<VMImageName>name-of-vm-image</VMImageName>
<MediaLocation>path-to-vhd</MediaLocation>
<AvailabilitySetName>name-of-availability-set</AvailabilitySetName>
<DataVirtualHardDisks>
<DataVirtualHardDisk>
<HostCaching>caching-mode</HostCaching>
<DiskLabel>label-of-data-disk</DiskLabel>
<DiskName>name-of-disk</DiskName>
<Lun>0</Lun>
<LogicalDiskSizeInGB>50</LogicalDiskSizeInGB>
<MediaLink>path-to-vhd</MediaLink>
</DataVirtualHardDisk>
</DataVirtualHardDisks>
<OSVirtualHardDisk>
<HostCaching>caching-mode</HostCaching>
<DiskLabel>label-of-operating-system-disk</DiskLabel>
<DiskName>name-of-disk</DiskName>
<MediaLink>path-to-vhd</MediaLink>
<SourceImageName>name-of-source-image</SourceImageName>
<OS>operating-system-of-image</OS>
<RemoteSourceImageLink>path-to-source-image</RemoteSourceImageLink>
<ResizedSizeInGB>125</ResizedSizeInGB>
</OSVirtualHardDisk>
<RoleSize>size-of-virtual-machine</RoleSize>
<ProvisionGuestAgent>true</ProvisionGuestAgent>
<VMImageInput>
<OSDiskConfiguration>
<ResizedSizeInGB>126</ResizedSizeInGB>
</OSDiskConfiguration>
<DataDiskConfigurations>
<DataDiskConfiguration>
<Name>disk-name</Name>
<ResizedSizeInGB>127</ResizedSizeInGB>
</DataDiskConfiguration>
</DataDiskConfigurations>
</VMImageInput>
</Role>
</RoleList>
<VirtualNetworkName>name-of-virtual-network</VirtualNetworkName>
<Dns>
<DnsServers>
<DnsServer>
<Name>dns-name</Name>
<Address>dns-ip-address</Address>
</DnsServer>
</DnsServers>
</Dns>
<ReservedIPName>name-of-reserved-ip</ReservedIPName>
<LoadBalancers>
<LoadBalancer>
<Name>name-of-internal-load-balancer</Name>
<FrontendIpConfiguration>
<Type>Private</Type>
<SubnetName>name-of-subnet</SubnetName>
<StaticVirtualNetworkIPAddress>static-ip-address</StaticVirtualNetworkIPAddress>
</FrontendIpConfiguration>
</LoadBalancer>
</LoadBalancers>
</Deployment>`
deployment := DeploymentRequest{}
if err := xml.Unmarshal([]byte(xmlString), &deployment); err != nil {
t.Fatal(err)
}
if deployment.Name != "name-of-deployment" {
t.Fatalf("Expected deployment.Name=\"name-of-deployment\", but got \"%s\"",
deployment.Name)
}
// ======
t.Logf("deployment.RoleList[0]: %+v", deployment.RoleList[0])
if expected := "name-of-the-virtual-machine"; deployment.RoleList[0].RoleName != expected {
t.Fatalf("Expected deployment.RoleList[0].RoleName=%v, but got %v", expected, deployment.RoleList[0].RoleName)
}
// ======
t.Logf("deployment.DnsServers[0]: %+v", (*deployment.DnsServers)[0])
if (*deployment.DnsServers)[0].Name != "dns-name" {
t.Fatalf("Expected deployment.DnsServers[0].Name=\"dns-name\", but got \"%s\"",
(*deployment.DnsServers)[0].Name)
}
// ======
t.Logf("deployment.LoadBalancers[0]: %+v", (*deployment.LoadBalancers)[0])
if (*deployment.LoadBalancers)[0].Name != "name-of-internal-load-balancer" {
t.Fatalf("Expected deployment.LoadBalancers[0].Name=\"name-of-internal-load-balancer\", but got \"%s\"",
(*deployment.LoadBalancers)[0].Name)
}
if (*deployment.LoadBalancers)[0].Type != IPAddressTypePrivate {
t.Fatalf("Expected deployment.LoadBalancers[0].Type=IPAddressTypePrivate, but got \"%s\"",
(*deployment.LoadBalancers)[0].Type)
}
if (*deployment.LoadBalancers)[0].StaticVirtualNetworkIPAddress != "static-ip-address" {
t.Fatalf("Expected deployment.LoadBalancers[0].StaticVirtualNetworkIPAddress=\"static-ip-address\", but got \"%s\"",
(*deployment.LoadBalancers)[0].StaticVirtualNetworkIPAddress)
}
// ======
t.Logf("(*deployment.RoleList[0].ResourceExtensionReferences)[0]: %+v", (*deployment.RoleList[0].ResourceExtensionReferences)[0])
if (*deployment.RoleList[0].ResourceExtensionReferences)[0].Name != "name-of-extension" {
t.Fatalf("Expected (*deployment.RoleList[0].ResourceExtensionReferences)[0].Name=\"name-of-extension\", but got \"%s\"",
(*deployment.RoleList[0].ResourceExtensionReferences)[0].Name)
}
if (*deployment.RoleList[0].ResourceExtensionReferences)[0].ParameterValues[0].Key != "name-of-parameter-key" {
t.Fatalf("Expected (*deployment.RoleList[0].ResourceExtensionReferences)[0].ParameterValues[0].Key=\"name-of-parameter-key\", but got %v",
(*deployment.RoleList[0].ResourceExtensionReferences)[0].ParameterValues[0].Key)
}
// ======
if deployment.RoleList[0].VMImageInput.DataDiskConfigurations[0].ResizedSizeInGB != 127 {
t.Fatalf("Expected deployment.RoleList[0].VMImageInput.DataDiskConfigurations[0].ResizedSizeInGB=127, but got %v",
deployment.RoleList[0].VMImageInput.DataDiskConfigurations[0].ResizedSizeInGB)
}
}
package virtualmachine
import (
"encoding/xml"
"testing"
)
func TestDocumentedDeploymentRequest(t *testing.T) {
// xml based on https://msdn.microsoft.com/en-us/library/azure/jj157194.aspx
// fixed typos, replaced strongly typed fields with values of correct type
xmlString := `<Deployment xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Name>name-of-deployment</Name>
<DeploymentSlot>deployment-environment</DeploymentSlot>
<Label>identifier-of-deployment</Label>
<RoleList>
<Role>
<RoleName>name-of-the-virtual-machine</RoleName>
<RoleType>PersistentVMRole</RoleType>
<ConfigurationSets>
<ConfigurationSet i:type="WindowsProvisioningConfigurationSet">
<ConfigurationSetType>WindowsProvisioningConfiguration</ConfigurationSetType>
<ComputerName>name-of-computer</ComputerName>
<AdminPassword>administrator-password</AdminPassword>
<EnableAutomaticUpdates>true</EnableAutomaticUpdates>
<TimeZone>time-zone</TimeZone>
<DomainJoin>
<Credentials>
<Domain>domain-to-join</Domain>
<Username>user-name-in-the-domain</Username>
<Password>password-for-the-user-name</Password>
</Credentials>
<JoinDomain>domain-to-join</JoinDomain>
<MachineObjectOU>distinguished-name-of-the-ou</MachineObjectOU>
</DomainJoin>
<StoredCertificateSettings>
<CertificateSetting>
<StoreLocation>LocalMachine</StoreLocation>
<StoreName>name-of-store-on-the-machine</StoreName>
<Thumbprint>certificate-thumbprint</Thumbprint>
</CertificateSetting>
</StoredCertificateSettings>
<WinRM>
<Listeners>
<Listener>
<Protocol>listener-protocol</Protocol>
</Listener>
<Listener>
<CertificateThumbprint>certificate-thumbprint</CertificateThumbprint>
<Protocol>listener-protocol</Protocol>
</Listener>
</Listeners>
</WinRM>
<AdminUsername>name-of-administrator-account</AdminUsername>
<CustomData>base-64-encoded-data</CustomData>
<AdditionalUnattendContent>
<Passes>
<UnattendPass>
<PassName>name-of-pass</PassName>
<Components>
<UnattendComponent>
<ComponentName>name-of-component</ComponentName>
<ComponentSettings>
<ComponentSetting>
<SettingName>name-of-setting</SettingName>
<Content>base-64-encoded-XML-content</Content>
</ComponentSetting>
</ComponentSettings>
</UnattendComponent>
</Components>
</UnattendPass>
</Passes>
</AdditionalUnattendContent>
</ConfigurationSet>
<ConfigurationSet i:type="LinuxProvisioningConfigurationSet">
<ConfigurationSetType>LinuxProvisioningConfiguration</ConfigurationSetType>
<HostName>host-name-for-the-virtual-machine</HostName>
<UserName>new-user-name</UserName>
<UserPassword>password-for-the-new-user</UserPassword>
<DisableSshPasswordAuthentication>true</DisableSshPasswordAuthentication>
<SSH>
<PublicKeys>
<PublicKey>
<FingerPrint>certificate-fingerprint</FingerPrint>
<Path>SSH-public-key-storage-location</Path>
</PublicKey>
</PublicKeys>
<KeyPairs>
<KeyPair>
<FingerPrint>certificate-fingerprint</FingerPrint>
<Path>SSH-public-key-storage-location</Path>
</KeyPair>
</KeyPairs>
</SSH>
<CustomData>base-64-encoded-data</CustomData>
</ConfigurationSet>
<ConfigurationSet>
<ConfigurationSetType>NetworkConfiguration</ConfigurationSetType>
<InputEndpoints>
<InputEndpoint>
<LoadBalancedEndpointSetName>name-of-load-balanced-set</LoadBalancedEndpointSetName>
<LocalPort>22</LocalPort>
<Name>ZZH</Name>
<Port>33</Port>
<LoadBalancerProbe>
<Path>/probe/me</Path>
<Port>80</Port>
<Protocol>http</Protocol>
<IntervalInSeconds>30</IntervalInSeconds>
<TimeoutInSeconds>5</TimeoutInSeconds>
</LoadBalancerProbe>
<Protocol>endpoint-protocol</Protocol>
<EnableDirectServerReturn>enable-direct-server-return</EnableDirectServerReturn>
<EndpointACL>
<Rules>
<Rule>
<Order>priority-of-the-rule</Order>
<Action>permit-rule</Action>
<RemoteSubnet>subnet-of-the-rule</RemoteSubnet>
<Description>description-of-the-rule</Description>
</Rule>
</Rules>
</EndpointACL>
<LoadBalancerName>name-of-internal-loadbalancer</LoadBalancerName>
<IdleTimeoutInMinutes>9</IdleTimeoutInMinutes>
</InputEndpoint>
</InputEndpoints>
<SubnetNames>
<SubnetName>name-of-subnet</SubnetName>
</SubnetNames>
<StaticVirtualNetworkIPAddress>ip-address</StaticVirtualNetworkIPAddress>
<PublicIPs>
<PublicIP>
<Name>name-of-public-ip</Name>
<IdleTimeoutInMinutes>11</IdleTimeoutInMinutes>
</PublicIP>
</PublicIPs>
</ConfigurationSet>
</ConfigurationSets>
<ResourceExtensionReferences>
<ResourceExtensionReference>
<ReferenceName>name-of-reference</ReferenceName>
<Publisher>name-of-publisher</Publisher>
<Name>name-of-extension</Name>
<Version>version-of-extension</Version>
<ResourceExtensionParameterValues>
<ResourceExtensionParameterValue>
<Key>name-of-parameter-key</Key>
<Value>parameter-value</Value>
<Type>type-of-parameter</Type>
</ResourceExtensionParameterValue>
</ResourceExtensionParameterValues>
<State>state-of-resource</State>
<Certificates>
<Certificate>
<Thumbprint>certificate-thumbprint</Thumbprint>
<ThumbprintAlgorithm>certificate-algorithm</ThumbprintAlgorithm>
</Certificate>
</Certificates>
</ResourceExtensionReference>
</ResourceExtensionReferences>
<VMImageName>name-of-vm-image</VMImageName>
<MediaLocation>path-to-vhd</MediaLocation>
<AvailabilitySetName>name-of-availability-set</AvailabilitySetName>
<DataVirtualHardDisks>
<DataVirtualHardDisk>
<HostCaching>caching-mode</HostCaching>
<DiskLabel>label-of-data-disk</DiskLabel>
<DiskName>name-of-disk</DiskName>
<Lun>0</Lun>
<LogicalDiskSizeInGB>50</LogicalDiskSizeInGB>
<MediaLink>path-to-vhd</MediaLink>
</DataVirtualHardDisk>
</DataVirtualHardDisks>
<OSVirtualHardDisk>
<HostCaching>caching-mode</HostCaching>
<DiskLabel>label-of-operating-system-disk</DiskLabel>
<DiskName>name-of-disk</DiskName>
<MediaLink>path-to-vhd</MediaLink>
<SourceImageName>name-of-source-image</SourceImageName>
<OS>operating-system-of-image</OS>
<RemoteSourceImageLink>path-to-source-image</RemoteSourceImageLink>
<ResizedSizeInGB>125</ResizedSizeInGB>
</OSVirtualHardDisk>
<RoleSize>size-of-virtual-machine</RoleSize>
<ProvisionGuestAgent>true</ProvisionGuestAgent>
<VMImageInput>
<OSDiskConfiguration>
<ResizedSizeInGB>126</ResizedSizeInGB>
</OSDiskConfiguration>
<DataDiskConfigurations>
<DataDiskConfiguration>
<Name>disk-name</Name>
<ResizedSizeInGB>127</ResizedSizeInGB>
</DataDiskConfiguration>
</DataDiskConfigurations>
</VMImageInput>
</Role>
</RoleList>
<VirtualNetworkName>name-of-virtual-network</VirtualNetworkName>
<Dns>
<DnsServers>
<DnsServer>
<Name>dns-name</Name>
<Address>dns-ip-address</Address>
</DnsServer>
</DnsServers>
</Dns>
<ReservedIPName>name-of-reserved-ip</ReservedIPName>
<LoadBalancers>
<LoadBalancer>
<Name>name-of-internal-load-balancer</Name>
<FrontendIpConfiguration>
<Type>Private</Type>
<SubnetName>name-of-subnet</SubnetName>
<StaticVirtualNetworkIPAddress>static-ip-address</StaticVirtualNetworkIPAddress>
</FrontendIpConfiguration>
</LoadBalancer>
</LoadBalancers>
</Deployment>`
deployment := DeploymentRequest{}
if err := xml.Unmarshal([]byte(xmlString), &deployment); err != nil {
t.Fatal(err)
}
if deployment.Name != "name-of-deployment" {
t.Fatalf("Expected deployment.Name=\"name-of-deployment\", but got \"%s\"",
deployment.Name)
}
// ======
t.Logf("deployment.RoleList[0]: %+v", deployment.RoleList[0])
if expected := "name-of-the-virtual-machine"; deployment.RoleList[0].RoleName != expected {
t.Fatalf("Expected deployment.RoleList[0].RoleName=%v, but got %v", expected, deployment.RoleList[0].RoleName)
}
// ======
t.Logf("deployment.DnsServers[0]: %+v", (*deployment.DnsServers)[0])
if (*deployment.DnsServers)[0].Name != "dns-name" {
t.Fatalf("Expected deployment.DnsServers[0].Name=\"dns-name\", but got \"%s\"",
(*deployment.DnsServers)[0].Name)
}
// ======
t.Logf("deployment.LoadBalancers[0]: %+v", (*deployment.LoadBalancers)[0])
if (*deployment.LoadBalancers)[0].Name != "name-of-internal-load-balancer" {
t.Fatalf("Expected deployment.LoadBalancers[0].Name=\"name-of-internal-load-balancer\", but got \"%s\"",
(*deployment.LoadBalancers)[0].Name)
}
if (*deployment.LoadBalancers)[0].Type != IPAddressTypePrivate {
t.Fatalf("Expected deployment.LoadBalancers[0].Type=IPAddressTypePrivate, but got \"%s\"",
(*deployment.LoadBalancers)[0].Type)
}
if (*deployment.LoadBalancers)[0].StaticVirtualNetworkIPAddress != "static-ip-address" {
t.Fatalf("Expected deployment.LoadBalancers[0].StaticVirtualNetworkIPAddress=\"static-ip-address\", but got \"%s\"",
(*deployment.LoadBalancers)[0].StaticVirtualNetworkIPAddress)
}
// ======
t.Logf("(*deployment.RoleList[0].ResourceExtensionReferences)[0]: %+v", (*deployment.RoleList[0].ResourceExtensionReferences)[0])
if (*deployment.RoleList[0].ResourceExtensionReferences)[0].Name != "name-of-extension" {
t.Fatalf("Expected (*deployment.RoleList[0].ResourceExtensionReferences)[0].Name=\"name-of-extension\", but got \"%s\"",
(*deployment.RoleList[0].ResourceExtensionReferences)[0].Name)
}
if (*deployment.RoleList[0].ResourceExtensionReferences)[0].ParameterValues[0].Key != "name-of-parameter-key" {
t.Fatalf("Expected (*deployment.RoleList[0].ResourceExtensionReferences)[0].ParameterValues[0].Key=\"name-of-parameter-key\", but got %v",
(*deployment.RoleList[0].ResourceExtensionReferences)[0].ParameterValues[0].Key)
}
// ======
if deployment.RoleList[0].VMImageInput.DataDiskConfigurations[0].ResizedSizeInGB != 127 {
t.Fatalf("Expected deployment.RoleList[0].VMImageInput.DataDiskConfigurations[0].ResizedSizeInGB=127, but got %v",
deployment.RoleList[0].VMImageInput.DataDiskConfigurations[0].ResizedSizeInGB)
}
}