From e3d54cb487d6db133318d2b690f246b641a24775 Mon Sep 17 00:00:00 2001 From: Jack Pearkes Date: Sun, 24 Aug 2014 15:40:54 -0700 Subject: [PATCH 1/5] providers/mailgun: initial commit --- builtin/providers/mailgun/config.go | 34 +++++ builtin/providers/mailgun/provider.go | 43 ++++++ builtin/providers/mailgun/provider_test.go | 67 ++++++++++ .../mailgun/resource_mailgun_domain.go | 126 ++++++++++++++++++ .../mailgun/resource_mailgun_domain_test.go | 116 ++++++++++++++++ 5 files changed, 386 insertions(+) create mode 100644 builtin/providers/mailgun/config.go create mode 100644 builtin/providers/mailgun/provider.go create mode 100644 builtin/providers/mailgun/provider_test.go create mode 100644 builtin/providers/mailgun/resource_mailgun_domain.go create mode 100644 builtin/providers/mailgun/resource_mailgun_domain_test.go diff --git a/builtin/providers/mailgun/config.go b/builtin/providers/mailgun/config.go new file mode 100644 index 0000000000..e58b5fcf38 --- /dev/null +++ b/builtin/providers/mailgun/config.go @@ -0,0 +1,34 @@ +package mailgun + +import ( + "log" + "os" + + "github.com/pearkes/mailgun" +) + +type Config struct { + APIKey string `mapstructure:"api_key"` +} + +// Client() returns a new client for accessing mailgun. +// +func (c *Config) Client() (*mailgun.Client, error) { + + // If we have env vars set (like in the acc) tests, + // we need to override the values passed in here. + if v := os.Getenv("MAILGUN_API_KEY"); v != "" { + c.APIKey = v + } + + // We don't set a domain right away + client, err := mailgun.NewClient(c.APIKey) + + if err != nil { + return nil, err + } + + log.Printf("[INFO] Mailgun Client configured ") + + return client, nil +} diff --git a/builtin/providers/mailgun/provider.go b/builtin/providers/mailgun/provider.go new file mode 100644 index 0000000000..245c6d438c --- /dev/null +++ b/builtin/providers/mailgun/provider.go @@ -0,0 +1,43 @@ +package mailgun + +import ( + "log" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/mitchellh/mapstructure" +) + +// Provider returns a terraform.ResourceProvider. +func Provider() *schema.Provider { + return &schema.Provider{ + Schema: map[string]*schema.Schema{ + "api_key": &schema.Schema{ + Type: schema.TypeString, + Optional: false, + }, + + "public_api_key": &schema.Schema{ + Type: schema.TypeString, + Optional: false, + }, + }, + + ResourcesMap: map[string]*schema.Resource{ + "mailgun_domain": resourceMailgunDomain(), + }, + + ConfigureFunc: providerConfigure, + } +} + +func providerConfigure(d *schema.ResourceData) (interface{}, error) { + var config Config + configRaw := d.Get("").(map[string]interface{}) + if err := mapstructure.Decode(configRaw, &config); err != nil { + return nil, err + } + + log.Println("[INFO] Initializing Mailgun client") + + return config.Client() +} diff --git a/builtin/providers/mailgun/provider_test.go b/builtin/providers/mailgun/provider_test.go new file mode 100644 index 0000000000..85dd603ea2 --- /dev/null +++ b/builtin/providers/mailgun/provider_test.go @@ -0,0 +1,67 @@ +package mailgun + +import ( + "os" + "testing" + + "github.com/hashicorp/terraform/config" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/terraform" + "github.com/pearkes/mailgun" +) + +var testAccProviders map[string]terraform.ResourceProvider +var testAccProvider *schema.Provider + +func init() { + testAccProvider = Provider() + testAccProviders = map[string]terraform.ResourceProvider{ + "mailgun": testAccProvider, + } +} + +func TestProvider(t *testing.T) { + if err := Provider().InternalValidate(); err != nil { + t.Fatalf("err: %s", err) + } +} + +func TestProvider_impl(t *testing.T) { + var _ terraform.ResourceProvider = Provider() +} + +func TestProviderConfigure(t *testing.T) { + var expectedKey string + + if v := os.Getenv("MAILGUN_API_KEY"); v != "" { + expectedKey = v + } else { + expectedKey = "foo" + } + + raw := map[string]interface{}{ + "api_key": expectedKey, + } + + rawConfig, err := config.NewRawConfig(raw) + if err != nil { + t.Fatalf("err: %s", err) + } + + rp := Provider() + err = rp.Configure(terraform.NewResourceConfig(rawConfig)) + if err != nil { + t.Fatalf("err: %s", err) + } + + config := rp.Meta().(*mailgun.Client) + if config.ApiKey != expectedKey { + t.Fatalf("bad: %#v", config) + } +} + +func testAccPreCheck(t *testing.T) { + if v := os.Getenv("MAILGUN_API_KEY"); v == "" { + t.Fatal("MAILGUN_API_KEY must be set for acceptance tests") + } +} diff --git a/builtin/providers/mailgun/resource_mailgun_domain.go b/builtin/providers/mailgun/resource_mailgun_domain.go new file mode 100644 index 0000000000..8b60414157 --- /dev/null +++ b/builtin/providers/mailgun/resource_mailgun_domain.go @@ -0,0 +1,126 @@ +package mailgun + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/pearkes/mailgun" +) + +func resourceMailgunDomain() *schema.Resource { + return &schema.Resource{ + Create: resourceMailgunDomainCreate, + Read: resourceMailgunDomainRead, + Delete: resourceMailgunDomainDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "spam_action": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + ForceNew: true, + Optional: true, + }, + + "smtp_password": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + ForceNew: true, + Optional: true, + }, + + "smtp_login": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Optional: true, + }, + + "wildcard": &schema.Schema{ + Type: schema.TypeBool, + Computed: true, + ForceNew: true, + Optional: true, + }, + }, + } +} + +func resourceMailgunDomainCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*mailgun.Client) + + opts := mailgun.CreateDomain{} + + opts.Name = d.Get("name").(string) + opts.SmtpPassword = d.Get("smtp_password").(string) + opts.SpamAction = d.Get("spam_action").(string) + opts.Wildcard = d.Get("wildcard").(bool) + + log.Printf("[DEBUG] Domain create configuration: %#v", opts) + + domain, err := client.CreateDomain(&opts) + + if err != nil { + return err + } + + d.SetId(domain) + + log.Printf("[INFO] Domain ID: %s", d.Id()) + + // Retrieve and update state of domain + _, err = resource_mailgin_domain_retrieve(d.Id(), client, d) + + if err != nil { + return err + } + + return nil +} + +func resourceMailgunDomainDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*mailgun.Client) + + log.Printf("[INFO] Deleting Domain: %s", d.Id()) + + // Destroy the domain + err := client.DestroyDomain(d.Id()) + if err != nil { + return fmt.Errorf("Error deleting domain: %s", err) + } + + return nil +} + +func resourceMailgunDomainRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*mailgun.Client) + + _, err := resource_mailgin_domain_retrieve(d.Id(), client, d) + + if err != nil { + return err + } + + return nil +} + +func resource_mailgin_domain_retrieve(id string, client *mailgun.Client, d *schema.ResourceData) (*mailgun.Domain, error) { + domain, err := client.RetrieveDomain(id) + + if err != nil { + return nil, fmt.Errorf("Error retrieving domain: %s", err) + } + + d.Set("name", domain.Name) + d.Set("smtp_password", domain.SmtpPassword) + d.Set("smtp_login", domain.SmtpLogin) + d.Set("wildcard", domain.Wildcard) + d.Set("spam_action", domain.SpamAction) + + return &domain, nil +} diff --git a/builtin/providers/mailgun/resource_mailgun_domain_test.go b/builtin/providers/mailgun/resource_mailgun_domain_test.go new file mode 100644 index 0000000000..09671e3e56 --- /dev/null +++ b/builtin/providers/mailgun/resource_mailgun_domain_test.go @@ -0,0 +1,116 @@ +package mailgun + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/pearkes/mailgun" +) + +func TestAccMailgunDomain_Basic(t *testing.T) { + var domain mailgun.Domain + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckMailgunDomainDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckMailgunDomainConfig_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckMailgunDomainExists("mailgun_domain.foobar", &domain), + testAccCheckMailgunDomainAttributes(&domain), + resource.TestCheckResourceAttr( + "mailgun_domain.foobar", "name", "terraform.example.com"), + resource.TestCheckResourceAttr( + "mailgun_domain.foobar", "spam_action", "disabled"), + resource.TestCheckResourceAttr( + "mailgun_domain.foobar", "smtp_password", "foobar"), + resource.TestCheckResourceAttr( + "mailgun_domain.foobar", "wildcard", "true"), + ), + }, + }, + }) +} + +func testAccCheckMailgunDomainDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*mailgun.Client) + + for _, rs := range s.Resources { + if rs.Type != "mailgun_domain" { + continue + } + + _, err := client.RetrieveDomain(rs.ID) + + if err == nil { + return fmt.Errorf("Domain still exists") + } + } + + return nil +} + +func testAccCheckMailgunDomainAttributes(Domain *mailgun.Domain) resource.TestCheckFunc { + return func(s *terraform.State) error { + + if Domain.Name != "terraform.example.com" { + return fmt.Errorf("Bad name: %s", Domain.Name) + } + + if Domain.SpamAction != "disabled" { + return fmt.Errorf("Bad spam_action: %s", Domain.SpamAction) + } + + if Domain.Wildcard != true { + return fmt.Errorf("Bad wildcard: %s", Domain.SpamAction) + } + + if Domain.SmtpPassword != "foobar" { + return fmt.Errorf("Bad smtp_password: %s", Domain.SmtpPassword) + } + + return nil + } +} + +func testAccCheckMailgunDomainExists(n string, Domain *mailgun.Domain) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.Resources[n] + + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.ID == "" { + return fmt.Errorf("No Domain ID is set") + } + + client := testAccProvider.Meta().(*mailgun.Client) + + foundDomain, err := client.RetrieveDomain(rs.ID) + + if err != nil { + return err + } + + if foundDomain.Name != rs.ID { + return fmt.Errorf("Domain not found") + } + + *Domain = foundDomain + + return nil + } +} + +const testAccCheckMailgunDomainConfig_basic = ` +resource "mailgun_domain" "foobar" { + name = "terraform.example.com" + spam_action = "disabled" + smtp_password = "foobar" + wildcard = true +}` From 872f852a5952f4817083eb58106da854277b7a8b Mon Sep 17 00:00:00 2001 From: Jack Pearkes Date: Sun, 24 Aug 2014 15:51:39 -0700 Subject: [PATCH 2/5] website: add docs for mailgun provider --- .../providers/mailgun/index.html.markdown | 34 +++++++++++++ .../providers/mailgun/r/domain.html.markdown | 48 +++++++++++++++++++ website/source/layouts/docs.erb | 4 ++ website/source/layouts/mailgun.erb | 26 ++++++++++ website/source/stylesheets/_docs.less | 3 +- website/source/stylesheets/main.css | 3 ++ 6 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 website/source/docs/providers/mailgun/index.html.markdown create mode 100644 website/source/docs/providers/mailgun/r/domain.html.markdown create mode 100644 website/source/layouts/mailgun.erb diff --git a/website/source/docs/providers/mailgun/index.html.markdown b/website/source/docs/providers/mailgun/index.html.markdown new file mode 100644 index 0000000000..13c89923e5 --- /dev/null +++ b/website/source/docs/providers/mailgun/index.html.markdown @@ -0,0 +1,34 @@ +--- +layout: "mailgun" +page_title: "Provider: Mailgun" +sidebar_current: "docs-mailgun-index" +--- + +# Provider + +The Mailgun provider is used to interact with the +resources supported by Mailgun. The provider needs to be configured +with the proper credentials before it can be used. + +Use the navigation to the left to read about the available resources. + +## Example Usage + +``` +# Configure the Mailgun provider +provider "mailgun" { + api_key = "${var.mailgun_api_key}" +} + +# Create a new domain +resource "mailgun_domain" "default" { + ... +} +``` + +## Argument Reference + +The following arguments are supported: + +* `api_key` - (Required) Mailgun API key + diff --git a/website/source/docs/providers/mailgun/r/domain.html.markdown b/website/source/docs/providers/mailgun/r/domain.html.markdown new file mode 100644 index 0000000000..4b5326bbed --- /dev/null +++ b/website/source/docs/providers/mailgun/r/domain.html.markdown @@ -0,0 +1,48 @@ +--- +layout: "mailgun" +page_title: "Mailgun: mailgun_domain" +sidebar_current: "docs-mailgun-resource-domain" +--- + +# mailgun\_domain + +Provides a Mailgun App resource. This can be used to +create and manage applications on Mailgun. + +## Example Usage + +``` +# Create a new mailgun domain +resource "mailgun_app" "default" { + name = "test.example.com" +} + +# Create SMTP Credentials +resource "mailgun_domain" "default" { + name = "test.example.com" + spam_action = "disabled" + smtp_password = "foobar" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The domain to add to Mailgun +* `smtp_password` - (Optional) Password for SMTP authentication +* `spam_action` - (Optional) `disabled` or `tag` Disable, no spam + filtering will occur for inbound messages. Tag, messages + will be tagged wtih a spam header. +* `wildcard` - (Optional) Boolean determines whether + the domain will accept email for sub-domains. + +## Attributes Reference + +The following attributes are exported: + +* `name` - The name of the domain. +* `smtp_login` - The login email for the SMTP server. +* `smtp_password` - The password to the SMTP server. +* `wildcard` - Whether or not the domain will accept email for sub-domains. +* `spam_action` - The spam filtering setting. diff --git a/website/source/layouts/docs.erb b/website/source/layouts/docs.erb index 24ad5905fb..efa809b5cc 100644 --- a/website/source/layouts/docs.erb +++ b/website/source/layouts/docs.erb @@ -99,6 +99,10 @@ > Heroku + + > + Mailgun + diff --git a/website/source/layouts/mailgun.erb b/website/source/layouts/mailgun.erb new file mode 100644 index 0000000000..36b650e885 --- /dev/null +++ b/website/source/layouts/mailgun.erb @@ -0,0 +1,26 @@ +<% wrap_layout :inner do %> + <% content_for :sidebar do %> + + <% end %> + + <%= yield %> + <% end %> diff --git a/website/source/stylesheets/_docs.less b/website/source/stylesheets/_docs.less index 38c44ff6c5..0f9d614cf6 100755 --- a/website/source/stylesheets/_docs.less +++ b/website/source/stylesheets/_docs.less @@ -10,6 +10,7 @@ body.layout-consul, body.layout-dnsimple, body.layout-cloudflare, body.layout-heroku, +body.layout-mailgun, body.layout-digitalocean, body.layout-aws, body.layout-docs, @@ -17,7 +18,7 @@ body.layout-inner, body.layout-downloads, body.layout-intro{ background: @light-black url('../images/sidebar-wire.png') left 62px no-repeat; - + >.container{ .col-md-8[role=main]{ min-height: 800px; diff --git a/website/source/stylesheets/main.css b/website/source/stylesheets/main.css index 55a83ed6f1..2f2593354d 100644 --- a/website/source/stylesheets/main.css +++ b/website/source/stylesheets/main.css @@ -1618,6 +1618,7 @@ body.layout-consul, body.layout-dnsimple, body.layout-cloudflare, body.layout-heroku, +body.layout-mailgun, body.layout-digitalocean, body.layout-aws, body.layout-docs, @@ -1630,6 +1631,7 @@ body.layout-consul > .container .col-md-8[role=main], body.layout-dnsimple > .container .col-md-8[role=main], body.layout-cloudflare > .container .col-md-8[role=main], body.layout-heroku > .container .col-md-8[role=main], +body.layout-mailgun > .container .col-md-8[role=main], body.layout-digitalocean > .container .col-md-8[role=main], body.layout-aws > .container .col-md-8[role=main], body.layout-docs > .container .col-md-8[role=main], @@ -1643,6 +1645,7 @@ body.layout-consul > .container .col-md-8[role=main] > div, body.layout-dnsimple > .container .col-md-8[role=main] > div, body.layout-cloudflare > .container .col-md-8[role=main] > div, body.layout-heroku > .container .col-md-8[role=main] > div, +body.layout-mailgun > .container .col-md-8[role=main] > div, body.layout-digitalocean > .container .col-md-8[role=main] > div, body.layout-aws > .container .col-md-8[role=main] > div, body.layout-docs > .container .col-md-8[role=main] > div, From b71ff288717b228585a6aa7f2ca668662503637f Mon Sep 17 00:00:00 2001 From: Jack Pearkes Date: Sun, 24 Aug 2014 16:00:17 -0700 Subject: [PATCH 3/5] providers/mailgun: smtp_password is required --- builtin/providers/mailgun/resource_mailgun_domain.go | 2 +- builtin/providers/mailgun/resource_mailgun_domain_test.go | 2 +- website/source/docs/providers/mailgun/r/domain.html.markdown | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/builtin/providers/mailgun/resource_mailgun_domain.go b/builtin/providers/mailgun/resource_mailgun_domain.go index 8b60414157..59f821fc50 100644 --- a/builtin/providers/mailgun/resource_mailgun_domain.go +++ b/builtin/providers/mailgun/resource_mailgun_domain.go @@ -32,7 +32,7 @@ func resourceMailgunDomain() *schema.Resource { Type: schema.TypeString, Computed: true, ForceNew: true, - Optional: true, + Required: true, }, "smtp_login": &schema.Schema{ diff --git a/builtin/providers/mailgun/resource_mailgun_domain_test.go b/builtin/providers/mailgun/resource_mailgun_domain_test.go index 09671e3e56..c25fe230f4 100644 --- a/builtin/providers/mailgun/resource_mailgun_domain_test.go +++ b/builtin/providers/mailgun/resource_mailgun_domain_test.go @@ -66,7 +66,7 @@ func testAccCheckMailgunDomainAttributes(Domain *mailgun.Domain) resource.TestCh } if Domain.Wildcard != true { - return fmt.Errorf("Bad wildcard: %s", Domain.SpamAction) + return fmt.Errorf("Bad wildcard: %s", Domain.Wildcard) } if Domain.SmtpPassword != "foobar" { diff --git a/website/source/docs/providers/mailgun/r/domain.html.markdown b/website/source/docs/providers/mailgun/r/domain.html.markdown index 4b5326bbed..6ff38be01f 100644 --- a/website/source/docs/providers/mailgun/r/domain.html.markdown +++ b/website/source/docs/providers/mailgun/r/domain.html.markdown @@ -13,8 +13,9 @@ create and manage applications on Mailgun. ``` # Create a new mailgun domain -resource "mailgun_app" "default" { +resource "mailgun_domain" "default" { name = "test.example.com" + smtp_password = "foobar" } # Create SMTP Credentials @@ -30,7 +31,7 @@ resource "mailgun_domain" "default" { The following arguments are supported: * `name` - (Required) The domain to add to Mailgun -* `smtp_password` - (Optional) Password for SMTP authentication +* `smtp_password` - (Required) Password for SMTP authentication * `spam_action` - (Optional) `disabled` or `tag` Disable, no spam filtering will occur for inbound messages. Tag, messages will be tagged wtih a spam header. From 9dfc7ad49dc368c1ac876d278a68ac04f1bc1926 Mon Sep 17 00:00:00 2001 From: Jack Pearkes Date: Sun, 24 Aug 2014 16:03:17 -0700 Subject: [PATCH 4/5] providers/mailgun: clarify domain docs --- .../source/docs/providers/mailgun/r/domain.html.markdown | 6 ------ 1 file changed, 6 deletions(-) diff --git a/website/source/docs/providers/mailgun/r/domain.html.markdown b/website/source/docs/providers/mailgun/r/domain.html.markdown index 6ff38be01f..d060b19b32 100644 --- a/website/source/docs/providers/mailgun/r/domain.html.markdown +++ b/website/source/docs/providers/mailgun/r/domain.html.markdown @@ -13,12 +13,6 @@ create and manage applications on Mailgun. ``` # Create a new mailgun domain -resource "mailgun_domain" "default" { - name = "test.example.com" - smtp_password = "foobar" -} - -# Create SMTP Credentials resource "mailgun_domain" "default" { name = "test.example.com" spam_action = "disabled" From b79834e859b4830b47b219458815c5046e9a363f Mon Sep 17 00:00:00 2001 From: Jack Pearkes Date: Mon, 25 Aug 2014 10:08:40 -0700 Subject: [PATCH 5/5] providers/mailgun: smtp_password is required, no need for pub api key --- builtin/providers/mailgun/config.go | 2 +- builtin/providers/mailgun/provider.go | 7 +------ builtin/providers/mailgun/resource_mailgun_domain.go | 1 - 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/builtin/providers/mailgun/config.go b/builtin/providers/mailgun/config.go index e58b5fcf38..4c9d6d2799 100644 --- a/builtin/providers/mailgun/config.go +++ b/builtin/providers/mailgun/config.go @@ -8,7 +8,7 @@ import ( ) type Config struct { - APIKey string `mapstructure:"api_key"` + APIKey string `mapstructure:"api_key"` } // Client() returns a new client for accessing mailgun. diff --git a/builtin/providers/mailgun/provider.go b/builtin/providers/mailgun/provider.go index 245c6d438c..25d29b343b 100644 --- a/builtin/providers/mailgun/provider.go +++ b/builtin/providers/mailgun/provider.go @@ -13,12 +13,7 @@ func Provider() *schema.Provider { Schema: map[string]*schema.Schema{ "api_key": &schema.Schema{ Type: schema.TypeString, - Optional: false, - }, - - "public_api_key": &schema.Schema{ - Type: schema.TypeString, - Optional: false, + Required: true, }, }, diff --git a/builtin/providers/mailgun/resource_mailgun_domain.go b/builtin/providers/mailgun/resource_mailgun_domain.go index 59f821fc50..e4b9cc6cb5 100644 --- a/builtin/providers/mailgun/resource_mailgun_domain.go +++ b/builtin/providers/mailgun/resource_mailgun_domain.go @@ -30,7 +30,6 @@ func resourceMailgunDomain() *schema.Resource { "smtp_password": &schema.Schema{ Type: schema.TypeString, - Computed: true, ForceNew: true, Required: true, },