terraform/builtin/providers/oneandone/resource_oneandone_firewall...

275 lines
6.2 KiB
Go

package oneandone
import (
"github.com/1and1/oneandone-cloudserver-sdk-go"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
"strings"
)
func resourceOneandOneFirewallPolicy() *schema.Resource {
return &schema.Resource{
Create: resourceOneandOneFirewallCreate,
Read: resourceOneandOneFirewallRead,
Update: resourceOneandOneFirewallUpdate,
Delete: resourceOneandOneFirewallDelete,
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
},
"description": {
Type: schema.TypeString,
Optional: true,
},
"rules": {
Type: schema.TypeList,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"protocol": {
Type: schema.TypeString,
Required: true,
},
"port_from": {
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntBetween(1, 65535),
},
"port_to": {
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntBetween(1, 65535),
},
"source_ip": {
Type: schema.TypeString,
Optional: true,
},
"id": {
Type: schema.TypeString,
Computed: true,
},
},
},
Required: true,
},
},
}
}
func resourceOneandOneFirewallCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
req := oneandone.FirewallPolicyRequest{
Name: d.Get("name").(string),
}
if desc, ok := d.GetOk("description"); ok {
req.Description = desc.(string)
}
req.Rules = getRules(d)
fw_id, fw, err := config.API.CreateFirewallPolicy(&req)
if err != nil {
return err
}
err = config.API.WaitForState(fw, "ACTIVE", 10, config.Retries)
if err != nil {
return err
}
d.SetId(fw_id)
if err != nil {
return err
}
return resourceOneandOneFirewallRead(d, meta)
}
func resourceOneandOneFirewallUpdate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
if d.HasChange("name") || d.HasChange("description") {
fw, err := config.API.UpdateFirewallPolicy(d.Id(), d.Get("name").(string), d.Get("description").(string))
if err != nil {
return err
}
err = config.API.WaitForState(fw, "ACTIVE", 10, config.Retries)
if err != nil {
return err
}
}
if d.HasChange("rules") {
oldR, newR := d.GetChange("rules")
oldValues := oldR.([]interface{})
newValues := newR.([]interface{})
if len(oldValues) > len(newValues) {
diff := difference(oldValues, newValues)
for _, old := range diff {
o := old.(map[string]interface{})
if o["id"] != nil {
old_id := o["id"].(string)
fw, err := config.API.DeleteFirewallPolicyRule(d.Id(), old_id)
if err != nil {
return err
}
err = config.API.WaitForState(fw, "ACTIVE", 10, config.Retries)
if err != nil {
return err
}
}
}
} else {
var rules []oneandone.FirewallPolicyRule
for _, raw := range newValues {
rl := raw.(map[string]interface{})
if rl["id"].(string) == "" {
rule := oneandone.FirewallPolicyRule{
Protocol: rl["protocol"].(string),
}
if rl["port_from"] != nil {
rule.PortFrom = oneandone.Int2Pointer(rl["port_from"].(int))
}
if rl["port_to"] != nil {
rule.PortTo = oneandone.Int2Pointer(rl["port_to"].(int))
}
if rl["source_ip"] != nil {
rule.SourceIp = rl["source_ip"].(string)
}
rules = append(rules, rule)
}
}
if len(rules) > 0 {
fw, err := config.API.AddFirewallPolicyRules(d.Id(), rules)
if err != nil {
return err
}
err = config.API.WaitForState(fw, "ACTIVE", 10, config.Retries)
}
}
}
return resourceOneandOneFirewallRead(d, meta)
}
func resourceOneandOneFirewallRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
fw, err := config.API.GetFirewallPolicy(d.Id())
if err != nil {
if strings.Contains(err.Error(), "404") {
d.SetId("")
return nil
}
return err
}
d.Set("rules", readRules(d, fw.Rules))
d.Set("description", fw.Description)
return nil
}
func resourceOneandOneFirewallDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
fp, err := config.API.DeleteFirewallPolicy(d.Id())
if err != nil {
return err
}
err = config.API.WaitUntilDeleted(fp)
if err != nil {
return err
}
return nil
}
func readRules(d *schema.ResourceData, rules []oneandone.FirewallPolicyRule) interface{} {
rawRules := d.Get("rules").([]interface{})
counter := 0
for _, rR := range rawRules {
if len(rules) > counter {
rawMap := rR.(map[string]interface{})
rawMap["id"] = rules[counter].Id
if rules[counter].SourceIp != "0.0.0.0" {
rawMap["source_ip"] = rules[counter].SourceIp
}
}
counter++
}
return rawRules
}
func getRules(d *schema.ResourceData) []oneandone.FirewallPolicyRule {
var rules []oneandone.FirewallPolicyRule
if raw, ok := d.GetOk("rules"); ok {
rawRules := raw.([]interface{})
for _, raw := range rawRules {
rl := raw.(map[string]interface{})
rule := oneandone.FirewallPolicyRule{
Protocol: rl["protocol"].(string),
}
if rl["port_from"] != nil {
rule.PortFrom = oneandone.Int2Pointer(rl["port_from"].(int))
}
if rl["port_to"] != nil {
rule.PortTo = oneandone.Int2Pointer(rl["port_to"].(int))
}
if rl["source_ip"] != nil {
rule.SourceIp = rl["source_ip"].(string)
}
rules = append(rules, rule)
}
}
return rules
}
func difference(oldV, newV []interface{}) (toreturn []interface{}) {
var (
lenMin int
longest []interface{}
)
// Determine the shortest length and the longest slice
if len(oldV) < len(newV) {
lenMin = len(oldV)
longest = newV
} else {
lenMin = len(newV)
longest = oldV
}
// compare common indeces
for i := 0; i < lenMin; i++ {
if oldV[i] == nil || newV[i] == nil {
continue
}
if oldV[i].(map[string]interface{})["id"] != newV[i].(map[string]interface{})["id"] {
toreturn = append(toreturn, newV) //out += fmt.Sprintf("=>\t%s\t%s\n", oldV[i], newV[i])
}
}
// add indeces not in common
for _, v := range longest[lenMin:] {
//out += fmt.Sprintf("=>\t%s\n", v)
toreturn = append(toreturn, v)
}
return toreturn
}