-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(member): implement new members resources
Creates a new members resource to allow terraform to manage the members of their organizations. This new resource allows configuring the roles and groups as well! Ref: LOG-12857 Signed-off-by: Jacob Hull <[email protected]> Co-authored-by: Keerthika Damodaraswamy <[email protected]>
- Loading branch information
1 parent
d5d6074
commit 224b3f2
Showing
7 changed files
with
333 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
# Resource: `logdna_member` | ||
|
||
This resource allows you to manage the team members of an organization. | ||
|
||
## Example | ||
|
||
```hcl | ||
provider "logdna" { | ||
servicekey = "xxxxxxxxxxxxxxxxxxxxxxxx" | ||
} | ||
resource "logdna_member" "admin_user" { | ||
email = "[email protected]" | ||
role = "admin" | ||
} | ||
``` | ||
|
||
## Argument Reference | ||
|
||
The following arguments are supported: | ||
|
||
- `email`: **string** _(Required)_ The email of the user. If a user with that email does not exist, they will be invited to join Mezmo. | ||
- `role`: **string** _(Required)_ The role of this user. Can be one of `admin`, `member`, and `readonly`. `owner` roles can only be changed through the UI. | ||
- `groups`: **string[]** _(Optional)_ The id of the groups the user belongs to. Defaults to an empty list. | ||
|
||
## Attributes Reference | ||
|
||
In addition to all the arguments above, the following attributes are exported: | ||
|
||
- `email`: **string** The email of the member. | ||
- `role`: **string** The role of the member. | ||
- `groups`: **string[]** The groups the member belongs to. | ||
|
||
## Import | ||
|
||
A member can be imported using their `email`, e.g., | ||
|
||
```sh | ||
$ terraform import logdna_member.user1 <email> | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
package logdna | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"log" | ||
"strings" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" | ||
) | ||
|
||
func resourceMemberCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { | ||
var diags diag.Diagnostics | ||
pc := m.(*providerConfig) | ||
|
||
member := memberRequest{} | ||
|
||
if diags = member.CreateRequestBody(d); diags.HasError() { | ||
return diags | ||
} | ||
|
||
req := newRequestConfig( | ||
pc, | ||
"POST", | ||
"/v1/config/members", | ||
member, | ||
) | ||
body, err := req.MakeRequest() | ||
log.Printf("[DEBUG] %s %s, payload is: %s", req.method, req.apiURL, body) | ||
|
||
if err != nil { | ||
return diag.FromErr(err) | ||
} | ||
|
||
createdMember := memberResponse{} | ||
err = json.Unmarshal(body, &createdMember) | ||
if err != nil { | ||
return diag.FromErr(err) | ||
} | ||
log.Printf("[DEBUG] After %s member, the created member is %+v", req.method, createdMember) | ||
|
||
d.SetId(createdMember.Email) | ||
|
||
return resourceMemberRead(ctx, d, m) | ||
} | ||
|
||
func resourceMemberRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { | ||
var diags diag.Diagnostics | ||
|
||
pc := m.(*providerConfig) | ||
memberID := d.Id() | ||
|
||
req := newRequestConfig( | ||
pc, | ||
"GET", | ||
fmt.Sprintf("/v1/config/members/%s", memberID), | ||
nil, | ||
) | ||
|
||
body, err := req.MakeRequest() | ||
|
||
log.Printf("[DEBUG] GET member raw response body %s\n", body) | ||
if err != nil { | ||
diags = append(diags, diag.Diagnostic{ | ||
Severity: diag.Error, | ||
Summary: "Cannot read the remote member resource", | ||
Detail: err.Error(), | ||
}) | ||
return diags | ||
} | ||
|
||
member := memberResponse{} | ||
err = json.Unmarshal(body, &member) | ||
if err != nil { | ||
diags = append(diags, diag.Diagnostic{ | ||
Severity: diag.Error, | ||
Summary: "Cannot unmarshal response from the remote member resource", | ||
Detail: err.Error(), | ||
}) | ||
return diags | ||
} | ||
log.Printf("[DEBUG] The GET member structure is as follows: %+v\n", member) | ||
|
||
// Top level keys can be set directly | ||
appendError(d.Set("email", member.Email), &diags) | ||
appendError(d.Set("role", member.Role), &diags) | ||
appendError(d.Set("groups", member.Groups), &diags) | ||
|
||
return diags | ||
} | ||
|
||
func resourceMemberUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { | ||
var diags diag.Diagnostics | ||
pc := m.(*providerConfig) | ||
memberID := d.Id() | ||
|
||
member := memberPutRequest{} | ||
if diags = member.CreateRequestBody(d); diags.HasError() { | ||
return diags | ||
} | ||
|
||
req := newRequestConfig( | ||
pc, | ||
"PUT", | ||
fmt.Sprintf("/v1/config/members/%s", memberID), | ||
member, | ||
) | ||
|
||
body, err := req.MakeRequest() | ||
log.Printf("[DEBUG] %s %s, payload is: %s", req.method, req.apiURL, body) | ||
|
||
if err != nil { | ||
return diag.FromErr(err) | ||
} | ||
|
||
log.Printf("[DEBUG] %s %s SUCCESS. Remote resource updated.", req.method, req.apiURL) | ||
|
||
return resourceMemberRead(ctx, d, m) | ||
} | ||
|
||
func resourceMemberDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { | ||
pc := m.(*providerConfig) | ||
memberID := d.Id() | ||
|
||
req := newRequestConfig( | ||
pc, | ||
"DELETE", | ||
fmt.Sprintf("/v1/config/members/%s", memberID), | ||
nil, | ||
) | ||
|
||
body, err := req.MakeRequest() | ||
log.Printf("[DEBUG] %s %s key %s", req.method, req.apiURL, body) | ||
|
||
if err != nil { | ||
return diag.FromErr(err) | ||
} | ||
|
||
d.SetId("") | ||
return nil | ||
} | ||
|
||
func resourceMember() *schema.Resource { | ||
return &schema.Resource{ | ||
CreateContext: resourceMemberCreate, | ||
UpdateContext: resourceMemberUpdate, | ||
ReadContext: resourceMemberRead, | ||
DeleteContext: resourceMemberDelete, | ||
Importer: &schema.ResourceImporter{ | ||
StateContext: schema.ImportStatePassthroughContext, | ||
}, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"email": { | ||
Type: schema.TypeString, | ||
ForceNew: true, | ||
Required: true, | ||
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { | ||
return strings.EqualFold(old, new) | ||
}, | ||
}, | ||
"role": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ValidateFunc: validation.StringInSlice([]string{"owner", "admin", "member", "readonly"}, false), | ||
}, | ||
"groups": { | ||
Type: schema.TypeList, | ||
Elem: &schema.Schema{Type: schema.TypeString}, | ||
Optional: true, | ||
}, | ||
}, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package logdna | ||
|
||
import ( | ||
"regexp" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" | ||
) | ||
|
||
func TestMember_ErrorRoleEmpty(t *testing.T) { | ||
args := map[string]string{ | ||
"email": `"[email protected]"`, | ||
} | ||
|
||
resource.Test(t, resource.TestCase{ | ||
Providers: testAccProviders, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: fmtTestConfigResource("member", "new", []string{serviceKey, apiHostUrl}, args, nilOpt, nilLst), | ||
ExpectError: regexp.MustCompile("The argument \"role\" is required, but no definition was found."), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func TestMember_Basic(t *testing.T) { | ||
memberArgs := map[string]string{ | ||
"email": `"[email protected]"`, | ||
"role": `"member"`, | ||
} | ||
|
||
adminArgs := map[string]string{ | ||
"email": `"[email protected]"`, | ||
"role": `"admin"`, | ||
} | ||
resource.Test(t, resource.TestCase{ | ||
Providers: testAccProviders, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: fmtTestConfigResource("member", "member", []string{serviceKey, apiHostUrl}, memberArgs, nilOpt, nilLst), | ||
Check: resource.ComposeTestCheckFunc( | ||
testResourceExists("member", "member"), | ||
resource.TestCheckResourceAttr("logdna_member.member", "email", strings.Replace(memberArgs["email"], "\"", "", 2)), | ||
resource.TestCheckResourceAttr("logdna_member.member", "role", strings.Replace(memberArgs["role"], "\"", "", 2)), | ||
), | ||
}, | ||
{ | ||
ResourceName: "logdna_member.member", | ||
ImportState: true, | ||
ImportStateVerify: true, | ||
}, | ||
{ | ||
Config: fmtTestConfigResource("member", "admin", []string{serviceKey, apiHostUrl}, adminArgs, nilOpt, nilLst), | ||
Check: resource.ComposeTestCheckFunc( | ||
testResourceExists("member", "admin"), | ||
resource.TestCheckResourceAttr("logdna_member.admin", "email", strings.Replace(adminArgs["email"], "\"", "", 2)), | ||
resource.TestCheckResourceAttr("logdna_member.admin", "role", strings.Replace(adminArgs["role"], "\"", "", 2)), | ||
), | ||
}, | ||
{ | ||
ResourceName: "logdna_member.admin", | ||
ImportState: true, | ||
ImportStateVerify: true, | ||
}, | ||
}, | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters