vendor: add github.com/Azure/azure-sdk-for-go and dependencies
This commit is contained in:
+28
@@ -0,0 +1,28 @@
|
||||
package vmutils
|
||||
|
||||
import (
|
||||
vm "github.com/Azure/azure-sdk-for-go/management/virtualmachine"
|
||||
)
|
||||
|
||||
func updateOrAddConfig(configs []vm.ConfigurationSet, configType vm.ConfigurationSetType, update func(*vm.ConfigurationSet)) []vm.ConfigurationSet {
|
||||
config := findConfig(configs, configType)
|
||||
if config == nil {
|
||||
configs = append(configs, vm.ConfigurationSet{ConfigurationSetType: configType})
|
||||
config = findConfig(configs, configType)
|
||||
}
|
||||
update(config)
|
||||
|
||||
return configs
|
||||
}
|
||||
|
||||
func findConfig(configs []vm.ConfigurationSet, configType vm.ConfigurationSetType) *vm.ConfigurationSet {
|
||||
for i, config := range configs {
|
||||
if config.ConfigurationSetType == configType {
|
||||
// need to return a pointer to the original set in configs,
|
||||
// not the copy made by the range iterator
|
||||
return &configs[i]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package vmutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
vm "github.com/Azure/azure-sdk-for-go/management/virtualmachine"
|
||||
vmdisk "github.com/Azure/azure-sdk-for-go/management/virtualmachinedisk"
|
||||
)
|
||||
|
||||
// ConfigureWithNewDataDisk adds configuration for a new (empty) data disk
|
||||
func ConfigureWithNewDataDisk(role *vm.Role, label, destinationVhdStorageURL string, sizeInGB int, cachingType vmdisk.HostCachingType) error {
|
||||
if role == nil {
|
||||
return fmt.Errorf(errParamNotSpecified, "role")
|
||||
}
|
||||
|
||||
appendDataDisk(role, vm.DataVirtualHardDisk{
|
||||
DiskLabel: label,
|
||||
HostCaching: cachingType,
|
||||
LogicalDiskSizeInGB: sizeInGB,
|
||||
MediaLink: destinationVhdStorageURL,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConfigureWithExistingDataDisk adds configuration for an existing data disk
|
||||
func ConfigureWithExistingDataDisk(role *vm.Role, diskName string, cachingType vmdisk.HostCachingType) error {
|
||||
if role == nil {
|
||||
return fmt.Errorf(errParamNotSpecified, "role")
|
||||
}
|
||||
|
||||
appendDataDisk(role, vm.DataVirtualHardDisk{
|
||||
DiskName: diskName,
|
||||
HostCaching: cachingType,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConfigureWithVhdDataDisk adds configuration for adding a vhd in a storage
|
||||
// account as a data disk
|
||||
func ConfigureWithVhdDataDisk(role *vm.Role, sourceVhdStorageURL string, cachingType vmdisk.HostCachingType) error {
|
||||
if role == nil {
|
||||
return fmt.Errorf(errParamNotSpecified, "role")
|
||||
}
|
||||
|
||||
appendDataDisk(role, vm.DataVirtualHardDisk{
|
||||
SourceMediaLink: sourceVhdStorageURL,
|
||||
HostCaching: cachingType,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func appendDataDisk(role *vm.Role, disk vm.DataVirtualHardDisk) {
|
||||
disk.Lun = len(role.DataVirtualHardDisks)
|
||||
role.DataVirtualHardDisks = append(role.DataVirtualHardDisks, disk)
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
package vmutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
vm "github.com/Azure/azure-sdk-for-go/management/virtualmachine"
|
||||
)
|
||||
|
||||
// ConfigureDeploymentFromRemoteImage configures VM Role to deploy from a remote
|
||||
// image source. "remoteImageSourceURL" can be any publically accessible URL to
|
||||
// a VHD file, including but not limited to a SAS Azure Storage blob url. "os"
|
||||
// needs to be either "Linux" or "Windows". "label" is optional.
|
||||
func ConfigureDeploymentFromRemoteImage(
|
||||
role *vm.Role,
|
||||
remoteImageSourceURL string,
|
||||
os string,
|
||||
newDiskName string,
|
||||
destinationVhdStorageURL string,
|
||||
label string) error {
|
||||
if role == nil {
|
||||
return fmt.Errorf(errParamNotSpecified, "role")
|
||||
}
|
||||
|
||||
role.OSVirtualHardDisk = &vm.OSVirtualHardDisk{
|
||||
RemoteSourceImageLink: remoteImageSourceURL,
|
||||
MediaLink: destinationVhdStorageURL,
|
||||
DiskName: newDiskName,
|
||||
OS: os,
|
||||
DiskLabel: label,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConfigureDeploymentFromPlatformImage configures VM Role to deploy from a
|
||||
// platform image. See osimage package for methods to retrieve a list of the
|
||||
// available platform images. "label" is optional.
|
||||
func ConfigureDeploymentFromPlatformImage(
|
||||
role *vm.Role,
|
||||
imageName string,
|
||||
mediaLink string,
|
||||
label string) error {
|
||||
if role == nil {
|
||||
return fmt.Errorf(errParamNotSpecified, "role")
|
||||
}
|
||||
|
||||
role.OSVirtualHardDisk = &vm.OSVirtualHardDisk{
|
||||
SourceImageName: imageName,
|
||||
MediaLink: mediaLink,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConfigureDeploymentFromPublishedVMImage configures VM Role to deploy from
|
||||
// a published (public) VM image.
|
||||
func ConfigureDeploymentFromPublishedVMImage(
|
||||
role *vm.Role,
|
||||
vmImageName string,
|
||||
mediaLocation string,
|
||||
provisionGuestAgent bool) error {
|
||||
if role == nil {
|
||||
return fmt.Errorf(errParamNotSpecified, "role")
|
||||
}
|
||||
|
||||
role.VMImageName = vmImageName
|
||||
role.MediaLocation = mediaLocation
|
||||
role.ProvisionGuestAgent = provisionGuestAgent
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConfigureDeploymentFromUserVMImage configures VM Role to deploy from a previously
|
||||
// captured (user generated) VM image.
|
||||
func ConfigureDeploymentFromUserVMImage(
|
||||
role *vm.Role,
|
||||
vmImageName string) error {
|
||||
if role == nil {
|
||||
return fmt.Errorf(errParamNotSpecified, "role")
|
||||
}
|
||||
|
||||
role.VMImageName = vmImageName
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConfigureDeploymentFromExistingOSDisk configures VM Role to deploy from an
|
||||
// existing disk. 'label' is optional.
|
||||
func ConfigureDeploymentFromExistingOSDisk(role *vm.Role, osDiskName, label string) error {
|
||||
role.OSVirtualHardDisk = &vm.OSVirtualHardDisk{
|
||||
DiskName: osDiskName,
|
||||
DiskLabel: label,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
package vmutils
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
vm "github.com/Azure/azure-sdk-for-go/management/virtualmachine"
|
||||
)
|
||||
|
||||
const (
|
||||
dockerPublicConfigVersion = 2
|
||||
)
|
||||
|
||||
func AddAzureVMExtensionConfiguration(role *vm.Role, name, publisher, version, referenceName, state string,
|
||||
publicConfigurationValue, privateConfigurationValue []byte) error {
|
||||
if role == nil {
|
||||
return fmt.Errorf(errParamNotSpecified, "role")
|
||||
}
|
||||
|
||||
extension := vm.ResourceExtensionReference{
|
||||
Name: name,
|
||||
Publisher: publisher,
|
||||
Version: version,
|
||||
ReferenceName: referenceName,
|
||||
State: state,
|
||||
}
|
||||
|
||||
if len(privateConfigurationValue) != 0 {
|
||||
extension.ParameterValues = append(extension.ParameterValues, vm.ResourceExtensionParameter{
|
||||
Key: "ignored",
|
||||
Value: base64.StdEncoding.EncodeToString(privateConfigurationValue),
|
||||
Type: "Private",
|
||||
})
|
||||
}
|
||||
|
||||
if len(publicConfigurationValue) != 0 {
|
||||
extension.ParameterValues = append(extension.ParameterValues, vm.ResourceExtensionParameter{
|
||||
Key: "ignored",
|
||||
Value: base64.StdEncoding.EncodeToString(publicConfigurationValue),
|
||||
Type: "Public",
|
||||
})
|
||||
}
|
||||
|
||||
if role.ResourceExtensionReferences == nil {
|
||||
role.ResourceExtensionReferences = &[]vm.ResourceExtensionReference{}
|
||||
}
|
||||
extensionList := append(*role.ResourceExtensionReferences, extension)
|
||||
role.ResourceExtensionReferences = &extensionList
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddAzureDockerVMExtensionConfiguration adds the DockerExtension to the role
|
||||
// configuratioon and opens a port "dockerPort"
|
||||
// TODO(ahmetalpbalkan) Deprecate this and move to 'docker-machine' codebase.
|
||||
func AddAzureDockerVMExtensionConfiguration(role *vm.Role, dockerPort int, version string) error {
|
||||
if role == nil {
|
||||
return fmt.Errorf(errParamNotSpecified, "role")
|
||||
}
|
||||
|
||||
if err := ConfigureWithExternalPort(role, "docker", dockerPort, dockerPort, vm.InputEndpointProtocolTCP); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
publicConfiguration, err := createDockerPublicConfig(dockerPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
privateConfiguration, err := json.Marshal(dockerPrivateConfig{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return AddAzureVMExtensionConfiguration(role,
|
||||
"DockerExtension", "MSOpenTech.Extensions",
|
||||
version, "DockerExtension", "enable",
|
||||
publicConfiguration, privateConfiguration)
|
||||
}
|
||||
|
||||
func createDockerPublicConfig(dockerPort int) ([]byte, error) {
|
||||
return json.Marshal(dockerPublicConfig{DockerPort: dockerPort, Version: dockerPublicConfigVersion})
|
||||
}
|
||||
|
||||
type dockerPublicConfig struct {
|
||||
DockerPort int `json:"dockerport"`
|
||||
Version int `json:"version"`
|
||||
}
|
||||
|
||||
type dockerPrivateConfig struct{}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package vmutils
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"testing"
|
||||
|
||||
vm "github.com/Azure/azure-sdk-for-go/management/virtualmachine"
|
||||
)
|
||||
|
||||
func Test_AddAzureVMExtensionConfiguration(t *testing.T) {
|
||||
|
||||
role := vm.Role{}
|
||||
AddAzureVMExtensionConfiguration(&role,
|
||||
"nameOfExtension", "nameOfPublisher", "versionOfExtension", "nameOfReference", "state", []byte{1, 2, 3}, []byte{})
|
||||
|
||||
data, err := xml.MarshalIndent(role, "", " ")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if expected := `<Role>
|
||||
<ConfigurationSets></ConfigurationSets>
|
||||
<ResourceExtensionReferences>
|
||||
<ResourceExtensionReference>
|
||||
<ReferenceName>nameOfReference</ReferenceName>
|
||||
<Publisher>nameOfPublisher</Publisher>
|
||||
<Name>nameOfExtension</Name>
|
||||
<Version>versionOfExtension</Version>
|
||||
<ResourceExtensionParameterValues>
|
||||
<ResourceExtensionParameterValue>
|
||||
<Key>ignored</Key>
|
||||
<Value>AQID</Value>
|
||||
<Type>Public</Type>
|
||||
</ResourceExtensionParameterValue>
|
||||
</ResourceExtensionParameterValues>
|
||||
<State>state</State>
|
||||
</ResourceExtensionReference>
|
||||
</ResourceExtensionReferences>
|
||||
<DataVirtualHardDisks></DataVirtualHardDisks>
|
||||
</Role>`; string(data) != expected {
|
||||
t.Fatalf("Expected %q, but got %q", expected, string(data))
|
||||
}
|
||||
}
|
||||
+458
@@ -0,0 +1,458 @@
|
||||
package vmutils
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/management"
|
||||
"github.com/Azure/azure-sdk-for-go/management/hostedservice"
|
||||
"github.com/Azure/azure-sdk-for-go/management/location"
|
||||
"github.com/Azure/azure-sdk-for-go/management/osimage"
|
||||
storage "github.com/Azure/azure-sdk-for-go/management/storageservice"
|
||||
"github.com/Azure/azure-sdk-for-go/management/testutils"
|
||||
vm "github.com/Azure/azure-sdk-for-go/management/virtualmachine"
|
||||
vmimage "github.com/Azure/azure-sdk-for-go/management/virtualmachineimage"
|
||||
)
|
||||
|
||||
func TestDeployPlatformImage(t *testing.T) {
|
||||
client := testutils.GetTestClient(t)
|
||||
vmname := GenerateName()
|
||||
sa := GetTestStorageAccount(t, client)
|
||||
location := sa.StorageServiceProperties.Location
|
||||
|
||||
role := NewVMConfiguration(vmname, "Standard_D3")
|
||||
ConfigureDeploymentFromPlatformImage(&role,
|
||||
GetLinuxTestImage(t, client).Name,
|
||||
fmt.Sprintf("http://%s.blob.core.windows.net/sdktest/%s.vhd", sa.ServiceName, vmname),
|
||||
GenerateName())
|
||||
ConfigureForLinux(&role, "myvm", "azureuser", GeneratePassword())
|
||||
ConfigureWithPublicSSH(&role)
|
||||
|
||||
testRoleConfiguration(t, client, role, location)
|
||||
}
|
||||
|
||||
func TestDeployPlatformWindowsImage(t *testing.T) {
|
||||
client := testutils.GetTestClient(t)
|
||||
vmname := GenerateName()
|
||||
sa := GetTestStorageAccount(t, client)
|
||||
location := sa.StorageServiceProperties.Location
|
||||
|
||||
role := NewVMConfiguration(vmname, "Standard_D3")
|
||||
ConfigureDeploymentFromPlatformImage(&role,
|
||||
GetWindowsTestImage(t, client).Name,
|
||||
fmt.Sprintf("http://%s.blob.core.windows.net/sdktest/%s.vhd", sa.ServiceName, vmname),
|
||||
GenerateName())
|
||||
ConfigureForWindows(&role, vmname, "azureuser", GeneratePassword(), true, "")
|
||||
ConfigureWinRMOverHTTP(&role)
|
||||
ConfigureWinRMOverHTTPS(&role, "")
|
||||
|
||||
testRoleConfiguration(t, client, role, location)
|
||||
}
|
||||
|
||||
func TestVMImageList(t *testing.T) {
|
||||
client := testutils.GetTestClient(t)
|
||||
vmic := vmimage.NewClient(client)
|
||||
il, _ := vmic.ListVirtualMachineImages(vmimage.ListParameters{})
|
||||
for _, im := range il.VMImages {
|
||||
t.Logf("%s -%s", im.Name, im.Description)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeployPlatformOSImageCaptureRedeploy(t *testing.T) {
|
||||
client := testutils.GetTestClient(t)
|
||||
vmname := GenerateName()
|
||||
sa := GetTestStorageAccount(t, client)
|
||||
location := sa.StorageServiceProperties.Location
|
||||
|
||||
role := NewVMConfiguration(vmname, "Standard_D3")
|
||||
ConfigureDeploymentFromPlatformImage(&role,
|
||||
GetLinuxTestImage(t, client).Name,
|
||||
fmt.Sprintf("http://%s.blob.core.windows.net/sdktest/%s.vhd", sa.ServiceName, vmname),
|
||||
GenerateName())
|
||||
ConfigureForLinux(&role, "myvm", "azureuser", GeneratePassword())
|
||||
ConfigureWithPublicSSH(&role)
|
||||
|
||||
t.Logf("Deploying VM: %s", vmname)
|
||||
createRoleConfiguration(t, client, role, location)
|
||||
|
||||
t.Logf("Wait for deployment to enter running state")
|
||||
vmc := vm.NewClient(client)
|
||||
status := vm.DeploymentStatusDeploying
|
||||
for status != vm.DeploymentStatusRunning {
|
||||
deployment, err := vmc.GetDeployment(vmname, vmname)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
break
|
||||
}
|
||||
status = deployment.Status
|
||||
}
|
||||
|
||||
t.Logf("Shutting down VM: %s", vmname)
|
||||
if err := Await(client, func() (management.OperationID, error) {
|
||||
return vmc.ShutdownRole(vmname, vmname, vmname, vm.PostShutdownActionStopped)
|
||||
}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if err := WaitForDeploymentPowerState(client, vmname, vmname, vm.PowerStateStopped); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
imagename := GenerateName()
|
||||
t.Logf("Capturing OSImage: %s", imagename)
|
||||
if err := Await(client, func() (management.OperationID, error) {
|
||||
return vmc.CaptureRole(vmname, vmname, vmname, imagename, imagename, nil)
|
||||
}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
im := GetUserOSImage(t, client, imagename)
|
||||
t.Logf("Found image: %+v", im)
|
||||
|
||||
newvmname := GenerateName()
|
||||
role = NewVMConfiguration(newvmname, "Standard_D3")
|
||||
ConfigureDeploymentFromPlatformImage(&role,
|
||||
im.Name,
|
||||
fmt.Sprintf("http://%s.blob.core.windows.net/sdktest/%s.vhd", sa.ServiceName, newvmname),
|
||||
GenerateName())
|
||||
ConfigureForLinux(&role, newvmname, "azureuser", GeneratePassword())
|
||||
ConfigureWithPublicSSH(&role)
|
||||
|
||||
t.Logf("Deploying new VM from freshly captured OS image: %s", newvmname)
|
||||
if err := Await(client, func() (management.OperationID, error) {
|
||||
return vmc.CreateDeployment(role, vmname, vm.CreateDeploymentOptions{})
|
||||
}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
deleteHostedService(t, client, vmname)
|
||||
}
|
||||
|
||||
func TestDeployPlatformVMImageCaptureRedeploy(t *testing.T) {
|
||||
client := testutils.GetTestClient(t)
|
||||
vmname := GenerateName()
|
||||
sa := GetTestStorageAccount(t, client)
|
||||
location := sa.StorageServiceProperties.Location
|
||||
|
||||
role := NewVMConfiguration(vmname, "Standard_D3")
|
||||
ConfigureDeploymentFromPlatformImage(&role,
|
||||
GetLinuxTestImage(t, client).Name,
|
||||
fmt.Sprintf("http://%s.blob.core.windows.net/sdktest/%s.vhd", sa.ServiceName, vmname),
|
||||
GenerateName())
|
||||
ConfigureForLinux(&role, "myvm", "azureuser", GeneratePassword())
|
||||
ConfigureWithPublicSSH(&role)
|
||||
|
||||
t.Logf("Deploying VM: %s", vmname)
|
||||
createRoleConfiguration(t, client, role, location)
|
||||
|
||||
t.Logf("Wait for deployment to enter running state")
|
||||
vmc := vm.NewClient(client)
|
||||
status := vm.DeploymentStatusDeploying
|
||||
for status != vm.DeploymentStatusRunning {
|
||||
deployment, err := vmc.GetDeployment(vmname, vmname)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
break
|
||||
}
|
||||
status = deployment.Status
|
||||
}
|
||||
|
||||
t.Logf("Shutting down VM: %s", vmname)
|
||||
if err := Await(client, func() (management.OperationID, error) {
|
||||
return vmc.ShutdownRole(vmname, vmname, vmname, vm.PostShutdownActionStopped)
|
||||
}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if err := WaitForDeploymentInstanceStatus(client, vmname, vmname, vm.InstanceStatusStoppedVM); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
imagename := GenerateName()
|
||||
t.Logf("Capturing VMImage: %s", imagename)
|
||||
if err := Await(client, func() (management.OperationID, error) {
|
||||
return vmimage.NewClient(client).Capture(vmname, vmname, vmname, imagename, imagename, vmimage.OSStateGeneralized, vmimage.CaptureParameters{})
|
||||
}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
im := GetUserVMImage(t, client, imagename)
|
||||
t.Logf("Found image: %+v", im)
|
||||
|
||||
newvmname := GenerateName()
|
||||
role = NewVMConfiguration(newvmname, "Standard_D3")
|
||||
ConfigureDeploymentFromUserVMImage(&role, im.Name)
|
||||
ConfigureForLinux(&role, newvmname, "azureuser", GeneratePassword())
|
||||
ConfigureWithPublicSSH(&role)
|
||||
|
||||
t.Logf("Deploying new VM from freshly captured VM image: %s", newvmname)
|
||||
if err := Await(client, func() (management.OperationID, error) {
|
||||
return vmc.CreateDeployment(role, vmname, vm.CreateDeploymentOptions{})
|
||||
}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
deleteHostedService(t, client, vmname)
|
||||
}
|
||||
|
||||
func TestDeployFromPublishedVmImage(t *testing.T) {
|
||||
client := testutils.GetTestClient(t)
|
||||
vmname := GenerateName()
|
||||
sa := GetTestStorageAccount(t, client)
|
||||
location := sa.StorageServiceProperties.Location
|
||||
|
||||
im := GetVMImage(t, client, func(im vmimage.VMImage) bool {
|
||||
return im.Name ==
|
||||
"fb83b3509582419d99629ce476bcb5c8__SQL-Server-2014-RTM-12.0.2430.0-OLTP-ENU-Win2012R2-cy14su11"
|
||||
})
|
||||
|
||||
role := NewVMConfiguration(vmname, "Standard_D4")
|
||||
ConfigureDeploymentFromPublishedVMImage(&role, im.Name,
|
||||
fmt.Sprintf("http://%s.blob.core.windows.net/%s", sa.ServiceName, vmname), false)
|
||||
ConfigureForWindows(&role, vmname, "azureuser", GeneratePassword(), true, "")
|
||||
ConfigureWithPublicSSH(&role)
|
||||
|
||||
testRoleConfiguration(t, client, role, location)
|
||||
}
|
||||
|
||||
func TestRoleStateOperations(t *testing.T) {
|
||||
client := testutils.GetTestClient(t)
|
||||
vmname := GenerateName()
|
||||
sa := GetTestStorageAccount(t, client)
|
||||
location := sa.StorageServiceProperties.Location
|
||||
|
||||
role := NewVMConfiguration(vmname, "Standard_D3")
|
||||
ConfigureDeploymentFromPlatformImage(&role,
|
||||
GetLinuxTestImage(t, client).Name,
|
||||
fmt.Sprintf("http://%s.blob.core.windows.net/sdktest/%s.vhd", sa.ServiceName, vmname),
|
||||
GenerateName())
|
||||
ConfigureForLinux(&role, "myvm", "azureuser", GeneratePassword())
|
||||
|
||||
createRoleConfiguration(t, client, role, location)
|
||||
|
||||
vmc := vm.NewClient(client)
|
||||
if err := Await(client, func() (management.OperationID, error) {
|
||||
return vmc.ShutdownRole(vmname, vmname, vmname, vm.PostShutdownActionStopped)
|
||||
}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if err := Await(client, func() (management.OperationID, error) {
|
||||
return vmc.StartRole(vmname, vmname, vmname)
|
||||
}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if err := Await(client, func() (management.OperationID, error) {
|
||||
return vmc.RestartRole(vmname, vmname, vmname)
|
||||
}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
deleteHostedService(t, client, vmname)
|
||||
}
|
||||
|
||||
func testRoleConfiguration(t *testing.T, client management.Client, role vm.Role, location string) {
|
||||
createRoleConfiguration(t, client, role, location)
|
||||
|
||||
deleteHostedService(t, client, role.RoleName)
|
||||
}
|
||||
|
||||
func createRoleConfiguration(t *testing.T, client management.Client, role vm.Role, location string) {
|
||||
vmc := vm.NewClient(client)
|
||||
hsc := hostedservice.NewClient(client)
|
||||
vmname := role.RoleName
|
||||
|
||||
if err := hsc.CreateHostedService(hostedservice.CreateHostedServiceParameters{
|
||||
ServiceName: vmname, Location: location,
|
||||
Label: base64.StdEncoding.EncodeToString([]byte(vmname))}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if err := Await(client, func() (management.OperationID, error) {
|
||||
return vmc.CreateDeployment(role, vmname, vm.CreateDeploymentOptions{})
|
||||
}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func deleteHostedService(t *testing.T, client management.Client, vmname string) {
|
||||
t.Logf("Deleting hosted service: %s", vmname)
|
||||
if err := Await(client, func() (management.OperationID, error) {
|
||||
return hostedservice.NewClient(client).DeleteHostedService(vmname, true)
|
||||
}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// === utility funcs ===
|
||||
|
||||
func GetTestStorageAccount(t *testing.T, client management.Client) storage.StorageServiceResponse {
|
||||
t.Log("Retrieving storage account")
|
||||
sc := storage.NewClient(client)
|
||||
var sa storage.StorageServiceResponse
|
||||
ssl, err := sc.ListStorageServices()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
if len(ssl.StorageServices) == 0 {
|
||||
t.Log("No storage accounts found, creating a new one")
|
||||
lc := location.NewClient(client)
|
||||
ll, err := lc.ListLocations()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
loc := ll.Locations[rnd.Intn(len(ll.Locations))].Name
|
||||
|
||||
t.Logf("Location for new storage account: %s", loc)
|
||||
name := GenerateName()
|
||||
op, err := sc.CreateStorageService(storage.StorageAccountCreateParameters{
|
||||
ServiceName: name,
|
||||
Label: base64.StdEncoding.EncodeToString([]byte(name)),
|
||||
Location: loc,
|
||||
AccountType: storage.AccountTypeStandardLRS})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := client.WaitForOperation(op, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sa, err = sc.GetStorageService(name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
|
||||
sa = ssl.StorageServices[rnd.Intn(len(ssl.StorageServices))]
|
||||
}
|
||||
|
||||
t.Logf("Selected storage account '%s' in location '%s'",
|
||||
sa.ServiceName, sa.StorageServiceProperties.Location)
|
||||
|
||||
return sa
|
||||
}
|
||||
|
||||
func GetLinuxTestImage(t *testing.T, client management.Client) osimage.OSImage {
|
||||
return GetOSImage(t, client, func(im osimage.OSImage) bool {
|
||||
return im.Category == "Public" && im.ImageFamily == "Ubuntu Server 14.04 LTS"
|
||||
})
|
||||
}
|
||||
|
||||
func GetWindowsTestImage(t *testing.T, client management.Client) osimage.OSImage {
|
||||
return GetOSImage(t, client, func(im osimage.OSImage) bool {
|
||||
return im.Category == "Public" && im.ImageFamily == "Windows Server 2012 R2 Datacenter"
|
||||
})
|
||||
}
|
||||
|
||||
func GetUserOSImage(t *testing.T, client management.Client, name string) osimage.OSImage {
|
||||
return GetOSImage(t, client, func(im osimage.OSImage) bool {
|
||||
return im.Category == "User" && im.Name == name
|
||||
})
|
||||
}
|
||||
|
||||
func GetOSImage(
|
||||
t *testing.T,
|
||||
client management.Client,
|
||||
filter func(osimage.OSImage) bool) osimage.OSImage {
|
||||
t.Log("Selecting OS image")
|
||||
osc := osimage.NewClient(client)
|
||||
allimages, err := osc.ListOSImages()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
filtered := []osimage.OSImage{}
|
||||
for _, im := range allimages.OSImages {
|
||||
if filter(im) {
|
||||
filtered = append(filtered, im)
|
||||
}
|
||||
}
|
||||
if len(filtered) == 0 {
|
||||
t.Fatal("Filter too restrictive, no images left?")
|
||||
}
|
||||
|
||||
image := filtered[0]
|
||||
for _, im := range filtered {
|
||||
if im.PublishedDate > image.PublishedDate {
|
||||
image = im
|
||||
}
|
||||
}
|
||||
|
||||
t.Logf("Selecting image '%s'", image.Name)
|
||||
return image
|
||||
}
|
||||
|
||||
func GetUserVMImage(t *testing.T, client management.Client, name string) vmimage.VMImage {
|
||||
return GetVMImage(t, client, func(im vmimage.VMImage) bool {
|
||||
return im.Category == "User" && im.Name == name
|
||||
})
|
||||
}
|
||||
|
||||
func GetVMImage(
|
||||
t *testing.T,
|
||||
client management.Client,
|
||||
filter func(vmimage.VMImage) bool) vmimage.VMImage {
|
||||
t.Log("Selecting VM image")
|
||||
allimages, err := vmimage.NewClient(client).ListVirtualMachineImages(vmimage.ListParameters{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
filtered := []vmimage.VMImage{}
|
||||
for _, im := range allimages.VMImages {
|
||||
if filter(im) {
|
||||
filtered = append(filtered, im)
|
||||
}
|
||||
}
|
||||
if len(filtered) == 0 {
|
||||
t.Fatal("Filter too restrictive, no images left?")
|
||||
}
|
||||
|
||||
image := filtered[0]
|
||||
for _, im := range filtered {
|
||||
if im.PublishedDate > image.PublishedDate {
|
||||
image = im
|
||||
}
|
||||
}
|
||||
|
||||
t.Logf("Selecting image '%s'", image.Name)
|
||||
return image
|
||||
}
|
||||
|
||||
func GenerateName() string {
|
||||
from := "1234567890abcdefghijklmnopqrstuvwxyz"
|
||||
return "sdk" + GenerateString(12, from)
|
||||
}
|
||||
|
||||
func GeneratePassword() string {
|
||||
pw := GenerateString(20, "1234567890") +
|
||||
GenerateString(20, "abcdefghijklmnopqrstuvwxyz") +
|
||||
GenerateString(20, "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
|
||||
rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
i := rnd.Intn(len(pw)-2) + 1
|
||||
|
||||
pw = string(append([]uint8(pw[i:]), pw[:i-1]...))
|
||||
|
||||
return pw
|
||||
}
|
||||
|
||||
func GenerateString(length int, from string) string {
|
||||
str := ""
|
||||
rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
for len(str) < length {
|
||||
str += string(from[rnd.Intn(len(from))])
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
type asyncFunc func() (operationId management.OperationID, err error)
|
||||
|
||||
func Await(client management.Client, async asyncFunc) error {
|
||||
requestID, err := async()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return client.WaitForOperation(requestID, nil)
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
package vmutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
vm "github.com/Azure/azure-sdk-for-go/management/virtualmachine"
|
||||
)
|
||||
|
||||
// ConfigureWithPublicSSH adds configuration exposing port 22 externally
|
||||
func ConfigureWithPublicSSH(role *vm.Role) error {
|
||||
if role == nil {
|
||||
return fmt.Errorf(errParamNotSpecified, "role")
|
||||
}
|
||||
|
||||
return ConfigureWithExternalPort(role, "SSH", 22, 22, vm.InputEndpointProtocolTCP)
|
||||
}
|
||||
|
||||
// ConfigureWithPublicRDP adds configuration exposing port 3389 externally
|
||||
func ConfigureWithPublicRDP(role *vm.Role) error {
|
||||
if role == nil {
|
||||
return fmt.Errorf(errParamNotSpecified, "role")
|
||||
}
|
||||
|
||||
return ConfigureWithExternalPort(role, "RDP", 3389, 3389, vm.InputEndpointProtocolTCP)
|
||||
}
|
||||
|
||||
// ConfigureWithPublicPowerShell adds configuration exposing port 5986
|
||||
// externally
|
||||
func ConfigureWithPublicPowerShell(role *vm.Role) error {
|
||||
if role == nil {
|
||||
return fmt.Errorf(errParamNotSpecified, "role")
|
||||
}
|
||||
|
||||
return ConfigureWithExternalPort(role, "PowerShell", 5986, 5986, vm.InputEndpointProtocolTCP)
|
||||
}
|
||||
|
||||
// ConfigureWithExternalPort adds a new InputEndpoint to the Role, exposing a
|
||||
// port externally
|
||||
func ConfigureWithExternalPort(role *vm.Role, name string, localport, externalport int, protocol vm.InputEndpointProtocol) error {
|
||||
if role == nil {
|
||||
return fmt.Errorf(errParamNotSpecified, "role")
|
||||
}
|
||||
|
||||
role.ConfigurationSets = updateOrAddConfig(role.ConfigurationSets, vm.ConfigurationSetTypeNetwork,
|
||||
func(config *vm.ConfigurationSet) {
|
||||
config.InputEndpoints = append(config.InputEndpoints, vm.InputEndpoint{
|
||||
LocalPort: localport,
|
||||
Name: name,
|
||||
Port: externalport,
|
||||
Protocol: protocol,
|
||||
})
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConfigureWithSecurityGroup associates the Role with a specific network security group
|
||||
func ConfigureWithSecurityGroup(role *vm.Role, networkSecurityGroup string) error {
|
||||
if role == nil {
|
||||
return fmt.Errorf(errParamNotSpecified, "role")
|
||||
}
|
||||
|
||||
role.ConfigurationSets = updateOrAddConfig(role.ConfigurationSets, vm.ConfigurationSetTypeNetwork,
|
||||
func(config *vm.ConfigurationSet) {
|
||||
config.NetworkSecurityGroup = networkSecurityGroup
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConfigureWithSubnet associates the Role with a specific subnet
|
||||
func ConfigureWithSubnet(role *vm.Role, subnet string) error {
|
||||
if role == nil {
|
||||
return fmt.Errorf(errParamNotSpecified, "role")
|
||||
}
|
||||
|
||||
role.ConfigurationSets = updateOrAddConfig(role.ConfigurationSets, vm.ConfigurationSetTypeNetwork,
|
||||
func(config *vm.ConfigurationSet) {
|
||||
config.SubnetNames = append(config.SubnetNames, subnet)
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
package vmutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/management"
|
||||
lc "github.com/Azure/azure-sdk-for-go/management/location"
|
||||
vm "github.com/Azure/azure-sdk-for-go/management/virtualmachine"
|
||||
)
|
||||
|
||||
// IsRoleSizeValid retrieves the available rolesizes using
|
||||
// vmclient.GetRoleSizeList() and returns whether that the provided roleSizeName
|
||||
// is part of that list
|
||||
func IsRoleSizeValid(vmclient vm.VirtualMachineClient, roleSizeName string) (bool, error) {
|
||||
if roleSizeName == "" {
|
||||
return false, fmt.Errorf(errParamNotSpecified, "roleSizeName")
|
||||
}
|
||||
|
||||
roleSizeList, err := vmclient.GetRoleSizeList()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, roleSize := range roleSizeList.RoleSizes {
|
||||
if roleSize.Name == roleSizeName {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// IsRoleSizeAvailableInLocation retrieves all available sizes in the specified
|
||||
// location and returns whether that the provided roleSizeName is part of that list.
|
||||
func IsRoleSizeAvailableInLocation(managementclient management.Client, location, roleSizeName string) (bool, error) {
|
||||
if location == "" {
|
||||
return false, fmt.Errorf(errParamNotSpecified, "location")
|
||||
}
|
||||
if roleSizeName == "" {
|
||||
return false, fmt.Errorf(errParamNotSpecified, "roleSizeName")
|
||||
}
|
||||
|
||||
locationClient := lc.NewClient(managementclient)
|
||||
locationInfo, err := getLocation(locationClient, location)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, availableRoleSize := range locationInfo.VirtualMachineRoleSizes {
|
||||
if availableRoleSize == roleSizeName {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func getLocation(c lc.LocationClient, location string) (*lc.Location, error) {
|
||||
if location == "" {
|
||||
return nil, fmt.Errorf(errParamNotSpecified, "location")
|
||||
}
|
||||
|
||||
locations, err := c.ListLocations()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, existingLocation := range locations.Locations {
|
||||
if existingLocation.Name != location {
|
||||
continue
|
||||
}
|
||||
|
||||
return &existingLocation, nil
|
||||
}
|
||||
return nil, fmt.Errorf("Invalid location: %s. Available locations: %s", location, locations)
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package vmutils
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/management"
|
||||
vm "github.com/Azure/azure-sdk-for-go/management/virtualmachine"
|
||||
)
|
||||
|
||||
// WaitForDeploymentPowerState blocks until all role instances in deployment
|
||||
// reach desired power state.
|
||||
func WaitForDeploymentPowerState(client management.Client, cloudServiceName, deploymentName string, desiredPowerstate vm.PowerState) error {
|
||||
for {
|
||||
deployment, err := vm.NewClient(client).GetDeployment(cloudServiceName, deploymentName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if allInstancesInPowerState(deployment.RoleInstanceList, desiredPowerstate) {
|
||||
return nil
|
||||
}
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func allInstancesInPowerState(instances []vm.RoleInstance, desiredPowerstate vm.PowerState) bool {
|
||||
for _, r := range instances {
|
||||
if r.PowerState != desiredPowerstate {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// WaitForDeploymentInstanceStatus blocks until all role instances in deployment
|
||||
// reach desired InstanceStatus.
|
||||
func WaitForDeploymentInstanceStatus(client management.Client, cloudServiceName, deploymentName string, desiredInstanceStatus vm.InstanceStatus) error {
|
||||
for {
|
||||
deployment, err := vm.NewClient(client).GetDeployment(cloudServiceName, deploymentName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if allInstancesInInstanceStatus(deployment.RoleInstanceList, desiredInstanceStatus) {
|
||||
return nil
|
||||
}
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func allInstancesInInstanceStatus(instances []vm.RoleInstance, desiredInstancestatus vm.InstanceStatus) bool {
|
||||
for _, r := range instances {
|
||||
if r.InstanceStatus != desiredInstancestatus {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
// Package vmutils provides convenience methods for creating Virtual
|
||||
// Machine Role configurations.
|
||||
package vmutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
vm "github.com/Azure/azure-sdk-for-go/management/virtualmachine"
|
||||
)
|
||||
|
||||
const (
|
||||
errParamNotSpecified = "Parameter %s is not specified."
|
||||
)
|
||||
|
||||
// NewVMConfiguration creates configuration for a new virtual machine Role.
|
||||
func NewVMConfiguration(name string, roleSize string) vm.Role {
|
||||
return vm.Role{
|
||||
RoleName: name,
|
||||
RoleType: "PersistentVMRole",
|
||||
RoleSize: roleSize,
|
||||
ProvisionGuestAgent: true,
|
||||
}
|
||||
}
|
||||
|
||||
// ConfigureForLinux adds configuration when deploying a generalized Linux
|
||||
// image. If "password" is left empty, SSH password security will be disabled by
|
||||
// default. Certificates with SSH public keys should already be uploaded to the
|
||||
// cloud service where the VM will be deployed and referenced here only by their
|
||||
// thumbprint.
|
||||
func ConfigureForLinux(role *vm.Role, hostname, user, password string, sshPubkeyCertificateThumbprint ...string) error {
|
||||
if role == nil {
|
||||
return fmt.Errorf(errParamNotSpecified, "role")
|
||||
}
|
||||
|
||||
role.ConfigurationSets = updateOrAddConfig(role.ConfigurationSets, vm.ConfigurationSetTypeLinuxProvisioning,
|
||||
func(config *vm.ConfigurationSet) {
|
||||
config.HostName = hostname
|
||||
config.UserName = user
|
||||
config.UserPassword = password
|
||||
if password != "" {
|
||||
config.DisableSSHPasswordAuthentication = "false"
|
||||
}
|
||||
if len(sshPubkeyCertificateThumbprint) != 0 {
|
||||
config.SSH = &vm.SSH{}
|
||||
for _, k := range sshPubkeyCertificateThumbprint {
|
||||
config.SSH.PublicKeys = append(config.SSH.PublicKeys,
|
||||
vm.PublicKey{
|
||||
Fingerprint: k,
|
||||
Path: "/home/" + user + "/.ssh/authorized_keys",
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConfigureForWindows adds configuration when deploying a generalized
|
||||
// Windows image. timeZone can be left empty. For a complete list of supported
|
||||
// time zone entries, you can either refer to the values listed in the registry
|
||||
// entry "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time
|
||||
// Zones" or you can use the tzutil command-line tool to list the valid time.
|
||||
func ConfigureForWindows(role *vm.Role, hostname, user, password string, enableAutomaticUpdates bool, timeZone string) error {
|
||||
if role == nil {
|
||||
return fmt.Errorf(errParamNotSpecified, "role")
|
||||
}
|
||||
|
||||
role.ConfigurationSets = updateOrAddConfig(role.ConfigurationSets, vm.ConfigurationSetTypeWindowsProvisioning,
|
||||
func(config *vm.ConfigurationSet) {
|
||||
config.ComputerName = hostname
|
||||
config.AdminUsername = user
|
||||
config.AdminPassword = password
|
||||
config.EnableAutomaticUpdates = enableAutomaticUpdates
|
||||
config.TimeZone = timeZone
|
||||
},
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConfigureWithCustomDataForLinux configures custom data for Linux-based images.
|
||||
// The customData contains either cloud-init or shell script to be executed upon start.
|
||||
//
|
||||
// The function expects the customData to be base64-encoded.
|
||||
func ConfigureWithCustomDataForLinux(role *vm.Role, customData string) error {
|
||||
return configureWithCustomData(role, customData, vm.ConfigurationSetTypeLinuxProvisioning)
|
||||
}
|
||||
|
||||
// ConfigureWithCustomDataForWindows configures custom data for Windows-based images.
|
||||
// The customData contains either cloud-init or shell script to be executed upon start.
|
||||
//
|
||||
// The function expects the customData to be base64-encoded.
|
||||
func ConfigureWithCustomDataForWindows(role *vm.Role, customData string) error {
|
||||
return configureWithCustomData(role, customData, vm.ConfigurationSetTypeWindowsProvisioning)
|
||||
}
|
||||
|
||||
func configureWithCustomData(role *vm.Role, customData string, typ vm.ConfigurationSetType) error {
|
||||
if role == nil {
|
||||
return fmt.Errorf(errParamNotSpecified, "role")
|
||||
}
|
||||
|
||||
role.ConfigurationSets = updateOrAddConfig(role.ConfigurationSets, typ,
|
||||
func(config *vm.ConfigurationSet) {
|
||||
config.CustomData = customData
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConfigureWindowsToJoinDomain adds configuration to join a new Windows vm to a
|
||||
// domain. "username" must be in UPN form (user@domain.com), "machineOU" can be
|
||||
// left empty
|
||||
func ConfigureWindowsToJoinDomain(role *vm.Role, username, password, domainToJoin, machineOU string) error {
|
||||
if role == nil {
|
||||
return fmt.Errorf(errParamNotSpecified, "role")
|
||||
}
|
||||
|
||||
winconfig := findConfig(role.ConfigurationSets, vm.ConfigurationSetTypeWindowsProvisioning)
|
||||
if winconfig != nil {
|
||||
winconfig.DomainJoin = &vm.DomainJoin{
|
||||
Credentials: vm.Credentials{Username: username, Password: password},
|
||||
JoinDomain: domainToJoin,
|
||||
MachineObjectOU: machineOU,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ConfigureWinRMListener(role *vm.Role, protocol vm.WinRMProtocol, certificateThumbprint string) error {
|
||||
|
||||
if role == nil {
|
||||
return fmt.Errorf(errParamNotSpecified, "role")
|
||||
}
|
||||
|
||||
winconfig := findConfig(role.ConfigurationSets, vm.ConfigurationSetTypeWindowsProvisioning)
|
||||
|
||||
if winconfig != nil {
|
||||
|
||||
listener := vm.WinRMListener{
|
||||
Protocol: protocol,
|
||||
CertificateThumbprint: certificateThumbprint,
|
||||
}
|
||||
|
||||
if winconfig.WinRMListeners == nil {
|
||||
winconfig.WinRMListeners = &[]vm.WinRMListener{}
|
||||
}
|
||||
|
||||
currentListeners := *winconfig.WinRMListeners
|
||||
|
||||
// replace existing listener if it's already configured
|
||||
for i, existingListener := range currentListeners {
|
||||
if existingListener.Protocol == protocol {
|
||||
currentListeners[i] = listener
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise append to list of listeners
|
||||
newListeners := append(currentListeners, listener)
|
||||
winconfig.WinRMListeners = &newListeners
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("WindowsProvisioningConfigurationSet not found in 'role'")
|
||||
}
|
||||
|
||||
func ConfigureWinRMOverHTTP(role *vm.Role) error {
|
||||
return ConfigureWinRMListener(role, vm.WinRMProtocolHTTP, "")
|
||||
}
|
||||
|
||||
func ConfigureWinRMOverHTTPS(role *vm.Role, certificateThumbprint string) error {
|
||||
return ConfigureWinRMListener(role, vm.WinRMProtocolHTTPS, certificateThumbprint)
|
||||
}
|
||||
+440
@@ -0,0 +1,440 @@
|
||||
package vmutils
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"testing"
|
||||
|
||||
vmdisk "github.com/Azure/azure-sdk-for-go/management/virtualmachinedisk"
|
||||
)
|
||||
|
||||
func TestNewLinuxVmRemoteImage(t *testing.T) {
|
||||
role := NewVMConfiguration("myvm", "Standard_D3")
|
||||
ConfigureDeploymentFromRemoteImage(&role,
|
||||
"http://remote.host/some.vhd?sv=12&sig=ukhfiuwef78687", "Linux",
|
||||
"myvm-os-disk", "http://mystorageacct.blob.core.windows.net/vhds/mybrandnewvm.vhd",
|
||||
"OSDisk")
|
||||
ConfigureForLinux(&role, "myvm", "azureuser", "P@ssword", "2398yyKJGd78e2389ydfncuirowebhf89yh3IUOBY")
|
||||
ConfigureWithPublicSSH(&role)
|
||||
|
||||
bytes, err := xml.MarshalIndent(role, "", " ")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := `<Role>
|
||||
<RoleName>myvm</RoleName>
|
||||
<RoleType>PersistentVMRole</RoleType>
|
||||
<ConfigurationSets>
|
||||
<ConfigurationSet>
|
||||
<ConfigurationSetType>LinuxProvisioningConfiguration</ConfigurationSetType>
|
||||
<StoredCertificateSettings></StoredCertificateSettings>
|
||||
<HostName>myvm</HostName>
|
||||
<UserName>azureuser</UserName>
|
||||
<UserPassword>P@ssword</UserPassword>
|
||||
<DisableSshPasswordAuthentication>false</DisableSshPasswordAuthentication>
|
||||
<SSH>
|
||||
<PublicKeys>
|
||||
<PublicKey>
|
||||
<Fingerprint>2398yyKJGd78e2389ydfncuirowebhf89yh3IUOBY</Fingerprint>
|
||||
<Path>/home/azureuser/.ssh/authorized_keys</Path>
|
||||
</PublicKey>
|
||||
</PublicKeys>
|
||||
<KeyPairs></KeyPairs>
|
||||
</SSH>
|
||||
<InputEndpoints></InputEndpoints>
|
||||
<SubnetNames></SubnetNames>
|
||||
<PublicIPs></PublicIPs>
|
||||
</ConfigurationSet>
|
||||
<ConfigurationSet>
|
||||
<ConfigurationSetType>NetworkConfiguration</ConfigurationSetType>
|
||||
<StoredCertificateSettings></StoredCertificateSettings>
|
||||
<InputEndpoints>
|
||||
<InputEndpoint>
|
||||
<LocalPort>22</LocalPort>
|
||||
<Name>SSH</Name>
|
||||
<Port>22</Port>
|
||||
<Protocol>TCP</Protocol>
|
||||
</InputEndpoint>
|
||||
</InputEndpoints>
|
||||
<SubnetNames></SubnetNames>
|
||||
<PublicIPs></PublicIPs>
|
||||
</ConfigurationSet>
|
||||
</ConfigurationSets>
|
||||
<DataVirtualHardDisks></DataVirtualHardDisks>
|
||||
<OSVirtualHardDisk>
|
||||
<DiskLabel>OSDisk</DiskLabel>
|
||||
<DiskName>myvm-os-disk</DiskName>
|
||||
<MediaLink>http://mystorageacct.blob.core.windows.net/vhds/mybrandnewvm.vhd</MediaLink>
|
||||
<OS>Linux</OS>
|
||||
<RemoteSourceImageLink>http://remote.host/some.vhd?sv=12&sig=ukhfiuwef78687</RemoteSourceImageLink>
|
||||
</OSVirtualHardDisk>
|
||||
<RoleSize>Standard_D3</RoleSize>
|
||||
<ProvisionGuestAgent>true</ProvisionGuestAgent>
|
||||
</Role>`
|
||||
|
||||
if string(bytes) != expected {
|
||||
t.Fatalf("Expected marshalled xml to be %q, but got %q", expected, string(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewLinuxVmPlatformImage(t *testing.T) {
|
||||
role := NewVMConfiguration("myplatformvm", "Standard_D3")
|
||||
ConfigureDeploymentFromPlatformImage(&role,
|
||||
"b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-14_04_2_LTS-amd64-server-20150309-en-us-30GB",
|
||||
"http://mystorageacct.blob.core.windows.net/vhds/mybrandnewvm.vhd", "mydisklabel")
|
||||
ConfigureForLinux(&role, "myvm", "azureuser", "", "2398yyKJGd78e2389ydfncuirdebhf89yh3IUOBY")
|
||||
|
||||
bytes, err := xml.MarshalIndent(role, "", " ")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := `<Role>
|
||||
<RoleName>myplatformvm</RoleName>
|
||||
<RoleType>PersistentVMRole</RoleType>
|
||||
<ConfigurationSets>
|
||||
<ConfigurationSet>
|
||||
<ConfigurationSetType>LinuxProvisioningConfiguration</ConfigurationSetType>
|
||||
<StoredCertificateSettings></StoredCertificateSettings>
|
||||
<HostName>myvm</HostName>
|
||||
<UserName>azureuser</UserName>
|
||||
<SSH>
|
||||
<PublicKeys>
|
||||
<PublicKey>
|
||||
<Fingerprint>2398yyKJGd78e2389ydfncuirdebhf89yh3IUOBY</Fingerprint>
|
||||
<Path>/home/azureuser/.ssh/authorized_keys</Path>
|
||||
</PublicKey>
|
||||
</PublicKeys>
|
||||
<KeyPairs></KeyPairs>
|
||||
</SSH>
|
||||
<InputEndpoints></InputEndpoints>
|
||||
<SubnetNames></SubnetNames>
|
||||
<PublicIPs></PublicIPs>
|
||||
</ConfigurationSet>
|
||||
</ConfigurationSets>
|
||||
<DataVirtualHardDisks></DataVirtualHardDisks>
|
||||
<OSVirtualHardDisk>
|
||||
<MediaLink>http://mystorageacct.blob.core.windows.net/vhds/mybrandnewvm.vhd</MediaLink>
|
||||
<SourceImageName>b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-14_04_2_LTS-amd64-server-20150309-en-us-30GB</SourceImageName>
|
||||
</OSVirtualHardDisk>
|
||||
<RoleSize>Standard_D3</RoleSize>
|
||||
<ProvisionGuestAgent>true</ProvisionGuestAgent>
|
||||
</Role>`
|
||||
|
||||
if string(bytes) != expected {
|
||||
t.Fatalf("Expected marshalled xml to be %q, but got %q", expected, string(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewVmFromVMImage(t *testing.T) {
|
||||
role := NewVMConfiguration("restoredbackup", "Standard_D1")
|
||||
ConfigureDeploymentFromPublishedVMImage(&role, "myvm-backup-20150209",
|
||||
"http://mystorageacct.blob.core.windows.net/vhds/myoldnewvm.vhd", false)
|
||||
|
||||
bytes, err := xml.MarshalIndent(role, "", " ")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := `<Role>
|
||||
<RoleName>restoredbackup</RoleName>
|
||||
<RoleType>PersistentVMRole</RoleType>
|
||||
<ConfigurationSets></ConfigurationSets>
|
||||
<VMImageName>myvm-backup-20150209</VMImageName>
|
||||
<MediaLocation>http://mystorageacct.blob.core.windows.net/vhds/myoldnewvm.vhd</MediaLocation>
|
||||
<DataVirtualHardDisks></DataVirtualHardDisks>
|
||||
<RoleSize>Standard_D1</RoleSize>
|
||||
</Role>`
|
||||
|
||||
if string(bytes) != expected {
|
||||
t.Fatalf("Expected marshalled xml to be %q, but got %q", expected, string(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewVmFromExistingDisk(t *testing.T) {
|
||||
role := NewVMConfiguration("blobvm", "Standard_D14")
|
||||
ConfigureDeploymentFromExistingOSDisk(&role, "myvm-backup-20150209", "OSDisk")
|
||||
ConfigureForWindows(&role, "WINVM", "azuser", "P2ssw@rd", true, "")
|
||||
ConfigureWindowsToJoinDomain(&role, "user@domain.com", "youReN3verG0nnaGu3ss", "redmond.corp.contoso.com", "")
|
||||
ConfigureWithNewDataDisk(&role, "my-brand-new-disk", "http://account.blob.core.windows.net/vhds/newdatadisk.vhd",
|
||||
30, vmdisk.HostCachingTypeReadWrite)
|
||||
ConfigureWithExistingDataDisk(&role, "data-disk", vmdisk.HostCachingTypeReadOnly)
|
||||
|
||||
bytes, err := xml.MarshalIndent(role, "", " ")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := `<Role>
|
||||
<RoleName>blobvm</RoleName>
|
||||
<RoleType>PersistentVMRole</RoleType>
|
||||
<ConfigurationSets>
|
||||
<ConfigurationSet>
|
||||
<ConfigurationSetType>WindowsProvisioningConfiguration</ConfigurationSetType>
|
||||
<ComputerName>WINVM</ComputerName>
|
||||
<AdminPassword>P2ssw@rd</AdminPassword>
|
||||
<EnableAutomaticUpdates>true</EnableAutomaticUpdates>
|
||||
<DomainJoin>
|
||||
<Credentials>
|
||||
<Domain></Domain>
|
||||
<Username>user@domain.com</Username>
|
||||
<Password>youReN3verG0nnaGu3ss</Password>
|
||||
</Credentials>
|
||||
<JoinDomain>redmond.corp.contoso.com</JoinDomain>
|
||||
</DomainJoin>
|
||||
<StoredCertificateSettings></StoredCertificateSettings>
|
||||
<AdminUsername>azuser</AdminUsername>
|
||||
<InputEndpoints></InputEndpoints>
|
||||
<SubnetNames></SubnetNames>
|
||||
<PublicIPs></PublicIPs>
|
||||
</ConfigurationSet>
|
||||
</ConfigurationSets>
|
||||
<DataVirtualHardDisks>
|
||||
<DataVirtualHardDisk>
|
||||
<HostCaching>ReadWrite</HostCaching>
|
||||
<DiskLabel>my-brand-new-disk</DiskLabel>
|
||||
<LogicalDiskSizeInGB>30</LogicalDiskSizeInGB>
|
||||
<MediaLink>http://account.blob.core.windows.net/vhds/newdatadisk.vhd</MediaLink>
|
||||
</DataVirtualHardDisk>
|
||||
<DataVirtualHardDisk>
|
||||
<HostCaching>ReadOnly</HostCaching>
|
||||
<DiskName>data-disk</DiskName>
|
||||
<Lun>1</Lun>
|
||||
</DataVirtualHardDisk>
|
||||
</DataVirtualHardDisks>
|
||||
<OSVirtualHardDisk>
|
||||
<DiskLabel>OSDisk</DiskLabel>
|
||||
<DiskName>myvm-backup-20150209</DiskName>
|
||||
</OSVirtualHardDisk>
|
||||
<RoleSize>Standard_D14</RoleSize>
|
||||
<ProvisionGuestAgent>true</ProvisionGuestAgent>
|
||||
</Role>`
|
||||
|
||||
if string(bytes) != expected {
|
||||
t.Fatalf("Expected marshalled xml to be %q, but got %q", expected, string(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
func TestWinRMOverHttps(t *testing.T) {
|
||||
role := NewVMConfiguration("winrmoverhttp", "Standard_D1")
|
||||
ConfigureForWindows(&role, "WINVM", "azuser", "P2ssw@rd", true, "")
|
||||
ConfigureWinRMOverHTTPS(&role, "abcdef")
|
||||
|
||||
bytes, err := xml.MarshalIndent(role, "", " ")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := `<Role>
|
||||
<RoleName>winrmoverhttp</RoleName>
|
||||
<RoleType>PersistentVMRole</RoleType>
|
||||
<ConfigurationSets>
|
||||
<ConfigurationSet>
|
||||
<ConfigurationSetType>WindowsProvisioningConfiguration</ConfigurationSetType>
|
||||
<ComputerName>WINVM</ComputerName>
|
||||
<AdminPassword>P2ssw@rd</AdminPassword>
|
||||
<EnableAutomaticUpdates>true</EnableAutomaticUpdates>
|
||||
<StoredCertificateSettings></StoredCertificateSettings>
|
||||
<WinRM>
|
||||
<Listeners>
|
||||
<Listener>
|
||||
<Protocol>Https</Protocol>
|
||||
<CertificateThumbprint>abcdef</CertificateThumbprint>
|
||||
</Listener>
|
||||
</Listeners>
|
||||
</WinRM>
|
||||
<AdminUsername>azuser</AdminUsername>
|
||||
<InputEndpoints></InputEndpoints>
|
||||
<SubnetNames></SubnetNames>
|
||||
<PublicIPs></PublicIPs>
|
||||
</ConfigurationSet>
|
||||
</ConfigurationSets>
|
||||
<DataVirtualHardDisks></DataVirtualHardDisks>
|
||||
<RoleSize>Standard_D1</RoleSize>
|
||||
<ProvisionGuestAgent>true</ProvisionGuestAgent>
|
||||
</Role>`
|
||||
|
||||
if string(bytes) != expected {
|
||||
t.Fatalf("Expected marshalled xml to be %q, but got %q", expected, string(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
func TestWinRMOverHttpsWithNoThumbprint(t *testing.T) {
|
||||
role := NewVMConfiguration("winrmoverhttp", "Standard_D1")
|
||||
ConfigureForWindows(&role, "WINVM", "azuser", "P2ssw@rd", true, "")
|
||||
ConfigureWinRMOverHTTPS(&role, "")
|
||||
|
||||
bytes, err := xml.MarshalIndent(role, "", " ")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := `<Role>
|
||||
<RoleName>winrmoverhttp</RoleName>
|
||||
<RoleType>PersistentVMRole</RoleType>
|
||||
<ConfigurationSets>
|
||||
<ConfigurationSet>
|
||||
<ConfigurationSetType>WindowsProvisioningConfiguration</ConfigurationSetType>
|
||||
<ComputerName>WINVM</ComputerName>
|
||||
<AdminPassword>P2ssw@rd</AdminPassword>
|
||||
<EnableAutomaticUpdates>true</EnableAutomaticUpdates>
|
||||
<StoredCertificateSettings></StoredCertificateSettings>
|
||||
<WinRM>
|
||||
<Listeners>
|
||||
<Listener>
|
||||
<Protocol>Https</Protocol>
|
||||
</Listener>
|
||||
</Listeners>
|
||||
</WinRM>
|
||||
<AdminUsername>azuser</AdminUsername>
|
||||
<InputEndpoints></InputEndpoints>
|
||||
<SubnetNames></SubnetNames>
|
||||
<PublicIPs></PublicIPs>
|
||||
</ConfigurationSet>
|
||||
</ConfigurationSets>
|
||||
<DataVirtualHardDisks></DataVirtualHardDisks>
|
||||
<RoleSize>Standard_D1</RoleSize>
|
||||
<ProvisionGuestAgent>true</ProvisionGuestAgent>
|
||||
</Role>`
|
||||
|
||||
if string(bytes) != expected {
|
||||
t.Fatalf("Expected marshalled xml to be %q, but got %q", expected, string(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
func TestWinRMOverHttp(t *testing.T) {
|
||||
role := NewVMConfiguration("winrmoverhttp", "Standard_D1")
|
||||
ConfigureForWindows(&role, "WINVM", "azuser", "P2ssw@rd", true, "")
|
||||
ConfigureWinRMOverHTTP(&role)
|
||||
|
||||
bytes, err := xml.MarshalIndent(role, "", " ")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := `<Role>
|
||||
<RoleName>winrmoverhttp</RoleName>
|
||||
<RoleType>PersistentVMRole</RoleType>
|
||||
<ConfigurationSets>
|
||||
<ConfigurationSet>
|
||||
<ConfigurationSetType>WindowsProvisioningConfiguration</ConfigurationSetType>
|
||||
<ComputerName>WINVM</ComputerName>
|
||||
<AdminPassword>P2ssw@rd</AdminPassword>
|
||||
<EnableAutomaticUpdates>true</EnableAutomaticUpdates>
|
||||
<StoredCertificateSettings></StoredCertificateSettings>
|
||||
<WinRM>
|
||||
<Listeners>
|
||||
<Listener>
|
||||
<Protocol>Http</Protocol>
|
||||
</Listener>
|
||||
</Listeners>
|
||||
</WinRM>
|
||||
<AdminUsername>azuser</AdminUsername>
|
||||
<InputEndpoints></InputEndpoints>
|
||||
<SubnetNames></SubnetNames>
|
||||
<PublicIPs></PublicIPs>
|
||||
</ConfigurationSet>
|
||||
</ConfigurationSets>
|
||||
<DataVirtualHardDisks></DataVirtualHardDisks>
|
||||
<RoleSize>Standard_D1</RoleSize>
|
||||
<ProvisionGuestAgent>true</ProvisionGuestAgent>
|
||||
</Role>`
|
||||
|
||||
if string(bytes) != expected {
|
||||
t.Fatalf("Expected marshalled xml to be %q, but got %q", expected, string(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSettingWinRMOverHttpTwice(t *testing.T) {
|
||||
role := NewVMConfiguration("winrmoverhttp", "Standard_D1")
|
||||
ConfigureForWindows(&role, "WINVM", "azuser", "P2ssw@rd", true, "")
|
||||
ConfigureWinRMOverHTTP(&role)
|
||||
ConfigureWinRMOverHTTP(&role)
|
||||
|
||||
bytes, err := xml.MarshalIndent(role, "", " ")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := `<Role>
|
||||
<RoleName>winrmoverhttp</RoleName>
|
||||
<RoleType>PersistentVMRole</RoleType>
|
||||
<ConfigurationSets>
|
||||
<ConfigurationSet>
|
||||
<ConfigurationSetType>WindowsProvisioningConfiguration</ConfigurationSetType>
|
||||
<ComputerName>WINVM</ComputerName>
|
||||
<AdminPassword>P2ssw@rd</AdminPassword>
|
||||
<EnableAutomaticUpdates>true</EnableAutomaticUpdates>
|
||||
<StoredCertificateSettings></StoredCertificateSettings>
|
||||
<WinRM>
|
||||
<Listeners>
|
||||
<Listener>
|
||||
<Protocol>Http</Protocol>
|
||||
</Listener>
|
||||
</Listeners>
|
||||
</WinRM>
|
||||
<AdminUsername>azuser</AdminUsername>
|
||||
<InputEndpoints></InputEndpoints>
|
||||
<SubnetNames></SubnetNames>
|
||||
<PublicIPs></PublicIPs>
|
||||
</ConfigurationSet>
|
||||
</ConfigurationSets>
|
||||
<DataVirtualHardDisks></DataVirtualHardDisks>
|
||||
<RoleSize>Standard_D1</RoleSize>
|
||||
<ProvisionGuestAgent>true</ProvisionGuestAgent>
|
||||
</Role>`
|
||||
|
||||
if string(bytes) != expected {
|
||||
t.Fatalf("Expected marshalled xml to be %q, but got %q", expected, string(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSettingWinRMOverHttpAndHttpsTwice(t *testing.T) {
|
||||
role := NewVMConfiguration("winrmoverhttp", "Standard_D1")
|
||||
ConfigureForWindows(&role, "WINVM", "azuser", "P2ssw@rd", true, "")
|
||||
ConfigureWinRMOverHTTP(&role)
|
||||
ConfigureWinRMOverHTTPS(&role, "")
|
||||
ConfigureWinRMOverHTTP(&role)
|
||||
ConfigureWinRMOverHTTPS(&role, "abcdef")
|
||||
|
||||
bytes, err := xml.MarshalIndent(role, "", " ")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := `<Role>
|
||||
<RoleName>winrmoverhttp</RoleName>
|
||||
<RoleType>PersistentVMRole</RoleType>
|
||||
<ConfigurationSets>
|
||||
<ConfigurationSet>
|
||||
<ConfigurationSetType>WindowsProvisioningConfiguration</ConfigurationSetType>
|
||||
<ComputerName>WINVM</ComputerName>
|
||||
<AdminPassword>P2ssw@rd</AdminPassword>
|
||||
<EnableAutomaticUpdates>true</EnableAutomaticUpdates>
|
||||
<StoredCertificateSettings></StoredCertificateSettings>
|
||||
<WinRM>
|
||||
<Listeners>
|
||||
<Listener>
|
||||
<Protocol>Http</Protocol>
|
||||
</Listener>
|
||||
<Listener>
|
||||
<Protocol>Https</Protocol>
|
||||
<CertificateThumbprint>abcdef</CertificateThumbprint>
|
||||
</Listener>
|
||||
</Listeners>
|
||||
</WinRM>
|
||||
<AdminUsername>azuser</AdminUsername>
|
||||
<InputEndpoints></InputEndpoints>
|
||||
<SubnetNames></SubnetNames>
|
||||
<PublicIPs></PublicIPs>
|
||||
</ConfigurationSet>
|
||||
</ConfigurationSets>
|
||||
<DataVirtualHardDisks></DataVirtualHardDisks>
|
||||
<RoleSize>Standard_D1</RoleSize>
|
||||
<ProvisionGuestAgent>true</ProvisionGuestAgent>
|
||||
</Role>`
|
||||
|
||||
if string(bytes) != expected {
|
||||
t.Fatalf("Expected marshalled xml to be %q, but got %q", expected, string(bytes))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user