Skip to content

Commit

Permalink
ds for user lookup
Browse files Browse the repository at this point in the history
  • Loading branch information
drewmullen committed Oct 2, 2023
1 parent 260abbb commit a2282dd
Show file tree
Hide file tree
Showing 5 changed files with 351 additions and 0 deletions.
23 changes: 23 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch a test function",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${fileDirname}",
"env": {"PKG_NAME": "${relativeFileDirname}"},
"args": [
"-test.v",
"-test.run",
"^${selectedText}$"
],
"showLog": true,
"envFile": "${workspaceFolder}/.vscode/private.env"
}
]
}
3 changes: 3 additions & 0 deletions .vscode/private.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
TF_ACC=1
TF_LOG=INFO
GOFLAGS='-mod=readonly'
127 changes: 127 additions & 0 deletions internal/provider/data_source_user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package provider

import (
"context"

"github.com/hashicorp/boundary/api/users"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

// const (
// loginNameKey = "login_name"
// primaryAccountID = "primary_account_id"
// )

func dataSourceUser() *schema.Resource {
return &schema.Resource{
Description: "The user data source allows you to find a Boundary user.",
ReadContext: resourceUserRead,

Schema: map[string]*schema.Schema{
IDKey: {
Description: "The ID of the user.",
Type: schema.TypeString,
Computed: true,
},
NameKey: {
Description: "The username.",
Type: schema.TypeString,
Required: true,
},
DescriptionKey: {
Description: "The user description.",
Type: schema.TypeString,
Computed: true,
},
ScopeIdKey: {
Description: "The scope ID in which the resource is created. Defaults to the provider's `default_scope` if unset.",
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Default: "global",
},
userAccountIDsKey: {
Description: "Account ID's to associate with this user resource.",
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
// TODO: add after basic functionality works

// authorizedActions: {
// Description: "A list of actions that the worker is entitled to perform.",
// Type: schema.TypeList,
// Elem: &schema.Schema{
// Type: schema.TypeString,
// },
// Computed: true,
// },
// loginNameKey: {
// Description: "Login name for user.",
// Type: schema.TypeString,
// Computed: true,
// },
// primaryAccountID: {
// Description: "Primary account ID.",
// Type: schema.TypeString,
// Computed: true,
// },
},
}
}

func dataSourceUserRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
usrs := users.NewClient(md.client)

opts := []users.Option{}

// Get user ID using name
name := d.Get("name").(string)
scopeID := d.Get("scope_id").(string)

opts = append(opts, users.WithName(name))

usersList, err := usrs.List(ctx, scopeID, opts...)

if err != nil {
return diag.Errorf("error calling list user: %v", err)
}
users := usersList.GetItems()

// check length, 0 means no user, > 1 means too many
if len(users) == 0 || users[0] == nil {
return diag.Errorf("no matching user found: %v", err)
}

if len(users) > 1 {
return diag.Errorf("error found more than 1 user: %v", err)
}

if err := setFromUserItem(d, *users[0]); err != nil {
return diag.FromErr(err)
}

return nil
}

func setFromUserItem(d *schema.ResourceData, user users.User) error {
if err := d.Set(NameKey, user.Name); err != nil {
return err
}
if err := d.Set(DescriptionKey, user.Description); err != nil {
return err
}
if err := d.Set(ScopeIdKey, user.ScopeId); err != nil {
return err
}
if err := d.Set(userAccountIDsKey, user.AccountIds); err != nil {
return err
}
d.SetId(user.Id)
return nil
}
195 changes: 195 additions & 0 deletions internal/provider/data_source_user_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package provider

import (
"fmt"
"testing"

"github.com/hashicorp/boundary/testing/controller"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

var (
orgUserDataSource = fmt.Sprintf(`
resource "boundary_user" "org1" {
name = "test"
description = "%s"
scope_id = boundary_scope.org1.id
depends_on = [boundary_role.org1_admin]
}
data "boundary_user" "org1" {
name = "test"
scope_id = boundary_scope.org1.id
depends_on = [boundary_user.org1]
}`, fooDescription)

// orgUserUpdate = fmt.Sprintf(`
// resource "boundary_user" "org1" {
// name = "test"
// description = "%s"
// scope_id = boundary_scope.org1.id
// depends_on = [boundary_role.org1_admin]
// }`, fooDescriptionUpdate)

// orgUserWithAccts = `
// resource "boundary_user" "org1" {
// name = "test"
// description = "with accts"
// scope_id = boundary_scope.org1.id
// account_ids = [
// boundary_account.foo.id
// ]
// depends_on = [boundary_role.org1_admin]
// }`

// orgUserWithAcctsUpdate = `
//
// resource "boundary_user" "org1" {
// name = "test"
// description = "with accts"
// scope_id = boundary_scope.org1.id
// depends_on = [boundary_role.org1_admin]
// }`
)

// NOTE: this test also tests out the direct token auth mechanism.

func TestAccUserDataSource(t *testing.T) {
tc := controller.NewTestController(t, tcConfig...)
defer tc.Shutdown()
url := tc.ApiAddrs()[0]
token := tc.Token().Token

resourceName := "boundary_user.org1"
dataSourceName := "data.boundary_user.org1"

var provider *schema.Provider
resource.Test(t, resource.TestCase{
ProviderFactories: providerFactories(&provider),
CheckDestroy: testAccCheckUserResourceDestroy(t, provider),
Steps: []resource.TestStep{
{
// test create
Config: testConfigWithToken(url, token, fooOrg, orgUserDataSource),
Check: resource.ComposeTestCheckFunc(
testAccCheckUserResourceExists(provider, resourceName),
resource.TestCheckResourceAttr(dataSourceName, DescriptionKey, fooDescription),
resource.TestCheckResourceAttr(dataSourceName, NameKey, "test"),
),
},
// importStep("boundary_user.org1"),
// {
// // test update description
// Config: testConfigWithToken(url, token, fooOrg, orgUserUpdate),
// Check: resource.ComposeTestCheckFunc(
// testAccCheckUserResourceExists(provider, "boundary_user.org1"),
// resource.TestCheckResourceAttr("boundary_user.org1", DescriptionKey, fooDescriptionUpdate),
// resource.TestCheckResourceAttr("boundary_user.org1", NameKey, "test"),
// ),
// },
// importStep("boundary_user.org1"),
},
})
}

// func TestAccUserWithAccounts(t *testing.T) {
// tc := controller.NewTestController(t, tcConfig...)
// defer tc.Shutdown()
// url := tc.ApiAddrs()[0]
// token := tc.Token().Token

// var provider *schema.Provider
// resource.Test(t, resource.TestCase{
// ProviderFactories: providerFactories(&provider),
// CheckDestroy: testAccCheckUserResourceDestroy(t, provider),
// Steps: []resource.TestStep{
// {
// // test create
// Config: testConfigWithToken(url, token, fooOrg, fooAccount, orgUserWithAccts),
// Check: resource.ComposeTestCheckFunc(
// testAccCheckUserResourceExists(provider, "boundary_user.org1"),
// testAccCheckAccountResourceExists(provider, "boundary_account.foo"),
// resource.TestCheckResourceAttr("boundary_user.org1", DescriptionKey, "with accts"),
// resource.TestCheckResourceAttr("boundary_user.org1", NameKey, "test"),
// testAccCheckUserResourceAccountsSet(provider, "boundary_user.org1", []string{"boundary_account.foo"}),
// ),
// },
// importStep("boundary_user.org1"),
// importStep("boundary_account.foo", "password"),
// {
// // test update description
// Config: testConfigWithToken(url, token, fooOrg, fooAccount, orgUserWithAcctsUpdate),
// Check: resource.ComposeTestCheckFunc(
// testAccCheckUserResourceExists(provider, "boundary_user.org1"),
// testAccCheckAccountResourceExists(provider, "boundary_account.foo"),
// resource.TestCheckResourceAttr("boundary_user.org1", DescriptionKey, "with accts"),
// resource.TestCheckResourceAttr("boundary_user.org1", NameKey, "test"),
// ),
// },
// importStep("boundary_user.org1"),
// importStep("boundary_account.foo", "password"),
// },
// })
// }

// func testAccCheckUserResourceAccountsSet(testProvider *schema.Provider, name string, accounts []string) resource.TestCheckFunc {
// return func(s *terraform.State) error {
// rs, ok := s.RootModule().Resources[name]
// if !ok {
// return fmt.Errorf("user resource not found: %s", name)
// }

// id := rs.Primary.ID
// if id == "" {
// return fmt.Errorf("user resource ID is not set")
// }

// // ensure accts are declared in state
// acctIDs := []string{}
// for _, acctResourceName := range acctIDs {
// ur, ok := s.RootModule().Resources[acctResourceName]
// if !ok {
// return fmt.Errorf("account resource not found: %s", acctResourceName)
// }

// acctID := ur.Primary.ID
// if id == "" {
// return fmt.Errorf("account resource ID not set")
// }

// acctIDs = append(acctIDs, acctID)
// }

// // check boundary to ensure it matches
// md := testProvider.Meta().(*metaData)
// usrClient := users.NewClient(md.client)

// u, err := usrClient.Read(context.Background(), id)
// if err != nil {
// return fmt.Errorf("Got an error when reading user %q: %v", id, err)
// }

// // for every account set on the user in the state, ensure
// // each group in boundary has the same setings
// if len(u.Item.AccountIds) == 0 {
// return fmt.Errorf("no account found on user")
// }

// for _, stateAccount := range acctIDs {
// ok := false
// for _, gotAccount := range u.Item.AccountIds {
// if gotAccount == stateAccount {
// ok = true
// }
// }
// if !ok {
// return fmt.Errorf("account in state not set in boundary:\n in state: %+v\n in boundary: %+v", acctIDs, u.Item.AccountIds)
// }
// }

// return nil
// }
// }
3 changes: 3 additions & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ func New() *schema.Provider {
"boundary_user": resourceUser(),
"boundary_worker": resourceWorker(),
},
DataSourcesMap: map[string]*schema.Resource{
"boundary_user": dataSourceUser(),
},
}

p.ConfigureContextFunc = providerConfigure(p)
Expand Down

0 comments on commit a2282dd

Please sign in to comment.