func (s *environSuite) assertStartInstanceRequests( c *gc.C, requests []*http.Request, args assertStartInstanceRequestsParams, ) startInstanceRequests { nsgId := `[resourceId('Microsoft.Network/networkSecurityGroups', 'juju-internal-nsg')]` securityRules := []network.SecurityRule{{ Name: to.StringPtr("SSHInbound"), Properties: &network.SecurityRulePropertiesFormat{ Description: to.StringPtr("Allow SSH access to all machines"), Protocol: network.TCP, SourceAddressPrefix: to.StringPtr("*"), SourcePortRange: to.StringPtr("*"), DestinationAddressPrefix: to.StringPtr("*"), DestinationPortRange: to.StringPtr("22"), Access: network.Allow, Priority: to.Int32Ptr(100), Direction: network.Inbound, }, }, { Name: to.StringPtr("JujuAPIInbound"), Properties: &network.SecurityRulePropertiesFormat{ Description: to.StringPtr("Allow API connections to controller machines"), Protocol: network.TCP, SourceAddressPrefix: to.StringPtr("*"), SourcePortRange: to.StringPtr("*"), DestinationAddressPrefix: to.StringPtr("192.168.16.0/20"), DestinationPortRange: to.StringPtr("17777"), Access: network.Allow, Priority: to.Int32Ptr(101), Direction: network.Inbound, }, }} subnets := []network.Subnet{{ Name: to.StringPtr("juju-internal-subnet"), Properties: &network.SubnetPropertiesFormat{ AddressPrefix: to.StringPtr("192.168.0.0/20"), NetworkSecurityGroup: &network.SecurityGroup{ ID: to.StringPtr(nsgId), }, }, }, { Name: to.StringPtr("juju-controller-subnet"), Properties: &network.SubnetPropertiesFormat{ AddressPrefix: to.StringPtr("192.168.16.0/20"), NetworkSecurityGroup: &network.SecurityGroup{ ID: to.StringPtr(nsgId), }, }, }} subnetName := "juju-internal-subnet" privateIPAddress := "192.168.0.4" if args.availabilitySetName == "juju-controller" { subnetName = "juju-controller-subnet" privateIPAddress = "192.168.16.4" } subnetId := fmt.Sprintf( `[concat(resourceId('Microsoft.Network/virtualNetworks', 'juju-internal-network'), '/subnets/%s')]`, subnetName, ) publicIPAddressId := `[resourceId('Microsoft.Network/publicIPAddresses', 'machine-0-public-ip')]` ipConfigurations := []network.InterfaceIPConfiguration{{ Name: to.StringPtr("primary"), Properties: &network.InterfaceIPConfigurationPropertiesFormat{ Primary: to.BoolPtr(true), PrivateIPAddress: to.StringPtr(privateIPAddress), PrivateIPAllocationMethod: network.Static, Subnet: &network.Subnet{ID: to.StringPtr(subnetId)}, PublicIPAddress: &network.PublicIPAddress{ ID: to.StringPtr(publicIPAddressId), }, }, }} nicId := `[resourceId('Microsoft.Network/networkInterfaces', 'machine-0-primary')]` nics := []compute.NetworkInterfaceReference{{ ID: to.StringPtr(nicId), Properties: &compute.NetworkInterfaceReferenceProperties{ Primary: to.BoolPtr(true), }, }} vmDependsOn := []string{ nicId, `[resourceId('Microsoft.Storage/storageAccounts', '` + storageAccountName + `')]`, } addressPrefixes := []string{"192.168.0.0/20", "192.168.16.0/20"} templateResources := []armtemplates.Resource{{ APIVersion: network.APIVersion, Type: "Microsoft.Network/networkSecurityGroups", Name: "juju-internal-nsg", Location: "westus", Tags: to.StringMap(s.envTags), Properties: &network.SecurityGroupPropertiesFormat{ SecurityRules: &securityRules, }, }, { APIVersion: network.APIVersion, Type: "Microsoft.Network/virtualNetworks", Name: "juju-internal-network", Location: "westus", Tags: to.StringMap(s.envTags), Properties: &network.VirtualNetworkPropertiesFormat{ AddressSpace: &network.AddressSpace{&addressPrefixes}, Subnets: &subnets, }, DependsOn: []string{nsgId}, }, { APIVersion: storage.APIVersion, Type: "Microsoft.Storage/storageAccounts", Name: storageAccountName, Location: "westus", Tags: to.StringMap(s.envTags), StorageSku: &storage.Sku{ Name: storage.SkuName("Standard_LRS"), }, }} var availabilitySetSubResource *compute.SubResource if args.availabilitySetName != "" { availabilitySetId := fmt.Sprintf( `[resourceId('Microsoft.Compute/availabilitySets','%s')]`, args.availabilitySetName, ) templateResources = append(templateResources, armtemplates.Resource{ APIVersion: compute.APIVersion, Type: "Microsoft.Compute/availabilitySets", Name: args.availabilitySetName, Location: "westus", Tags: to.StringMap(s.envTags), }) availabilitySetSubResource = &compute.SubResource{ ID: to.StringPtr(availabilitySetId), } vmDependsOn = append([]string{availabilitySetId}, vmDependsOn...) } templateResources = append(templateResources, []armtemplates.Resource{{ APIVersion: network.APIVersion, Type: "Microsoft.Network/publicIPAddresses", Name: "machine-0-public-ip", Location: "westus", Tags: to.StringMap(s.vmTags), Properties: &network.PublicIPAddressPropertiesFormat{ PublicIPAllocationMethod: network.Dynamic, }, }, { APIVersion: network.APIVersion, Type: "Microsoft.Network/networkInterfaces", Name: "machine-0-primary", Location: "westus", Tags: to.StringMap(s.vmTags), Properties: &network.InterfacePropertiesFormat{ IPConfigurations: &ipConfigurations, }, DependsOn: []string{ publicIPAddressId, `[resourceId('Microsoft.Network/virtualNetworks', 'juju-internal-network')]`, }, }, { APIVersion: compute.APIVersion, Type: "Microsoft.Compute/virtualMachines", Name: "machine-0", Location: "westus", Tags: to.StringMap(s.vmTags), Properties: &compute.VirtualMachineProperties{ HardwareProfile: &compute.HardwareProfile{ VMSize: "Standard_D1", }, StorageProfile: &compute.StorageProfile{ ImageReference: args.imageReference, OsDisk: &compute.OSDisk{ Name: to.StringPtr("machine-0"), CreateOption: compute.FromImage, Caching: compute.ReadWrite, Vhd: &compute.VirtualHardDisk{ URI: to.StringPtr(fmt.Sprintf( `[concat(reference(resourceId('Microsoft.Storage/storageAccounts', '%s'), '%s').primaryEndpoints.blob, 'osvhds/machine-0.vhd')]`, storageAccountName, storage.APIVersion, )), }, DiskSizeGB: to.Int32Ptr(int32(args.diskSizeGB)), }, }, OsProfile: args.osProfile, NetworkProfile: &compute.NetworkProfile{&nics}, AvailabilitySet: availabilitySetSubResource, }, DependsOn: vmDependsOn, }}...) if args.vmExtension != nil { templateResources = append(templateResources, armtemplates.Resource{ APIVersion: compute.APIVersion, Type: "Microsoft.Compute/virtualMachines/extensions", Name: "machine-0/JujuCustomScriptExtension", Location: "westus", Tags: to.StringMap(s.vmTags), Properties: args.vmExtension, DependsOn: []string{"Microsoft.Compute/virtualMachines/machine-0"}, }) } templateMap := map[string]interface{}{ "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "resources": templateResources, } deployment := &resources.Deployment{ &resources.DeploymentProperties{ Template: &templateMap, Mode: resources.Incremental, }, } // Validate HTTP request bodies. var startInstanceRequests startInstanceRequests if args.vmExtension != nil { // It must be Windows or CentOS, so // there should be no image query. c.Assert(requests, gc.HasLen, numExpectedStartInstanceRequests-1) c.Assert(requests[0].Method, gc.Equals, "GET") // vmSizes c.Assert(requests[1].Method, gc.Equals, "PUT") // create deployment startInstanceRequests.vmSizes = requests[0] startInstanceRequests.deployment = requests[1] } else { c.Assert(requests, gc.HasLen, numExpectedStartInstanceRequests) c.Assert(requests[0].Method, gc.Equals, "GET") // vmSizes c.Assert(requests[1].Method, gc.Equals, "GET") // skus c.Assert(requests[2].Method, gc.Equals, "PUT") // create deployment startInstanceRequests.vmSizes = requests[0] startInstanceRequests.skus = requests[1] startInstanceRequests.deployment = requests[2] } // Marshal/unmarshal the deployment we expect, so it's in map form. var expected resources.Deployment data, err := json.Marshal(&deployment) c.Assert(err, jc.ErrorIsNil) err = json.Unmarshal(data, &expected) c.Assert(err, jc.ErrorIsNil) // Check that we send what we expect. CustomData is non-deterministic, // so don't compare it. // TODO(axw) shouldn't CustomData be deterministic? Look into this. var actual resources.Deployment unmarshalRequestBody(c, startInstanceRequests.deployment, &actual) c.Assert(actual.Properties, gc.NotNil) c.Assert(actual.Properties.Template, gc.NotNil) resources := (*actual.Properties.Template)["resources"].([]interface{}) c.Assert(resources, gc.HasLen, len(templateResources)) vmResourceIndex := len(resources) - 1 if args.vmExtension != nil { vmResourceIndex-- } vmResource := resources[vmResourceIndex].(map[string]interface{}) vmResourceProperties := vmResource["properties"].(map[string]interface{}) osProfile := vmResourceProperties["osProfile"].(map[string]interface{}) osProfile["customData"] = "<juju-goes-here>" c.Assert(actual, jc.DeepEquals, expected) return startInstanceRequests }
func toTags(tags *map[string]*string) map[string]string { if tags == nil { return nil } return to.StringMap(*tags) }