Merge pull request #5175 from rickard-von-essen/cloudstack-sg

cloudstack: Add support for Security Groups
This commit is contained in:
Matthew Hooker 2017-08-31 12:04:49 -07:00 committed by GitHub
commit 4884efd0d4
6 changed files with 129 additions and 4 deletions

View File

@ -71,6 +71,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
SSHAgentAuth: b.config.Comm.SSHAgentAuth, SSHAgentAuth: b.config.Comm.SSHAgentAuth,
TemporaryKeyPairName: b.config.TemporaryKeypairName, TemporaryKeyPairName: b.config.TemporaryKeypairName,
}, },
&stepCreateSecurityGroup{},
&stepCreateInstance{ &stepCreateInstance{
Ctx: b.config.ctx, Ctx: b.config.ctx,
Debug: b.config.PackerDebug, Debug: b.config.PackerDebug,

View File

@ -28,22 +28,24 @@ type Config struct {
SSLNoVerify bool `mapstructure:"ssl_no_verify"` SSLNoVerify bool `mapstructure:"ssl_no_verify"`
CIDRList []string `mapstructure:"cidr_list"` CIDRList []string `mapstructure:"cidr_list"`
CreateSecurityGroup bool `mapstructure:"create_security_group"`
DiskOffering string `mapstructure:"disk_offering"` DiskOffering string `mapstructure:"disk_offering"`
DiskSize int64 `mapstructure:"disk_size"` DiskSize int64 `mapstructure:"disk_size"`
Expunge bool `mapstructure:"expunge"` Expunge bool `mapstructure:"expunge"`
Hypervisor string `mapstructure:"hypervisor"` Hypervisor string `mapstructure:"hypervisor"`
InstanceName string `mapstructure:"instance_name"` InstanceName string `mapstructure:"instance_name"`
Keypair string `mapstructure:"keypair"` Keypair string `mapstructure:"keypair"`
TemporaryKeypairName string `mapstructure:"temporary_keypair_name"`
Network string `mapstructure:"network"` Network string `mapstructure:"network"`
Project string `mapstructure:"project"` Project string `mapstructure:"project"`
PublicIPAddress string `mapstructure:"public_ip_address"` PublicIPAddress string `mapstructure:"public_ip_address"`
SecurityGroups []string `mapstructure:"security_groups"`
ServiceOffering string `mapstructure:"service_offering"` ServiceOffering string `mapstructure:"service_offering"`
SourceTemplate string `mapstructure:"source_template"`
SourceISO string `mapstructure:"source_iso"` SourceISO string `mapstructure:"source_iso"`
SourceTemplate string `mapstructure:"source_template"`
TemporaryKeypairName string `mapstructure:"temporary_keypair_name"`
UseLocalIPAddress bool `mapstructure:"use_local_ip_address"`
UserData string `mapstructure:"user_data"` UserData string `mapstructure:"user_data"`
UserDataFile string `mapstructure:"user_data_file"` UserDataFile string `mapstructure:"user_data_file"`
UseLocalIPAddress bool `mapstructure:"use_local_ip_address"`
Zone string `mapstructure:"zone"` Zone string `mapstructure:"zone"`
TemplateName string `mapstructure:"template_name"` TemplateName string `mapstructure:"template_name"`
@ -99,7 +101,7 @@ func NewConfig(raws ...interface{}) (*Config, error) {
c.AsyncTimeout = 30 * time.Minute c.AsyncTimeout = 30 * time.Minute
} }
if len(c.CIDRList) == 0 && !c.UseLocalIPAddress { if len(c.CIDRList) == 0 {
c.CIDRList = []string{"0.0.0.0/0"} c.CIDRList = []string{"0.0.0.0/0"}
} }
@ -146,6 +148,10 @@ func NewConfig(raws ...interface{}) (*Config, error) {
errs = packer.MultiErrorAppend(errs, errors.New("a network must be specified")) errs = packer.MultiErrorAppend(errs, errors.New("a network must be specified"))
} }
if c.CreateSecurityGroup && !c.Expunge {
errs = packer.MultiErrorAppend(errs, errors.New("auto creating a temporary security group requires expunge"))
}
if c.ServiceOffering == "" { if c.ServiceOffering == "" {
errs = packer.MultiErrorAppend(errs, errors.New("a service_offering must be specified")) errs = packer.MultiErrorAppend(errs, errors.New("a service_offering must be specified"))
} }

View File

@ -49,6 +49,10 @@ func (s *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction
p.SetKeypair(keypair.(string)) p.SetKeypair(keypair.(string))
} }
if securitygroups, ok := state.GetOk("security_groups"); ok {
p.SetSecuritygroupids(securitygroups.([]string))
}
// If we use an ISO, configure the disk offering. // If we use an ISO, configure the disk offering.
if config.SourceISO != "" { if config.SourceISO != "" {
p.SetDiskofferingid(config.DiskOffering) p.SetDiskofferingid(config.DiskOffering)

View File

@ -0,0 +1,94 @@
package cloudstack
import (
"fmt"
"github.com/hashicorp/packer/common/uuid"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"github.com/xanzy/go-cloudstack/cloudstack"
)
type stepCreateSecurityGroup struct {
tempSG string
}
func (s *stepCreateSecurityGroup) Run(state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*cloudstack.CloudStackClient)
config := state.Get("config").(*Config)
ui := state.Get("ui").(packer.Ui)
if len(config.SecurityGroups) > 0 {
state.Put("security_groups", config.SecurityGroups)
return multistep.ActionContinue
}
if !config.CreateSecurityGroup {
return multistep.ActionContinue
}
ui.Say("Creating temporary Security Group...")
p := client.SecurityGroup.NewCreateSecurityGroupParams(
fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID()),
)
p.SetDescription("Temporary SG created by Packer")
if config.Project != "" {
p.SetProjectid(config.Project)
}
sg, err := client.SecurityGroup.CreateSecurityGroup(p)
if err != nil {
err := fmt.Errorf("Failed to create security group: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
s.tempSG = sg.Id
state.Put("security_groups", []string{sg.Id})
// Create Ingress rule
i := client.SecurityGroup.NewAuthorizeSecurityGroupIngressParams()
i.SetCidrlist(config.CIDRList)
i.SetProtocol("TCP")
i.SetSecuritygroupid(sg.Id)
i.SetStartport(config.Comm.Port())
i.SetEndport(config.Comm.Port())
if config.Project != "" {
i.SetProjectid(config.Project)
}
_, err = client.SecurityGroup.AuthorizeSecurityGroupIngress(i)
if err != nil {
err := fmt.Errorf("Failed to authorize security group ingress rule: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
return multistep.ActionContinue
}
// Cleanup any resources that may have been created during the Run phase.
func (s *stepCreateSecurityGroup) Cleanup(state multistep.StateBag) {
client := state.Get("client").(*cloudstack.CloudStackClient)
config := state.Get("config").(*Config)
ui := state.Get("ui").(packer.Ui)
if s.tempSG == "" {
return
}
ui.Say(fmt.Sprintf("Cleanup temporary security group: %s ...", s.tempSG))
p := client.SecurityGroup.NewDeleteSecurityGroupParams()
p.SetId(s.tempSG)
if config.Project != "" {
p.SetProjectid(config.Project)
}
if _, err := client.SecurityGroup.DeleteSecurityGroup(p); err != nil {
ui.Error(err.Error())
ui.Error(fmt.Sprintf("Error deleting security group: %s. Please destroy it manually.\n", s.tempSG))
}
}

View File

@ -83,6 +83,18 @@ func (s *stepPrepareConfig) Run(state multistep.StateBag) multistep.StepAction {
} }
} }
// Then try to get the SG's UUID's.
if len(config.SecurityGroups) > 0 {
for i := range config.SecurityGroups {
if !isUUID(config.SecurityGroups[i]) {
config.SecurityGroups[i], _, err = client.SecurityGroup.GetSecurityGroupID(config.SecurityGroups[i], cloudstack.WithProject(config.Project))
if err != nil {
errs = packer.MultiErrorAppend(errs, &retrieveErr{"network", config.SecurityGroups[i], err})
}
}
}
}
if !isUUID(config.ServiceOffering) { if !isUUID(config.ServiceOffering) {
config.ServiceOffering, _, err = client.ServiceOffering.GetServiceOfferingID(config.ServiceOffering) config.ServiceOffering, _, err = client.ServiceOffering.GetServiceOfferingID(config.ServiceOffering)
if err != nil { if err != nil {

View File

@ -74,6 +74,11 @@ builder.
connect to the instance. Defaults to `[ "0.0.0.0/0" ]`. Only required connect to the instance. Defaults to `[ "0.0.0.0/0" ]`. Only required
when `use_local_ip_address` is `false`. when `use_local_ip_address` is `false`.
- `create_security_group` (boolean) - If `true` a temporary security group
will be created which allows traffic towards the instance from the
`cidr_list`. This option will be ignored if `security_groups` is also
defined. Requires `expunge` set to `true`. Defaults to `false`.
- `disk_offering` (string) - The name or ID of the disk offering used for the - `disk_offering` (string) - The name or ID of the disk offering used for the
instance. This option is only available (and also required) when using instance. This option is only available (and also required) when using
`source_iso`. `source_iso`.
@ -118,6 +123,9 @@ builder.
connecting any provisioners to. If not provided, a temporary public IP connecting any provisioners to. If not provided, a temporary public IP
address will be associated and released during the Packer run. address will be associated and released during the Packer run.
- `security_groups` (array of strings) - A list of security group IDs or names
to associate the instance with.
- `ssh_agent_auth` (boolean) - If true, the local SSH agent will be used to - `ssh_agent_auth` (boolean) - If true, the local SSH agent will be used to
authenticate connections to the source instance. No temporary keypair will authenticate connections to the source instance. No temporary keypair will
be created, and the values of `ssh_password` and `ssh_private_key_file` will be created, and the values of `ssh_password` and `ssh_private_key_file` will