254 lines
7.3 KiB
Go
254 lines
7.3 KiB
Go
package cloudstack
|
|
|
|
import (
|
|
"fmt"
|
|
"math/rand"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/hashicorp/packer/packer"
|
|
"github.com/mitchellh/multistep"
|
|
"github.com/xanzy/go-cloudstack/cloudstack"
|
|
)
|
|
|
|
type stepSetupNetworking struct {
|
|
privatePort int
|
|
publicPort int
|
|
}
|
|
|
|
func (s *stepSetupNetworking) Run(state multistep.StateBag) multistep.StepAction {
|
|
client := state.Get("client").(*cloudstack.CloudStackClient)
|
|
config := state.Get("config").(*Config)
|
|
ui := state.Get("ui").(packer.Ui)
|
|
|
|
ui.Say("Setup networking...")
|
|
|
|
if config.UseLocalIPAddress {
|
|
ui.Message("Using the local IP address...")
|
|
ui.Message("Networking has been setup!")
|
|
return multistep.ActionContinue
|
|
}
|
|
|
|
// Generate a random public port used to configure our port forward.
|
|
rand.Seed(time.Now().UnixNano())
|
|
s.publicPort = 50000 + rand.Intn(10000)
|
|
|
|
// Set the currently configured port to be the private port.
|
|
s.privatePort = config.Comm.Port()
|
|
|
|
// Set the SSH or WinRM port to be the randomly generated public port.
|
|
switch config.Comm.Type {
|
|
case "ssh":
|
|
config.Comm.SSHPort = s.publicPort
|
|
case "winrm":
|
|
config.Comm.WinRMPort = s.publicPort
|
|
}
|
|
|
|
// Retrieve the instance ID from the previously saved state.
|
|
instanceID, ok := state.Get("instance_id").(string)
|
|
if !ok || instanceID == "" {
|
|
err := fmt.Errorf("Could not retrieve instance_id from state!")
|
|
state.Put("error", err)
|
|
ui.Error(err.Error())
|
|
return multistep.ActionHalt
|
|
}
|
|
|
|
network, _, err := client.Network.GetNetworkByID(
|
|
config.Network,
|
|
cloudstack.WithProject(config.Project),
|
|
)
|
|
if err != nil {
|
|
err := fmt.Errorf("Failed to retrieve the network object: %s", err)
|
|
state.Put("error", err)
|
|
ui.Error(err.Error())
|
|
return multistep.ActionHalt
|
|
}
|
|
|
|
if config.PublicIPAddress == "" {
|
|
ui.Message("Associating public IP address...")
|
|
p := client.Address.NewAssociateIpAddressParams()
|
|
|
|
if config.Project != "" {
|
|
p.SetProjectid(config.Project)
|
|
}
|
|
|
|
if network.Vpcid != "" {
|
|
p.SetVpcid(network.Vpcid)
|
|
} else {
|
|
p.SetNetworkid(network.Id)
|
|
}
|
|
|
|
p.SetZoneid(config.Zone)
|
|
|
|
// Associate a new public IP address.
|
|
ipAddr, err := client.Address.AssociateIpAddress(p)
|
|
if err != nil {
|
|
err := fmt.Errorf("Failed to associate public IP address: %s", err)
|
|
state.Put("error", err)
|
|
ui.Error(err.Error())
|
|
return multistep.ActionHalt
|
|
}
|
|
|
|
// Set the IP address and it's ID.
|
|
config.PublicIPAddress = ipAddr.Id
|
|
config.hostAddress = ipAddr.Ipaddress
|
|
|
|
// Store the IP address ID.
|
|
state.Put("ip_address_id", ipAddr.Id)
|
|
}
|
|
|
|
ui.Message("Creating port forward...")
|
|
p := client.Firewall.NewCreatePortForwardingRuleParams(
|
|
config.PublicIPAddress,
|
|
s.privatePort,
|
|
"TCP",
|
|
s.publicPort,
|
|
instanceID,
|
|
)
|
|
|
|
// Configure the port forward.
|
|
p.SetNetworkid(network.Id)
|
|
p.SetOpenfirewall(false)
|
|
|
|
// Create the port forward.
|
|
forward, err := client.Firewall.CreatePortForwardingRule(p)
|
|
if err != nil {
|
|
err := fmt.Errorf("Failed to create port forward: %s", err)
|
|
state.Put("error", err)
|
|
ui.Error(err.Error())
|
|
return multistep.ActionHalt
|
|
}
|
|
|
|
// Store the port forward ID.
|
|
state.Put("port_forward_id", forward.Id)
|
|
|
|
if network.Vpcid != "" {
|
|
ui.Message("Creating network ACL rule...")
|
|
|
|
if network.Aclid == "" {
|
|
err := fmt.Errorf("Failed to configure the firewall: no ACL connected to the VPC network")
|
|
state.Put("error", err)
|
|
ui.Error(err.Error())
|
|
return multistep.ActionHalt
|
|
}
|
|
|
|
// Create a new parameter struct.
|
|
p := client.NetworkACL.NewCreateNetworkACLParams("TCP")
|
|
|
|
// Configure the network ACL rule.
|
|
p.SetAclid(network.Aclid)
|
|
p.SetAction("allow")
|
|
p.SetCidrlist(config.CIDRList)
|
|
p.SetStartport(s.privatePort)
|
|
p.SetEndport(s.privatePort)
|
|
p.SetTraffictype("ingress")
|
|
|
|
// Create the network ACL rule.
|
|
aclRule, err := client.NetworkACL.CreateNetworkACL(p)
|
|
if err != nil {
|
|
err := fmt.Errorf("Failed to create network ACL rule: %s", err)
|
|
state.Put("error", err)
|
|
ui.Error(err.Error())
|
|
return multistep.ActionHalt
|
|
}
|
|
|
|
// Store the network ACL rule ID.
|
|
state.Put("network_acl_rule_id", aclRule.Id)
|
|
} else {
|
|
ui.Message("Creating firewall rule...")
|
|
|
|
// Create a new parameter struct.
|
|
p := client.Firewall.NewCreateFirewallRuleParams(config.PublicIPAddress, "TCP")
|
|
|
|
// Configure the firewall rule.
|
|
p.SetCidrlist(config.CIDRList)
|
|
p.SetStartport(s.publicPort)
|
|
p.SetEndport(s.publicPort)
|
|
|
|
fwRule, err := client.Firewall.CreateFirewallRule(p)
|
|
if err != nil {
|
|
err := fmt.Errorf("Failed to create firewall rule: %s", err)
|
|
state.Put("error", err)
|
|
ui.Error(err.Error())
|
|
return multistep.ActionHalt
|
|
}
|
|
|
|
// Store the firewall rule ID.
|
|
state.Put("firewall_rule_id", fwRule.Id)
|
|
}
|
|
|
|
ui.Message("Networking has been setup!")
|
|
return multistep.ActionContinue
|
|
}
|
|
|
|
// Cleanup any resources that may have been created during the Run phase.
|
|
func (s *stepSetupNetworking) Cleanup(state multistep.StateBag) {
|
|
client := state.Get("client").(*cloudstack.CloudStackClient)
|
|
ui := state.Get("ui").(packer.Ui)
|
|
|
|
ui.Say("Cleanup networking...")
|
|
|
|
if fwRuleID, ok := state.Get("firewall_rule_id").(string); ok && fwRuleID != "" {
|
|
// Create a new parameter struct.
|
|
p := client.Firewall.NewDeleteFirewallRuleParams(fwRuleID)
|
|
|
|
ui.Message("Deleting firewal rule...")
|
|
if _, err := client.Firewall.DeleteFirewallRule(p); err != nil {
|
|
// This is a very poor way to be told the ID does no longer exist :(
|
|
if !strings.Contains(err.Error(), fmt.Sprintf(
|
|
"Invalid parameter id value=%s due to incorrect long value format, "+
|
|
"or entity does not exist", fwRuleID)) {
|
|
ui.Error(fmt.Sprintf("Error deleting firewall rule: %s", err))
|
|
}
|
|
}
|
|
}
|
|
|
|
if aclRuleID, ok := state.Get("network_acl_rule_id").(string); ok && aclRuleID != "" {
|
|
// Create a new parameter struct.
|
|
p := client.NetworkACL.NewDeleteNetworkACLParams(aclRuleID)
|
|
|
|
ui.Message("Deleting network ACL rule...")
|
|
if _, err := client.NetworkACL.DeleteNetworkACL(p); err != nil {
|
|
// This is a very poor way to be told the ID does no longer exist :(
|
|
if !strings.Contains(err.Error(), fmt.Sprintf(
|
|
"Invalid parameter id value=%s due to incorrect long value format, "+
|
|
"or entity does not exist", aclRuleID)) {
|
|
ui.Error(fmt.Sprintf("Error deleting network ACL rule: %s", err))
|
|
}
|
|
}
|
|
}
|
|
|
|
if forwardID, ok := state.Get("port_forward_id").(string); ok && forwardID != "" {
|
|
// Create a new parameter struct.
|
|
p := client.Firewall.NewDeletePortForwardingRuleParams(forwardID)
|
|
|
|
ui.Message("Deleting port forward...")
|
|
if _, err := client.Firewall.DeletePortForwardingRule(p); err != nil {
|
|
// This is a very poor way to be told the ID does no longer exist :(
|
|
if !strings.Contains(err.Error(), fmt.Sprintf(
|
|
"Invalid parameter id value=%s due to incorrect long value format, "+
|
|
"or entity does not exist", forwardID)) {
|
|
ui.Error(fmt.Sprintf("Error deleting port forward: %s", err))
|
|
}
|
|
}
|
|
}
|
|
|
|
if ipAddrID, ok := state.Get("ip_address_id").(string); ok && ipAddrID != "" {
|
|
// Create a new parameter struct.
|
|
p := client.Address.NewDisassociateIpAddressParams(ipAddrID)
|
|
|
|
ui.Message("Releasing public IP address...")
|
|
if _, err := client.Address.DisassociateIpAddress(p); err != nil {
|
|
// This is a very poor way to be told the ID does no longer exist :(
|
|
if !strings.Contains(err.Error(), fmt.Sprintf(
|
|
"Invalid parameter id value=%s due to incorrect long value format, "+
|
|
"or entity does not exist", ipAddrID)) {
|
|
ui.Error(fmt.Sprintf("Error releasing public IP address: %s", err))
|
|
}
|
|
}
|
|
}
|
|
|
|
ui.Message("Networking has been cleaned!")
|
|
return
|
|
}
|