Skip to content

Commit

Permalink
Fix #10 Add support for additional SMTP User (domain credential) (#17)
Browse files Browse the repository at this point in the history
* fix #10 add fist code for credential support

* Fix create and delete credential

* Fix tests

* Add update and forcenew when change email

* Add import and read

* Replace field email to login

* - get rid of setting MAILGUN_TEST_DOMAIN on credential tests
- test fixes
- minor refactor
- code cleaup

* Fix typo

* avoid password logging

Co-authored-by: Wojciech Gębiś <[email protected]>
Co-authored-by: wgebis <[email protected]>
  • Loading branch information
3 people authored May 18, 2021
1 parent bed3892 commit 40e6f2b
Show file tree
Hide file tree
Showing 9 changed files with 456 additions and 34 deletions.
52 changes: 52 additions & 0 deletions docs/resources/domain_credential.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
page_title: "Mailgun: mailgun_domain"
---

# mailgun\_domain_cedential

Provides a Mailgun domain credential resource. This can be used to create and manage credential in domain of Mailgun.

~> **Note:** Please note that starting of v0.6.1 due to using new Mailgun Client API (v4), there is no possibility to retrieve previously created secrets via API. In order get it worked, it's recommended to mark `password` as ignored under `lifecycle` block. See below.

## Example Usage

```hcl
# Create a new Mailgun credential
resource "mailgun_domain_credential" "foobar" {
domain = "toto.com"
email = "[email protected]"
password = "supersecretpassword1234"
region = "us"
lifecycle {
ignore_changes = [ "password" ]
}
}
```

## Argument Reference

The following arguments are supported:

* `domain` - (Required) The domain to add credential of Mailgun.
* `email` - (Required) The email address to create.
* `password` - (Required) Password for user authentication.
* `region` - (Optional) The region where domain will be created. Default value is `us`.

## Attributes Reference

The following attributes are exported:

* `domain` - The name of the domain.
* `email` - The email address.
* `password` - Password for user authentication.
* `region` - The name of the region.

## Import

Domain credential can be imported using `region:email` via `import` command. Region has to be chosen from `eu` or `us` (when no selection `us` is applied).
Password is always exported to `null`.

```hcl
terraform import mailgun_domain_credential.test us:[email protected]
```
17 changes: 17 additions & 0 deletions mailgun/mailgun_helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package mailgun

import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"strings"
)

func setDefaultRegionForImport(d *schema.ResourceData) {
parts := strings.SplitN(d.Id(), ":", 2)

if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
d.Set("region", "us")
} else {
d.Set("region", parts[0])
d.SetId(parts[1])
}
}
8 changes: 5 additions & 3 deletions mailgun/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package mailgun

import (
"context"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"log"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

Expand All @@ -24,8 +25,9 @@ func Provider() *schema.Provider {
},

ResourcesMap: map[string]*schema.Resource{
"mailgun_domain": resourceMailgunDomain(),
"mailgun_route": resourceMailgunRoute(),
"mailgun_domain": resourceMailgunDomain(),
"mailgun_route": resourceMailgunRoute(),
"mailgun_domain_credential": resourceMailgunCredential(),
},
}

Expand Down
173 changes: 173 additions & 0 deletions mailgun/resource_mailgun_credential.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package mailgun

import (
"context"
"fmt"
"log"
"strings"
"time"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/mailgun/mailgun-go/v4"
)

func resourceMailgunCredential() *schema.Resource {
log.Printf("[DEBUG] resourceMailgunCredential()")

return &schema.Resource{
CreateContext: resourceMailgunCredentialCreate,
Read: resourceMailgunCredentialRead,
Update: resourceMailgunCredentialUpdate,
Delete: resourceMailgunCredentialDelete,
Importer: &schema.ResourceImporter{
StateContext: resourceMailgunCredentialImport,
},

Schema: map[string]*schema.Schema{
"login": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

"password": {
Type: schema.TypeString,
ForceNew: false,
Required: true,
Sensitive: true,
},

"domain": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

"region": {
Type: schema.TypeString,
ForceNew: true,
Optional: true,
Default: "us",
},
},
}
}

func resourceMailgunCredentialImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {

setDefaultRegionForImport(d)

log.Printf("[DEBUG] Import credential for region '%s' and email '%s'", d.Get("region"), d.Id())

return []*schema.ResourceData{d}, nil
}

func resourceMailgunCredentialCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client, errc := meta.(*Config).GetClientForDomain(d.Get("region").(string), d.Get("domain").(string))
if errc != nil {
return diag.FromErr(errc)
}

email := fmt.Sprintf("%s@%s", d.Get("login").(string), d.Get("domain").(string))
password := d.Get("password").(string)

log.Printf("[DEBUG] Credential create configuration: email: %s", email)

err := client.CreateCredential(context.Background(), email, password)

if err != nil {
return diag.FromErr(err)
}

d.SetId(email)

log.Printf("[INFO] Create credential ID: %s", d.Id())

return nil
}

func resourceMailgunCredentialUpdate(d *schema.ResourceData, meta interface{}) error {
client, errc := meta.(*Config).GetClientForDomain(d.Get("region").(string), d.Get("domain").(string))
if errc != nil {
return errc
}

email := fmt.Sprintf("%s@%s", d.Get("login").(string), d.Get("domain").(string))
password := d.Get("password").(string)

log.Printf("[DEBUG] Credential create configuration: email: %s", email)

err := client.ChangeCredentialPassword(context.Background(), email, password)

if err != nil {
return err
}

d.SetId(email)

log.Printf("[INFO] Update credential ID: %s", d.Id())

return nil
}

func resourceMailgunCredentialDelete(d *schema.ResourceData, meta interface{}) error {
client, errc := meta.(*Config).GetClientForDomain(d.Get("region").(string), d.Get("domain").(string))
if errc != nil {
return errc
}

email := fmt.Sprintf("%s@%s", d.Get("login").(string), d.Get("domain").(string))
err := client.DeleteCredential(context.Background(), email)

if err != nil {
return fmt.Errorf("Error deleting credential: %s", err)
}

return nil
}

func resourceMailgunCredentialRead(d *schema.ResourceData, meta interface{}) error {
parts := strings.SplitN(d.Id(), "@", 2)

if len(parts) != 2 {
return fmt.Errorf("The ID of credential '%s' don't contains domain!", d.Id())
}

login := parts[0]
domain := parts[1]

client, errc := meta.(*Config).GetClientForDomain(d.Get("region").(string), domain)
if errc != nil {
return errc
}

log.Printf("[DEBUG] Read credential for region '%s' and email '%s'", d.Get("region"), d.Id())

itCredentials := client.ListCredentials(nil)

ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
defer cancel()

var page []mailgun.Credential

for itCredentials.Next(ctx, &page) {
log.Printf("[DEBUG] Read credential get new page")

for _, c := range page {
if c.Login == d.Id() {
d.Set("login", login)
d.Set("domain", domain)
d.Set("password", c.Password)

return nil
}
}
}

if err := itCredentials.Err(); err != nil {
return err
}

return fmt.Errorf("The credential '%s' not found!", d.Id())
}
Loading

0 comments on commit 40e6f2b

Please sign in to comment.