Skip to content

Commit a445448

Browse files
committed
Refactor NAT rule creation.
1 parent fe0db93 commit a445448

File tree

3 files changed

+143
-50
lines changed

3 files changed

+143
-50
lines changed

client.go

Lines changed: 124 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -226,39 +226,7 @@ func (driver *Driver) deployServer() (*compute.Server, error) {
226226
log.Debugf("Server '%s' ('%s') has been successfully deployed...", driver.ServerID, server.Name)
227227

228228
driver.PrivateIPAddress = *server.Network.PrimaryAdapter.PrivateIPv4Address
229-
230-
log.Debugf("Creating NAT rule for server '%s' ('%s')...", driver.MachineName, driver.PrivateIPAddress)
231-
232-
natRule, err := driver.getExistingNATRuleByInternalIP(driver.PrivateIPAddress)
233-
if natRule == nil {
234-
err = driver.ensurePublicIPAvailable()
235-
if err != nil {
236-
return nil, err
237-
}
238-
239-
natRuleID, err := client.AddNATRule(driver.NetworkDomainID, driver.PrivateIPAddress, nil)
240-
if err != nil {
241-
return nil, err
242-
}
243-
natRule, err = client.GetNATRule(natRuleID)
244-
if err != nil {
245-
return nil, err
246-
}
247-
if natRule == nil {
248-
return nil, fmt.Errorf("Failed to retrieve newly-created NAT rule '%s' for server '%s'", natRuleID, driver.MachineName)
249-
}
250-
} else {
251-
log.Debugf("NAT rule already exists (Id = '%s').", natRule.ID)
252-
}
253-
254-
driver.IPAddress = natRule.ExternalIPAddress
255-
256-
log.Debugf("Created NAT rule '%s' for server '%s' (Ext:'%s' -> Int:'%s').",
257-
driver.NATRuleID,
258-
driver.MachineName,
259-
driver.IPAddress,
260-
driver.PrivateIPAddress,
261-
)
229+
driver.IPAddress = driver.PrivateIPAddress // NAT rule not created yet.
262230

263231
return server, nil
264232
}
@@ -297,39 +265,111 @@ func (driver *Driver) buildDeploymentConfiguration() (deploymentConfiguration co
297265
return
298266
}
299267

300-
// Ensure that at least one public IP address is available in the target network domain.
301-
func (driver *Driver) ensurePublicIPAvailable() error {
302-
if driver.NetworkDomainID == "" {
303-
return errors.New("Network domain has not been resolved.")
268+
// Has a NAT rule been created for the server?
269+
func (driver *Driver) isNATRuleCreated() bool {
270+
return driver.NATRuleID != ""
271+
}
272+
273+
// Create a NAT rule to expose the server.
274+
func (driver *Driver) createNATRuleForServer() error {
275+
if !driver.isServerCreated() {
276+
return errors.New("Server has not been created")
304277
}
305278

306-
log.Debugf("Verifying that network domain '%s' has a public IP available for server '%s'...", driver.NetworkDomainName, driver.MachineName)
279+
if driver.isNATRuleCreated() {
280+
return fmt.Errorf("NAT rule '%s' has already been created for server '%s'", driver.NATRuleID, driver.MachineName)
281+
}
282+
283+
log.Debugf("Creating NAT rule for server '%s' ('%s')...", driver.MachineName, driver.PrivateIPAddress)
284+
285+
natRule, err := driver.getExistingNATRuleByInternalIP(driver.PrivateIPAddress)
286+
if natRule == nil {
287+
err = driver.ensurePublicIPAvailable()
288+
if err != nil {
289+
return err
290+
}
291+
292+
client, err := driver.getCloudControlClient()
293+
if err != nil {
294+
return err
295+
}
296+
297+
driver.NATRuleID, err = client.AddNATRule(driver.NetworkDomainID, driver.PrivateIPAddress, nil)
298+
if err != nil {
299+
return err
300+
}
301+
natRule, err = client.GetNATRule(driver.NATRuleID)
302+
if err != nil {
303+
return err
304+
}
305+
if natRule == nil {
306+
return fmt.Errorf("Failed to retrieve newly-created NAT rule '%s' for server '%s'", driver.NATRuleID, driver.MachineName)
307+
}
308+
309+
log.Debugf("Created NAT rule '%s' for server '%s'", driver.NATRuleID)
310+
} else {
311+
driver.NATRuleID = natRule.ID
312+
313+
log.Debugf("NAT rule already exists (Id = '%s').", driver.NATRuleID)
314+
}
315+
316+
driver.IPAddress = natRule.ExternalIPAddress
317+
318+
log.Debugf("Created NAT rule '%s' for server '%s' (Ext:'%s' -> Int:'%s').",
319+
driver.NATRuleID,
320+
driver.MachineName,
321+
driver.IPAddress,
322+
driver.PrivateIPAddress,
323+
)
324+
325+
return nil
326+
}
327+
328+
// Delete the the server's NAT rule (if any).
329+
func (driver *Driver) deleteNATRuleForServer() error {
330+
if !driver.isServerCreated() {
331+
return errors.New("Server has not been created")
332+
}
333+
334+
if !driver.isNATRuleCreated() {
335+
log.Debugf("Not deleting NAT rule for server '%s' (no NAT rule was created for it).")
336+
337+
return nil
338+
}
339+
340+
log.Debugf("Deleting NAT rule '%s' for server '%s' (Ext:'%s' -> Int:'%s')...", driver.NATRuleID, driver.MachineName, driver.IPAddress, driver.PrivateIPAddress)
307341

308342
client, err := driver.getCloudControlClient()
309343
if err != nil {
310344
return err
311345
}
312346

313-
availableIPs, err := client.GetAvailablePublicIPAddresses(driver.NetworkDomainID)
347+
natRule, err := client.GetNATRule(driver.NATRuleID)
314348
if err != nil {
315349
return err
316350
}
351+
if natRule == nil {
352+
log.Debugf("NAT rule '%s' not found; will treat it as already deleted.")
317353

318-
if len(availableIPs) == 0 {
319-
log.Debugf("There are no available public IPs in network domain '%s'; a new block of public IPs will be allocated.", driver.NetworkDomainID)
354+
driver.NATRuleID = ""
320355

321-
blockID, err := client.AddPublicIPBlock(driver.NetworkDomainID)
322-
if err != nil {
323-
return err
324-
}
356+
return nil
357+
}
325358

326-
log.Debugf("Allocated new public IP block '%s'.", blockID)
359+
err = client.DeleteNATRule(driver.NATRuleID)
360+
if err != nil {
361+
return err
327362
}
328363

364+
log.Debugf("Deleted NAT rule '%s'.", driver.NATRuleID)
365+
366+
driver.NATRuleID = ""
367+
driver.IPAddress = driver.PrivateIPAddress
368+
329369
return nil
330370
}
331371

332-
// Find the existing NAT rule (if any) for the specified internal IPv4 address.
372+
// Find the existing NAT rule (if any) that forwards IPv4 traffic to specified internal address.
333373
func (driver *Driver) getExistingNATRuleByInternalIP(internalIPAddress string) (*compute.NATRule, error) {
334374
if driver.NetworkDomainID == "" {
335375
return nil, errors.New("Network domain has not been resolved.")
@@ -362,3 +402,40 @@ func (driver *Driver) getExistingNATRuleByInternalIP(internalIPAddress string) (
362402

363403
return nil, nil
364404
}
405+
406+
// Ensure that at least one public IP address is available in the target network domain.
407+
func (driver *Driver) ensurePublicIPAvailable() error {
408+
if driver.NetworkDomainID == "" {
409+
return errors.New("Network domain has not been resolved.")
410+
}
411+
412+
log.Debugf("Verifying that network domain '%s' has a public IP available for server '%s'...", driver.NetworkDomainName, driver.MachineName)
413+
414+
client, err := driver.getCloudControlClient()
415+
if err != nil {
416+
return err
417+
}
418+
419+
availableIPs, err := client.GetAvailablePublicIPAddresses(driver.NetworkDomainID)
420+
if err != nil {
421+
return err
422+
}
423+
424+
if len(availableIPs) == 0 {
425+
log.Debugf("There are no available public IPs in network domain '%s'; a new block of public IPs will be allocated.", driver.NetworkDomainID)
426+
427+
blockID, err := client.AddPublicIPBlock(driver.NetworkDomainID)
428+
if err != nil {
429+
return err
430+
}
431+
432+
log.Debugf("Allocated new public IP block '%s'.", blockID)
433+
}
434+
435+
return nil
436+
}
437+
438+
// Has a firewall rule been created to allow inbound SSH for the server?
439+
func (driver *Driver) isSSHFirewallRuleCreated() bool {
440+
return driver.SSHFirewallRuleID != ""
441+
}

driver.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ type Driver struct {
6464
// The initial password used to authenticate to target machines when installing the SSH key.
6565
SSHBootstrapPassword string
6666

67+
// Create a firewall rule to allow SSH access to the taret server?
68+
CreateSSHFirewallRule bool
69+
70+
// The Id of the firewall rule (if any) created for inbound SSH access to the target server.
71+
SSHFirewallRuleID string
72+
6773
// The CloudControl API client.
6874
client *compute.Client
6975
}
@@ -129,6 +135,10 @@ func (driver *Driver) GetCreateFlags() []mcnflag.Flag {
129135
Usage: "The initial SSH password used to bootstrap SSH key authentication",
130136
Value: "",
131137
},
138+
mcnflag.BoolFlag{
139+
Name: "ddcloud-create-ssh-firewall-rule",
140+
Usage: "Create a firewall rule to allow SSH access to the taret server? Default: false",
141+
},
132142
}
133143
}
134144

@@ -154,6 +164,8 @@ func (driver *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
154164

155165
driver.SSHBootstrapPassword = flags.String("ddcloud-ssh-bootstrap-password")
156166

167+
driver.CreateSSHFirewallRule = flags.Bool("ddcloud-create-ssh-firewall-rule")
168+
157169
log.Debugf("docker-machine-driver-ddcloud %s", DriverVersion)
158170

159171
return nil
@@ -219,9 +231,13 @@ func (driver *Driver) Create() error {
219231
return err
220232
}
221233

222-
log.Infof("Server '%s' has private IP '%s'.", driver.MachineName, driver.IPAddress)
234+
err = driver.createNATRuleForServer()
235+
if err != nil {
236+
return err
237+
}
223238

224-
// TODO: Create NAT and firewall rules, if required.
239+
log.Infof("Server '%s' has private IP '%s'.", driver.MachineName, driver.PrivateIPAddress)
240+
log.Infof("Server '%s' has public IP '%s'.", driver.MachineName, driver.IPAddress)
225241

226242
log.Infof("Configuring SSH key for server '%s' ('%s')...", driver.MachineName, driver.IPAddress)
227243
err = driver.installSSHKey()

0 commit comments

Comments
 (0)