109
func createInternalVirtualNetwork(
111
client network.ManagementClient,
112
subscriptionId, resourceGroup string,
114
tags map[string]string,
115
) (*network.VirtualNetwork, error) {
116
addressPrefixes := []string{internalSubnetPrefix, controllerSubnetPrefix}
117
vnet := network.VirtualNetwork{
118
Location: to.StringPtr(location),
119
Tags: to.StringMapPtr(tags),
120
Properties: &network.VirtualNetworkPropertiesFormat{
121
AddressSpace: &network.AddressSpace{&addressPrefixes},
124
logger.Debugf("creating virtual network %q", internalNetworkName)
125
vnetClient := network.VirtualNetworksClient{client}
126
if err := callAPI(func() (autorest.Response, error) {
127
return vnetClient.CreateOrUpdate(
128
resourceGroup, internalNetworkName, vnet,
129
nil, // abort channel
132
return nil, errors.Annotatef(err, "creating virtual network %q", internalNetworkName)
134
vnet.ID = to.StringPtr(internalNetworkId(subscriptionId, resourceGroup))
138
func createInternalNetworkSecurityGroup(
140
client network.ManagementClient,
141
subscriptionId, resourceGroup string,
143
tags map[string]string,
145
) (*network.SecurityGroup, error) {
108
// networkTemplateResources returns resource definitions for creating network
109
// resources shared by all machines in a model.
110
func networkTemplateResources(
112
envTags map[string]string,
114
) []armtemplates.Resource {
146
115
// Create a network security group for the environment. There is only
147
116
// one NSG per environment (there's a limit of 100 per subscription),
148
117
// in which we manage rules for each exposed machine.
149
securityRules := []network.SecurityRule{sshSecurityRule}
151
rule := apiSecurityRule
152
properties := *rule.Properties
153
properties.DestinationPortRange = to.StringPtr(fmt.Sprint(*apiPort))
154
rule.Properties = &properties
155
securityRules = append(securityRules, rule)
157
nsg := network.SecurityGroup{
158
Location: to.StringPtr(location),
159
Tags: to.StringMapPtr(tags),
118
apiSecurityRule := apiSecurityRule
119
properties := *apiSecurityRule.Properties
120
properties.DestinationPortRange = to.StringPtr(fmt.Sprint(apiPort))
121
apiSecurityRule.Properties = &properties
122
securityRules := []network.SecurityRule{sshSecurityRule, apiSecurityRule}
124
// NOTE(axw) we create the API rule for all models to avoid having to
125
// make queries when creating resources, making deployment faster and
126
// more robust. The controller subnet is never used in non-controller
127
// models, so there are no security implications.
128
nsgId := fmt.Sprintf(
129
`[resourceId('Microsoft.Network/networkSecurityGroups', '%s')]`,
130
internalSecurityGroupName,
132
subnets := []network.Subnet{{
133
Name: to.StringPtr(internalSubnetName),
134
Properties: &network.SubnetPropertiesFormat{
135
AddressPrefix: to.StringPtr(internalSubnetPrefix),
136
NetworkSecurityGroup: &network.SecurityGroup{
137
ID: to.StringPtr(nsgId),
141
Name: to.StringPtr(controllerSubnetName),
142
Properties: &network.SubnetPropertiesFormat{
143
AddressPrefix: to.StringPtr(controllerSubnetPrefix),
144
NetworkSecurityGroup: &network.SecurityGroup{
145
ID: to.StringPtr(nsgId),
150
addressPrefixes := []string{internalSubnetPrefix, controllerSubnetPrefix}
151
resources := []armtemplates.Resource{{
152
APIVersion: network.APIVersion,
153
Type: "Microsoft.Network/networkSecurityGroups",
154
Name: internalSecurityGroupName,
160
157
Properties: &network.SecurityGroupPropertiesFormat{
161
158
SecurityRules: &securityRules,
164
securityGroupClient := network.SecurityGroupsClient{client}
165
securityGroupName := internalSecurityGroupName
166
logger.Debugf("creating security group %q", securityGroupName)
167
if err := callAPI(func() (autorest.Response, error) {
168
return securityGroupClient.CreateOrUpdate(
169
resourceGroup, securityGroupName, nsg,
170
nil, // abort channel
173
return nil, errors.Annotatef(err, "creating security group %q", securityGroupName)
175
nsg.ID = to.StringPtr(internalNetworkSecurityGroupId(subscriptionId, resourceGroup))
179
// createInternalSubnet creates an internal subnet for the specified resource group,
180
// within the specified virtual network.
182
// NOTE(axw) this method expects an up-to-date VirtualNetwork, and expects that are
183
// no concurrent subnet additions to the virtual network. At the moment we have only
184
// three places where we modify subnets: at bootstrap, when a new environment is
185
// created, and when an environment is destroyed.
186
func createInternalNetworkSubnet(
188
client network.ManagementClient,
189
subscriptionId, resourceGroup string,
190
subnetName, addressPrefix string,
192
tags map[string]string,
193
) (*network.Subnet, error) {
194
// Now create a subnet with the next available address prefix, and
195
// associate the subnet with the NSG created above.
196
subnet := network.Subnet{
197
Properties: &network.SubnetPropertiesFormat{
198
AddressPrefix: to.StringPtr(addressPrefix),
199
NetworkSecurityGroup: &network.SecurityGroup{
200
ID: to.StringPtr(internalNetworkSecurityGroupId(
201
subscriptionId, resourceGroup,
206
logger.Debugf("creating subnet %q (%s)", subnetName, addressPrefix)
207
subnetClient := network.SubnetsClient{client}
208
if err := callAPI(func() (autorest.Response, error) {
209
return subnetClient.CreateOrUpdate(
210
resourceGroup, internalNetworkName, subnetName, subnet,
211
nil, // abort channel
214
return nil, errors.Annotatef(err, "creating subnet %q", subnetName)
216
subnet.ID = to.StringPtr(internalNetworkSubnetId(
217
subscriptionId, resourceGroup, subnetName,
222
type subnetParams struct {
227
// newNetworkProfile creates a public IP and NIC(s) for the VM with the
228
// specified name. A separate NIC will be created for each subnet; the
229
// first subnet in the list will be associated with the primary NIC.
230
func newNetworkProfile(
232
client network.ManagementClient,
235
subscriptionId, resourceGroup string,
237
tags map[string]string,
238
) (*compute.NetworkProfile, error) {
239
logger.Debugf("creating network profile for %q", vmName)
241
// Create a public IP for the NIC. Public IP addresses are dynamic.
242
logger.Debugf("- allocating public IP address")
243
pipClient := network.PublicIPAddressesClient{client}
244
publicIPAddress := network.PublicIPAddress{
245
Location: to.StringPtr(location),
246
Tags: to.StringMapPtr(tags),
247
Properties: &network.PublicIPAddressPropertiesFormat{
248
PublicIPAllocationMethod: network.Dynamic,
251
publicIPAddressName := vmName + "-public-ip"
252
if err := callAPI(func() (autorest.Response, error) {
253
return pipClient.CreateOrUpdate(
254
resourceGroup, publicIPAddressName, publicIPAddress,
255
nil, // abort channel
258
return nil, errors.Annotatef(err, "creating public IP address for %q", vmName)
260
publicIPAddress.ID = to.StringPtr(publicIPAddressId(
261
subscriptionId, resourceGroup, publicIPAddressName,
264
// Controller and non-controller machines are assigned to separate
265
// subnets. This enables us to create controller-specific NSG rules
266
// just by targeting the controller subnet.
267
subnetName := internalSubnetName
268
subnetPrefix := internalSubnetPrefix
270
subnetName = controllerSubnetName
271
subnetPrefix = controllerSubnetPrefix
273
subnetId := internalNetworkSubnetId(subscriptionId, resourceGroup, subnetName)
275
// Determine the next available private IP address.
276
nicClient := network.InterfacesClient{client}
277
privateIPAddress, err := nextSubnetIPAddress(nicClient, resourceGroup, subnetPrefix)
279
return nil, errors.Annotatef(err, "querying private IP addresses")
282
// Create a primary NIC for the machine. The private IP address needs
283
// to be static so that we can create NSG rules that don't become
285
logger.Debugf("- creating primary NIC")
286
ipConfigurations := []network.InterfaceIPConfiguration{{
287
Name: to.StringPtr("primary"),
288
Properties: &network.InterfaceIPConfigurationPropertiesFormat{
289
Primary: to.BoolPtr(true),
290
PrivateIPAddress: to.StringPtr(privateIPAddress),
291
PrivateIPAllocationMethod: network.Static,
292
Subnet: &network.Subnet{ID: to.StringPtr(subnetId)},
293
PublicIPAddress: &network.PublicIPAddress{
294
ID: publicIPAddress.ID,
298
nicName := vmName + "-primary"
299
nic := network.Interface{
300
Location: to.StringPtr(location),
301
Tags: to.StringMapPtr(tags),
302
Properties: &network.InterfacePropertiesFormat{
303
IPConfigurations: &ipConfigurations,
306
if err := callAPI(func() (autorest.Response, error) {
307
return nicClient.CreateOrUpdate(resourceGroup, nicName, nic, nil)
309
return nil, errors.Annotatef(err, "creating network interface for %q", vmName)
312
nics := []compute.NetworkInterfaceReference{{
313
ID: to.StringPtr(networkInterfaceId(subscriptionId, resourceGroup, nicName)),
314
Properties: &compute.NetworkInterfaceReferenceProperties{
315
Primary: to.BoolPtr(true),
318
return &compute.NetworkProfile{&nics}, nil
161
APIVersion: network.APIVersion,
162
Type: "Microsoft.Network/virtualNetworks",
163
Name: internalNetworkName,
166
Properties: &network.VirtualNetworkPropertiesFormat{
167
AddressSpace: &network.AddressSpace{&addressPrefixes},
170
DependsOn: []string{nsgId},
321
175
// nextSecurityRulePriority returns the next available priority in the given
344
// nextSubnetIPAddress returns the next available IP address in the given subnet.
345
func nextSubnetIPAddress(
346
nicClient network.InterfacesClient,
347
resourceGroup string,
198
// machineSubnetIP returns the private IP address to use for the given
200
func machineSubnetIP(subnetPrefix, machineId string) (net.IP, error) {
350
201
_, ipnet, err := net.ParseCIDR(subnetPrefix)
352
return "", errors.Annotate(err, "parsing subnet prefix")
354
results, err := nicClient.List(resourceGroup)
356
return "", errors.Annotate(err, "listing NICs")
358
var ipsInUse []net.IP
359
if results.Value != nil {
360
ipsInUse = make([]net.IP, 0, len(*results.Value))
361
for _, item := range *results.Value {
362
if item.Properties.IPConfigurations == nil {
365
for _, ipConfiguration := range *item.Properties.IPConfigurations {
366
ip := net.ParseIP(to.String(ipConfiguration.Properties.PrivateIPAddress))
367
if ip != nil && ipnet.Contains(ip) {
368
ipsInUse = append(ipsInUse, ip)
373
ip, err := iputils.NextSubnetIP(ipnet, ipsInUse)
375
return "", errors.Trace(err)
377
return ip.String(), nil
380
// internalNetworkSubnetId returns the Azure resource ID of the subnet with
381
// the specified name, within the internal network subnet for the specified
383
func internalNetworkSubnetId(subscriptionId, resourceGroup, subnetName string) string {
385
internalNetworkId(subscriptionId, resourceGroup),
386
"subnets", subnetName,
390
func internalNetworkId(subscriptionId, resourceGroup string) string {
391
return resourceId(subscriptionId, resourceGroup, "Microsoft.Network",
392
"virtualNetworks", internalNetworkName,
396
func internalNetworkSecurityGroupId(subscriptionId, resourceGroup string) string {
397
return resourceId(subscriptionId, resourceGroup, "Microsoft.Network",
398
"networkSecurityGroups", internalSecurityGroupName,
402
func publicIPAddressId(subscriptionId, resourceGroup, publicIPAddressName string) string {
403
return resourceId(subscriptionId, resourceGroup, "Microsoft.Network",
404
"publicIPAddresses", publicIPAddressName,
408
func networkInterfaceId(subscriptionId, resourceGroup, interfaceName string) string {
409
return resourceId(subscriptionId, resourceGroup, "Microsoft.Network",
410
"networkInterfaces", interfaceName,
414
func resourceId(subscriptionId, resourceGroup, provider string, resourceId ...string) string {
415
args := append([]string{
416
"/subscriptions", subscriptionId,
417
"resourceGroups", resourceGroup,
418
"providers", provider,
420
return path.Join(args...)
203
return nil, errors.Annotate(err, "parsing subnet prefix")
205
n, err := strconv.Atoi(machineId)
207
return nil, errors.Annotate(err, "parsing machine ID")
209
ip := iputils.NthSubnetIP(ipnet, n)
211
// TODO(axw) getting nil means we've cycled through roughly
212
// 2^12 machines. To work around this limitation, we must
213
// maintain an in-memory set of in-use IP addresses for each
215
return nil, errors.Errorf(
216
"no available IP addresses in %s", subnetPrefix,