275 lines
6.2 KiB
Go
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
|
|
}
|