-
-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix bug in s3hub cp subcommand #46
Conversation
Warning Rate Limit Exceeded@nao1215 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 6 minutes and 51 seconds before requesting another review. How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. WalkthroughThe recent modifications enhance the functionality related to S3 bucket management and file copying operations. Specifically, they ensure that all objects within an S3 bucket are deleted before the bucket itself is removed, and improve the copying process by refining how local and S3 targets are handled. This includes better struct organization, method refactoring, and improved testing for robustness and reliability in file management tasks. Changes
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (invoked as PR comments)
Additionally, you can add CodeRabbit Configration File (
|
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review Status
Actionable comments generated: 4
Configuration used: CodeRabbit UI
Files ignored due to path filters (2)
go.mod
is excluded by:!**/*.mod
go.sum
is excluded by:!**/*.sum
Files selected for processing (3)
- app/external/s3_helper_test.go (1 hunks)
- cmd/subcmd/s3hub/cp.go (4 hunks)
- cmd/subcmd/s3hub/cp_test.go (2 hunks)
Additional comments: 1
cmd/subcmd/s3hub/cp.go (1)
- 133-142: The method
copyTargetsInLocal
usesfile.WalkDir
to list files when the source is a directory. Ensure thatfile.WalkDir
correctly handles symbolic links and does not follow them, to prevent potential security issues or unintended behavior.Verification successful
The implementation of
WalkDir
inutils/file/file.go
usesfilepath.WalkDir
from the Go standard library, which iterates over each entry in a directory. The function checks if an entry is a directory and skips it if true, appending only file paths to thefiles
slice. This implementation does not explicitly handle symbolic links, relying on the behavior offilepath.WalkDir
.The Go documentation for
filepath.WalkDir
states that it does not follow symbolic links unless the link is to a directory and the function is called directly on that link. SinceWalkDir
does not explicitly follow symbolic links and only appends file paths (excluding directories), it inherently does not follow symbolic links to files. However, if a symbolic link points to a directory andWalkDir
is called on that directory, the behavior would follow the standardfilepath.WalkDir
behavior, which includes traversing into symbolic links that point to directories.Given this, the original concern about
file.WalkDir
potentially following symbolic links and leading to security issues or unintended behavior is addressed by the inherent behavior offilepath.WalkDir
. It does not follow symbolic links to files, and its handling of symbolic links to directories is consistent with the documented behavior of the Go standard library.Scripts Executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Verify that file.WalkDir does not follow symbolic linksLength of output: 0
Script:
#!/bin/bash # Attempt to find the implementation of file.WalkDir to verify its behavior regarding symbolic links rg "func WalkDir" --type=goLength of output: 97
Script:
#!/bin/bash # Now that we've located the file, let's examine the implementation of WalkDir to understand how it handles symbolic links cat utils/file/file.goLength of output: 587
// DeleteAllS3BucketDelete deletes all S3 buckets and objects. | ||
func DeleteAllS3BucketDelete(t *testing.T, client *s3.Client) { | ||
t.Helper() | ||
ctx := context.Background() | ||
|
||
buckets, err := client.ListBuckets(context.Background(), &s3.ListBucketsInput{}) | ||
buckets, err := client.ListBuckets(ctx, &s3.ListBucketsInput{}) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
for _, bucket := range buckets.Buckets { | ||
if _, err := client.DeleteBucket(context.Background(), &s3.DeleteBucketInput{Bucket: bucket.Name}); err != nil { | ||
output, err := client.ListObjects(ctx, &s3.ListObjectsInput{ | ||
Bucket: bucket.Name, | ||
}) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
for _, object := range output.Contents { | ||
if _, err := client.DeleteObject(ctx, &s3.DeleteObjectInput{ | ||
Bucket: bucket.Name, | ||
Key: object.Key, | ||
}); err != nil { | ||
t.Fatal(err) | ||
} | ||
} | ||
|
||
if _, err := client.DeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: bucket.Name}); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The implementation of DeleteAllS3BucketDelete
now includes deleting all objects within an S3 bucket before attempting to delete the bucket itself. This change addresses potential issues with deleting non-empty buckets. However, it's important to handle pagination in the ListObjects
call to ensure all objects are deleted, especially in buckets with a large number of objects.
+ // Ensure to handle pagination for buckets with many objects
+ var continuationToken *string = nil
+ for {
+ output, err := client.ListObjects(ctx, &s3.ListObjectsInput{
+ Bucket: bucket.Name,
+ ContinuationToken: continuationToken,
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+ for _, object := range output.Contents {
+ _, err := client.DeleteObject(ctx, &s3.DeleteObjectInput{
+ Bucket: bucket.Name,
+ Key: object.Key,
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+ if !output.IsTruncated {
+ break
+ }
+ continuationToken = output.NextContinuationToken
+ }
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.
// DeleteAllS3BucketDelete deletes all S3 buckets and objects. | |
func DeleteAllS3BucketDelete(t *testing.T, client *s3.Client) { | |
t.Helper() | |
ctx := context.Background() | |
buckets, err := client.ListBuckets(context.Background(), &s3.ListBucketsInput{}) | |
buckets, err := client.ListBuckets(ctx, &s3.ListBucketsInput{}) | |
if err != nil { | |
t.Fatal(err) | |
} | |
for _, bucket := range buckets.Buckets { | |
if _, err := client.DeleteBucket(context.Background(), &s3.DeleteBucketInput{Bucket: bucket.Name}); err != nil { | |
output, err := client.ListObjects(ctx, &s3.ListObjectsInput{ | |
Bucket: bucket.Name, | |
}) | |
if err != nil { | |
t.Fatal(err) | |
} | |
for _, object := range output.Contents { | |
if _, err := client.DeleteObject(ctx, &s3.DeleteObjectInput{ | |
Bucket: bucket.Name, | |
Key: object.Key, | |
}); err != nil { | |
t.Fatal(err) | |
} | |
} | |
if _, err := client.DeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: bucket.Name}); err != nil { | |
// DeleteAllS3BucketDelete deletes all S3 buckets and objects. | |
func DeleteAllS3BucketDelete(t *testing.T, client *s3.Client) { | |
t.Helper() | |
ctx := context.Background() | |
buckets, err := client.ListBuckets(ctx, &s3.ListBucketsInput{}) | |
if err != nil { | |
t.Fatal(err) | |
} | |
for _, bucket := range buckets.Buckets { | |
// Ensure to handle pagination for buckets with many objects | |
var continuationToken *string = nil | |
for { | |
output, err := client.ListObjects(ctx, &s3.ListObjectsInput{ | |
Bucket: bucket.Name, | |
ContinuationToken: continuationToken, | |
}) | |
if err != nil { | |
t.Fatal(err) | |
} | |
for _, object := range output.Contents { | |
_, err := client.DeleteObject(ctx, &s3.DeleteObjectInput{ | |
Bucket: bucket.Name, | |
Key: object.Key, | |
}) | |
if err != nil { | |
t.Fatal(err) | |
} | |
} | |
if !output.IsTruncated { | |
break | |
} | |
continuationToken = output.NextContinuationToken | |
} | |
if _, err := client.DeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: bucket.Name}); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DeleteAllS3BucketDelete() method only used for unit test. So, there is no many s3 objects. We don't use pagination when execute unit test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nao1215, understood! Given that DeleteAllS3BucketDelete
is used solely for unit testing with a limited number of objects, your approach makes sense. Skipping pagination under these conditions seems reasonable. Thank you for clarifying the context and usage of this method. If there's anything else you need, feel free to ask!
This comment has been minimized.
This comment has been minimized.
@coderabbitai review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review Status
Actionable comments generated: 0
Configuration used: CodeRabbit UI
Files selected for processing (4)
- app/domain/model/s3.go (1 hunks)
- app/domain/model/s3_test.go (1 hunks)
- cmd/subcmd/s3hub/cp.go (4 hunks)
- cmd/subcmd/s3hub/cp_test.go (2 hunks)
Files skipped from review as they are similar to previous changes (2)
- cmd/subcmd/s3hub/cp.go
- cmd/subcmd/s3hub/cp_test.go
Additional comments: 2
app/domain/model/s3.go (1)
- 232-237: The
Split
method now cleans theS3Key
usingfilepath.Clean
before returning it. This is a good practice to ensure theS3Key
is in a standard format, especially when dealing with file paths that might include relative components like..
.app/domain/model/s3_test.go (1)
- 662-673: The new test cases added to
TestBucket_Split
effectively cover scenarios with multiple levels in the bucket path and include parent directory references. These additions enhance the test coverage for theSplit
method, ensuring it behaves as expected in more complex scenarios.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review Status
Actionable comments generated: 0
Configuration used: CodeRabbit UI
Files selected for processing (6)
- LICENSE (1 hunks)
- app/external/s3.go (1 hunks)
- app/interactor/mock/s3.go (1 hunks)
- app/interactor/s3.go (1 hunks)
- cmd/subcmd/s3hub/cp.go (5 hunks)
- cmd/subcmd/s3hub/cp_test.go (2 hunks)
Files skipped from review due to trivial changes (3)
- LICENSE
- app/external/s3.go
- app/interactor/s3.go
Files skipped from review as they are similar to previous changes (2)
- cmd/subcmd/s3hub/cp.go
- cmd/subcmd/s3hub/cp_test.go
Additional comments: 3
app/interactor/mock/s3.go (3)
- 1-7: The package and import declarations are correctly structured, ensuring that the mock implementation is properly isolated within the
mock
package and has access to necessary types from theusecase
package.- 9-10: The
S3ObjectsLister
type is declared as a function type that matches the signature of theListS3Objects
method in theS3ObjectLister
interface. This is a common and effective pattern for creating mocks in Go, allowing for flexible testing scenarios.- 12-15: The implementation of the
ListS3Objects
method correctly delegates the call to the function itself, passing through the context and input parameters. This ensures that any function assigned to an instance ofS3ObjectsLister
will be called whenListS3Objects
is invoked, allowing for dynamic behavior in tests based on the function provided. This pattern is both simple and powerful for mocking external dependencies.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
HOTTEST report
Reported by hottest |
Code Metrics Report
Details | | main (d03dc7a) | #46 (f389d2f) | +/- |
|---------------------|----------------|---------------|-------|
+ | Coverage | 27.3% | 29.2% | +1.9% |
| Files | 42 | 43 | +1 |
| Lines | 1348 | 1357 | +9 |
+ | Covered | 368 | 396 | +28 |
- | Test Execution Time | 4s | 6s | +2s | Code coverage of files in pull request scope (60.8% → 66.9%)
Reported by octocov |
Summary by CodeRabbit
copyPathPair
functionality, ensuring robustness and reliability in file copying scenarios across different storage locations.