packer-cn/builder/cloudstack/step_configure_networking.go

258 lines
7.5 KiB
Go

package cloudstack
import (
"context"
"fmt"
"math/rand"
"strings"
"time"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/xanzy/go-cloudstack/cloudstack"
)
type stepSetupNetworking struct {
privatePort int
publicPort int
}
func (s *stepSetupNetworking) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*cloudstack.CloudStackClient)
config := state.Get("config").(*Config)
ui := state.Get("ui").(packersdk.Ui)
ui.Say("Setup networking...")
if config.UseLocalIPAddress {
ui.Message("Using the local IP address...")
state.Put("commPort", config.Comm.Port())
ui.Message("Networking has been setup!")
return multistep.ActionContinue
}
if config.PublicPort != 0 {
s.publicPort = config.PublicPort
} else {
// Generate a random public port used to configure our port forward.
rand.Seed(time.Now().UnixNano())
s.publicPort = 50000 + rand.Intn(10000)
}
state.Put("commPort", s.publicPort)
// Set the currently configured port to be the private port.
s.privatePort = config.Comm.Port()
// 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
state.Put("ipaddress", 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 config.PreventFirewallChanges {
ui.Message("Networking has been setup (without firewall changes)!")
return multistep.ActionContinue
}
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").(packersdk.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 firewall 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
}