205
195
{Name: to.StringPtr("16.04-LTS")},
208
s.publicIPAddress = &network.PublicIPAddress{
209
ID: to.StringPtr("/subscriptions/22222222-2222-2222-2222-222222222222/resourceGroups/juju-testenv-model-deadbeef-0bad-400d-8000-4b1d0d06f00d/providers/Microsoft.Network/publicIPAddresses/machine-0-public-ip"),
210
Name: to.StringPtr("machine-0-public-ip"),
211
Location: to.StringPtr("westus"),
213
Properties: &network.PublicIPAddressPropertiesFormat{
214
PublicIPAllocationMethod: network.Dynamic,
215
IPAddress: to.StringPtr("1.2.3.4"),
216
ProvisioningState: to.StringPtr("Succeeded"),
220
// Existing IPs/NICs. These are the results of querying NICs so we
221
// can tell which IP to allocate.
222
oldIPConfigurations := []network.InterfaceIPConfiguration{{
223
ID: to.StringPtr("ip-configuration-0-id"),
224
Name: to.StringPtr("ip-configuration-0"),
225
Properties: &network.InterfaceIPConfigurationPropertiesFormat{
226
PrivateIPAddress: to.StringPtr("192.168.0.4"),
227
PrivateIPAllocationMethod: network.Static,
228
Subnet: s.internalSubnet,
229
ProvisioningState: to.StringPtr("Succeeded"),
232
oldNetworkInterfaces := []network.Interface{{
233
ID: to.StringPtr("network-interface-0-id"),
234
Name: to.StringPtr("network-interface-0"),
235
Properties: &network.InterfacePropertiesFormat{
236
IPConfigurations: &oldIPConfigurations,
237
Primary: to.BoolPtr(true),
238
ProvisioningState: to.StringPtr("Succeeded"),
241
s.oldNetworkInterfaces = &network.InterfaceListResult{
242
Value: &oldNetworkInterfaces,
245
// The newly created IP/NIC.
246
newIPConfigurations := []network.InterfaceIPConfiguration{{
247
ID: to.StringPtr("ip-configuration-1-id"),
248
Name: to.StringPtr("primary"),
249
Properties: &network.InterfaceIPConfigurationPropertiesFormat{
250
PrivateIPAddress: to.StringPtr("192.168.0.5"),
251
PrivateIPAllocationMethod: network.Static,
252
Subnet: s.internalSubnet,
253
PublicIPAddress: s.publicIPAddress,
254
ProvisioningState: to.StringPtr("Succeeded"),
257
s.newNetworkInterface = &network.Interface{
258
ID: to.StringPtr("/subscriptions/22222222-2222-2222-2222-222222222222/resourceGroups/juju-testenv-model-deadbeef-0bad-400d-8000-4b1d0d06f00d/providers/Microsoft.Network/networkInterfaces/machine-0-primary"),
259
Name: to.StringPtr("network-interface-1"),
260
Location: to.StringPtr("westus"),
262
Properties: &network.InterfacePropertiesFormat{
263
IPConfigurations: &newIPConfigurations,
264
ProvisioningState: to.StringPtr("Succeeded"),
268
s.jujuAvailabilitySet = &compute.AvailabilitySet{
269
ID: to.StringPtr("juju-availability-set-id"),
270
Name: to.StringPtr("juju"),
271
Location: to.StringPtr("westus"),
275
s.sshPublicKeys = []compute.SSHPublicKey{{
276
Path: to.StringPtr("/home/ubuntu/.ssh/authorized_keys"),
277
KeyData: to.StringPtr(testing.FakeAuthKeys),
279
s.networkInterfaceReferences = []compute.NetworkInterfaceReference{{
280
ID: s.newNetworkInterface.ID,
281
Properties: &compute.NetworkInterfaceReferenceProperties{
282
Primary: to.BoolPtr(true),
285
s.virtualMachine = &compute.VirtualMachine{
286
ID: to.StringPtr("machine-0-id"),
287
Name: to.StringPtr("machine-0"),
288
Location: to.StringPtr("westus"),
290
Properties: &compute.VirtualMachineProperties{
291
HardwareProfile: &compute.HardwareProfile{
292
VMSize: "Standard_D1",
294
StorageProfile: &compute.StorageProfile{
295
ImageReference: &compute.ImageReference{
296
Publisher: to.StringPtr("Canonical"),
297
Offer: to.StringPtr("UbuntuServer"),
298
Sku: to.StringPtr("12.10"),
299
Version: to.StringPtr("latest"),
301
OsDisk: &compute.OSDisk{
302
Name: to.StringPtr("machine-0"),
303
CreateOption: compute.FromImage,
304
Caching: compute.ReadWrite,
305
Vhd: &compute.VirtualHardDisk{
306
URI: to.StringPtr(fmt.Sprintf(
307
"https://%s.blob.storage.azurestack.local/osvhds/machine-0.vhd",
311
// 30 GiB is roughly 32 GB.
312
DiskSizeGB: to.Int32Ptr(32),
315
OsProfile: &compute.OSProfile{
316
ComputerName: to.StringPtr("machine-0"),
317
CustomData: to.StringPtr("<juju-goes-here>"),
318
AdminUsername: to.StringPtr("ubuntu"),
319
LinuxConfiguration: &compute.LinuxConfiguration{
320
DisablePasswordAuthentication: to.BoolPtr(true),
321
SSH: &compute.SSHConfiguration{
322
PublicKeys: &s.sshPublicKeys,
326
NetworkProfile: &compute.NetworkProfile{
327
NetworkInterfaces: &s.networkInterfaceReferences,
329
AvailabilitySet: &compute.SubResource{ID: s.jujuAvailabilitySet.ID},
330
ProvisioningState: to.StringPtr("Succeeded"),
337
201
func (s *environSuite) openEnviron(c *gc.C, attrs ...testing.Attrs) environs.Environ {
720
637
_, err := env.StartInstance(params)
721
638
c.Assert(err, jc.ErrorIsNil)
722
requests := s.assertStartInstanceRequests(c, s.requests)
723
availabilitySetName := path.Base(requests.availabilitySet.URL.Path)
724
c.Assert(availabilitySetName, gc.Equals, "mysql")
727
const numExpectedStartInstanceRequests = 8
729
func (s *environSuite) assertStartInstanceRequests(c *gc.C, requests []*http.Request) startInstanceRequests {
730
// The values defined here are the *request* values. They lack IDs,
731
// Names (in most places), and ProvisioningStates. The values defined
732
// on the suite are the *response* values; they are supersets of the
735
publicIPAddress := &network.PublicIPAddress{
736
Location: to.StringPtr("westus"),
738
Properties: &network.PublicIPAddressPropertiesFormat{
739
PublicIPAllocationMethod: network.Dynamic,
639
s.assertStartInstanceRequests(c, s.requests, assertStartInstanceRequestsParams{
640
availabilitySetName: "mysql",
641
imageReference: &quantalImageReference,
643
osProfile: &linuxOsProfile,
647
const numExpectedStartInstanceRequests = 3
649
type assertStartInstanceRequestsParams struct {
650
availabilitySetName string
651
imageReference *compute.ImageReference
652
vmExtension *compute.VirtualMachineExtensionProperties
654
osProfile *compute.OSProfile
657
func (s *environSuite) assertStartInstanceRequests(
659
requests []*http.Request,
660
args assertStartInstanceRequestsParams,
661
) startInstanceRequests {
662
nsgId := `[resourceId('Microsoft.Network/networkSecurityGroups', 'juju-internal-nsg')]`
663
securityRules := []network.SecurityRule{{
664
Name: to.StringPtr("SSHInbound"),
665
Properties: &network.SecurityRulePropertiesFormat{
666
Description: to.StringPtr("Allow SSH access to all machines"),
667
Protocol: network.TCP,
668
SourceAddressPrefix: to.StringPtr("*"),
669
SourcePortRange: to.StringPtr("*"),
670
DestinationAddressPrefix: to.StringPtr("*"),
671
DestinationPortRange: to.StringPtr("22"),
672
Access: network.Allow,
673
Priority: to.Int32Ptr(100),
674
Direction: network.Inbound,
677
Name: to.StringPtr("JujuAPIInbound"),
678
Properties: &network.SecurityRulePropertiesFormat{
679
Description: to.StringPtr("Allow API connections to controller machines"),
680
Protocol: network.TCP,
681
SourceAddressPrefix: to.StringPtr("*"),
682
SourcePortRange: to.StringPtr("*"),
683
DestinationAddressPrefix: to.StringPtr("192.168.16.0/20"),
684
DestinationPortRange: to.StringPtr("17777"),
685
Access: network.Allow,
686
Priority: to.Int32Ptr(101),
687
Direction: network.Inbound,
690
subnets := []network.Subnet{{
691
Name: to.StringPtr("juju-internal-subnet"),
692
Properties: &network.SubnetPropertiesFormat{
693
AddressPrefix: to.StringPtr("192.168.0.0/20"),
694
NetworkSecurityGroup: &network.SecurityGroup{
695
ID: to.StringPtr(nsgId),
699
Name: to.StringPtr("juju-controller-subnet"),
700
Properties: &network.SubnetPropertiesFormat{
701
AddressPrefix: to.StringPtr("192.168.16.0/20"),
702
NetworkSecurityGroup: &network.SecurityGroup{
703
ID: to.StringPtr(nsgId),
708
subnetName := "juju-internal-subnet"
709
privateIPAddress := "192.168.0.4"
710
if args.availabilitySetName == "juju-controller" {
711
subnetName = "juju-controller-subnet"
712
privateIPAddress = "192.168.16.4"
742
newIPConfigurations := []network.InterfaceIPConfiguration{{
714
subnetId := fmt.Sprintf(
715
`[concat(resourceId('Microsoft.Network/virtualNetworks', 'juju-internal-network'), '/subnets/%s')]`,
719
publicIPAddressId := `[resourceId('Microsoft.Network/publicIPAddresses', 'machine-0-public-ip')]`
721
ipConfigurations := []network.InterfaceIPConfiguration{{
743
722
Name: to.StringPtr("primary"),
744
723
Properties: &network.InterfaceIPConfigurationPropertiesFormat{
745
724
Primary: to.BoolPtr(true),
746
PrivateIPAddress: to.StringPtr("192.168.0.5"),
725
PrivateIPAddress: to.StringPtr(privateIPAddress),
747
726
PrivateIPAllocationMethod: network.Static,
748
Subnet: &network.Subnet{
749
ID: s.internalSubnet.ID,
727
Subnet: &network.Subnet{ID: to.StringPtr(subnetId)},
751
728
PublicIPAddress: &network.PublicIPAddress{
752
ID: s.publicIPAddress.ID,
729
ID: to.StringPtr(publicIPAddressId),
756
newNetworkInterface := &network.Interface{
757
Location: to.StringPtr("westus"),
734
nicId := `[resourceId('Microsoft.Network/networkInterfaces', 'machine-0-primary')]`
735
nics := []compute.NetworkInterfaceReference{{
736
ID: to.StringPtr(nicId),
737
Properties: &compute.NetworkInterfaceReferenceProperties{
738
Primary: to.BoolPtr(true),
741
vmDependsOn := []string{
743
`[resourceId('Microsoft.Storage/storageAccounts', '` + storageAccountName + `')]`,
746
addressPrefixes := []string{"192.168.0.0/20", "192.168.16.0/20"}
747
templateResources := []armtemplates.Resource{{
748
APIVersion: network.APIVersion,
749
Type: "Microsoft.Network/networkSecurityGroups",
750
Name: "juju-internal-nsg",
752
Tags: to.StringMap(s.envTags),
753
Properties: &network.SecurityGroupPropertiesFormat{
754
SecurityRules: &securityRules,
757
APIVersion: network.APIVersion,
758
Type: "Microsoft.Network/virtualNetworks",
759
Name: "juju-internal-network",
761
Tags: to.StringMap(s.envTags),
762
Properties: &network.VirtualNetworkPropertiesFormat{
763
AddressSpace: &network.AddressSpace{&addressPrefixes},
766
DependsOn: []string{nsgId},
768
APIVersion: storage.APIVersion,
769
Type: "Microsoft.Storage/storageAccounts",
770
Name: storageAccountName,
772
Tags: to.StringMap(s.envTags),
773
StorageSku: &storage.Sku{
774
Name: storage.SkuName("Standard_LRS"),
778
var availabilitySetSubResource *compute.SubResource
779
if args.availabilitySetName != "" {
780
availabilitySetId := fmt.Sprintf(
781
`[resourceId('Microsoft.Compute/availabilitySets','%s')]`,
782
args.availabilitySetName,
784
templateResources = append(templateResources, armtemplates.Resource{
785
APIVersion: compute.APIVersion,
786
Type: "Microsoft.Compute/availabilitySets",
787
Name: args.availabilitySetName,
789
Tags: to.StringMap(s.envTags),
791
availabilitySetSubResource = &compute.SubResource{
792
ID: to.StringPtr(availabilitySetId),
794
vmDependsOn = append([]string{availabilitySetId}, vmDependsOn...)
797
templateResources = append(templateResources, []armtemplates.Resource{{
798
APIVersion: network.APIVersion,
799
Type: "Microsoft.Network/publicIPAddresses",
800
Name: "machine-0-public-ip",
802
Tags: to.StringMap(s.vmTags),
803
Properties: &network.PublicIPAddressPropertiesFormat{
804
PublicIPAllocationMethod: network.Dynamic,
807
APIVersion: network.APIVersion,
808
Type: "Microsoft.Network/networkInterfaces",
809
Name: "machine-0-primary",
811
Tags: to.StringMap(s.vmTags),
759
812
Properties: &network.InterfacePropertiesFormat{
760
IPConfigurations: &newIPConfigurations,
763
jujuAvailabilitySet := &compute.AvailabilitySet{
764
Location: to.StringPtr("westus"),
767
virtualMachine := &compute.VirtualMachine{
768
Name: to.StringPtr("machine-0"),
769
Location: to.StringPtr("westus"),
813
IPConfigurations: &ipConfigurations,
817
`[resourceId('Microsoft.Network/virtualNetworks', 'juju-internal-network')]`,
820
APIVersion: compute.APIVersion,
821
Type: "Microsoft.Compute/virtualMachines",
824
Tags: to.StringMap(s.vmTags),
771
825
Properties: &compute.VirtualMachineProperties{
772
826
HardwareProfile: &compute.HardwareProfile{
773
827
VMSize: "Standard_D1",
775
829
StorageProfile: &compute.StorageProfile{
776
ImageReference: &compute.ImageReference{
777
Publisher: to.StringPtr("Canonical"),
778
Offer: to.StringPtr("UbuntuServer"),
779
Sku: to.StringPtr("12.10"),
780
Version: to.StringPtr("latest"),
830
ImageReference: args.imageReference,
782
831
OsDisk: &compute.OSDisk{
783
832
Name: to.StringPtr("machine-0"),
784
833
CreateOption: compute.FromImage,
785
834
Caching: compute.ReadWrite,
786
835
Vhd: &compute.VirtualHardDisk{
787
836
URI: to.StringPtr(fmt.Sprintf(
788
"https://%s.blob.storage.azurestack.local/osvhds/machine-0.vhd",
837
`[concat(reference(resourceId('Microsoft.Storage/storageAccounts', '%s'), '%s').primaryEndpoints.blob, 'osvhds/machine-0.vhd')]`,
838
storageAccountName, storage.APIVersion,
792
// 30 GiB is roughly 32 GB.
793
DiskSizeGB: to.Int32Ptr(32),
796
OsProfile: &compute.OSProfile{
797
ComputerName: to.StringPtr("machine-0"),
798
CustomData: to.StringPtr("<juju-goes-here>"),
799
AdminUsername: to.StringPtr("ubuntu"),
800
LinuxConfiguration: &compute.LinuxConfiguration{
801
DisablePasswordAuthentication: to.BoolPtr(true),
802
SSH: &compute.SSHConfiguration{
803
PublicKeys: &s.sshPublicKeys,
807
NetworkProfile: &compute.NetworkProfile{
808
NetworkInterfaces: &s.networkInterfaceReferences,
810
AvailabilitySet: &compute.SubResource{ID: s.jujuAvailabilitySet.ID},
841
DiskSizeGB: to.Int32Ptr(int32(args.diskSizeGB)),
844
OsProfile: args.osProfile,
845
NetworkProfile: &compute.NetworkProfile{&nics},
846
AvailabilitySet: availabilitySetSubResource,
848
DependsOn: vmDependsOn,
850
if args.vmExtension != nil {
851
templateResources = append(templateResources, armtemplates.Resource{
852
APIVersion: compute.APIVersion,
853
Type: "Microsoft.Compute/virtualMachines/extensions",
854
Name: "machine-0/JujuCustomScriptExtension",
856
Tags: to.StringMap(s.vmTags),
857
Properties: args.vmExtension,
858
DependsOn: []string{"Microsoft.Compute/virtualMachines/machine-0"},
861
templateMap := map[string]interface{}{
862
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
863
"contentVersion": "1.0.0.0",
864
"resources": templateResources,
866
deployment := &resources.Deployment{
867
&resources.DeploymentProperties{
868
Template: &templateMap,
869
Mode: resources.Incremental,
814
873
// Validate HTTP request bodies.
815
c.Assert(requests, gc.HasLen, numExpectedStartInstanceRequests)
816
c.Assert(requests[0].Method, gc.Equals, "GET") // vmSizes
817
c.Assert(requests[1].Method, gc.Equals, "GET") // storage accounts
818
c.Assert(requests[2].Method, gc.Equals, "GET") // skus
819
c.Assert(requests[3].Method, gc.Equals, "PUT")
820
assertRequestBody(c, requests[3], publicIPAddress)
821
c.Assert(requests[4].Method, gc.Equals, "GET") // list NICs (to choose private IP address)
822
c.Assert(requests[5].Method, gc.Equals, "PUT") // create NIC
823
assertRequestBody(c, requests[5], newNetworkInterface)
824
c.Assert(requests[6].Method, gc.Equals, "PUT") // create availability set
825
assertRequestBody(c, requests[6], jujuAvailabilitySet)
826
c.Assert(requests[7].Method, gc.Equals, "PUT") // create VM
827
assertCreateVirtualMachineRequestBody(c, requests[7], virtualMachine)
829
return startInstanceRequests{
830
vmSizes: requests[0],
831
storageAccounts: requests[1],
833
publicIPAddress: requests[3],
835
networkInterface: requests[5],
836
availabilitySet: requests[6],
837
virtualMachine: requests[7],
874
var startInstanceRequests startInstanceRequests
875
if args.vmExtension != nil {
876
// It must be Windows or CentOS, so
877
// there should be no image query.
878
c.Assert(requests, gc.HasLen, numExpectedStartInstanceRequests-1)
879
c.Assert(requests[0].Method, gc.Equals, "GET") // vmSizes
880
c.Assert(requests[1].Method, gc.Equals, "PUT") // create deployment
881
startInstanceRequests.vmSizes = requests[0]
882
startInstanceRequests.deployment = requests[1]
884
c.Assert(requests, gc.HasLen, numExpectedStartInstanceRequests)
885
c.Assert(requests[0].Method, gc.Equals, "GET") // vmSizes
886
c.Assert(requests[1].Method, gc.Equals, "GET") // skus
887
c.Assert(requests[2].Method, gc.Equals, "PUT") // create deployment
888
startInstanceRequests.vmSizes = requests[0]
889
startInstanceRequests.skus = requests[1]
890
startInstanceRequests.deployment = requests[2]
841
func assertCreateVirtualMachineRequestBody(c *gc.C, req *http.Request, expect *compute.VirtualMachine) {
842
// CustomData is non-deterministic, so don't compare it.
893
// Marshal/unmarshal the deployment we expect, so it's in map form.
894
var expected resources.Deployment
895
data, err := json.Marshal(&deployment)
896
c.Assert(err, jc.ErrorIsNil)
897
err = json.Unmarshal(data, &expected)
898
c.Assert(err, jc.ErrorIsNil)
900
// Check that we send what we expect. CustomData is non-deterministic,
901
// so don't compare it.
843
902
// TODO(axw) shouldn't CustomData be deterministic? Look into this.
844
var virtualMachine compute.VirtualMachine
845
unmarshalRequestBody(c, req, &virtualMachine)
846
c.Assert(to.String(virtualMachine.Properties.OsProfile.CustomData), gc.Not(gc.HasLen), 0)
847
virtualMachine.Properties.OsProfile.CustomData = to.StringPtr("<juju-goes-here>")
848
c.Assert(&virtualMachine, jc.DeepEquals, expect)
903
var actual resources.Deployment
904
unmarshalRequestBody(c, startInstanceRequests.deployment, &actual)
905
c.Assert(actual.Properties, gc.NotNil)
906
c.Assert(actual.Properties.Template, gc.NotNil)
907
resources := (*actual.Properties.Template)["resources"].([]interface{})
908
c.Assert(resources, gc.HasLen, len(templateResources))
910
vmResourceIndex := len(resources) - 1
911
if args.vmExtension != nil {
914
vmResource := resources[vmResourceIndex].(map[string]interface{})
915
vmResourceProperties := vmResource["properties"].(map[string]interface{})
916
osProfile := vmResourceProperties["osProfile"].(map[string]interface{})
917
osProfile["customData"] = "<juju-goes-here>"
918
c.Assert(actual, jc.DeepEquals, expected)
920
return startInstanceRequests
851
923
type startInstanceRequests struct {
852
vmSizes *http.Request
853
storageAccounts *http.Request
856
publicIPAddress *http.Request
858
networkInterface *http.Request
859
availabilitySet *http.Request
860
virtualMachine *http.Request
924
vmSizes *http.Request
926
deployment *http.Request
863
929
func (s *environSuite) TestBootstrap(c *gc.C) {
866
932
ctx := envtesting.BootstrapContext(c)
867
933
env := prepareForBootstrap(c, ctx, s.provider, &s.sender)
869
s.sender = s.initResourceGroupSenders(true)
935
s.sender = s.initResourceGroupSenders()
870
936
s.sender = append(s.sender, s.startInstanceSenders(true)...)
872
938
result, err := env.Bootstrap(
873
939
ctx, environs.BootstrapParams{
874
940
ControllerConfig: testing.FakeControllerConfig(),
875
AvailableTools: makeToolsList(series.LatestLts()),
941
AvailableTools: makeToolsList("quantal"),
942
BootstrapSeries: "quantal",
878
945
c.Assert(err, jc.ErrorIsNil)
879
946
c.Assert(result.Arch, gc.Equals, "amd64")
880
c.Assert(result.Series, gc.Equals, series.LatestLts())
882
c.Assert(len(s.requests), gc.Equals, 15)
884
c.Assert(s.requests[0].Method, gc.Equals, "PUT") // resource group
885
c.Assert(s.requests[1].Method, gc.Equals, "PUT") // create vnet
886
c.Assert(s.requests[2].Method, gc.Equals, "PUT") // create network security group
887
c.Assert(s.requests[3].Method, gc.Equals, "PUT") // create subnet (internal)
888
c.Assert(s.requests[4].Method, gc.Equals, "PUT") // create subnet (controller)
889
c.Assert(s.requests[5].Method, gc.Equals, "POST") // check storage account name
890
c.Assert(s.requests[6].Method, gc.Equals, "PUT") // create storage account
892
s.group.Properties = nil
893
assertRequestBody(c, s.requests[0], &s.group)
897
s.vnet.Properties.ProvisioningState = nil
898
assertRequestBody(c, s.requests[1], s.vnet)
900
securityRules := []network.SecurityRule{{
901
Name: to.StringPtr("SSHInbound"),
902
Properties: &network.SecurityRulePropertiesFormat{
903
Description: to.StringPtr("Allow SSH access to all machines"),
904
Protocol: network.TCP,
905
SourceAddressPrefix: to.StringPtr("*"),
906
SourcePortRange: to.StringPtr("*"),
907
DestinationAddressPrefix: to.StringPtr("*"),
908
DestinationPortRange: to.StringPtr("22"),
909
Access: network.Allow,
910
Priority: to.Int32Ptr(100),
911
Direction: network.Inbound,
914
Name: to.StringPtr("JujuAPIInbound"),
915
Properties: &network.SecurityRulePropertiesFormat{
916
Description: to.StringPtr("Allow API connections to controller machines"),
917
Protocol: network.TCP,
918
SourceAddressPrefix: to.StringPtr("*"),
919
SourcePortRange: to.StringPtr("*"),
920
DestinationAddressPrefix: to.StringPtr("192.168.16.0/20"),
921
DestinationPortRange: to.StringPtr("17777"),
922
Access: network.Allow,
923
Priority: to.Int32Ptr(101),
924
Direction: network.Inbound,
927
assertRequestBody(c, s.requests[2], &network.SecurityGroup{
928
Location: to.StringPtr("westus"),
930
Properties: &network.SecurityGroupPropertiesFormat{
931
SecurityRules: &securityRules,
935
s.internalSubnet.ID = nil
936
s.internalSubnet.Name = nil
937
s.internalSubnet.Properties.ProvisioningState = nil
938
assertRequestBody(c, s.requests[3], s.internalSubnet)
939
s.controllerSubnet.ID = nil
940
s.controllerSubnet.Name = nil
941
s.controllerSubnet.Properties.ProvisioningState = nil
942
assertRequestBody(c, s.requests[4], s.controllerSubnet)
944
assertRequestBody(c, s.requests[5], &storage.AccountCheckNameAvailabilityParameters{
945
Name: to.StringPtr(fakeStorageAccount),
946
Type: to.StringPtr("Microsoft.Storage/storageAccounts"),
949
assertRequestBody(c, s.requests[6], &storage.AccountCreateParameters{
950
Location: to.StringPtr("westus"),
951
Tags: s.storageAccount.Tags,
953
Name: storage.StandardLRS,
947
c.Assert(result.Series, gc.Equals, "quantal")
949
c.Assert(len(s.requests), gc.Equals, numExpectedStartInstanceRequests+1)
950
s.vmTags[tags.JujuIsController] = to.StringPtr("true")
951
s.assertStartInstanceRequests(c, s.requests[1:], assertStartInstanceRequestsParams{
952
availabilitySetName: "juju-controller",
953
imageReference: &quantalImageReference,
955
osProfile: &linuxOsProfile,
995
999
nic0 := makeNetworkInterface("nic-0", "machine-0", nic0IPConfiguration)
997
1001
s.sender = azuretesting.Senders{
998
s.networkInterfacesSender(
1000
makeNetworkInterface("nic-1", "machine-1"),
1001
makeNetworkInterface("nic-2", "machine-1"),
1003
s.virtualMachinesSender(makeVirtualMachine("machine-0")),
1004
s.publicIPAddressesSender(
1005
makePublicIPAddress("pip-0", "machine-0", "1.2.3.4"),
1007
s.storageAccountsSender(),
1002
s.makeSender(".*/deployments/machine-0/cancel", nil), // POST
1003
s.storageAccountSender(),
1008
1004
s.storageAccountKeysSender(),
1005
s.networkInterfacesSender(nic0),
1006
s.publicIPAddressesSender(makePublicIPAddress("pip-0", "machine-0", "1.2.3.4")),
1009
1007
s.makeSender(".*/virtualMachines/machine-0", nil), // DELETE
1010
1008
s.makeSender(".*/networkSecurityGroups/juju-internal-nsg", nsg), // GET
1011
1009
s.makeSender(".*/networkSecurityGroups/juju-internal-nsg/securityRules/machine-0-80", nil), // DELETE
1012
1010
s.makeSender(".*/networkSecurityGroups/juju-internal-nsg/securityRules/machine-0-1000-2000", nil), // DELETE
1013
s.makeSender(".*/networkInterfaces/nic-0", nic0), // PUT
1011
s.makeSender(".*/networkInterfaces/nic-0", nil), // DELETE
1014
1012
s.makeSender(".*/publicIPAddresses/pip-0", nil), // DELETE
1015
s.makeSender(".*/networkInterfaces/nic-0", nil), // DELETE
1016
s.makeSender(".*/virtualMachines/machine-1", nil), // DELETE
1017
s.makeSender(".*/networkSecurityGroups/juju-internal-nsg", nsg), // GET
1018
s.makeSender(".*/networkInterfaces/nic-1", nil), // DELETE
1019
s.makeSender(".*/networkInterfaces/nic-2", nil), // DELETE
1013
s.makeSender(".*/deployments/machine-0", nil), // DELETE
1021
err := env.StopInstances("machine-0", "machine-1", "machine-2")
1015
err := env.StopInstances("machine-0")
1022
1016
c.Assert(err, jc.ErrorIsNil)
1024
1018
s.storageClient.CheckCallNames(c,
1025
"NewClient", "DeleteBlobIfExists", "DeleteBlobIfExists",
1019
"NewClient", "DeleteBlobIfExists",
1027
1021
s.storageClient.CheckCall(c, 1, "DeleteBlobIfExists", "osvhds", "machine-0")
1028
s.storageClient.CheckCall(c, 2, "DeleteBlobIfExists", "osvhds", "machine-1")
1024
func (s *environSuite) TestStopInstancesMultiple(c *gc.C) {
1025
env := s.openEnviron(c)
1027
vmDeleteSender0 := s.makeSender(".*/virtualMachines/machine-[01]", nil)
1028
vmDeleteSender1 := s.makeSender(".*/virtualMachines/machine-[01]", nil)
1029
vmDeleteSender0.SetError(errors.New("blargh"))
1030
vmDeleteSender1.SetError(errors.New("blargh"))
1032
s.sender = azuretesting.Senders{
1033
s.makeSender(".*/deployments/machine-[01]/cancel", nil), // POST
1034
s.makeSender(".*/deployments/machine-[01]/cancel", nil), // POST
1036
// We should only query the NICs, public IPs, and storage
1037
// account/keys, regardless of how many instances are deleted.
1038
s.storageAccountSender(),
1039
s.storageAccountKeysSender(),
1040
s.networkInterfacesSender(),
1041
s.publicIPAddressesSender(),
1046
err := env.StopInstances("machine-0", "machine-1")
1047
c.Assert(err, gc.ErrorMatches, `deleting instance "machine-[01]":.*blargh`)
1050
func (s *environSuite) TestStopInstancesDeploymentNotFound(c *gc.C) {
1051
env := s.openEnviron(c)
1053
cancelSender := mocks.NewSender()
1054
cancelSender.AppendResponse(mocks.NewResponseWithStatus(
1055
"deployment not found", http.StatusNotFound,
1057
s.sender = azuretesting.Senders{cancelSender}
1058
err := env.StopInstances("machine-0")
1059
c.Assert(err, jc.ErrorIsNil)
1031
1062
func (s *environSuite) TestStopInstancesStorageAccountNoKeys(c *gc.C) {
1032
1063
s.PatchValue(&s.storageAccountKeys.Keys, nil)
1033
s.testStopInstancesStorageKeysError(c, "getting storage account key: storage account keys not found")
1064
s.testStopInstancesStorageAccountNotFound(c)
1036
1067
func (s *environSuite) TestStopInstancesStorageAccountNoFullKey(c *gc.C) {
1037
1068
keys := *s.storageAccountKeys.Keys
1038
1069
s.PatchValue(&keys[0].Permissions, storage.READ)
1039
s.testStopInstancesStorageKeysError(c, `getting storage account key: storage account key with "FULL" permission not found`)
1070
s.testStopInstancesStorageAccountNotFound(c)
1042
func (s *environSuite) testStopInstancesStorageKeysError(c *gc.C, expect string) {
1073
func (s *environSuite) testStopInstancesStorageAccountNotFound(c *gc.C) {
1043
1074
env := s.openEnviron(c)
1045
nic0IPConfiguration := makeIPConfiguration("192.168.0.4")
1046
nic0IPConfiguration.Properties.PublicIPAddress = &network.PublicIPAddress{}
1047
nic0 := makeNetworkInterface("nic-0", "machine-0", nic0IPConfiguration)
1048
1075
s.sender = azuretesting.Senders{
1049
s.networkInterfacesSender(nic0),
1050
s.virtualMachinesSender(makeVirtualMachine("machine-0")),
1051
s.publicIPAddressesSender(makePublicIPAddress("pip-0", "machine-0", "1.2.3.4")),
1052
s.storageAccountsSender(),
1076
s.makeSender("/deployments/machine-0", s.deployment), // Cancel
1077
s.storageAccountSender(),
1053
1078
s.storageAccountKeysSender(),
1056
err := env.StopInstances("machine-0")
1057
c.Assert(err, gc.ErrorMatches, expect)
1079
s.networkInterfacesSender(), // GET: no NICs
1080
s.publicIPAddressesSender(), // GET: no public IPs
1081
s.makeSender(".*/virtualMachines/machine-0", nil), // DELETE
1082
s.makeSender(".*/networkSecurityGroups/juju-internal-nsg", makeSecurityGroup()), // GET: no rules
1083
s.makeSender(".*/deployments/machine-0", nil), // DELETE
1085
err := env.StopInstances("machine-0")
1086
c.Assert(err, jc.ErrorIsNil)
1089
func (s *environSuite) TestStopInstancesStorageAccountError(c *gc.C) {
1090
env := s.openEnviron(c)
1091
errorSender := s.storageAccountSender()
1092
errorSender.SetError(errors.New("blargh"))
1093
s.sender = azuretesting.Senders{
1094
s.makeSender("/deployments/machine-0", s.deployment), // Cancel
1097
err := env.StopInstances("machine-0")
1098
c.Assert(err, gc.ErrorMatches, "getting storage account:.*blargh")
1101
func (s *environSuite) TestStopInstancesStorageAccountKeysError(c *gc.C) {
1102
env := s.openEnviron(c)
1103
errorSender := s.storageAccountKeysSender()
1104
errorSender.SetError(errors.New("blargh"))
1105
s.sender = azuretesting.Senders{
1106
s.makeSender("/deployments/machine-0", s.deployment), // Cancel
1107
s.storageAccountSender(),
1110
err := env.StopInstances("machine-0")
1111
c.Assert(err, gc.ErrorMatches, "getting storage account key:.*blargh")
1060
1114
func (s *environSuite) TestConstraintsValidatorUnsupported(c *gc.C) {