Skip to content

Commit

Permalink
fix: service user update lag (#1988)
Browse files Browse the repository at this point in the history
  • Loading branch information
byashimov authored Jan 14, 2025
1 parent b4cc154 commit a0a8eeb
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ nav_order: 1
- Replaced `aiven-go-client/v2` with `aiven/go-client-codegen` in `aiven_pg_user` resource/data source
- Fix `aiven_pg_user` creating in bulk occasionally results in a 404 error
- Use `aiven_pg_user` handlers and schema in `aiven_alloydbomni_user` resource
- Ensure the service user's password is updated correctly

## [4.31.1] - 2024-12-23

Expand Down
15 changes: 0 additions & 15 deletions internal/common/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (

"github.com/aiven/aiven-go-client/v2"
avngen "github.com/aiven/go-client-codegen"
"github.com/avast/retry-go"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
Expand Down Expand Up @@ -120,17 +119,3 @@ func TokenOpt(v string) ClientOpt {
o.token = v
}
}

// RetryCrudNotFound retries the handler if the error is NotFound
// This happens when GET called right after CREATE, and the resource is not yet available
func RetryCrudNotFound(f CrudHandler) CrudHandler {
return func(ctx context.Context, d *schema.ResourceData, client avngen.Client) error {
return retry.Do(
func() error {
return f(ctx, d, client)
},
retry.Context(ctx),
retry.RetryIf(avngen.IsNotFound),
)
}
}
14 changes: 14 additions & 0 deletions internal/schemautil/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package schemautil

import (
"context"
"errors"
"fmt"
"strings"

Expand All @@ -10,6 +11,7 @@ import (
"github.com/aiven/go-client-codegen/handler/service"
"github.com/docker/go-units"
"github.com/hashicorp/go-cty/cty"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
Expand Down Expand Up @@ -190,3 +192,15 @@ func StringToDiagWarning(msg string) diag.Diagnostics {
func ErrorToDiagWarning(err error) diag.Diagnostics {
return StringToDiagWarning(err.Error())
}

// ErrorFromDiagnostics converts diag.Diagnostics to error.
// Warning: ignores diag.Warning level diagnostics.
func ErrorFromDiagnostics(diags diag.Diagnostics) error {
var err error
for _, v := range diags {
if v.Severity == diag.Error {
err = multierror.Append(err, errors.New(v.Summary))
}
}
return err
}
34 changes: 32 additions & 2 deletions internal/schemautil/service_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import (
"context"
"fmt"
"log"
"time"

"github.com/aiven/aiven-go-client/v2"
avngen "github.com/aiven/go-client-codegen"
"github.com/avast/retry-go"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
Expand Down Expand Up @@ -43,7 +45,13 @@ func ResourceServiceUserCreate(ctx context.Context, d *schema.ResourceData, m in

d.SetId(BuildResourceID(projectName, serviceName, username))

return ResourceServiceUserRead(ctx, d, m)
// Retry because the user may not be immediately available
err = RetryNotFound(ctx, func() error {
err := ResourceServiceUserRead(ctx, d, m)
return ErrorFromDiagnostics(err)
})

return diag.FromErr(err)
}

func ResourceServiceUserUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
Expand All @@ -65,6 +73,11 @@ func ResourceServiceUserUpdate(ctx context.Context, d *schema.ResourceData, m in
return ResourceServiceUserRead(ctx, d, m)
}

// RetryPasswordIsNullAttempts
// User password might be Null https://api.aiven.io/doc/#tag/Service/operation/ServiceUserGet
// > Account password. A null value indicates a user overridden password.
const RetryPasswordIsNullAttempts = 5

func ResourceServiceUserRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
client := m.(*aiven.Client)

Expand All @@ -73,7 +86,24 @@ func ResourceServiceUserRead(ctx context.Context, d *schema.ResourceData, m inte
return diag.FromErr(err)
}

user, err := client.ServiceUsers.Get(ctx, projectName, serviceName, username)
var user *aiven.ServiceUser
err = retry.Do(
func() error {
user, err = client.ServiceUsers.Get(ctx, projectName, serviceName, username)
if err != nil {
return retry.Unrecoverable(err)
}
// The field is not nullable, so we compare to an empty string
if user.Password == "" {
return fmt.Errorf("password is not received from the API")
}
return nil
},
retry.Context(ctx),
retry.Delay(time.Second),
retry.Attempts(RetryPasswordIsNullAttempts),
)

if err != nil {
return diag.FromErr(ResourceReadHandleNotFound(err, d))
}
Expand Down
13 changes: 13 additions & 0 deletions internal/schemautil/wait.go
Original file line number Diff line number Diff line change
Expand Up @@ -374,3 +374,16 @@ func WaitUntilNotFound(ctx context.Context, retryableFunc retryGo.RetryableFunc)
retryGo.Delay(common.DefaultStateChangeDelay),
)
}

// retryNotFoundAttempts just a random number
const retryNotFoundAttempts = 10

func RetryNotFound(ctx context.Context, retryableFunc retryGo.RetryableFunc) error {
return retryGo.Do(
retryableFunc,
retryGo.Context(ctx),
retryGo.Delay(time.Second),
retryGo.Attempts(retryNotFoundAttempts),
retryGo.RetryIf(IsNotFound),
)
}
33 changes: 29 additions & 4 deletions internal/sdkprovider/service/pg/pg_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package pg
import (
"context"
"fmt"
"time"

avngen "github.com/aiven/go-client-codegen"
"github.com/aiven/go-client-codegen/handler/service"
"github.com/avast/retry-go"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"

"github.com/aiven/terraform-provider-aiven/internal/common"
Expand Down Expand Up @@ -92,7 +94,8 @@ func ResourcePGUserCreate(ctx context.Context, d *schema.ResourceData, client av
return err
}

if _, ok := d.GetOk("password"); ok {
password := d.Get("password").(string)
if password != "" {
_, err = client.ServiceUserCredentialsModify(
ctx, projectName, serviceName, username,
&service.ServiceUserCredentialsModifyIn{
Expand All @@ -106,7 +109,11 @@ func ResourcePGUserCreate(ctx context.Context, d *schema.ResourceData, client av
}

d.SetId(schemautil.BuildResourceID(projectName, serviceName, username))
return common.RetryCrudNotFound(ResourcePGUserRead)(ctx, d, client)

// Retry because the user may not be immediately available
return schemautil.RetryNotFound(ctx, func() error {
return ResourcePGUserRead(ctx, d, client)
})
}

func ResourcePGUserUpdate(ctx context.Context, d *schema.ResourceData, client avngen.Client) error {
Expand All @@ -127,8 +134,8 @@ func ResourcePGUserUpdate(ctx context.Context, d *schema.ResourceData, client av
return err
}

allowReplication := d.Get("pg_allow_replication").(bool)
if d.HasChange("pg_allow_replication") {
allowReplication := d.Get("pg_allow_replication").(bool)
req := &service.ServiceUserCredentialsModifyIn{
Operation: service.ServiceUserCredentialsModifyOperationTypeSetAccessControl,
AccessControl: &service.AccessControlIn{
Expand All @@ -150,7 +157,25 @@ func ResourcePGUserRead(ctx context.Context, d *schema.ResourceData, client avng
return err
}

user, err := client.ServiceUserGet(ctx, projectName, serviceName, username)
// See schemautil.RetryPasswordIsNullAttempts
var user *service.ServiceUserGetOut
err = retry.Do(
func() error {
user, err = client.ServiceUserGet(ctx, projectName, serviceName, username)
if err != nil {
return retry.Unrecoverable(err)
}
// The field is not nullable, so we compare to an empty string
if user.Password == "" {
return fmt.Errorf("password is not received from the API")
}
return nil
},
retry.Context(ctx),
retry.Delay(time.Second),
retry.Attempts(schemautil.RetryPasswordIsNullAttempts),
)

if err != nil {
return schemautil.ResourceReadHandleNotFound(err, d)
}
Expand Down

0 comments on commit a0a8eeb

Please sign in to comment.