From a36b0edf01bcabaa66b6116baaed025811d0c1e8 Mon Sep 17 00:00:00 2001 From: drewmullen Date: Mon, 2 Oct 2023 16:03:04 -0400 Subject: [PATCH 01/19] ds for user lookup --- .vscode/settings.json | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 359dd236..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "go.testEnvVars": { - "TF_ACC": "1" - } -} \ No newline at end of file From 74a01b5891ca691642b286a058ea33db361945ed Mon Sep 17 00:00:00 2001 From: drewmullen Date: Mon, 2 Oct 2023 16:03:04 -0400 Subject: [PATCH 02/19] ds for user lookup --- .vscode/launch.json | 23 +++ .vscode/private.env | 3 + internal/provider/data_source_user.go | 127 ++++++++++++++ internal/provider/data_source_user_test.go | 195 +++++++++++++++++++++ 4 files changed, 348 insertions(+) create mode 100644 .vscode/launch.json create mode 100644 .vscode/private.env create mode 100644 internal/provider/data_source_user.go create mode 100644 internal/provider/data_source_user_test.go diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..e6138559 --- /dev/null +++ b/.vscode/launch.json @@ -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" + } + ] +} diff --git a/.vscode/private.env b/.vscode/private.env new file mode 100644 index 00000000..231960e0 --- /dev/null +++ b/.vscode/private.env @@ -0,0 +1,3 @@ +TF_ACC=1 +TF_LOG=INFO +GOFLAGS='-mod=readonly' diff --git a/internal/provider/data_source_user.go b/internal/provider/data_source_user.go new file mode 100644 index 00000000..fc09c2d5 --- /dev/null +++ b/internal/provider/data_source_user.go @@ -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 +} diff --git a/internal/provider/data_source_user_test.go b/internal/provider/data_source_user_test.go new file mode 100644 index 00000000..f76042ce --- /dev/null +++ b/internal/provider/data_source_user_test.go @@ -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 +// } +// } From 0bebd8a606267f31e8986099545e59aaa28c4c21 Mon Sep 17 00:00:00 2001 From: drewmullen Date: Mon, 2 Oct 2023 16:22:39 -0400 Subject: [PATCH 03/19] relative env not necessary outside aws provider --- .vscode/launch.json | 1 - 1 file changed, 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index e6138559..c1042956 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,7 +10,6 @@ "request": "launch", "mode": "auto", "program": "${fileDirname}", - "env": {"PKG_NAME": "${relativeFileDirname}"}, "args": [ "-test.v", "-test.run", From 642a88f8ad7aa01448e2a407d4038997a646ae19 Mon Sep 17 00:00:00 2001 From: drewmullen Date: Mon, 2 Oct 2023 16:33:18 -0400 Subject: [PATCH 04/19] properly call data source function --- internal/provider/data_source_user.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/provider/data_source_user.go b/internal/provider/data_source_user.go index fc09c2d5..972f425f 100644 --- a/internal/provider/data_source_user.go +++ b/internal/provider/data_source_user.go @@ -19,7 +19,7 @@ import ( func dataSourceUser() *schema.Resource { return &schema.Resource{ Description: "The user data source allows you to find a Boundary user.", - ReadContext: resourceUserRead, + ReadContext: dataSourceUserRead, Schema: map[string]*schema.Schema{ IDKey: { From 46e10c864bb4d8d9212376008e755642bff6dd59 Mon Sep 17 00:00:00 2001 From: drewmullen Date: Mon, 2 Oct 2023 16:55:15 -0400 Subject: [PATCH 05/19] basic or user and global user working --- go.sum | 2 + internal/provider/data_source_user.go | 4 +- internal/provider/data_source_user_test.go | 49 ++++++++++++++++++---- 3 files changed, 47 insertions(+), 8 deletions(-) diff --git a/go.sum b/go.sum index d4baa6f1..3542aa18 100644 --- a/go.sum +++ b/go.sum @@ -59,6 +59,8 @@ github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEV github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 h1:KLq8BE0KwCL+mmXnjLWEAOYO+2l2AE4YMmqG1ZpZHBs= github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +github.com/YakDriver/regexache v0.23.0 h1:kv3j4XKhbx/vqUilSBgizXDUXHvvH1KdYekdmGwz4C4= +github.com/YakDriver/regexache v0.23.0/go.mod h1:K4BZ3MYKAqSFbYWqmbsG+OzYUDyJjnMEr27DJEsVG3U= github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE= diff --git a/internal/provider/data_source_user.go b/internal/provider/data_source_user.go index 972f425f..dccc6907 100644 --- a/internal/provider/data_source_user.go +++ b/internal/provider/data_source_user.go @@ -5,6 +5,7 @@ package provider import ( "context" + "fmt" "github.com/hashicorp/boundary/api/users" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" @@ -84,7 +85,8 @@ func dataSourceUserRead(ctx context.Context, d *schema.ResourceData, meta interf name := d.Get("name").(string) scopeID := d.Get("scope_id").(string) - opts = append(opts, users.WithName(name)) + // opts = append(opts, users.WithName(name)) + opts = append(opts, users.WithFilter(fmt.Sprintf("\"/item/name\" matches \"%s\"", name))) usersList, err := usrs.List(ctx, scopeID, opts...) diff --git a/internal/provider/data_source_user_test.go b/internal/provider/data_source_user_test.go index f76042ce..3a4e81a3 100644 --- a/internal/provider/data_source_user_test.go +++ b/internal/provider/data_source_user_test.go @@ -7,6 +7,7 @@ import ( "fmt" "testing" + "github.com/YakDriver/regexache" "github.com/hashicorp/boundary/testing/controller" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -26,13 +27,12 @@ data "boundary_user" "org1" { 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) + globalUserDataSource = ` +data "boundary_user" "admin" { + name = "admin" + scope_id = "global" + depends_on = [boundary_role.org1_admin] +}` // orgUserWithAccts = ` // resource "boundary_user" "org1" { @@ -95,6 +95,41 @@ func TestAccUserDataSource(t *testing.T) { }) } +func TestAccUserGlobalUserDataSource(t *testing.T) { + tc := controller.NewTestController(t, tcConfig...) + defer tc.Shutdown() + url := tc.ApiAddrs()[0] + token := tc.Token().Token + + dataSourceName := "data.boundary_user.admin" + + var provider *schema.Provider + resource.Test(t, resource.TestCase{ + ProviderFactories: providerFactories(&provider), + Steps: []resource.TestStep{ + { + // test create + Config: testConfigWithToken(url, token, fooOrg, globalUserDataSource), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataSourceName, NameKey, "admin"), + resource.TestMatchResourceAttr(dataSourceName, "id", regexache.MustCompile(`^u_.+`)), + ), + }, + // 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() From 1d67ec487115099c1920aa5023da73be60f5b400 Mon Sep 17 00:00:00 2001 From: drewmullen Date: Mon, 2 Oct 2023 16:56:48 -0400 Subject: [PATCH 06/19] naming convention for easily testing ds tests only --- internal/provider/data_source_user_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/provider/data_source_user_test.go b/internal/provider/data_source_user_test.go index 3a4e81a3..e9461b28 100644 --- a/internal/provider/data_source_user_test.go +++ b/internal/provider/data_source_user_test.go @@ -57,7 +57,7 @@ data "boundary_user" "admin" { // NOTE: this test also tests out the direct token auth mechanism. -func TestAccUserDataSource(t *testing.T) { +func TestAccUserDataSource_basic(t *testing.T) { tc := controller.NewTestController(t, tcConfig...) defer tc.Shutdown() url := tc.ApiAddrs()[0] @@ -95,7 +95,7 @@ func TestAccUserDataSource(t *testing.T) { }) } -func TestAccUserGlobalUserDataSource(t *testing.T) { +func TestAccUserDataSource_global(t *testing.T) { tc := controller.NewTestController(t, tcConfig...) defer tc.Shutdown() url := tc.ApiAddrs()[0] From 0eedb07b2f5bc983a5e4833d59bbb245718cc441 Mon Sep 17 00:00:00 2001 From: drewmullen Date: Mon, 2 Oct 2023 20:11:54 -0400 Subject: [PATCH 07/19] update tests and add extra parameters --- internal/provider/const.go | 5 + internal/provider/data_source_user.go | 114 +++++++++++---- internal/provider/data_source_user_test.go | 156 ++------------------- internal/provider/filter.go | 10 ++ 4 files changed, 112 insertions(+), 173 deletions(-) create mode 100644 internal/provider/filter.go diff --git a/internal/provider/const.go b/internal/provider/const.go index 1e069257..1730403b 100644 --- a/internal/provider/const.go +++ b/internal/provider/const.go @@ -46,4 +46,9 @@ const ( internalForceUpdateKey = "internal_force_update" // workerFilter is used for common "worker_filter" resource attribute WorkerFilterKey = "worker_filter" + // common User arguments + LoginNameKey = "login_name" + PrimaryAccountID = "primary_account_id" + ScopeKey = "scope" + ParentScopeId = "parent_scope_id" ) diff --git a/internal/provider/data_source_user.go b/internal/provider/data_source_user.go index dccc6907..7f8807ca 100644 --- a/internal/provider/data_source_user.go +++ b/internal/provider/data_source_user.go @@ -5,18 +5,13 @@ package provider import ( "context" - "fmt" + "github.com/hashicorp/boundary/api/scopes" "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.", @@ -51,26 +46,52 @@ func dataSourceUser() *schema.Resource { 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, - // }, + ScopeKey: { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + IDKey: { + Type: schema.TypeString, + Computed: true, + }, + NameKey: { + Type: schema.TypeString, + Computed: true, + }, + TypeKey: { + Type: schema.TypeString, + Computed: true, + }, + DescriptionKey: { + Type: schema.TypeString, + Computed: true, + }, + ParentScopeId: { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + 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, + }, }, } } @@ -85,8 +106,7 @@ func dataSourceUserRead(ctx context.Context, d *schema.ResourceData, meta interf name := d.Get("name").(string) scopeID := d.Get("scope_id").(string) - // opts = append(opts, users.WithName(name)) - opts = append(opts, users.WithFilter(fmt.Sprintf("\"/item/name\" matches \"%s\"", name))) + opts = append(opts, users.WithFilter(FilterWithItemNameMatches(name))) usersList, err := usrs.List(ctx, scopeID, opts...) @@ -124,6 +144,44 @@ func setFromUserItem(d *schema.ResourceData, user users.User) error { if err := d.Set(userAccountIDsKey, user.AccountIds); err != nil { return err } + if err := d.Set(authorizedActions, user.AuthorizedActions); err != nil { + return err + } + if err := d.Set(LoginNameKey, user.LoginName); err != nil { + return err + } + if err := d.Set(PrimaryAccountID, user.PrimaryAccountId); err != nil { + return err + } + + d.Set(ScopeKey, flattenScopeInfo(user.Scope)) + d.SetId(user.Id) return nil } + +func flattenScopeInfo(scope *scopes.ScopeInfo) []interface{} { + if scope == nil { + return []interface{}{} + } + + m := make(map[string]interface{}) + + if v := scope.Id; v != "" { + m[IDKey] = v + } + if v := scope.Type; v != "" { + m[TypeKey] = v + } + if v := scope.Description; v != "" { + m[DescriptionKey] = v + } + if v := scope.ParentScopeId; v != "" { + m[ParentScopeId] = v + } + if v := scope.Name; v != "" { + m[NameKey] = v + } + + return []interface{}{m} +} diff --git a/internal/provider/data_source_user_test.go b/internal/provider/data_source_user_test.go index e9461b28..054339c2 100644 --- a/internal/provider/data_source_user_test.go +++ b/internal/provider/data_source_user_test.go @@ -33,31 +33,11 @@ data "boundary_user" "admin" { scope_id = "global" depends_on = [boundary_role.org1_admin] }` - -// 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_basic(t *testing.T) { +func TestAccUserDataSource_basicOrgUser(t *testing.T) { tc := controller.NewTestController(t, tcConfig...) defer tc.Shutdown() url := tc.ApiAddrs()[0] @@ -80,22 +60,11 @@ func TestAccUserDataSource_basic(t *testing.T) { 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 TestAccUserDataSource_global(t *testing.T) { +func TestAccUserDataSource_globalAdminUser(t *testing.T) { tc := controller.NewTestController(t, tcConfig...) defer tc.Shutdown() url := tc.ApiAddrs()[0] @@ -108,123 +77,20 @@ func TestAccUserDataSource_global(t *testing.T) { ProviderFactories: providerFactories(&provider), Steps: []resource.TestStep{ { - // test create Config: testConfigWithToken(url, token, fooOrg, globalUserDataSource), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(dataSourceName, NameKey, "admin"), - resource.TestMatchResourceAttr(dataSourceName, "id", regexache.MustCompile(`^u_.+`)), + resource.TestCheckResourceAttr(dataSourceName, DescriptionKey, "Initial admin user within the \"global\" scope"), + resource.TestCheckResourceAttr(dataSourceName, LoginNameKey, "testuser"), + resource.TestMatchResourceAttr(dataSourceName, IDKey, regexache.MustCompile(`^u_.+`)), + resource.TestMatchResourceAttr(dataSourceName, PrimaryAccountID, regexache.MustCompile(`^acctpw_.+`)), + resource.TestCheckResourceAttr(dataSourceName, "authorized_actions.#", "7"), + resource.TestCheckResourceAttr(dataSourceName, "scope.0.name", "global"), + resource.TestCheckResourceAttr(dataSourceName, "scope.0.id", "global"), + resource.TestCheckResourceAttr(dataSourceName, "scope.0.type", "global"), + resource.TestCheckResourceAttr(dataSourceName, "scope.0.description", "Global Scope"), ), }, - // 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 -// } -// } diff --git a/internal/provider/filter.go b/internal/provider/filter.go new file mode 100644 index 00000000..382b6196 --- /dev/null +++ b/internal/provider/filter.go @@ -0,0 +1,10 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package provider + +import "fmt" + +func FilterWithItemNameMatches(name string) string { + return fmt.Sprintf("\"/item/name\" matches \"%s\"", name) +} From fbe33da023c8b39240fa12c4252779d9a4642364 Mon Sep 17 00:00:00 2001 From: drewmullen Date: Mon, 2 Oct 2023 20:20:27 -0400 Subject: [PATCH 08/19] docs --- docs/data-sources/user.md | 45 +++++++++++++++++++++++++++ internal/provider/data_source_user.go | 2 +- 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 docs/data-sources/user.md diff --git a/docs/data-sources/user.md b/docs/data-sources/user.md new file mode 100644 index 00000000..5e414530 --- /dev/null +++ b/docs/data-sources/user.md @@ -0,0 +1,45 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "boundary_user Data Source - terraform-provider-boundary" +subcategory: "" +description: |- + The user data source allows you to find a Boundary user. +--- + +# boundary_user (Data Source) + +The user data source allows you to find a Boundary user. + + + + +## Schema + +### Required + +- `name` (String) The username to search for. + +### Optional + +- `scope_id` (String) The scope ID in which the resource is created. Defaults `global` if unset. + +### Read-Only + +- `account_ids` (Set of String) Account ID's to associate with this user resource. +- `authorized_actions` (List of String) A list of actions that the worker is entitled to perform. +- `description` (String) The user description. +- `id` (String) The ID of the user. +- `login_name` (String) Login name for user. +- `primary_account_id` (String) Primary account ID. +- `scope` (List of Object) (see [below for nested schema](#nestedatt--scope)) + + +### Nested Schema for `scope` + +Read-Only: + +- `description` (String) +- `id` (String) +- `name` (String) +- `parent_scope_id` (String) +- `type` (String) diff --git a/internal/provider/data_source_user.go b/internal/provider/data_source_user.go index 7f8807ca..30402a63 100644 --- a/internal/provider/data_source_user.go +++ b/internal/provider/data_source_user.go @@ -24,7 +24,7 @@ func dataSourceUser() *schema.Resource { Computed: true, }, NameKey: { - Description: "The username.", + Description: "The username to search for.", Type: schema.TypeString, Required: true, }, From 4554b4b4419ce2d5274fd6899257aca343da3cd0 Mon Sep 17 00:00:00 2001 From: drewmullen Date: Mon, 2 Oct 2023 20:22:08 -0400 Subject: [PATCH 09/19] rely on default scope for test --- internal/provider/data_source_user_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/provider/data_source_user_test.go b/internal/provider/data_source_user_test.go index 054339c2..8ad1c8b1 100644 --- a/internal/provider/data_source_user_test.go +++ b/internal/provider/data_source_user_test.go @@ -30,7 +30,6 @@ data "boundary_user" "org1" { globalUserDataSource = ` data "boundary_user" "admin" { name = "admin" - scope_id = "global" depends_on = [boundary_role.org1_admin] }` ) From 65a57c00e97f87df46393d01f9faa2528a4098e8 Mon Sep 17 00:00:00 2001 From: drewmullen Date: Wed, 11 Oct 2023 13:34:14 -0700 Subject: [PATCH 10/19] remove vscode config --- .vscode/launch.json | 22 ---------------------- .vscode/private.env | 3 --- 2 files changed, 25 deletions(-) delete mode 100644 .vscode/launch.json delete mode 100644 .vscode/private.env diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index c1042956..00000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - // 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}", - "args": [ - "-test.v", - "-test.run", - "^${selectedText}$" - ], - "showLog": true, - "envFile": "${workspaceFolder}/.vscode/private.env" - } - ] -} diff --git a/.vscode/private.env b/.vscode/private.env deleted file mode 100644 index 231960e0..00000000 --- a/.vscode/private.env +++ /dev/null @@ -1,3 +0,0 @@ -TF_ACC=1 -TF_LOG=INFO -GOFLAGS='-mod=readonly' From 1ce96bc602800d9919174e63dd95a3fa37e819b3 Mon Sep 17 00:00:00 2001 From: drewmullen Date: Wed, 11 Oct 2023 13:35:53 -0700 Subject: [PATCH 11/19] scope DS missing necessary template for doc gen --- .gitignore | 1 + docs/data-sources/scope.md | 5 ++--- .../data-sources/boundary_scope/data-source.tf | 6 +++--- templates/data-sources/scope.md.tmpl | 17 +++++++++++++++++ 4 files changed, 23 insertions(+), 6 deletions(-) create mode 100644 templates/data-sources/scope.md.tmpl diff --git a/.gitignore b/.gitignore index 3ec33e1a..b68032cc 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ website/node_modules *.iml *.test *.iml +.vscode website/vendor diff --git a/docs/data-sources/scope.md b/docs/data-sources/scope.md index 7f3966df..cb177ddd 100644 --- a/docs/data-sources/scope.md +++ b/docs/data-sources/scope.md @@ -1,5 +1,4 @@ --- -# generated by https://github.com/hashicorp/terraform-plugin-docs page_title: "boundary_scope Data Source - terraform-provider-boundary" subcategory: "" description: |- @@ -15,12 +14,12 @@ The scope data source allows you to discover an existing Boundary scope by name. ```terraform # Retrieve the ID of a Boundary project data "boundary_scope" "org" { - name = "SecOps" + name = "SecOps" parent_scope_id = "global" } data "boundary_scope" "project" { - name = "2111" + name = "2111" parent_scope_id = data.boundary_scope.id } ``` diff --git a/examples/data-sources/boundary_scope/data-source.tf b/examples/data-sources/boundary_scope/data-source.tf index 049ce21c..3eb375d3 100644 --- a/examples/data-sources/boundary_scope/data-source.tf +++ b/examples/data-sources/boundary_scope/data-source.tf @@ -1,10 +1,10 @@ # Retrieve the ID of a Boundary project data "boundary_scope" "org" { - name = "SecOps" + name = "SecOps" parent_scope_id = "global" } data "boundary_scope" "project" { - name = "2111" + name = "2111" parent_scope_id = data.boundary_scope.id -} \ No newline at end of file +} diff --git a/templates/data-sources/scope.md.tmpl b/templates/data-sources/scope.md.tmpl new file mode 100644 index 00000000..225b58eb --- /dev/null +++ b/templates/data-sources/scope.md.tmpl @@ -0,0 +1,17 @@ +--- +page_title: "boundary_scope Data Source - terraform-provider-boundary" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# boundary_scope (Data Source) + +{{ .Description | trimspace }} +Please note that the Global scope will always have an id of "global", and does not need to be discovered with this data source. + +## Example Usage + +{{tffile "examples/data-sources/scope/data-source.tf"}} + +{{ .SchemaMarkdown | trimspace }} From 9de7c9b6000bc95ee6c92b0cc0bcaa5688326e80 Mon Sep 17 00:00:00 2001 From: drewmullen Date: Wed, 11 Oct 2023 13:40:03 -0700 Subject: [PATCH 12/19] include user template for example inclusion in doc gen --- docs/data-sources/user.md | 21 ++++++++++++++++++++- examples/data-sources/user/data-source.tf | 17 +++++++++++++++++ templates/data-sources/user.md.tmpl | 16 ++++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 examples/data-sources/user/data-source.tf create mode 100644 templates/data-sources/user.md.tmpl diff --git a/docs/data-sources/user.md b/docs/data-sources/user.md index 5e414530..4456c917 100644 --- a/docs/data-sources/user.md +++ b/docs/data-sources/user.md @@ -1,5 +1,4 @@ --- -# generated by https://github.com/hashicorp/terraform-plugin-docs page_title: "boundary_user Data Source - terraform-provider-boundary" subcategory: "" description: |- @@ -10,7 +9,27 @@ description: |- The user data source allows you to find a Boundary user. +## Example Usage +```terraform +# Retrieve a user from the global scope + +data "boundary_user" "global_scope_admin" { + name = "admin" +} + +# User from a org scope + +data "boundary_user" "org_user" { + name = "username" + scope_id = data.boundary_scope.org.id +} + +data "boundary_scope" "org" { + name = "my-org" + parent_scope_id = data.boundary_scope.org.id +} +``` ## Schema diff --git a/examples/data-sources/user/data-source.tf b/examples/data-sources/user/data-source.tf new file mode 100644 index 00000000..a5ef3c3c --- /dev/null +++ b/examples/data-sources/user/data-source.tf @@ -0,0 +1,17 @@ +# Retrieve a user from the global scope + +data "boundary_user" "global_scope_admin" { + name = "admin" +} + +# User from a org scope + +data "boundary_user" "org_user" { + name = "username" + scope_id = data.boundary_scope.org.id +} + +data "boundary_scope" "org" { + name = "my-org" + parent_scope_id = data.boundary_scope.org.id +} diff --git a/templates/data-sources/user.md.tmpl b/templates/data-sources/user.md.tmpl new file mode 100644 index 00000000..b5366b4d --- /dev/null +++ b/templates/data-sources/user.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "boundary_user Data Source - terraform-provider-boundary" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# boundary_user (Data Source) + +{{ .Description | trimspace }} + +## Example Usage + +{{tffile "examples/data-sources/user/data-source.tf"}} + +{{ .SchemaMarkdown | trimspace }} From ce405de55dad50db74ed3c46a1b05b81c609918b Mon Sep 17 00:00:00 2001 From: drewmullen Date: Wed, 11 Oct 2023 13:49:36 -0700 Subject: [PATCH 13/19] add validation to params --- internal/provider/data_source_user.go | 25 ++++++++++++++----------- internal/provider/provider.go | 1 + 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/internal/provider/data_source_user.go b/internal/provider/data_source_user.go index 30402a63..46914d5a 100644 --- a/internal/provider/data_source_user.go +++ b/internal/provider/data_source_user.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/boundary/api/users" "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 dataSourceUser() *schema.Resource { @@ -24,9 +25,10 @@ func dataSourceUser() *schema.Resource { Computed: true, }, NameKey: { - Description: "The username to search for.", - Type: schema.TypeString, - Required: true, + Description: "The username to search for.", + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, }, DescriptionKey: { Description: "The user description.", @@ -34,11 +36,12 @@ func dataSourceUser() *schema.Resource { 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", + Description: "The scope ID in which the resource is created. Defaults `global` if unset.", + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: "global", + ValidateFunc: validation.StringIsNotEmpty, }, userAccountIDsKey: { Description: "Account ID's to associate with this user resource.", @@ -103,8 +106,8 @@ func dataSourceUserRead(ctx context.Context, d *schema.ResourceData, meta interf opts := []users.Option{} // Get user ID using name - name := d.Get("name").(string) - scopeID := d.Get("scope_id").(string) + name := d.Get(NameKey).(string) + scopeID := d.Get(ScopeIdKey).(string) opts = append(opts, users.WithFilter(FilterWithItemNameMatches(name))) @@ -116,7 +119,7 @@ func dataSourceUserRead(ctx context.Context, d *schema.ResourceData, meta interf users := usersList.GetItems() // check length, 0 means no user, > 1 means too many - if len(users) == 0 || users[0] == nil { + if len(users) == 0 { return diag.Errorf("no matching user found: %v", err) } diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 24f8c45b..0ab7fca7 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -129,6 +129,7 @@ func New() *schema.Provider { }, DataSourcesMap: map[string]*schema.Resource{ "boundary_scope": dataSourceScope(), + "boundary_user": dataSourceUser(), }, } From 709877e5b7d1705d88db0ac871cfc539e8755854 Mon Sep 17 00:00:00 2001 From: drewmullen Date: Wed, 11 Oct 2023 21:03:31 -0700 Subject: [PATCH 14/19] rm unnecessary forcenew --- internal/provider/data_source_user.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/provider/data_source_user.go b/internal/provider/data_source_user.go index 46914d5a..4db68d0b 100644 --- a/internal/provider/data_source_user.go +++ b/internal/provider/data_source_user.go @@ -39,7 +39,6 @@ func dataSourceUser() *schema.Resource { Description: "The scope ID in which the resource is created. Defaults `global` if unset.", Type: schema.TypeString, Optional: true, - ForceNew: true, Default: "global", ValidateFunc: validation.StringIsNotEmpty, }, From 63cad886c17b710278c0522d499e6eb4cc0daefb Mon Sep 17 00:00:00 2001 From: drewmullen Date: Wed, 11 Oct 2023 21:07:40 -0700 Subject: [PATCH 15/19] wip --- go.mod | 1 + 1 file changed, 1 insertion(+) diff --git a/go.mod b/go.mod index 28bf745c..06112204 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.21 require ( github.com/hashicorp/boundary v0.13.1-0.20231012004550-1ed0a13004b9 + github.com/YakDriver/regexache v0.23.0 github.com/hashicorp/boundary/api v0.0.41 github.com/hashicorp/boundary/sdk v0.0.37 github.com/hashicorp/cap v0.4.0 From 4a976518d172aeb6cacd7336fc680164647cc1cf Mon Sep 17 00:00:00 2001 From: drewmullen Date: Thu, 12 Oct 2023 11:57:05 -0700 Subject: [PATCH 16/19] rename example with prefix --- docs/data-sources/scope.md | 1 + examples/data-sources/{user => boundary_user}/data-source.tf | 0 templates/data-sources/scope.md.tmpl | 2 +- templates/data-sources/user.md.tmpl | 2 +- 4 files changed, 3 insertions(+), 2 deletions(-) rename examples/data-sources/{user => boundary_user}/data-source.tf (100%) diff --git a/docs/data-sources/scope.md b/docs/data-sources/scope.md index cb177ddd..c367e851 100644 --- a/docs/data-sources/scope.md +++ b/docs/data-sources/scope.md @@ -8,6 +8,7 @@ description: |- # boundary_scope (Data Source) The scope data source allows you to discover an existing Boundary scope by name. +Please note that the Global scope will always have an id of "global", and does not need to be discovered with this data source. ## Example Usage diff --git a/examples/data-sources/user/data-source.tf b/examples/data-sources/boundary_user/data-source.tf similarity index 100% rename from examples/data-sources/user/data-source.tf rename to examples/data-sources/boundary_user/data-source.tf diff --git a/templates/data-sources/scope.md.tmpl b/templates/data-sources/scope.md.tmpl index 225b58eb..8c5d282a 100644 --- a/templates/data-sources/scope.md.tmpl +++ b/templates/data-sources/scope.md.tmpl @@ -12,6 +12,6 @@ Please note that the Global scope will always have an id of "global", and does n ## Example Usage -{{tffile "examples/data-sources/scope/data-source.tf"}} +{{tffile "examples/data-sources/boundary_scope/data-source.tf"}} {{ .SchemaMarkdown | trimspace }} diff --git a/templates/data-sources/user.md.tmpl b/templates/data-sources/user.md.tmpl index b5366b4d..f37de4d1 100644 --- a/templates/data-sources/user.md.tmpl +++ b/templates/data-sources/user.md.tmpl @@ -11,6 +11,6 @@ description: |- ## Example Usage -{{tffile "examples/data-sources/user/data-source.tf"}} +{{tffile "examples/data-sources/boundary_user/data-source.tf"}} {{ .SchemaMarkdown | trimspace }} From 47fba7ae1e9c169d99e9812da1edcba5a9f8aa0b Mon Sep 17 00:00:00 2001 From: drewmullen Date: Mon, 16 Oct 2023 08:34:35 -0400 Subject: [PATCH 17/19] update docs and switch headings to ATX for more than 2 levels --- .gitignore | 1 - .vscode/launch.json | 22 +++++++++++++++++ .vscode/private.env | 3 +++ .vscode/settings.json | 5 ++++ README.md | 56 +++++++++++++++++++++++++------------------ 5 files changed, 63 insertions(+), 24 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/private.env create mode 100644 .vscode/settings.json diff --git a/.gitignore b/.gitignore index b68032cc..3ec33e1a 100644 --- a/.gitignore +++ b/.gitignore @@ -24,7 +24,6 @@ website/node_modules *.iml *.test *.iml -.vscode website/vendor diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..c1042956 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,22 @@ +{ + // 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}", + "args": [ + "-test.v", + "-test.run", + "^${selectedText}$" + ], + "showLog": true, + "envFile": "${workspaceFolder}/.vscode/private.env" + } + ] +} diff --git a/.vscode/private.env b/.vscode/private.env new file mode 100644 index 00000000..231960e0 --- /dev/null +++ b/.vscode/private.env @@ -0,0 +1,3 @@ +TF_ACC=1 +TF_LOG=INFO +GOFLAGS='-mod=readonly' diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..359dd236 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "go.testEnvVars": { + "TF_ACC": "1" + } +} \ No newline at end of file diff --git a/README.md b/README.md index d9957567..ee7048d5 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,15 @@ ![](boundary.png) -Terraform Provider Boundary -================== +# Terraform Provider Boundary Available in the [Terraform Registry](https://registry.terraform.io/providers/hashicorp/boundary/latest). -Requirements ------------- +## Requirements - [Terraform](https://www.terraform.io/downloads.html) >= 0.12.x - [Go](https://golang.org/doc/install) >= 1.20 -Building The Provider ---------------------- +## Building The Provider 1. Clone the repository 1. Enter the repository directory @@ -22,32 +19,30 @@ You'll need to ensure that your Terraform file contains the information necessar ```hcl terraform { - required_providers { - boundary = { - source = "localhost/providers/boundary" - version = "0.0.1" - } - } + required_providers { + boundary = { + source = "localhost/providers/boundary" + version = "0.0.1" + } + } } ``` -Adding Dependencies ---------------------- +## Adding Dependencies This provider uses [Go modules](https://github.com/golang/go/wiki/Modules). Please see the Go documentation for the most up to date information about using Go modules. To add a new dependency `github.com/author/dependency` to your Terraform provider: -``` +```shell go get github.com/author/dependency go mod tidy ``` Then commit the changes to `go.mod` and `go.sum`. -Developing the Provider ---------------------------- +## Developing the Provider If you wish to work on the provider, you'll first need [Go](http://www.golang.org) installed on your machine (see [Requirements](#requirements) above). @@ -72,17 +67,32 @@ $ make testacc For more details on the docker image and troubleshooting see the [boundary testing doc](https://github.com/hashicorp/boundary/blob/main/CONTRIBUTING.md#testing). -Generating Docs ----------------------- +## Debugging the provider + +If you're using vscode, this provider has delve debugging baked into the [.vscode](https://github.com/hashicorp/terraform-provider-boundary/tree/main/.vscode) directory. To debug you can find instructions [here.](https://dev.to/drewmullen/vscode-terraform-provider-development-setup-debugging-6bn#usage) + +## Generating Docs + +This provider uses the [terraform-plugin-docs](https://github.com/hashicorp/terraform-plugin-docs/) tool to generate documentation. Each resource and data source requires a [templates](https://github.com/hashicorp/terraform-provider-boundary/tree/main/templates) and [examples](https://github.com/hashicorp/terraform-provider-boundary/tree/main/examples) then you execute a binary generate the docs which are placed in [./docs](https://github.com/hashicorp/terraform-provider-boundary/tree/main/docs). + +### New Resources & Data Sources (documentation templates) + +If you're adding a new resource or a data source, you must include a documentation template for your new R/DS in [./templates](https://github.com/hashicorp/terraform-provider-boundary/tree/main/templates) directory. This provider uses the [terraform-plugin-docs](https://github.com/hashicorp/terraform-plugin-docs/) tool to generate documentation. For full details see the link. + +### Documentation Examples for Resources & Data Sources + +Examples are required for all resources and data sources. This provider uses the [terraform-plugin-docs](https://github.com/hashicorp/terraform-plugin-docs/) tool to generate documentation. To add examples, add a new file to the [./examples](https://github.com/hashicorp/terraform-provider-boundary/tree/main/examples) directory or update the existing files. Next make sure the corresponding template in [./templates](https://github.com/hashicorp/terraform-provider-boundary/tree/main/templates) directory references this example. + +### Generating From the root of the repo run: -``` +```shell go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs ``` -Using the provider ----------------------- +## Using the provider + Please see our detailed docs for individual resource usage. Below is a complex example using the Boundary provider to configure all resource types available: ```hcl @@ -140,7 +150,7 @@ resource "boundary_user" "users" { scope_id = boundary_scope.corp.id } -// organization level group for readonly users +// organization level group for readonly users resource "boundary_group" "readonly" { name = "readonly" description = "Organization group for readonly users" From edc04bf71a9ada230611d9a179b2a06e9d7c6613 Mon Sep 17 00:00:00 2001 From: drewmullen Date: Mon, 16 Oct 2023 12:51:13 -0400 Subject: [PATCH 18/19] go mod tidy --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 06112204..82b488cf 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/hashicorp/terraform-provider-boundary go 1.21 require ( - github.com/hashicorp/boundary v0.13.1-0.20231012004550-1ed0a13004b9 github.com/YakDriver/regexache v0.23.0 + github.com/hashicorp/boundary v0.13.1-0.20231012004550-1ed0a13004b9 github.com/hashicorp/boundary/api v0.0.41 github.com/hashicorp/boundary/sdk v0.0.37 github.com/hashicorp/cap v0.4.0 From b5eb4a707e126895d4e4b5481415087f7d1d8e17 Mon Sep 17 00:00:00 2001 From: drewmullen Date: Tue, 17 Oct 2023 18:52:07 -0400 Subject: [PATCH 19/19] finally done? --- internal/provider/data_source_user.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/provider/data_source_user.go b/internal/provider/data_source_user.go index 4db68d0b..29f6bc32 100644 --- a/internal/provider/data_source_user.go +++ b/internal/provider/data_source_user.go @@ -111,7 +111,6 @@ func dataSourceUserRead(ctx context.Context, d *schema.ResourceData, meta interf opts = append(opts, users.WithFilter(FilterWithItemNameMatches(name))) usersList, err := usrs.List(ctx, scopeID, opts...) - if err != nil { return diag.Errorf("error calling list user: %v", err) }