diff --git a/cloudflare/provider.go b/cloudflare/provider.go index 5dae2005b6..ca8295fe16 100644 --- a/cloudflare/provider.go +++ b/cloudflare/provider.go @@ -25,7 +25,8 @@ func Provider() terraform.ResourceProvider { }, ResourcesMap: map[string]*schema.Resource{ - "cloudflare_record": resourceCloudFlareRecord(), + "cloudflare_record": resourceCloudFlareRecord(), + "cloudflare_page_rule": resourceCloudFlarePageRule(), }, ConfigureFunc: providerConfigure, diff --git a/cloudflare/resource_cloudflare_page_rule.go b/cloudflare/resource_cloudflare_page_rule.go new file mode 100644 index 0000000000..9bbb338eb8 --- /dev/null +++ b/cloudflare/resource_cloudflare_page_rule.go @@ -0,0 +1,139 @@ +package cloudflare + +import ( + "fmt" + "log" + + "github.com/cloudflare/cloudflare-go" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceCloudFlarePageRule() *schema.Resource { + return &schema.Resource{ + Create: resourceCloudFlarePageRuleCreate, + Read: resourceCloudFlarePageRuleRead, + Update: resourceCloudFlarePageRuleUpdate, + Delete: resourceCloudFlarePageRuleDelete, + + SchemaVersion: 1, + Schema: map[string]*schema.Schema{ + "domain": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "targets": &schema.Schema{ + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "url_pattern": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + + "actions": &schema.Schema{ + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "action": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ValidateFunc: validatePageRuleActionID, + }, + + "value": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validatePageRuleActionValue, + }, + }, + }, + }, + + "priority": &schema.Schema{ + Type: schema.TypeInt, + Default: 1, + Optional: true, + }, + + "status": &schema.Schema{ + Type: schema.TypeString, + Default: "active", + Optional: true, + ValidateFunc: validatePageRuleStatus, + }, + }, + } +} + +func resourceCloudFlarePageRuleCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*cloudflare.API) + + targets := d.Get("targets").([]interface{}) + actions := d.Get("actions").([]interface{}) + + newPageRuleTargets := make([]cloudflare.PageRuleTarget, 0, len(targets)) + newPageRuleActions := make([]cloudflare.PageRuleAction, 0, len(actions)) + + for _, target := range targets { + newPageRuleTarget := cloudflare.PageRuleTarget{ + Target: "url", + Constraint: struct { + Operator string `json:"operator"` + Value string `json:"value"` + }{ + Operator: "matches", + Value: target.(schema.Resource).Schema["url_pattern"].Elem.(string), + }, + } + newPageRuleTargets = append(newPageRuleTargets, newPageRuleTarget) + } + + for _, action := range actions { + newPageRuleActions = append(newPageRuleActions, cloudflare.PageRuleAction{ + ID: action.(schema.Resource).Schema["action"].Elem.(string), + Value: action.(schema.Resource).Schema["value"].Elem.(string), + }) + } + + newPageRule := cloudflare.PageRule{ + Targets: newPageRuleTargets, + Actions: newPageRuleActions, + Priority: d.Get("priority").(int), + Status: d.Get("status").(string), + } + + zoneName := d.Get("domain").(string) + + zoneId, err := client.ZoneIDByName(zoneName) + if err != nil { + return fmt.Errorf("Error finding zone %q: %s", zoneName, err) + } + + d.Set("zone_id", zoneId) + log.Printf("[DEBUG] CloudFlare Page Rule create configuration: %#v", newPageRule) + + err = client.CreatePageRule(zoneId, newPageRule) + if err != nil { + return fmt.Errorf("Failed to create page rule: %s", err) + } + + return resourceCloudFlarePageRuleRead(d, meta) +} + +func resourceCloudFlarePageRuleRead(d *schema.ResourceData, meta interface{}) error { + return fmt.Errorf("Page Rule Read not implemented.") +} + +func resourceCloudFlarePageRuleUpdate(d *schema.ResourceData, meta interface{}) error { + return fmt.Errorf("Page Rule Update not implemented.") +} + +func resourceCloudFlarePageRuleDelete(d *schema.ResourceData, meta interface{}) error { + return fmt.Errorf("Page Rule Delete not implemented.") +} diff --git a/cloudflare/validators.go b/cloudflare/validators.go index 696a45f4f0..0e3ec6ca6e 100644 --- a/cloudflare/validators.go +++ b/cloudflare/validators.go @@ -7,44 +7,26 @@ import ( ) // validateRecordType ensures that the cloudflare record type is valid -func validateRecordType(t string, proxied bool) error { - switch t { - case "A": - return nil - case "AAAA": - return nil - case "CNAME": - return nil - case "TXT": - if !proxied { - return nil - } - case "SRV": - if !proxied { - return nil - } - case "LOC": - if !proxied { - return nil - } - case "MX": - if !proxied { - return nil - } - case "NS": - if !proxied { - return nil - } - case "SPF": - if !proxied { - return nil - } - default: - return fmt.Errorf( - `Invalid type %q. Valid types are "A", "AAAA", "CNAME", "TXT", "SRV", "LOC", "MX", "NS" or "SPF"`, t) +func validateRecordType(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + + validTypes := map[string]struct{}{ + "A": {}, + "AAAA": {}, + "CNAME": {}, + "TXT": {}, + "SRV": {}, + "LOC": {}, + "MX": {}, + "NS": {}, + "SPF": {}, } - return fmt.Errorf("Type %q cannot be proxied", t) + if _, ok := validTypes[value]; !ok { + errors = append(errors, fmt.Errorf( + `%q contains an invalid type %q. Valid types are "A", "AAAA", "CNAME", "TXT", "SRV", "LOC", "MX", "NS" or "SPF"`, k, value)) + } + return } // validateRecordName ensures that based on supplied record type, the name content matches @@ -67,3 +49,55 @@ func validateRecordName(t string, value string) error { return nil } + +func validatePageRuleStatus(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + + validStatuses := map[string]struct{}{ + "active": {}, + "paused": {}, + } + + if _, ok := validStatuses[value]; !ok { + errors = append(errors, fmt.Errorf( + `%q contains an invalid status %q. Valid statuses are "active" or "paused"`, k, value)) + } + return +} + +func validatePageRuleActionID(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + + validIDs := map[string]struct{}{ + "always_online": {}, + "always_use_https": {}, + "browser_cache_ttl": {}, + "browser_check": {}, + "cache_level": {}, + "disable_apps": {}, + "disable_performance": {}, + "disable_railgun": {}, + "disable_security": {}, + "edge_cache_ttl": {}, + "email_obfuscation": {}, + "forwarding_url": {}, + "ip_geolocation": {}, + "mirage": {}, + "rocket_loader": {}, + "security_level": {}, + "server_side_exclude": {}, + "smart_errors": {}, + "ssl": {}, + "waf": {}, + } + + if _, ok := validIDs[value]; !ok { + errors = append(errors, fmt.Errorf( + `%q contains an invalid action ID %q. Valid IDs are "always_online", "always_use_https", "browser_cache_ttl", "browser_check", "cache_level", "disable_apps", "disable_performance", "disable_railgun", "disable_security", "edge_cache_ttl", "email_obfuscation", "forwarding_url", "ip_geolocation", "mirage", "rocket_loader", "security_level", "server_side_exclude", "smart_errors", "ssl", or "waf"`, k, value)) + } + return +} + +func validatePageRuleActionValue(v interface{}, k string) (ws []string, errors []error) { + return []string{}, []error{fmt.Errorf("Page Rule action value validation not implemented.")} +} diff --git a/cloudflare/validators_test.go b/cloudflare/validators_test.go index 6e39760997..58f2a508f6 100644 --- a/cloudflare/validators_test.go +++ b/cloudflare/validators_test.go @@ -3,38 +3,36 @@ package cloudflare import "testing" func TestValidateRecordType(t *testing.T) { - validTypes := map[string]bool{ - "A": true, - "AAAA": true, - "CNAME": true, - "TXT": false, - "SRV": false, - "LOC": false, - "MX": false, - "NS": false, - "SPF": false, - } - for k, v := range validTypes { - err := validateRecordType(k, v) - if err != nil { - t.Fatalf("%s should be a valid record type: %s", k, err) - } - } - - invalidTypes := map[string]bool{ - "a": false, - "cName": false, - "txt": false, - "SRv": false, - "foo": false, - "bar": false, - "TXT": true, - "SRV": true, - "SPF": true, - } - for k, v := range invalidTypes { - if err := validateRecordType(k, v); err == nil { - t.Fatalf("%s should be an invalid record type", k) + validTypes := []string{ + "A", + "AAAA", + "CNAME", + "TXT", + "SRV", + "LOC", + "MX", + "NS", + "SPF", + } + for _, v := range validTypes { + _, errors := validateRecordType(v, "type") + if len(errors) != 0 { + t.Fatalf("%q should be a valid record type: %q", v, errors) + } + } + + invalidTypes := []string{ + "a", + "cName", + "txt", + "SRv", + "foo", + "bar", + } + for _, v := range invalidTypes { + _, errors := validateRecordType(v, "type") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid record type", v) } } } @@ -61,3 +59,77 @@ func TestValidateRecordName(t *testing.T) { } } } + +func TestValidatePageRuleStatus(t *testing.T) { + validStatuses := []string{ + "active", + "paused", + } + for _, v := range validStatuses { + _, errors := validatePageRuleStatus(v, "status") + if len(errors) != 0 { + t.Fatalf("%q should be a valid page rule status: %q", v, errors) + } + } + + invalidStatuses := []string{ + "on", + "live", + "yes", + "no", + "true", + "false", + "running", + "stopped", + } + for _, v := range invalidStatuses { + _, errors := validatePageRuleStatus(v, "status") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid page rule status: %q", v, errors) + } + } +} + +func TestValidatePageRuleActionIDs(t *testing.T) { + validActionIDs := []string{ + "always_online", + "always_use_https", + "browser_cache_ttl", + "browser_check", + "cache_level", + "disable_apps", + "disable_performance", + "disable_railgun", + "disable_security", + "edge_cache_ttl", + "email_obfuscation", + "forwarding_url", + "ip_geolocation", + "mirage", + "rocket_loader", + "security_level", + "server_side_exclude", + "smart_errors", + "ssl", + "waf", + } + for _, v := range validActionIDs { + _, errors := validatePageRuleActionID(v, "action") + if len(errors) != 0 { + t.Fatalf("%q should be a valid page rule action: %q", v, errors) + } + } + + invalidActionIDs := []string{ + "foo", + "tls", + "disable_foobar", + "hunter2", + } + for _, v := range invalidActionIDs { + _, errors := validatePageRuleActionID(v, "action") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid page rule action: %q", v, errors) + } + } +}