Repository: go-to-k/delstack
Pull request #80 (2023-01-21 13:58:11)

refactor: create a wait function for wrap


Patch:
diff --git a/.gitignore b/.gitignore
index eced22f..803cf47 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,4 +23,5 @@ go.work
 dist/
 cover_file.html
 /delstack
-test_root_output.yaml
\ No newline at end of file
+test_root_output.yaml
+testfiles/
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 8261bc4..95439e1 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -12,6 +12,7 @@
         "Unshallow",
         "unshallow",
         "gomod",
+        "retryable",
     ],
     "gopls": {
         "analyses": {
diff --git a/internal/operation/operator_factory.go b/internal/operation/operator_factory.go
index e3325ab..5d49f39 100644
--- a/internal/operation/operator_factory.go
+++ b/internal/operation/operator_factory.go
@@ -10,6 +10,8 @@ import (
 	"github.com/go-to-k/delstack/pkg/client"
 )
 
+const awsSDKRetryMaxAttempts = 3
+
 type IOperatorFactory interface {
 	CreateStackOperator(targetResourceTypes []string) *StackOperator
 	CreateBackupVaultOperator() *BackupVaultOperator
@@ -32,7 +34,10 @@ func NewOperatorFactory(config aws.Config) *OperatorFactory {
 }
 
 func (f *OperatorFactory) CreateStackOperator(targetResourceTypes []string) *StackOperator {
-	sdkCfnClient := cloudformation.NewFromConfig(f.config)
+	sdkCfnClient := cloudformation.NewFromConfig(f.config, func(o *cloudformation.Options) {
+		o.RetryMaxAttempts = awsSDKRetryMaxAttempts
+		o.RetryMode = aws.RetryModeStandard
+	})
 	sdkCfnWaiter := cloudformation.NewStackDeleteCompleteWaiter(sdkCfnClient)
 
 	return NewStackOperator(
@@ -46,7 +51,10 @@ func (f *OperatorFactory) CreateStackOperator(targetResourceTypes []string) *Sta
 }
 
 func (f *OperatorFactory) CreateBackupVaultOperator() *BackupVaultOperator {
-	sdkBackupClient := backup.NewFromConfig(f.config)
+	sdkBackupClient := backup.NewFromConfig(f.config, func(o *backup.Options) {
+		o.RetryMaxAttempts = awsSDKRetryMaxAttempts
+		o.RetryMode = aws.RetryModeStandard
+	})
 
 	return NewBackupVaultOperator(
 		client.NewBackup(
@@ -56,7 +64,10 @@ func (f *OperatorFactory) CreateBackupVaultOperator() *BackupVaultOperator {
 }
 
 func (f *OperatorFactory) CreateEcrOperator() *EcrOperator {
-	sdkEcrClient := ecr.NewFromConfig(f.config)
+	sdkEcrClient := ecr.NewFromConfig(f.config, func(o *ecr.Options) {
+		o.RetryMaxAttempts = awsSDKRetryMaxAttempts
+		o.RetryMode = aws.RetryModeStandard
+	})
 
 	return NewEcrOperator(
 		client.NewEcr(
@@ -66,7 +77,10 @@ func (f *OperatorFactory) CreateEcrOperator() *EcrOperator {
 }
 
 func (f *OperatorFactory) CreateRoleOperator() *RoleOperator {
-	sdkIamClient := iam.NewFromConfig(f.config)
+	sdkIamClient := iam.NewFromConfig(f.config, func(o *iam.Options) {
+		o.RetryMaxAttempts = awsSDKRetryMaxAttempts
+		o.RetryMode = aws.RetryModeStandard
+	})
 
 	return NewRoleOperator(
 		client.NewIam(
@@ -76,7 +90,10 @@ func (f *OperatorFactory) CreateRoleOperator() *RoleOperator {
 }
 
 func (f *OperatorFactory) CreateBucketOperator() *BucketOperator {
-	sdkS3Client := s3.NewFromConfig(f.config)
+	sdkS3Client := s3.NewFromConfig(f.config, func(o *s3.Options) {
+		o.RetryMaxAttempts = awsSDKRetryMaxAttempts
+		o.RetryMode = aws.RetryModeStandard
+	})
 
 	return NewBucketOperator(
 		client.NewS3(
diff --git a/pkg/client/iam.go b/pkg/client/iam.go
index cba25e9..a02daf1 100644
--- a/pkg/client/iam.go
+++ b/pkg/client/iam.go
@@ -33,39 +33,21 @@ func (i *Iam) DeleteRole(ctx context.Context, roleName *string, sleepTimeSec int
 		RoleName: roleName,
 	}
 
-	_, err := i.deleteRoleWithRetry(ctx, input, roleName, sleepTimeSec)
-	return err
-}
-
-func (i *Iam) deleteRoleWithRetry(
-	ctx context.Context,
-	input *iam.DeleteRoleInput,
-	roleName *string,
-	sleepTimeSec int,
-) (*iam.DeleteRoleOutput, error) {
-	retryCount := 0
-
-	for {
-		select {
-		case <-ctx.Done():
-			return nil, ctx.Err()
-		default:
-		}
-
-		output, err := i.client.DeleteRole(ctx, input)
-		if err != nil && strings.Contains(err.Error(), "api error Throttling: Rate exceeded") {
-			retryCount++
-			if err := WaitForRetry(retryCount, sleepTimeSec, roleName, err); err != nil {
-				return nil, err
-			}
-			continue
-		}
-		if err != nil {
-			return nil, err
-		}
-
-		return output, nil
+	retryable := func(err error) bool {
+		return strings.Contains(err.Error(), "api error Throttling: Rate exceeded")
 	}
+
+	_, err := Retry(
+		&RetryInput[iam.DeleteRoleInput, iam.DeleteRoleOutput, iam.Options]{
+			Ctx:              ctx,
+			SleepTimeSec:     sleepTimeSec,
+			TargetResource:   roleName,
+			Input:            input,
+			ApiCaller:        i.client.DeleteRole,
+			RetryableChecker: retryable,
+		},
+	)
+	return err
 }
 
 func (i *Iam) ListAttachedRolePolicies(ctx context.Context, roleName *string) ([]types.AttachedPolicy, error) {
@@ -116,39 +98,21 @@ func (i *Iam) DetachRolePolicy(ctx context.Context, roleName *string, PolicyArn
 		RoleName:  roleName,
 	}
 
-	_, err := i.detachRolePolicyWithRetry(ctx, input, roleName, sleepTimeSec)
-	return err
-}
-
-func (i *Iam) detachRolePolicyWithRetry(
-	ctx context.Context,
-	input *iam.DetachRolePolicyInput,
-	roleName *string,
-	sleepTimeSec int,
-) (*iam.DetachRolePolicyOutput, error) {
-	retryCount := 0
-
-	for {
-		select {
-		case <-ctx.Done():
-			return nil, ctx.Err()
-		default:
-		}
-
-		output, err := i.client.DetachRolePolicy(ctx, input)
-		if err != nil && strings.Contains(err.Error(), "api error Throttling: Rate exceeded") {
-			retryCount++
-			if err := WaitForRetry(retryCount, sleepTimeSec, roleName, err); err != nil {
-				return nil, err
-			}
-			continue
-		}
-		if err != nil {
-			return nil, err
-		}
-
-		return output, nil
+	retryable := func(err error) bool {
+		return strings.Contains(err.Error(), "api error Throttling: Rate exceeded")
 	}
+
+	_, err := Retry(
+		&RetryInput[iam.DetachRolePolicyInput, iam.DetachRolePolicyOutput, iam.Options]{
+			Ctx:              ctx,
+			SleepTimeSec:     sleepTimeSec,
+			TargetResource:   roleName,
+			Input:            input,
+			ApiCaller:        i.client.DetachRolePolicy,
+			RetryableChecker: retryable,
+		},
+	)
+	return err
 }
 
 func (i *Iam) CheckRoleExists(ctx context.Context, roleName *string) (bool, error) {
diff --git a/pkg/client/s3.go b/pkg/client/s3.go
index 4c52437..e36fa6f 100644
--- a/pkg/client/s3.go
+++ b/pkg/client/s3.go
@@ -94,7 +94,20 @@ func (s *S3) DeleteObjects(ctx context.Context, bucketName *string, objects []ty
 		eg.Go(func() error {
 			defer sem.Release(1)
 
-			output, err := s.deleteObjectsWithRetry(ctx, input, bucketName, sleepTimeSec)
+			retryable := func(err error) bool {
+				return strings.Contains(err.Error(), "api error SlowDown")
+			}
+
+			output, err := Retry(
+				&RetryInput[s3.DeleteObjectsInput, s3.DeleteObjectsOutput, s3.Options]{
+					Ctx:              ctx,
+					SleepTimeSec:     sleepTimeSec,
+					TargetResource:   bucketName,
+					Input:            input,
+					ApiCaller:        s.client.DeleteObjects,
+					RetryableChecker: retryable,
+				},
+			)
 			if err != nil {
 				return err
 			}
@@ -123,37 +136,6 @@ func (s *S3) DeleteObjects(ctx context.Context, bucketName *string, objects []ty
 	return errors, nil
 }
 
-func (s *S3) deleteObjectsWithRetry(
-	ctx context.Context,
-	input *s3.DeleteObjectsInput,
-	bucketName *string,
-	sleepTimeSec int,
-) (*s3.DeleteObjectsOutput, error) {
-	retryCount := 0
-
-	for {
-		select {
-		case <-ctx.Done():
-			return nil, ctx.Err()
-		default:
-		}
-
-		output, err := s.client.DeleteObjects(ctx, input)
-		if err != nil && strings.Contains(err.Error(), "api error SlowDown") {
-			retryCount++
-			if err := WaitForRetry(retryCount, sleepTimeSec, bucketName, err); err != nil {
-				return nil, err
-			}
-			continue
-		}
-		if err != nil {
-			return nil, err
-		}
-
-		return output, nil
-	}
-}
-
 func (s *S3) ListObjectVersions(ctx context.Context, bucketName *string) ([]types.ObjectIdentifier, error) {
 	var keyMarker *string
 	var versionIdMarker *string
diff --git a/pkg/client/waiter.go b/pkg/client/waiter.go
index 071d430..93e0f7b 100644
--- a/pkg/client/waiter.go
+++ b/pkg/client/waiter.go
@@ -1,6 +1,7 @@
 package client
 
 import (
+	"context"
 	"fmt"
 	"math/rand"
 	"strconv"
@@ -9,18 +10,63 @@ import (
 
 const maxRetryCount = 10
 
-func WaitForRetry(retryCount int, sleepTimeSec int, targetResource *string, err error) error {
+// T: Input type for API Request.
+// U: Output type for API Response.
+// V: Option Functions type for API Request.
+type RetryInput[T, U, V any] struct {
+	Ctx              context.Context
+	SleepTimeSec     int
+	TargetResource   *string
+	Input            *T
+	ApiOptions       []func(*V)
+	ApiCaller        func(ctx context.Context, input *T, optFns ...func(*V)) (*U, error)
+	RetryableChecker func(error) bool
+}
+
+// T: Input type for API Request.
+// U: Output type for API Response.
+// V: Option Functions type for API Request.
+func Retry[T, U, V any](
+	in *RetryInput[T, U, V],
+) (*U, error) {
+	retryCount := 0
+
+	for {
+		output, err := in.ApiCaller(in.Ctx, in.Input, in.ApiOptions...)
+		if err == nil {
+			return output, nil
+		}
+
+		if in.RetryableChecker(err) {
+			retryCount++
+			if err := waitForRetry(in.Ctx, retryCount, in.SleepTimeSec, in.TargetResource, err); err != nil {
+				return nil, err
+			}
+			continue
+		}
+		return nil, err
+	}
+}
+
+func waitForRetry(ctx context.Context, retryCount int, sleepTimeSec int, targetResource *string, err error) error {
 	if retryCount > maxRetryCount {
 		errorDetail := err.Error() + "\nRetryCount(" + strconv.Itoa(maxRetryCount) + ") over, but failed to delete. "
 		return fmt.Errorf("RetryCountOverError: %v, %v", *targetResource, errorDetail)
 	}
 
+	select {
+	case <-ctx.Done():
+		return ctx.Err()
+	case <-time.After(getRandomSleepTime(sleepTimeSec)):
+	}
+	return nil
+}
+
+func getRandomSleepTime(sleepTimeSec int) time.Duration {
 	rand.Seed(time.Now().UnixNano())
 	waitTime := 1
 	if sleepTimeSec > 1 {
 		waitTime += rand.Intn(sleepTimeSec)
 	}
-	time.Sleep(time.Duration(waitTime) * time.Second)
-
-	return nil
+	return time.Duration(waitTime) * time.Second
 }


Test patch:
diff --git a/pkg/client/backup_test.go b/pkg/client/backup_test.go
index f564bb8..4494e85 100644
--- a/pkg/client/backup_test.go
+++ b/pkg/client/backup_test.go
@@ -13,6 +13,22 @@ import (
 	"github.com/aws/smithy-go/middleware"
 )
 
+type tokenKeyForBackup struct{}
+
+func getNextTokenForBackupInitialize(
+	ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler,
+) (
+	out middleware.InitializeOutput, metadata middleware.Metadata, err error,
+) {
+	switch v := in.Parameters.(type) {
+	case *backup.ListRecoveryPointsByBackupVaultInput:
+		ctx = middleware.WithStackValue(ctx, tokenKeyForBackup{}, v.NextToken)
+	case *backup.ListBackupVaultsInput:
+		ctx = middleware.WithStackValue(ctx, tokenKeyForBackup{}, v.NextToken)
+	}
+	return next.HandleInitialize(ctx, in)
+}
+
 /*
 	Test Cases
 */
@@ -36,7 +52,7 @@ func TestBackup_ListRecoveryPointsByBackupVault(t *testing.T) {
 		wantErr bool
 	}{
 		{
-			name: "check backup vault exists successfully",
+			name: "list recovery points by backup vault successfully",
 			args: args{
 				ctx:             context.Background(),
 				backupVaultName: aws.String("test"),
@@ -81,7 +97,7 @@ func TestBackup_ListRecoveryPointsByBackupVault(t *testing.T) {
 			wantErr: false,
 		},
 		{
-			name: "check backup vault exists failure",
+			name: "list recovery points by backup vault failure",
 			args: args{
 				ctx:             context.Background(),
 				backupVaultName: aws.String("test"),
@@ -105,6 +121,156 @@ func TestBackup_ListRecoveryPointsByBackupVault(t *testing.T) {
 			},
 			wantErr: true,
 		},
+		{
+			name: "list recovery points by backup vault with next token successfully",
+			args: args{
+				ctx:             context.Background(),
+				backupVaultName: aws.String("test"),
+				withAPIOptionsFunc: func(stack *middleware.Stack) error {
+					err := stack.Initialize.Add(
+						middleware.InitializeMiddlewareFunc(
+							"GetNextToken",
+							getNextTokenForBackupInitialize,
+						), middleware.Before,
+					)
+					if err != nil {
+						return err
+					}
+
+					err = stack.Finalize.Add(
+						middleware.FinalizeMiddlewareFunc(
+							"ListRecoveryPointsByBackupVaultWithNextTokenMock",
+							func(ctx context.Context, input middleware.FinalizeInput, handler middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) {
+								token := middleware.GetStackValue(ctx, tokenKeyForBackup{}).(*string)
+
+								var nextToken *string
+								var recoveryPoints []types.RecoveryPointByBackupVault
+								if token == nil {
+									nextToken = aws.String("NextToken")
+									recoveryPoints = []types.RecoveryPointByBackupVault{
+										{
+											BackupVaultName: aws.String("BackupVaultName1"),
+											BackupVaultArn:  aws.String("BackupVaultArn1"),
+										},
+										{
+											BackupVaultName: aws.String("BackupVaultName2"),
+											BackupVaultArn:  aws.String("BackupVaultArn2"),
+										},
+									}
+									return middleware.FinalizeOutput{
+										Result: &backup.ListRecoveryPointsByBackupVaultOutput{
+											NextToken:      nextToken,
+											RecoveryPoints: recoveryPoints,
+										},
+									}, middleware.Metadata{}, nil
+								} else {
+									recoveryPoints = []types.RecoveryPointByBackupVault{
+										{
+											BackupVaultName: aws.String("BackupVaultName3"),
+											BackupVaultArn:  aws.String("BackupVaultArn3"),
+										},
+										{
+											BackupVaultName: aws.String("BackupVaultName4"),
+											BackupVaultArn:  aws.String("BackupVaultArn4"),
+										},
+									}
+									return middleware.FinalizeOutput{
+										Result: &backup.ListRecoveryPointsByBackupVaultOutput{
+											NextToken:      nextToken,
+											RecoveryPoints: recoveryPoints,
+										},
+									}, middleware.Metadata{}, nil
+								}
+							},
+						),
+						middleware.Before,
+					)
+					return err
+				},
+			},
+			want: want{
+				output: []types.RecoveryPointByBackupVault{
+					{
+						BackupVaultArn:  aws.String("BackupVaultArn1"),
+						BackupVaultName: aws.String("BackupVaultName1"),
+					},
+					{
+						BackupVaultArn:  aws.String("BackupVaultArn2"),
+						BackupVaultName: aws.String("BackupVaultName2"),
+					},
+					{
+						BackupVaultArn:  aws.String("BackupVaultArn3"),
+						BackupVaultName: aws.String("BackupVaultName3"),
+					},
+					{
+						BackupVaultArn:  aws.String("BackupVaultArn4"),
+						BackupVaultName: aws.String("BackupVaultName4"),
+					},
+				},
+				err: nil,
+			},
+			wantErr: false,
+		},
+		{
+			name: "list recovery points by backup vault with next token failure",
+			args: args{
+				ctx:             context.Background(),
+				backupVaultName: aws.String("test"),
+				withAPIOptionsFunc: func(stack *middleware.Stack) error {
+					err := stack.Initialize.Add(
+						middleware.InitializeMiddlewareFunc(
+							"GetNextToken",
+							getNextTokenForBackupInitialize,
+						), middleware.Before,
+					)
+					if err != nil {
+						return err
+					}
+
+					err = stack.Finalize.Add(
+						middleware.FinalizeMiddlewareFunc(
+							"ListRecoveryPointsByBackupVaultWithNextTokenErrorMock",
+							func(ctx context.Context, input middleware.FinalizeInput, handler middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) {
+								token := middleware.GetStackValue(ctx, tokenKeyForBackup{}).(*string)
+
+								var nextToken *string
+								var recoveryPoints []types.RecoveryPointByBackupVault
+								if token == nil {
+									nextToken = aws.String("NextToken")
+									recoveryPoints = []types.RecoveryPointByBackupVault{
+										{
+											BackupVaultName: aws.String("BackupVaultName1"),
+											BackupVaultArn:  aws.String("BackupVaultArn1"),
+										},
+										{
+											BackupVaultName: aws.String("BackupVaultName2"),
+											BackupVaultArn:  aws.String("BackupVaultArn2"),
+										},
+									}
+									return middleware.FinalizeOutput{
+										Result: &backup.ListRecoveryPointsByBackupVaultOutput{
+											NextToken:      nextToken,
+											RecoveryPoints: recoveryPoints,
+										},
+									}, middleware.Metadata{}, nil
+								} else {
+									return middleware.FinalizeOutput{
+										Result: &backup.ListRecoveryPointsByBackupVaultOutput{},
+									}, middleware.Metadata{}, fmt.Errorf("ListRecoveryPointsByBackupVaultError")
+								}
+							},
+						),
+						middleware.Before,
+					)
+					return err
+				},
+			},
+			want: want{
+				output: nil,
+				err:    fmt.Errorf("operation error Backup: ListRecoveryPointsByBackupVault, ListRecoveryPointsByBackupVaultError"),
+			},
+			wantErr: true,
+		},
 	}
 
 	for _, tt := range cases {
@@ -455,7 +621,7 @@ func TestBackup_CheckBackupVaultExists(t *testing.T) {
 		wantErr bool
 	}{
 		{
-			name: "check backup vault for backup vault exists",
+			name: "check backup vault exists",
 			args: args{
 				ctx:             context.Background(),
 				backupVaultName: aws.String("test"),
@@ -489,7 +655,7 @@ func TestBackup_CheckBackupVaultExists(t *testing.T) {
 			wantErr: false,
 		},
 		{
-			name: "check backup vault for backup vault do not exist",
+			name: "check backup vault does not exist",
 			args: args{
 				ctx:             context.Background(),
 				backupVaultName: aws.String("test"),
@@ -523,7 +689,7 @@ func TestBackup_CheckBackupVaultExists(t *testing.T) {
 			wantErr: false,
 		},
 		{
-			name: "check bucket exists failure",
+			name: "check backup vault exists failure",
 			args: args{
 				ctx:             context.Background(),
 				backupVaultName: aws.String("test"),
@@ -547,6 +713,202 @@ func TestBackup_CheckBackupVaultExists(t *testing.T) {
 			},
 			wantErr: true,
 		},
+		{
+			name: "check backup vault exists with next token",
+			args: args{
+				ctx:             context.Background(),
+				backupVaultName: aws.String("test"),
+				withAPIOptionsFunc: func(stack *middleware.Stack) error {
+					err := stack.Initialize.Add(
+						middleware.InitializeMiddlewareFunc(
+							"GetNextToken",
+							getNextTokenForBackupInitialize,
+						), middleware.Before,
+					)
+					if err != nil {
+						return err
+					}
+
+					err = stack.Finalize.Add(
+						middleware.FinalizeMiddlewareFunc(
+							"ListBackupVaultsWithNextTokenMock",
+							func(ctx context.Context, input middleware.FinalizeInput, handler middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) {
+								token := middleware.GetStackValue(ctx, tokenKeyForBackup{}).(*string)
+
+								var nextToken *string
+								var backupVaultListMember []types.BackupVaultListMember
+								if token == nil {
+									nextToken = aws.String("NextToken")
+									backupVaultListMember = []types.BackupVaultListMember{
+										{
+											BackupVaultName: aws.String("test0"),
+										},
+										{
+											BackupVaultName: aws.String("test2"),
+										},
+									}
+									return middleware.FinalizeOutput{
+										Result: &backup.ListBackupVaultsOutput{
+											NextToken:       nextToken,
+											BackupVaultList: backupVaultListMember,
+										},
+									}, middleware.Metadata{}, nil
+								} else {
+									backupVaultListMember = []types.BackupVaultListMember{
+										{
+											BackupVaultName: aws.String("test"),
+										},
+										{
+											BackupVaultName: aws.String("test3"),
+										},
+									}
+									return middleware.FinalizeOutput{
+										Result: &backup.ListBackupVaultsOutput{
+											NextToken:       nextToken,
+											BackupVaultList: backupVaultListMember,
+										},
+									}, middleware.Metadata{}, nil
+								}
+							},
+						),
+						middleware.Before,
+					)
+					return err
+				},
+			},
+			want: want{
+				exists: true,
+				err:    nil,
+			},
+			wantErr: false,
+		},
+		{
+			name: "check backup vault does not exist with next token",
+			args: args{
+				ctx:             context.Background(),
+				backupVaultName: aws.String("test"),
+				withAPIOptionsFunc: func(stack *middleware.Stack) error {
+					err := stack.Initialize.Add(
+						middleware.InitializeMiddlewareFunc(
+							"GetNextToken",
+							getNextTokenForBackupInitialize,
+						), middleware.Before,
+					)
+					if err != nil {
+						return err
+					}
+
+					err = stack.Finalize.Add(
+						middleware.FinalizeMiddlewareFunc(
+							"ListBackupVaultsWithNextTokenMock",
+							func(ctx context.Context, input middleware.FinalizeInput, handler middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) {
+								token := middleware.GetStackValue(ctx, tokenKeyForBackup{}).(*string)
+
+								var nextToken *string
+								var backupVaultListMember []types.BackupVaultListMember
+								if token == nil {
+									nextToken = aws.String("NextToken")
+									backupVaultListMember = []types.BackupVaultListMember{
+										{
+											BackupVaultName: aws.String("test0"),
+										},
+										{
+											BackupVaultName: aws.String("test2"),
+										},
+									}
+									return middleware.FinalizeOutput{
+										Result: &backup.ListBackupVaultsOutput{
+											NextToken:       nextToken,
+											BackupVaultList: backupVaultListMember,
+										},
+									}, middleware.Metadata{}, nil
+								} else {
+									backupVaultListMember = []types.BackupVaultListMember{
+										{
+											BackupVaultName: aws.String("test1"),
+										},
+										{
+											BackupVaultName: aws.String("test3"),
+										},
+									}
+									return middleware.FinalizeOutput{
+										Result: &backup.ListBackupVaultsOutput{
+											NextToken:       nextToken,
+											BackupVaultList: backupVaultListMember,
+										},
+									}, middleware.Metadata{}, nil
+								}
+							},
+						),
+						middleware.Before,
+					)
+					return err
+				},
+			},
+			want: want{
+				exists: false,
+				err:    nil,
+			},
+			wantErr: false,
+		},
+		{
+			name: "check backup vault exists failure",
+			args: args{
+				ctx:             context.Background(),
+				backupVaultName: aws.String("test"),
+				withAPIOptionsFunc: func(stack *middleware.Stack) error {
+					err := stack.Initialize.Add(
+						middleware.InitializeMiddlewareFunc(
+							"GetNextToken",
+							getNextTokenForBackupInitialize,
+						), middleware.Before,
+					)
+					if err != nil {
+						return err
+					}
+
+					err = stack.Finalize.Add(
+						middleware.FinalizeMiddlewareFunc(
+							"ListBackupVaultsWithNextTokenErrorMock",
+							func(ctx context.Context, input middleware.FinalizeInput, handler middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) {
+								token := middleware.GetStackValue(ctx, tokenKeyForBackup{}).(*string)
+
+								var nextToken *string
+								var backupVaultListMember []types.BackupVaultListMember
+								if token == nil {
+									nextToken = aws.String("NextToken")
+									backupVaultListMember = []types.BackupVaultListMember{
+										{
+											BackupVaultName: aws.String("test0"),
+										},
+										{
+											BackupVaultName: aws.String("test2"),
+										},
+									}
+									return middleware.FinalizeOutput{
+										Result: &backup.ListBackupVaultsOutput{
+											NextToken:       nextToken,
+											BackupVaultList: backupVaultListMember,
+										},
+									}, middleware.Metadata{}, nil
+								} else {
+									return middleware.FinalizeOutput{
+										Result: &backup.ListBackupVaultsOutput{},
+									}, middleware.Metadata{}, fmt.Errorf("ListBackupVaultsError")
+								}
+							},
+						),
+						middleware.Before,
+					)
+					return err
+				},
+			},
+			want: want{
+				exists: false,
+				err:    fmt.Errorf("operation error Backup: ListBackupVaults, ListBackupVaultsError"),
+			},
+			wantErr: true,
+		},
 	}
 
 	for _, tt := range cases {
diff --git a/pkg/client/cloudformation_test.go b/pkg/client/cloudformation_test.go
index cc3e3fa..10e152d 100644
--- a/pkg/client/cloudformation_test.go
+++ b/pkg/client/cloudformation_test.go
@@ -14,6 +14,22 @@ import (
 	"github.com/aws/smithy-go/middleware"
 )
 
+type tokenKeyForCloudFormation struct{}
+
+func getNextTokenForCloudFormationInitialize(
+	ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler,
+) (
+	out middleware.InitializeOutput, metadata middleware.Metadata, err error,
+) {
+	switch v := in.Parameters.(type) {
+	case *cloudformation.ListStackResourcesInput:
+		ctx = middleware.WithStackValue(ctx, tokenKeyForCloudFormation{}, v.NextToken)
+	case *cloudformation.ListStacksInput:
+		ctx = middleware.WithStackValue(ctx, tokenKeyForCloudFormation{}, v.NextToken)
+	}
+	return next.HandleInitialize(ctx, in)
+}
+
 /*
 	Test Cases
 */
@@ -594,6 +610,189 @@ func TestCloudFormation_ListStackResources(t *testing.T) {
 			},
 			wantErr: true,
 		},
+		{
+			name: "list stack resources with next token successfully",
+			args: args{
+				ctx:       context.Background(),
+				stackName: aws.String("test"),
+				withAPIOptionsFunc: func(stack *middleware.Stack) error {
+					err := stack.Initialize.Add(
+						middleware.InitializeMiddlewareFunc(
+							"GetNextToken",
+							getNextTokenForCloudFormationInitialize,
+						), middleware.Before,
+					)
+					if err != nil {
+						return err
+					}
+
+					err = stack.Finalize.Add(
+						middleware.FinalizeMiddlewareFunc(
+							"ListStackResourcesWithNextTokenMock",
+							func(ctx context.Context, input middleware.FinalizeInput, handler middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) {
+								token := middleware.GetStackValue(ctx, tokenKeyForCloudFormation{}).(*string)
+
+								var nextToken *string
+								var stackResourceSummaries []types.StackResourceSummary
+								if token == nil {
+									nextToken = aws.String("NextToken")
+									stackResourceSummaries = []types.StackResourceSummary{
+										{
+											LogicalResourceId:  aws.String("LogicalResourceId1"),
+											ResourceStatus:     "DELETE_FAILED",
+											ResourceType:       aws.String("AWS::CloudFormation::Stack"),
+											PhysicalResourceId: aws.String("PhysicalResourceId1"),
+										},
+										{
+											LogicalResourceId:  aws.String("LogicalResourceId2"),
+											ResourceStatus:     "DELETE_FAILED",
+											ResourceType:       aws.String("AWS::S3::Bucket"),
+											PhysicalResourceId: aws.String("PhysicalResourceId2"),
+										},
+									}
+									return middleware.FinalizeOutput{
+										Result: &cloudformation.ListStackResourcesOutput{
+											NextToken:              nextToken,
+											StackResourceSummaries: stackResourceSummaries,
+										},
+									}, middleware.Metadata{}, nil
+								} else {
+									stackResourceSummaries = []types.StackResourceSummary{
+										{
+											LogicalResourceId:  aws.String("LogicalResourceId3"),
+											ResourceStatus:     "DELETE_FAILED",
+											ResourceType:       aws.String("AWS::CloudFormation::Stack"),
+											PhysicalResourceId: aws.String("PhysicalResourceId3"),
+										},
+										{
+											LogicalResourceId:  aws.String("LogicalResourceId4"),
+											ResourceStatus:     "DELETE_FAILED",
+											ResourceType:       aws.String("AWS::S3::Bucket"),
+											PhysicalResourceId: aws.String("PhysicalResourceId4"),
+										},
+									}
+									return middleware.FinalizeOutput{
+										Result: &cloudformation.ListStackResourcesOutput{
+											NextToken:              nextToken,
+											StackResourceSummaries: stackResourceSummaries,
+										},
+									}, middleware.Metadata{}, nil
+								}
+							},
+						),
+						middleware.Before,
+					)
+					return err
+				},
+			},
+			want: want{
+				output: []types.StackResourceSummary{
+					{
+						LogicalResourceId:  aws.String("LogicalResourceId1"),
+						ResourceStatus:     "DELETE_FAILED",
+						ResourceType:       aws.String("AWS::CloudFormation::Stack"),
+						PhysicalResourceId: aws.String("PhysicalResourceId1"),
+					},
+					{
+						LogicalResourceId:  aws.String("LogicalResourceId2"),
+						ResourceStatus:     "DELETE_FAILED",
+						ResourceType:       aws.String("AWS::S3::Bucket"),
+						PhysicalResourceId: aws.String("PhysicalResourceId2"),
+					},
+					{
+						LogicalResourceId:  aws.String("LogicalResourceId3"),
+						ResourceStatus:     "DELETE_FAILED",
+						ResourceType:       aws.String("AWS::CloudFormation::Stack"),
+						PhysicalResourceId: aws.String("PhysicalResourceId3"),
+					},
+					{
+						LogicalResourceId:  aws.String("LogicalResourceId4"),
+						ResourceStatus:     "DELETE_FAILED",
+						ResourceType:       aws.String("AWS::S3::Bucket"),
+						PhysicalResourceId: aws.String("PhysicalResourceId4"),
+					},
+				},
+				err: nil,
+			},
+			wantErr: false,
+		},
+		{
+			name: "list stack resources with next token failure",
+			args: args{
+				ctx:       context.Background(),
+				stackName: aws.String("test"),
+				withAPIOptionsFunc: func(stack *middleware.Stack) error {
+					err := stack.Initialize.Add(
+						middleware.InitializeMiddlewareFunc(
+							"GetNextToken",
+							getNextTokenForCloudFormationInitialize,
+						), middleware.Before,
+					)
+					if err != nil {
+						return err
+					}
+
+					err = stack.Finalize.Add(
+						middleware.FinalizeMiddlewareFunc(
+							"ListStackResourcesWithNextTokenErrorMock",
+							func(ctx context.Context, input middleware.FinalizeInput, handler middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) {
+								token := middleware.GetStackValue(ctx, tokenKeyForCloudFormation{}).(*string)
+
+								var nextToken *string
+								var stackResourceSummaries []types.StackResourceSummary
+								if token == nil {
+									nextToken = aws.String("NextToken")
+									stackResourceSummaries = []types.StackResourceSummary{
+										{
+											LogicalResourceId:  aws.String("LogicalResourceId1"),
+											ResourceStatus:     "DELETE_FAILED",
+											ResourceType:       aws.String("AWS::CloudFormation::Stack"),
+											PhysicalResourceId: aws.String("PhysicalResourceId1"),
+										},
+										{
+											LogicalResourceId:  aws.String("LogicalResourceId2"),
+											ResourceStatus:     "DELETE_FAILED",
+											ResourceType:       aws.String("AWS::S3::Bucket"),
+											PhysicalResourceId: aws.String("PhysicalResourceId2"),
+										},
+									}
+									return middleware.FinalizeOutput{
+										Result: &cloudformation.ListStackResourcesOutput{
+											NextToken:              nextToken,
+											StackResourceSummaries: stackResourceSummaries,
+										},
+									}, middleware.Metadata{}, nil
+								} else {
+									return middleware.FinalizeOutput{
+										Result: &cloudformation.ListStackResourcesOutput{},
+									}, middleware.Metadata{}, fmt.Errorf("ListStackResourcesError")
+								}
+							},
+						),
+						middleware.Before,
+					)
+					return err
+				},
+			},
+			want: want{
+				output: []types.StackResourceSummary{
+					{
+						LogicalResourceId:  aws.String("LogicalResourceId1"),
+						ResourceStatus:     "DELETE_FAILED",
+						ResourceType:       aws.String("AWS::CloudFormation::Stack"),
+						PhysicalResourceId: aws.String("PhysicalResourceId1"),
+					},
+					{
+						LogicalResourceId:  aws.String("LogicalResourceId2"),
+						ResourceStatus:     "DELETE_FAILED",
+						ResourceType:       aws.String("AWS::S3::Bucket"),
+						PhysicalResourceId: aws.String("PhysicalResourceId2"),
+					},
+				},
+				err: fmt.Errorf("operation error CloudFormation: ListStackResources, ListStackResourcesError"),
+			},
+			wantErr: true,
+		},
 	}
 
 	for _, tt := range cases {
@@ -741,6 +940,162 @@ func TestCloudFormation_ListStacks(t *testing.T) {
 			},
 			wantErr: true,
 		},
+		{
+			name: "list stacks with next token successfully",
+			args: args{
+				ctx: context.Background(),
+				withAPIOptionsFunc: func(stack *middleware.Stack) error {
+					err := stack.Initialize.Add(
+						middleware.InitializeMiddlewareFunc(
+							"GetNextToken",
+							getNextTokenForCloudFormationInitialize,
+						), middleware.Before,
+					)
+					if err != nil {
+						return err
+					}
+
+					err = stack.Finalize.Add(
+						middleware.FinalizeMiddlewareFunc(
+							"ListStacksWithNextTokenMock",
+							func(ctx context.Context, input middleware.FinalizeInput, handler middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) {
+								token := middleware.GetStackValue(ctx, tokenKeyForCloudFormation{}).(*string)
+
+								var nextToken *string
+								var stackSummaries []types.StackSummary
+								if token == nil {
+									nextToken = aws.String("NextToken")
+									stackSummaries = []types.StackSummary{
+										{
+											StackName:   aws.String("TestStack1"),
+											StackStatus: types.StackStatusCreateComplete,
+										},
+										{
+											StackName:   aws.String("TestStack2"),
+											StackStatus: types.StackStatusCreateComplete,
+										},
+									}
+									return middleware.FinalizeOutput{
+										Result: &cloudformation.ListStacksOutput{
+											NextToken:      nextToken,
+											StackSummaries: stackSummaries,
+										},
+									}, middleware.Metadata{}, nil
+								} else {
+									stackSummaries = []types.StackSummary{
+										{
+											StackName:   aws.String("TestStack3"),
+											StackStatus: types.StackStatusCreateComplete,
+										},
+										{
+											StackName:   aws.String("TestStack4"),
+											StackStatus: types.StackStatusCreateComplete,
+										},
+									}
+									return middleware.FinalizeOutput{
+										Result: &cloudformation.ListStacksOutput{
+											NextToken:      nextToken,
+											StackSummaries: stackSummaries,
+										},
+									}, middleware.Metadata{}, nil
+								}
+							},
+						),
+						middleware.Before,
+					)
+					return err
+				},
+			},
+			want: want{
+				output: []types.StackSummary{
+					{
+						StackName:   aws.String("TestStack1"),
+						StackStatus: types.StackStatusCreateComplete,
+					},
+					{
+						StackName:   aws.String("TestStack2"),
+						StackStatus: types.StackStatusCreateComplete,
+					},
+					{
+						StackName:   aws.String("TestStack3"),
+						StackStatus: types.StackStatusCreateComplete,
+					},
+					{
+						StackName:   aws.String("TestStack4"),
+						StackStatus: types.StackStatusCreateComplete,
+					},
+				},
+				err: nil,
+			},
+			wantErr: false,
+		},
+		{
+			name: "list stacks with next token failure",
+			args: args{
+				ctx: context.Background(),
+				withAPIOptionsFunc: func(stack *middleware.Stack) error {
+					err := stack.Initialize.Add(
+						middleware.InitializeMiddlewareFunc(
+							"GetNextToken",
+							getNextTokenForCloudFormationInitialize,
+						), middleware.Before,
+					)
+					if err != nil {
+						return err
+					}
+
+					err = stack.Finalize.Add(
+						middleware.FinalizeMiddlewareFunc(
+							"ListStacksWithNextTokenErrorMock",
+							func(ctx context.Context, input middleware.FinalizeInput, handler middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) {
+								token := middleware.GetStackValue(ctx, tokenKeyForCloudFormation{}).(*string)
+
+								var nextToken *string
+								var stackSummaries []types.StackSummary
+								if token == nil {
+									nextToken = aws.String("NextToken")
+									stackSummaries = []types.StackSummary{
+										{
+											StackName:   aws.String("TestStack1"),
+											StackStatus: types.StackStatusCreateComplete,
+										},
+										{
+											StackName:   aws.String("TestStack2"),
+											StackStatus: types.StackStatusCreateComplete,
+										},
+									}
+									return middleware.FinalizeOutput{
+										Result: &cloudformation.ListStacksOutput{
+											NextToken:      nextToken,
+											StackSummaries: stackSummaries,
+										},
+									}, middleware.Metadata{}, nil
+								} else {
+									return middleware.FinalizeOutput{
+										Result: &cloudformation.ListStacksOutput{},
+									}, middleware.Metadata{}, fmt.Errorf("ListStacksError")
+								}
+							},
+						),
+						middleware.Before,
+					)
+					return err
+				},
+			},
+			want: want{
+				output: []types.StackSummary{
+					{
+						StackName:   aws.String("TestStack1"),
+						StackStatus: types.StackStatusCreateComplete,
+					},
+					{
+						StackName:   aws.String("TestStack2"),
+						StackStatus: types.StackStatusCreateComplete,
+					},
+				}, err: fmt.Errorf("operation error CloudFormation: ListStacks, ListStacksError"),
+			},
+			wantErr: true,
+		},
 	}
 
 	for _, tt := range cases {
diff --git a/pkg/client/iam_test.go b/pkg/client/iam_test.go
index 52b206b..46465ec 100644
--- a/pkg/client/iam_test.go
+++ b/pkg/client/iam_test.go
@@ -16,6 +16,20 @@ import (
 
 const sleepTimeSecForIam = 1
 
+type markerKeyForIam struct{}
+
+func getNextMarkerForIamInitialize(
+	ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler,
+) (
+	out middleware.InitializeOutput, metadata middleware.Metadata, err error,
+) {
+	switch v := in.Parameters.(type) {
+	case *iam.ListAttachedRolePoliciesInput:
+		ctx = middleware.WithStackValue(ctx, markerKeyForIam{}, v.Marker)
+	}
+	return next.HandleInitialize(ctx, in)
+}
+
 /*
 	Test Cases
 */
@@ -127,35 +141,47 @@ func TestIam_DeleteRole(t *testing.T) {
 	}
 }
 
-func TestIam_deleteRoleWithRetry(t *testing.T) {
+func TestIam_ListAttachedRolePolicies(t *testing.T) {
 	type args struct {
 		ctx                context.Context
-		input              *iam.DeleteRoleInput
 		roleName           *string
 		withAPIOptionsFunc func(*middleware.Stack) error
 	}
 
+	type want struct {
+		output []types.AttachedPolicy
+		err    error
+	}
+
 	cases := []struct {
 		name    string
 		args    args
-		want    error
+		want    want
 		wantErr bool
 	}{
 		{
-			name: "delete role successfully",
+			name: "list attached role policies successfully",
 			args: args{
-				ctx: context.Background(),
-				input: &iam.DeleteRoleInput{
-					RoleName: aws.String("test"),
-				},
+				ctx:      context.Background(),
 				roleName: aws.String("test"),
 				withAPIOptionsFunc: func(stack *middleware.Stack) error {
 					return stack.Finalize.Add(
 						middleware.FinalizeMiddlewareFunc(
-							"DeleteRoleMock",
+							"ListAttachedRolePoliciesMock",
 							func(context.Context, middleware.FinalizeInput, middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) {
 								return middleware.FinalizeOutput{
-									Result: &iam.DeleteRoleOutput{},
+									Result: &iam.ListAttachedRolePoliciesOutput{
+										AttachedPolicies: []types.AttachedPolicy{
+											{
+												PolicyArn:  aws.String("PolicyArn1"),
+												PolicyName: aws.String("PolicyName1"),
+											},
+											{
+												PolicyArn:  aws.String("PolicyArn2"),
+												PolicyName: aws.String("PolicyName2"),
+											},
+										},
+									},
 								}, middleware.Metadata{}, nil
 							},
 						),
@@ -163,133 +189,138 @@ func TestIam_deleteRoleWithRetry(t *testing.T) {
 					)
 				},
 			},
-			want:    nil,
+			want: want{
+				output: []types.AttachedPolicy{
+					{
+						PolicyArn:  aws.String("PolicyArn1"),
+						PolicyName: aws.String("PolicyName1"),
+					},
+					{
+						PolicyArn:  aws.String("PolicyArn2"),
+						PolicyName: aws.String("PolicyName2"),
+					},
+				},
+				err: nil,
+			},
 			wantErr: false,
 		},
 		{
-			name: "delete role failure",
+			name: "list attached role policies are empty",
 			args: args{
-				ctx: context.Background(),
-				input: &iam.DeleteRoleInput{
-					RoleName: aws.String("test"),
-				},
+				ctx:      context.Background(),
 				roleName: aws.String("test"),
 				withAPIOptionsFunc: func(stack *middleware.Stack) error {
 					return stack.Finalize.Add(
 						middleware.FinalizeMiddlewareFunc(
-							"DeleteRoleErrorMock",
+							"ListAttachedRolePoliciesMock",
 							func(context.Context, middleware.FinalizeInput, middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) {
 								return middleware.FinalizeOutput{
-									Result: &iam.DeleteRoleOutput{},
-								}, middleware.Metadata{}, fmt.Errorf("DeleteRoleError")
+									Result: &iam.ListAttachedRolePoliciesOutput{
+										AttachedPolicies: []types.AttachedPolicy{},
+									},
+								}, middleware.Metadata{}, nil
 							},
 						),
 						middleware.Before,
 					)
 				},
 			},
-			want:    fmt.Errorf("operation error IAM: DeleteRole, DeleteRoleError"),
-			wantErr: true,
+			want: want{
+				output: []types.AttachedPolicy{},
+				err:    nil,
+			},
+			wantErr: false,
 		},
 		{
-			name: "delete role failure for api error",
+			name: "list attached role policies failure",
 			args: args{
-				ctx: context.Background(),
-				input: &iam.DeleteRoleInput{
-					RoleName: aws.String("test"),
-				},
+				ctx:      context.Background(),
 				roleName: aws.String("test"),
 				withAPIOptionsFunc: func(stack *middleware.Stack) error {
 					return stack.Finalize.Add(
 						middleware.FinalizeMiddlewareFunc(
-							"DeleteRoleApiErrorMock",
+							"ListAttachedRolePoliciesErrorMock",
 							func(context.Context, middleware.FinalizeInput, middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) {
 								return middleware.FinalizeOutput{
-									Result: &iam.DeleteRoleOutput{},
-								}, middleware.Metadata{}, fmt.Errorf("api error Throttling: Rate exceeded")
+									Result: &iam.ListAttachedRolePoliciesOutput{},
+								}, middleware.Metadata{}, fmt.Errorf("ListAttachedRolePoliciesError")
 							},
 						),
 						middleware.Before,
 					)
 				},
 			},
-			want:    fmt.Errorf("RetryCountOverError: test, operation error IAM: DeleteRole, api error Throttling: Rate exceeded\nRetryCount(" + strconv.Itoa(maxRetryCount) + ") over, but failed to delete. "),
+			want: want{
+				output: nil,
+				err:    fmt.Errorf("operation error IAM: ListAttachedRolePolicies, ListAttachedRolePoliciesError"),
+			},
 			wantErr: true,
 		},
-	}
-
-	for _, tt := range cases {
-		t.Run(tt.name, func(t *testing.T) {
-			cfg, err := config.LoadDefaultConfig(
-				tt.args.ctx,
-				config.WithRegion("ap-northeast-1"),
-				config.WithAPIOptions([]func(*middleware.Stack) error{tt.args.withAPIOptionsFunc}),
-			)
-			if err != nil {
-				t.Fatal(err)
-			}
-
-			client := iam.NewFromConfig(cfg)
-			iamClient := NewIam(client)
-
-			_, err = iamClient.deleteRoleWithRetry(tt.args.ctx, tt.args.input, tt.args.roleName, sleepTimeSecForIam)
-			if (err != nil) != tt.wantErr {
-				t.Errorf("error = %#v, wantErr %#v", err, tt.wantErr)
-				return
-			}
-			if tt.wantErr && err.Error() != tt.want.Error() {
-				t.Errorf("err = %#v, want %#v", err, tt.want)
-			}
-		})
-	}
-}
-
-func TestIam_ListAttachedRolePolicies(t *testing.T) {
-	type args struct {
-		ctx                context.Context
-		roleName           *string
-		withAPIOptionsFunc func(*middleware.Stack) error
-	}
-
-	type want struct {
-		output []types.AttachedPolicy
-		err    error
-	}
-
-	cases := []struct {
-		name    string
-		args    args
-		want    want
-		wantErr bool
-	}{
 		{
-			name: "list attached role policies successfully",
+			name: "list attached role policies with next marker successfully",
 			args: args{
 				ctx:      context.Background(),
 				roleName: aws.String("test"),
 				withAPIOptionsFunc: func(stack *middleware.Stack) error {
-					return stack.Finalize.Add(
+					err := stack.Initialize.Add(
+						middleware.InitializeMiddlewareFunc(
+							"GetNextMarker",
+							getNextMarkerForIamInitialize,
+						), middleware.Before,
+					)
+					if err != nil {
+						return err
+					}
+
+					err = stack.Finalize.Add(
 						middleware.FinalizeMiddlewareFunc(
-							"ListAttachedRolePoliciesMock",
-							func(context.Context, middleware.FinalizeInput, middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) {
-								return middleware.FinalizeOutput{
-									Result: &iam.ListAttachedRolePoliciesOutput{
-										AttachedPolicies: []types.AttachedPolicy{
-											{
-												PolicyArn:  aws.String("PolicyArn1"),
-												PolicyName: aws.String("PolicyName1"),
-											},
-											{
-												PolicyArn:  aws.String("PolicyArn2"),
-												PolicyName: aws.String("PolicyName2"),
-											},
+							"ListAttachedRolePoliciesWithNextMarkerMock",
+							func(ctx context.Context, input middleware.FinalizeInput, handler middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) {
+								marker := middleware.GetStackValue(ctx, markerKeyForIam{}).(*string)
+
+								var nextMarker *string
+								var attachedPolicies []types.AttachedPolicy
+								if marker == nil {
+									nextMarker = aws.String("NextMarker")
+									attachedPolicies = []types.AttachedPolicy{
+										{
+											PolicyArn:  aws.String("PolicyArn1"),
+											PolicyName: aws.String("PolicyName1"),
 										},
-									},
-								}, middleware.Metadata{}, nil
+										{
+											PolicyArn:  aws.String("PolicyArn2"),
+											PolicyName: aws.String("PolicyName2"),
+										},
+									}
+									return middleware.FinalizeOutput{
+										Result: &iam.ListAttachedRolePoliciesOutput{
+											Marker:           nextMarker,
+											AttachedPolicies: attachedPolicies,
+										},
+									}, middleware.Metadata{}, nil
+								} else {
+									attachedPolicies = []types.AttachedPolicy{
+										{
+											PolicyArn:  aws.String("PolicyArn3"),
+											PolicyName: aws.String("PolicyName3"),
+										},
+										{
+											PolicyArn:  aws.String("PolicyArn4"),
+											PolicyName: aws.String("PolicyName4"),
+										},
+									}
+									return middleware.FinalizeOutput{
+										Result: &iam.ListAttachedRolePoliciesOutput{
+											Marker:           nextMarker,
+											AttachedPolicies: attachedPolicies,
+										},
+									}, middleware.Metadata{}, nil
+								}
 							},
 						),
 						middleware.Before,
 					)
+					return err
 				},
 			},
 			want: want{
@@ -302,28 +333,71 @@ func TestIam_ListAttachedRolePolicies(t *testing.T) {
 						PolicyArn:  aws.String("PolicyArn2"),
 						PolicyName: aws.String("PolicyName2"),
 					},
+					{
+						PolicyArn:  aws.String("PolicyArn3"),
+						PolicyName: aws.String("PolicyName3"),
+					},
+					{
+						PolicyArn:  aws.String("PolicyArn4"),
+						PolicyName: aws.String("PolicyName4"),
+					},
 				},
 				err: nil,
 			},
 			wantErr: false,
 		},
 		{
-			name: "list attached role policies failure",
+			name: "list attached role policies with next marker failure",
 			args: args{
 				ctx:      context.Background(),
 				roleName: aws.String("test"),
 				withAPIOptionsFunc: func(stack *middleware.Stack) error {
-					return stack.Finalize.Add(
+					err := stack.Initialize.Add(
+						middleware.InitializeMiddlewareFunc(
+							"GetNextMarker",
+							getNextMarkerForIamInitialize,
+						), middleware.Before,
+					)
+					if err != nil {
+						return err
+					}
+
+					err = stack.Finalize.Add(
 						middleware.FinalizeMiddlewareFunc(
-							"ListAttachedRolePoliciesErrorMock",
-							func(context.Context, middleware.FinalizeInput, middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) {
-								return middleware.FinalizeOutput{
-									Result: &iam.ListAttachedRolePoliciesOutput{},
-								}, middleware.Metadata{}, fmt.Errorf("ListAttachedRolePoliciesError")
+							"ListAttachedRolePoliciesWithNextMarkerErrorMock",
+							func(ctx context.Context, input middleware.FinalizeInput, handler middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) {
+								marker := middleware.GetStackValue(ctx, markerKeyForIam{}).(*string)
+
+								var nextMarker *string
+								var attachedPolicies []types.AttachedPolicy
+								if marker == nil {
+									nextMarker = aws.String("NextMarker")
+									attachedPolicies = []types.AttachedPolicy{
+										{
+											PolicyArn:  aws.String("PolicyArn1"),
+											PolicyName: aws.String("PolicyName1"),
+										},
+										{
+											PolicyArn:  aws.String("PolicyArn2"),
+											PolicyName: aws.String("PolicyName2"),
+										},
+									}
+									return middleware.FinalizeOutput{
+										Result: &iam.ListAttachedRolePoliciesOutput{
+											Marker:           nextMarker,
+											AttachedPolicies: attachedPolicies,
+										},
+									}, middleware.Metadata{}, nil
+								} else {
+									return middleware.FinalizeOutput{
+										Result: &iam.ListAttachedRolePoliciesOutput{},
+									}, middleware.Metadata{}, fmt.Errorf("ListAttachedRolePoliciesError")
+								}
 							},
 						),
 						middleware.Before,
 					)
+					return err
 				},
 			},
 			want: want{
@@ -636,126 +710,6 @@ func TestIam_DetachRolePolicy(t *testing.T) {
 	}
 }
 
-func TestIam_detachRolePolicyWithRetry(t *testing.T) {
-	type args struct {
-		ctx                context.Context
-		input              *iam.DetachRolePolicyInput
-		roleName           *string
-		withAPIOptionsFunc func(*middleware.Stack) error
-	}
-
-	cases := []struct {
-		name    string
-		args    args
-		want    error
-		wantErr bool
-	}{
-		{
-			name: "detach role policy successfully",
-			args: args{
-				ctx: context.Background(),
-				input: &iam.DetachRolePolicyInput{
-					PolicyArn: aws.String("test"),
-					RoleName:  aws.String("test"),
-				},
-				roleName: aws.String("test"),
-				withAPIOptionsFunc: func(stack *middleware.Stack) error {
-					return stack.Finalize.Add(
-						middleware.FinalizeMiddlewareFunc(
-							"DetachRolePolicyMock",
-							func(context.Context, middleware.FinalizeInput, middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) {
-								return middleware.FinalizeOutput{
-									Result: &iam.DetachRolePolicyOutput{},
-								}, middleware.Metadata{}, nil
-							},
-						),
-						middleware.Before,
-					)
-				},
-			},
-			want:    nil,
-			wantErr: false,
-		},
-		{
-			name: "detach role policy failure",
-			args: args{
-				ctx: context.Background(),
-				input: &iam.DetachRolePolicyInput{
-					PolicyArn: aws.String("test"),
-					RoleName:  aws.String("test"),
-				},
-				roleName: aws.String("test"),
-				withAPIOptionsFunc: func(stack *middleware.Stack) error {
-					return stack.Finalize.Add(
-						middleware.FinalizeMiddlewareFunc(
-							"DetachRolePolicyErrorMock",
-							func(context.Context, middleware.FinalizeInput, middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) {
-								return middleware.FinalizeOutput{
-									Result: &iam.DetachRolePolicyOutput{},
-								}, middleware.Metadata{}, fmt.Errorf("DetachRolePolicyError")
-							},
-						),
-						middleware.Before,
-					)
-				},
-			},
-			want:    fmt.Errorf("operation error IAM: DetachRolePolicy, DetachRolePolicyError"),
-			wantErr: true,
-		},
-		{
-			name: "detach role policy failure for api error",
-			args: args{
-				ctx: context.Background(),
-				input: &iam.DetachRolePolicyInput{
-					PolicyArn: aws.String("test"),
-					RoleName:  aws.String("test"),
-				},
-				roleName: aws.String("test"),
-				withAPIOptionsFunc: func(stack *middleware.Stack) error {
-					return stack.Finalize.Add(
-						middleware.FinalizeMiddlewareFunc(
-							"DetachRolePolicyMock",
-							func(context.Context, middleware.FinalizeInput, middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) {
-								return middleware.FinalizeOutput{
-									Result: &iam.DetachRolePolicyOutput{},
-								}, middleware.Metadata{}, fmt.Errorf("api error Throttling: Rate exceeded")
-							},
-						),
-						middleware.Before,
-					)
-				},
-			},
-			want:    fmt.Errorf("RetryCountOverError: test, operation error IAM: DetachRolePolicy, api error Throttling: Rate exceeded\nRetryCount(" + strconv.Itoa(maxRetryCount) + ") over, but failed to delete. "),
-			wantErr: true,
-		},
-	}
-
-	for _, tt := range cases {
-		t.Run(tt.name, func(t *testing.T) {
-			cfg, err := config.LoadDefaultConfig(
-				tt.args.ctx,
-				config.WithRegion("ap-northeast-1"),
-				config.WithAPIOptions([]func(*middleware.Stack) error{tt.args.withAPIOptionsFunc}),
-			)
-			if err != nil {
-				t.Fatal(err)
-			}
-
-			client := iam.NewFromConfig(cfg)
-			iamClient := NewIam(client)
-
-			_, err = iamClient.detachRolePolicyWithRetry(tt.args.ctx, tt.args.input, tt.args.roleName, sleepTimeSecForIam)
-			if (err != nil) != tt.wantErr {
-				t.Errorf("error = %#v, wantErr %#v", err, tt.wantErr)
-				return
-			}
-			if tt.wantErr && err.Error() != tt.want.Error() {
-				t.Errorf("err = %#v, want %#v", err, tt.want)
-			}
-		})
-	}
-}
-
 func TestIam_CheckRoleExists(t *testing.T) {
 	type args struct {
 		ctx                context.Context
diff --git a/pkg/client/s3_test.go b/pkg/client/s3_test.go
index 06f2808..54a20fc 100644
--- a/pkg/client/s3_test.go
+++ b/pkg/client/s3_test.go
@@ -17,6 +17,22 @@ import (
 
 const sleepTimeSecForS3 = 1
 
+type keyMarkerKeyForS3 struct{}
+type versionIdMarkerKeyForS3 struct{}
+
+func getNextMarkerForS3Initialize(
+	ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler,
+) (
+	out middleware.InitializeOutput, metadata middleware.Metadata, err error,
+) {
+	switch v := in.Parameters.(type) {
+	case *s3.ListObjectVersionsInput:
+		ctx = middleware.WithStackValue(ctx, keyMarkerKeyForS3{}, v.KeyMarker)
+		ctx = middleware.WithStackValue(ctx, versionIdMarkerKeyForS3{}, v.VersionIdMarker)
+	}
+	return next.HandleInitialize(ctx, in)
+}
+
 /*
 	Test Cases
 */
@@ -374,290 +390,6 @@ func TestS3_DeleteObjects(t *testing.T) {
 	}
 }
 
-func TestS3_deleteObjectsWithRetry(t *testing.T) {
-	objectsOverLimit := []types.ObjectIdentifier{}
-	s3DeleteObjectsSizeOverLimit := s3DeleteObjectsSizeLimit*int(runtime.NumCPU())*2 + 1 // loop over cpu core size for channel waiting when next loop
-	for i := 0; i < s3DeleteObjectsSizeOverLimit; i++ {
-		objectsOverLimit = append(objectsOverLimit, types.ObjectIdentifier{
-			Key:       aws.String("Key"),
-			VersionId: aws.String("VersionId"),
-		})
-	}
-
-	type args struct {
-		ctx                context.Context
-		input              *s3.DeleteObjectsInput
-		bucketName         *string
-		withAPIOptionsFunc func(*middleware.Stack) error
-	}
-
-	type partialOutput struct {
-		deleted []types.DeletedObject
-		errors  []types.Error
-	}
-
-	type want struct {
-		output *partialOutput
-		err    error
-	}
-
-	cases := []struct {
-		name    string
-		args    args
-		want    want
-		wantErr bool
-	}{
-		{
-			name: "delete objects successfully",
-			args: args{
-				ctx: context.Background(),
-				input: &s3.DeleteObjectsInput{
-					Bucket: aws.String("test"),
-					Delete: &types.Delete{
-						Objects: []types.ObjectIdentifier{{
-							Key:       aws.String("Key"),
-							VersionId: aws.String("VersionId")},
-						},
-						Quiet: *aws.Bool(true),
-					},
-				},
-				bucketName: aws.String("test"),
-				withAPIOptionsFunc: func(stack *middleware.Stack) error {
-					return stack.Finalize.Add(
-						middleware.FinalizeMiddlewareFunc(
-							"DeleteObjectsMock",
-							func(context.Context, middleware.FinalizeInput, middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) {
-								return middleware.FinalizeOutput{
-									Result: &s3.DeleteObjectsOutput{
-										Deleted: []types.DeletedObject{},
-										Errors:  []types.Error{},
-									},
-								}, middleware.Metadata{}, nil
-							},
-						),
-						middleware.Before,
-					)
-				},
-			},
-			want: want{
-				output: &partialOutput{
-					deleted: []types.DeletedObject{},
-					errors:  []types.Error{},
-				},
-				err: nil,
-			},
-			wantErr: false,
-		},
-		{
-			name: "delete objects over limit successfully",
-			args: args{
-				ctx: context.Background(),
-				input: &s3.DeleteObjectsInput{
-					Bucket: aws.String("test"),
-					Delete: &types.Delete{
-						Objects: objectsOverLimit,
-						Quiet:   *aws.Bool(true),
-					},
-				},
-				bucketName: aws.String("test"),
-				withAPIOptionsFunc: func(stack *middleware.Stack) error {
-					return stack.Finalize.Add(
-						middleware.FinalizeMiddlewareFunc(
-							"DeleteObjectsOverLimitMock",
-							func(context.Context, middleware.FinalizeInput, middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) {
-								return middleware.FinalizeOutput{
-									Result: &s3.DeleteObjectsOutput{
-										Deleted: []types.DeletedObject{},
-										Errors:  []types.Error{},
-									},
-								}, middleware.Metadata{}, nil
-							},
-						),
-						middleware.Before,
-					)
-				},
-			},
-			want: want{
-				output: &partialOutput{
-					deleted: []types.DeletedObject{},
-					errors:  []types.Error{},
-				},
-				err: nil,
-			},
-			wantErr: false,
-		},
-		{
-			name: "delete objects failure",
-			args: args{
-				ctx: context.Background(),
-				input: &s3.DeleteObjectsInput{
-					Bucket: aws.String("test"),
-					Delete: &types.Delete{
-						Objects: []types.ObjectIdentifier{{
-							Key:       aws.String("Key"),
-							VersionId: aws.String("VersionId")},
-						},
-						Quiet: *aws.Bool(true),
-					},
-				},
-				bucketName: aws.String("test"),
-				withAPIOptionsFunc: func(stack *middleware.Stack) error {
-					return stack.Finalize.Add(
-						middleware.FinalizeMiddlewareFunc(
-							"DeleteObjectsErrorMock",
-							func(context.Context, middleware.FinalizeInput, middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) {
-								return middleware.FinalizeOutput{
-									Result: &s3.DeleteObjectsOutput{
-										Deleted: []types.DeletedObject{},
-										Errors:  []types.Error{},
-									},
-								}, middleware.Metadata{}, fmt.Errorf("DeleteObjectsError")
-							},
-						),
-						middleware.Before,
-					)
-				},
-			},
-			want: want{
-				output: nil,
-				err:    fmt.Errorf("operation error S3: DeleteObjects, DeleteObjectsError"),
-			},
-			wantErr: true,
-		},
-		{
-			name: "delete objects failure for api error",
-			args: args{
-				ctx: context.Background(),
-				input: &s3.DeleteObjectsInput{
-					Bucket: aws.String("test"),
-					Delete: &types.Delete{
-						Objects: []types.ObjectIdentifier{{
-							Key:       aws.String("Key"),
-							VersionId: aws.String("VersionId")},
-						},
-						Quiet: *aws.Bool(true),
-					},
-				},
-				bucketName: aws.String("test"),
-				withAPIOptionsFunc: func(stack *middleware.Stack) error {
-					return stack.Finalize.Add(
-						middleware.FinalizeMiddlewareFunc(
-							"DeleteObjectsApiErrorMock",
-							func(context.Context, middleware.FinalizeInput, middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) {
-								return middleware.FinalizeOutput{
-									Result: &s3.DeleteObjectsOutput{
-										Deleted: []types.DeletedObject{},
-										Errors:  []types.Error{},
-									},
-								}, middleware.Metadata{}, fmt.Errorf("api error SlowDown")
-							},
-						),
-						middleware.Before,
-					)
-				},
-			},
-			want: want{
-				output: nil,
-				err:    fmt.Errorf("RetryCountOverError: test, operation error S3: DeleteObjects, api error SlowDown\nRetryCount(" + strconv.Itoa(maxRetryCount) + ") over, but failed to delete. "),
-			},
-			wantErr: true,
-		},
-		{
-			name: "delete objects failure for output errors",
-			args: args{
-				ctx: context.Background(),
-				input: &s3.DeleteObjectsInput{
-					Bucket: aws.String("test"),
-					Delete: &types.Delete{
-						Objects: []types.ObjectIdentifier{{
-							Key:       aws.String("Key"),
-							VersionId: aws.String("VersionId")},
-						},
-						Quiet: *aws.Bool(true),
-					},
-				},
-				bucketName: aws.String("test"),
-				withAPIOptionsFunc: func(stack *middleware.Stack) error {
-					return stack.Finalize.Add(
-						middleware.FinalizeMiddlewareFunc(
-							"DeleteObjectsOutputErrorsMock",
-							func(context.Context, middleware.FinalizeInput, middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) {
-								return middleware.FinalizeOutput{
-									Result: &s3.DeleteObjectsOutput{
-										Deleted: []types.DeletedObject{},
-										Errors: []types.Error{
-											{
-												Key:       aws.String("Key"),
-												Code:      aws.String("Code"),
-												Message:   aws.String("Message"),
-												VersionId: aws.String("VersionId"),
-											},
-										},
-									},
-								}, middleware.Metadata{}, nil
-							},
-						),
-						middleware.Before,
-					)
-				},
-			},
-			want: want{
-				output: &partialOutput{
-					deleted: []types.DeletedObject{},
-					errors: []types.Error{
-						{
-							Key:       aws.String("Key"),
-							Code:      aws.String("Code"),
-							Message:   aws.String("Message"),
-							VersionId: aws.String("VersionId"),
-						},
-					},
-				},
-				err: nil,
-			},
-			wantErr: false,
-		},
-	}
-
-	for _, tt := range cases {
-		t.Run(tt.name, func(t *testing.T) {
-			cfg, err := config.LoadDefaultConfig(
-				tt.args.ctx,
-				config.WithRegion("ap-northeast-1"),
-				config.WithAPIOptions([]func(*middleware.Stack) error{tt.args.withAPIOptionsFunc}),
-			)
-			if err != nil {
-				t.Fatal(err)
-			}
-
-			client := s3.NewFromConfig(cfg)
-			s3Client := NewS3(client)
-
-			output, err := s3Client.deleteObjectsWithRetry(tt.args.ctx, tt.args.input, tt.args.bucketName, sleepTimeSecForS3)
-			if (err != nil) != tt.wantErr {
-				t.Errorf("error = %#v, wantErr %#v", err.Error(), tt.wantErr)
-				return
-			}
-			if tt.wantErr && err.Error() != tt.want.err.Error() {
-				t.Errorf("err = %#v, want %#v", err.Error(), tt.want.err.Error())
-				return
-			}
-			if (output == nil) != (tt.want.output == nil) {
-				t.Errorf("output = %#v, want %#v", output, tt.want.output)
-			}
-			if (output != nil) && (tt.want.output != nil) {
-				got := &partialOutput{
-					deleted: output.Deleted,
-					errors:  output.Errors,
-				}
-				if !reflect.DeepEqual(got, tt.want.output) {
-					t.Errorf("output = %#v, want %#v", got, tt.want.output)
-				}
-			}
-		})
-	}
-}
-
 func TestS3_ListObjectVersions(t *testing.T) {
 	type args struct {
 		ctx                context.Context
@@ -852,6 +584,176 @@ func TestS3_ListObjectVersions(t *testing.T) {
 			},
 			wantErr: false,
 		},
+		{
+			name: "list objects versions with marker successfully",
+			args: args{
+				ctx:        context.Background(),
+				bucketName: aws.String("test"),
+				withAPIOptionsFunc: func(stack *middleware.Stack) error {
+					err := stack.Initialize.Add(
+						middleware.InitializeMiddlewareFunc(
+							"GetNextMarker",
+							getNextMarkerForS3Initialize,
+						), middleware.Before,
+					)
+					if err != nil {
+						return err
+					}
+
+					err = stack.Finalize.Add(
+						middleware.FinalizeMiddlewareFunc(
+							"ListObjectVersionsWithMarkerMock",
+							func(ctx context.Context, input middleware.FinalizeInput, handler middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) {
+								keyMarker := middleware.GetStackValue(ctx, keyMarkerKeyForS3{}).(*string)
+								versionIdMarker := middleware.GetStackValue(ctx, versionIdMarkerKeyForS3{}).(*string)
+
+								var nextKeyMarker *string
+								var nextVersionIdMarker *string
+								var objectVersions []types.ObjectVersion
+								var objectDeleteMarkers []types.DeleteMarkerEntry
+								if keyMarker == nil && versionIdMarker == nil {
+									nextKeyMarker = aws.String("NextMarker")
+									nextVersionIdMarker = aws.String("NextMarker")
+									objectVersions = []types.ObjectVersion{
+										{
+											Key:       aws.String("KeyForVersions1"),
+											VersionId: aws.String("VersionIdForVersions1"),
+										},
+									}
+									objectDeleteMarkers = []types.DeleteMarkerEntry{
+										{
+											Key:       aws.String("KeyForDeleteMarkers1"),
+											VersionId: aws.String("VersionIdForDeleteMarkers1"),
+										},
+									}
+									return middleware.FinalizeOutput{
+										Result: &s3.ListObjectVersionsOutput{
+											Versions:            objectVersions,
+											DeleteMarkers:       objectDeleteMarkers,
+											NextKeyMarker:       nextKeyMarker,
+											NextVersionIdMarker: nextVersionIdMarker,
+										},
+									}, middleware.Metadata{}, nil
+								} else {
+									objectVersions = []types.ObjectVersion{
+										{
+											Key:       aws.String("KeyForVersions2"),
+											VersionId: aws.String("VersionIdForVersions2"),
+										},
+									}
+									objectDeleteMarkers = []types.DeleteMarkerEntry{
+										{
+											Key:       aws.String("KeyForDeleteMarkers2"),
+											VersionId: aws.String("VersionIdForDeleteMarkers2"),
+										},
+									}
+									return middleware.FinalizeOutput{
+										Result: &s3.ListObjectVersionsOutput{
+											Versions:            objectVersions,
+											DeleteMarkers:       objectDeleteMarkers,
+											NextKeyMarker:       nextKeyMarker,
+											NextVersionIdMarker: nextVersionIdMarker,
+										},
+									}, middleware.Metadata{}, nil
+								}
+							},
+						),
+						middleware.Before,
+					)
+					return err
+				},
+			},
+			want: want{
+				output: []types.ObjectIdentifier{
+					{
+						Key:       aws.String("KeyForVersions1"),
+						VersionId: aws.String("VersionIdForVersions1"),
+					},
+					{
+						Key:       aws.String("KeyForDeleteMarkers1"),
+						VersionId: aws.String("VersionIdForDeleteMarkers1"),
+					},
+					{
+						Key:       aws.String("KeyForVersions2"),
+						VersionId: aws.String("VersionIdForVersions2"),
+					},
+					{
+						Key:       aws.String("KeyForDeleteMarkers2"),
+						VersionId: aws.String("VersionIdForDeleteMarkers2"),
+					},
+				},
+				err: nil,
+			},
+			wantErr: false,
+		},
+		{
+			name: "list objects versions with marker failure",
+			args: args{
+				ctx:        context.Background(),
+				bucketName: aws.String("test"),
+				withAPIOptionsFunc: func(stack *middleware.Stack) error {
+					err := stack.Initialize.Add(
+						middleware.InitializeMiddlewareFunc(
+							"GetNextMarker",
+							getNextMarkerForS3Initialize,
+						), middleware.Before,
+					)
+					if err != nil {
+						return err
+					}
+
+					err = stack.Finalize.Add(
+						middleware.FinalizeMiddlewareFunc(
+							"ListObjectVersionsWithMarkerErrorMock",
+							func(ctx context.Context, input middleware.FinalizeInput, handler middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) {
+								keyMarker := middleware.GetStackValue(ctx, keyMarkerKeyForS3{}).(*string)
+								versionIdMarker := middleware.GetStackValue(ctx, versionIdMarkerKeyForS3{}).(*string)
+
+								var nextKeyMarker *string
+								var nextVersionIdMarker *string
+								var objectVersions []types.ObjectVersion
+								var objectDeleteMarkers []types.DeleteMarkerEntry
+								if keyMarker == nil && versionIdMarker == nil {
+									nextKeyMarker = aws.String("NextMarker")
+									nextVersionIdMarker = aws.String("NextMarker")
+									objectVersions = []types.ObjectVersion{
+										{
+											Key:       aws.String("KeyForVersions1"),
+											VersionId: aws.String("VersionIdForVersions1"),
+										},
+									}
+									objectDeleteMarkers = []types.DeleteMarkerEntry{
+										{
+											Key:       aws.String("KeyForDeleteMarkers1"),
+											VersionId: aws.String("VersionIdForDeleteMarkers1"),
+										},
+									}
+									return middleware.FinalizeOutput{
+										Result: &s3.ListObjectVersionsOutput{
+											Versions:            objectVersions,
+											DeleteMarkers:       objectDeleteMarkers,
+											NextKeyMarker:       nextKeyMarker,
+											NextVersionIdMarker: nextVersionIdMarker,
+										},
+									}, middleware.Metadata{}, nil
+								} else {
+									return middleware.FinalizeOutput{
+										Result: &s3.ListObjectVersionsOutput{},
+									}, middleware.Metadata{}, fmt.Errorf("ListObjectVersionsError")
+								}
+							},
+						),
+						middleware.Before,
+					)
+					return err
+				},
+			},
+			want: want{
+				output: nil,
+				err:    fmt.Errorf("operation error S3: ListObjectVersions, ListObjectVersionsError"),
+			},
+			wantErr: true,
+		},
 	}
 
 	for _, tt := range cases {
diff --git a/pkg/client/waiter_test.go b/pkg/client/waiter_test.go
index c23073d..888d1a7 100644
--- a/pkg/client/waiter_test.go
+++ b/pkg/client/waiter_test.go
@@ -1,6 +1,7 @@
 package client
 
 import (
+	"context"
 	"fmt"
 	"strconv"
 	"testing"
@@ -12,8 +13,139 @@ import (
 	Test Cases
 */
 
-func TestWaitForRetry(t *testing.T) {
+func TestRetry(t *testing.T) {
+	tests := []struct {
+		name    string
+		args    RetryInput[struct{}, struct{}, struct{}]
+		want    error
+		wantErr bool
+	}{
+		{
+			name: "retry and then occur an error: ApiCaller returns error and RetryableChecker returns true",
+			args: RetryInput[struct{}, struct{}, struct{}]{
+				Ctx:            context.Background(),
+				SleepTimeSec:   1,
+				TargetResource: aws.String("resource"),
+				Input:          &struct{}{},
+				ApiCaller: func(ctx context.Context, input *struct{}, optFns ...func(*struct{})) (*struct{}, error) {
+					return input, fmt.Errorf("ApiFuncError")
+				},
+				RetryableChecker: func(err error) bool {
+					return true
+				},
+			},
+			want:    fmt.Errorf("RetryCountOverError: resource, ApiFuncError\nRetryCount(" + strconv.Itoa(maxRetryCount) + ") over, but failed to delete. "),
+			wantErr: true,
+		},
+		{
+			name: "do not retry and then occur an error: ApiCaller returns error and RetryableChecker returns false",
+			args: RetryInput[struct{}, struct{}, struct{}]{
+				Ctx:            context.Background(),
+				SleepTimeSec:   1,
+				TargetResource: aws.String("resource"),
+				Input:          &struct{}{},
+				ApiCaller: func(ctx context.Context, input *struct{}, optFns ...func(*struct{})) (*struct{}, error) {
+					return input, fmt.Errorf("ApiFuncError")
+				},
+				RetryableChecker: func(err error) bool {
+					return false
+				},
+			},
+			want:    fmt.Errorf("ApiFuncError"),
+			wantErr: true,
+		},
+		{
+			name: "success: ApiCaller do not return error and RetryableChecker returns true but is not concerned about",
+			args: RetryInput[struct{}, struct{}, struct{}]{
+				Ctx:            context.Background(),
+				SleepTimeSec:   1,
+				TargetResource: aws.String("resource"),
+				Input:          &struct{}{},
+				ApiCaller: func(ctx context.Context, input *struct{}, optFns ...func(*struct{})) (*struct{}, error) {
+					return input, nil
+				},
+				RetryableChecker: func(err error) bool {
+					return true
+				},
+			},
+			want:    nil,
+			wantErr: false,
+		},
+		{
+			name: "success: ApiCaller do not return error and RetryableChecker returns false but is not concerned about",
+			args: RetryInput[struct{}, struct{}, struct{}]{
+				Ctx:            context.Background(),
+				SleepTimeSec:   1,
+				TargetResource: aws.String("resource"),
+				Input:          &struct{}{},
+				ApiCaller: func(ctx context.Context, input *struct{}, optFns ...func(*struct{})) (*struct{}, error) {
+					return input, nil
+				},
+				RetryableChecker: func(err error) bool {
+					return false
+				},
+			},
+			want:    nil,
+			wantErr: false,
+		},
+		{
+			name: "success: ApiCaller do not return error with empty ApiOptions",
+			args: RetryInput[struct{}, struct{}, struct{}]{
+				Ctx:            context.Background(),
+				SleepTimeSec:   1,
+				TargetResource: aws.String("resource"),
+				Input:          &struct{}{},
+				ApiOptions:     []func(*struct{}){},
+				ApiCaller: func(ctx context.Context, input *struct{}, optFns ...func(*struct{})) (*struct{}, error) {
+					return input, nil
+				},
+				RetryableChecker: func(err error) bool {
+					return false
+				},
+			},
+			want:    nil,
+			wantErr: false,
+		},
+		{
+			name: "success: ApiCaller do not return error with some ApiOptions",
+			args: RetryInput[struct{}, struct{}, struct{}]{
+				Ctx:            context.Background(),
+				SleepTimeSec:   1,
+				TargetResource: aws.String("resource"),
+				Input:          &struct{}{},
+				ApiOptions: []func(*struct{}){
+					func(*struct{}) {},
+					func(*struct{}) {},
+				},
+				ApiCaller: func(ctx context.Context, input *struct{}, optFns ...func(*struct{})) (*struct{}, error) {
+					return input, nil
+				},
+				RetryableChecker: func(err error) bool {
+					return false
+				},
+			},
+			want:    nil,
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			_, err := Retry(&tt.args)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("Retry() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if tt.wantErr && err.Error() != tt.want.Error() {
+				t.Errorf("Retry() error = %v, want %v", err.Error(), tt.want.Error())
+				return
+			}
+		})
+	}
+}
+
+func Test_waitForRetry(t *testing.T) {
 	type args struct {
+		ctx                context.Context
 		retryCount         int
 		sleepTimeSec       int
 		targetResourceType *string
@@ -29,6 +161,7 @@ func TestWaitForRetry(t *testing.T) {
 		{
 			name: "sleepTimeSec = 0: not error",
 			args: args{
+				ctx:                context.Background(),
 				retryCount:         0,
 				sleepTimeSec:       0,
 				targetResourceType: aws.String("resource"),
@@ -40,6 +173,7 @@ func TestWaitForRetry(t *testing.T) {
 		{
 			name: "retryCount = 0: not error",
 			args: args{
+				ctx:                context.Background(),
 				retryCount:         0,
 				sleepTimeSec:       1,
 				targetResourceType: aws.String("resource"),
@@ -51,6 +185,7 @@ func TestWaitForRetry(t *testing.T) {
 		{
 			name: "retryCount = MaxRetryCount - 1: not error",
 			args: args{
+				ctx:                context.Background(),
 				retryCount:         maxRetryCount - 1,
 				sleepTimeSec:       1,
 				targetResourceType: aws.String("resource"),
@@ -62,6 +197,7 @@ func TestWaitForRetry(t *testing.T) {
 		{
 			name: "retryCount = MaxRetryCount: not error",
 			args: args{
+				ctx:                context.Background(),
 				retryCount:         maxRetryCount,
 				sleepTimeSec:       1,
 				targetResourceType: aws.String("resource"),
@@ -73,6 +209,7 @@ func TestWaitForRetry(t *testing.T) {
 		{
 			name: "retryCount = MaxRetryCount + 1: RetryCountOverError",
 			args: args{
+				ctx:                context.Background(),
 				retryCount:         maxRetryCount + 1,
 				sleepTimeSec:       1,
 				targetResourceType: aws.String("resource"),
@@ -85,7 +222,8 @@ func TestWaitForRetry(t *testing.T) {
 
 	for _, tt := range cases {
 		t.Run(tt.name, func(t *testing.T) {
-			err := WaitForRetry(
+			err := waitForRetry(
+				tt.args.ctx,
 				tt.args.retryCount,
 				tt.args.sleepTimeSec,
 				tt.args.targetResourceType,