diff --git a/CHANGELOG.md b/CHANGELOG.md index 8522475d..8847aab2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,10 @@ Canonical reference for changes, improvements, and bugfixes for the Boundary Ter ### Bug Fixes -* Improve error message when authenticating to boundary. +* Improve error message when authenticating to boundary ([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/290)). +* Set state before returning an error when creating a resource + ([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/289)) ## 1.1.1 (October 12, 2022) diff --git a/internal/provider/resource_group.go b/internal/provider/resource_group.go index 60aa4ee1..051d1ed1 100644 --- a/internal/provider/resource_group.go +++ b/internal/provider/resource_group.go @@ -75,7 +75,7 @@ func setFromGroupResponseMap(d *schema.ResourceData, raw map[string]interface{}) return nil } -func resourceGroupCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func resourceGroupCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) (errs diag.Diagnostics) { md := meta.(*metaData) var scopeId string @@ -108,7 +108,12 @@ func resourceGroupCreate(ctx context.Context, d *schema.ResourceData, meta inter if gcr == nil { return diag.Errorf("group nil after create") } - raw := gcr.GetResponse().Map + apiResponse := gcr.GetResponse().Map + defer func() { + if err := setFromGroupResponseMap(d, apiResponse); err != nil { + errs = append(errs, diag.FromErr(err)...) + } + }() if val, ok := d.GetOk(groupMemberIdsKey); ok { list := val.(*schema.Set).List() @@ -118,16 +123,12 @@ func resourceGroupCreate(ctx context.Context, d *schema.ResourceData, meta inter } gcsmr, err := grps.SetMembers(ctx, gcr.Item.Id, gcr.Item.Version, memberIds) if err != nil { - return diag.Errorf("error setting principals on role: %v", err) + return diag.Errorf("error setting members on group: %v", err) } if gcsmr == nil { return diag.Errorf("group nil after setting members") } - raw = gcsmr.GetResponse().Map - } - - if err := setFromGroupResponseMap(d, raw); err != nil { - return diag.FromErr(err) + apiResponse = gcsmr.GetResponse().Map } return nil diff --git a/internal/provider/resource_host_set_static.go b/internal/provider/resource_host_set_static.go index 9f5ce441..be4da02a 100644 --- a/internal/provider/resource_host_set_static.go +++ b/internal/provider/resource_host_set_static.go @@ -139,7 +139,7 @@ func setFromHostSetStaticResponseMap(d *schema.ResourceData, raw map[string]inte return nil } -func resourceHostSetStaticCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func resourceHostSetStaticCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) (errs diag.Diagnostics) { md := meta.(*metaData) var hostsetHostCatalogId string @@ -195,7 +195,12 @@ func resourceHostSetStaticCreate(ctx context.Context, d *schema.ResourceData, me if hscr == nil { return diag.Errorf("nil host set after create") } - raw := hscr.GetResponse().Map + apiResponse := hscr.GetResponse().Map + defer func() { + if err := setFromHostSetStaticResponseMap(d, apiResponse); err != nil { + errs = append(errs, diag.FromErr(err)...) + } + }() if hostIds != nil { hsshr, err := hsClient.SetHosts(ctx, hscr.Item.Id, hscr.Item.Version, hostIds) @@ -205,11 +210,7 @@ func resourceHostSetStaticCreate(ctx context.Context, d *schema.ResourceData, me if hsshr == nil { return diag.Errorf("nil host set after setting hosts") } - raw = hsshr.GetResponse().Map - } - - if err := setFromHostSetStaticResponseMap(d, raw); err != nil { - return diag.FromErr(err) + apiResponse = hsshr.GetResponse().Map } return nil diff --git a/internal/provider/resource_role.go b/internal/provider/resource_role.go index d3eea62a..ce659cc4 100644 --- a/internal/provider/resource_role.go +++ b/internal/provider/resource_role.go @@ -94,7 +94,7 @@ func setFromRoleResponseMap(d *schema.ResourceData, raw map[string]interface{}) return nil } -func resourceRoleCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func resourceRoleCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) (errs diag.Diagnostics) { md := meta.(*metaData) var scopeId string @@ -151,18 +151,22 @@ func resourceRoleCreate(ctx context.Context, d *schema.ResourceData, meta interf if tcr == nil { return diag.Errorf("nil role after create") } - raw := tcr.GetResponse().Map + apiResponse := tcr.GetResponse().Map + defer func() { + if err := setFromRoleResponseMap(d, apiResponse); err != nil { + errs = append(errs, diag.FromErr(err)...) + } + }() - var diags diag.Diagnostics if principalIds != nil { tspr, err := rc.SetPrincipals(ctx, tcr.Item.Id, 0, principalIds, roles.WithAutomaticVersioning(true)) switch { case err != nil: - diags = append(diags, diag.Diagnostic{Severity: diag.Error, Summary: "error setting principals", Detail: err.Error()}) + errs = append(errs, diag.Diagnostic{Severity: diag.Error, Summary: "error setting principals", Detail: err.Error()}) case tspr == nil: - diags = append(diags, diag.Diagnostic{Severity: diag.Error, Summary: "nil role after setting principal IDs"}) + errs = append(errs, diag.Diagnostic{Severity: diag.Error, Summary: "nil role after setting principals"}) default: - raw = tspr.GetResponse().Map + apiResponse = tspr.GetResponse().Map } } @@ -170,19 +174,15 @@ func resourceRoleCreate(ctx context.Context, d *schema.ResourceData, meta interf tsgr, err := rc.SetGrants(ctx, tcr.Item.Id, 0, grantStrings, roles.WithAutomaticVersioning(true)) switch { case err != nil: - diags = append(diags, diag.Diagnostic{Severity: diag.Error, Summary: "error setting grants", Detail: err.Error()}) + errs = append(errs, diag.Diagnostic{Severity: diag.Error, Summary: "error setting grants", Detail: err.Error()}) case tsgr == nil: - diags = append(diags, diag.Diagnostic{Severity: diag.Error, Summary: "nil role after setting grant strings"}) + errs = append(errs, diag.Diagnostic{Severity: diag.Error, Summary: "nil role after setting grants"}) default: - raw = tsgr.GetResponse().Map + apiResponse = tsgr.GetResponse().Map } } - if err := setFromRoleResponseMap(d, raw); err != nil { - return diag.FromErr(err) - } - - return diags + return errs } func resourceRoleRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { diff --git a/internal/provider/resource_target.go b/internal/provider/resource_target.go index 0dcee91f..08996d70 100644 --- a/internal/provider/resource_target.go +++ b/internal/provider/resource_target.go @@ -155,7 +155,7 @@ func setFromTargetResponseMap(d *schema.ResourceData, raw map[string]interface{} return nil } -func resourceTargetCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func resourceTargetCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) (errs diag.Diagnostics) { md := meta.(*metaData) var scopeId string @@ -256,7 +256,6 @@ func resourceTargetCreate(ctx context.Context, d *schema.ResourceData, meta inte } tc := targets.NewClient(md.client) - tcr, err := tc.Create(ctx, typeStr, scopeId, opts...) if err != nil { return diag.Errorf("error creating target: %v", err) @@ -264,7 +263,12 @@ func resourceTargetCreate(ctx context.Context, d *schema.ResourceData, meta inte if tcr == nil { return diag.Errorf("target nil after create") } - raw := tcr.GetResponse().Map + apiResponse := tcr.GetResponse().Map + defer func() { + if err := setFromTargetResponseMap(d, apiResponse); err != nil { + errs = append(errs, diag.FromErr(err)...) + } + }() version := tcr.Item.Version if hostSourceIds != nil { @@ -272,7 +276,10 @@ func resourceTargetCreate(ctx context.Context, d *schema.ResourceData, meta inte if err != nil { return diag.Errorf("error setting host sources on target: %v", err) } - raw = tur.GetResponse().Map + if tur == nil { + return diag.Errorf("nil target after setting host sources") + } + apiResponse = tur.GetResponse().Map version = tur.Item.Version } @@ -288,11 +295,10 @@ func resourceTargetCreate(ctx context.Context, d *schema.ResourceData, meta inte if err != nil { return diag.Errorf("error setting credential sources on target: %v", err) } - raw = tur.GetResponse().Map - } - - if err := setFromTargetResponseMap(d, raw); err != nil { - return diag.FromErr(err) + if tur == nil { + return diag.Errorf("nil target after setting credential sources") + } + apiResponse = tur.GetResponse().Map } return nil diff --git a/internal/provider/resource_target_test.go b/internal/provider/resource_target_test.go index 184fb816..77e755db 100644 --- a/internal/provider/resource_target_test.go +++ b/internal/provider/resource_target_test.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "net/http" + "regexp" "testing" "github.com/hashicorp/boundary/api" @@ -130,6 +131,21 @@ resource "boundary_target" "foo" { session_connection_limit = 7 worker_filter = "type == \"bar\"" }`, fooTargetDescriptionUpdate) + + fooTargetPartialSuccess = fmt.Sprintf(` +resource "boundary_target" "foo" { + name = "expected_to_fail" + description = "%s" + type = "tcp" + scope_id = boundary_scope.proj1.id + default_port = 80 + depends_on = [boundary_role.proj1_admin] + session_max_seconds = 7000 + session_connection_limit = 7 + injected_application_credential_source_ids = [ + boundary_credential_library_vault.bar.id + ] +}`, fooTargetDescription) ) func TestAccTarget(t *testing.T) { @@ -200,6 +216,37 @@ func TestAccTarget(t *testing.T) { ), }, importStep("boundary_target.foo"), + { + // test updating state file when the target is created, but fails on associating an invalid injected credential source to a tcp target type + Config: testConfig(url, fooOrg, firstProjectFoo, credStoreRes, fooBarCredLibs, fooTargetPartialSuccess), + Check: resource.ComposeTestCheckFunc( + testAccCheckTargetResourceExists(provider, "boundary_target.foo"), + resource.TestCheckResourceAttr("boundary_target.foo", DescriptionKey, ""), + resource.TestCheckResourceAttr("boundary_target.foo", targetDefaultPortKey, "80"), + resource.TestCheckResourceAttr("boundary_target.foo", targetSessionMaxSecondsKey, "7000"), + resource.TestCheckResourceAttr("boundary_target.foo", targetSessionConnectionLimitKey, "7"), + resource.TestCheckResourceAttr("boundary_target.foo", targetHostSourceIdsKey+".%", "0"), + resource.TestCheckResourceAttr("boundary_target.foo", targetInjectedAppCredentialSourceIdsKey+".%", "0"), + ), + ExpectError: regexp.MustCompile("Unable to set credential sources in target: tcp.VetCredentialSources: tcp.Target only supports credential purpose"), + }, + importStep("boundary_target.foo", targetInjectedAppCredentialSourceIdsKey), + { + // test resolving invalid injected credential source error without raising duplicate name error, due to state file not being in sync. + Config: testConfig(url, fooOrg, firstProjectFoo, credStoreRes, fooBarCredLibs, fooTargetUpdateUnsetHostAndCredSources), + Check: resource.ComposeTestCheckFunc( + testAccCheckTargetResourceExists(provider, "boundary_target.foo"), + resource.TestCheckResourceAttr("boundary_target.foo", DescriptionKey, fooTargetDescriptionUpdate), + resource.TestCheckResourceAttr("boundary_target.foo", targetDefaultPortKey, "80"), + resource.TestCheckResourceAttr("boundary_target.foo", targetSessionMaxSecondsKey, "7000"), + resource.TestCheckResourceAttr("boundary_target.foo", targetSessionConnectionLimitKey, "7"), + resource.TestCheckResourceAttr("boundary_target.foo", targetWorkerFilterKey, `type == "bar"`), + resource.TestCheckResourceAttr("boundary_target.foo", targetHostSourceIdsKey+".%", "0"), + resource.TestCheckResourceAttr("boundary_target.foo", targetInjectedAppCredentialSourceIdsKey+".%", "0"), + resource.TestCheckResourceAttr("boundary_target.foo", targetBrokeredCredentialSourceIdsKey+".%", "0"), + ), + }, + importStep("boundary_target.foo"), }, }) } diff --git a/internal/provider/resource_user.go b/internal/provider/resource_user.go index 3e2678dd..9cb9ce4c 100644 --- a/internal/provider/resource_user.go +++ b/internal/provider/resource_user.go @@ -73,7 +73,7 @@ func setFromUserResponseMap(d *schema.ResourceData, raw map[string]interface{}) return nil } -func resourceUserCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func resourceUserCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) (errs diag.Diagnostics) { md := meta.(*metaData) var scopeId string @@ -106,7 +106,12 @@ func resourceUserCreate(ctx context.Context, d *schema.ResourceData, meta interf if ucr == nil { return diag.Errorf("user nil after create") } - raw := ucr.GetResponse().Map + apiResponse := ucr.GetResponse().Map + defer func() { + if err := setFromUserResponseMap(d, apiResponse); err != nil { + errs = append(errs, diag.FromErr(err)...) + } + }() if val, ok := d.GetOk(userAccountIDsKey); ok { list := val.(*schema.Set).List() @@ -121,11 +126,7 @@ func resourceUserCreate(ctx context.Context, d *schema.ResourceData, meta interf if usrac == nil { return diag.Errorf("user nil after setting accounts") } - raw = usrac.GetResponse().Map - } - - if err := setFromUserResponseMap(d, raw); err != nil { - return diag.FromErr(err) + apiResponse = usrac.GetResponse().Map } return nil