Skip to content

Commit

Permalink
add six_bucket_delete fxn
Browse files Browse the repository at this point in the history
  • Loading branch information
sckott committed Apr 4, 2024
1 parent 6d822f1 commit f606486
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 20 deletions.
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export(set_s3_interface)
export(six_admin_setup)
export(six_bucket_add_user)
export(six_bucket_change_user)
export(six_bucket_delete)
export(six_bucket_permissions)
export(six_bucket_remove_user)
export(six_user_create)
Expand Down Expand Up @@ -145,6 +146,7 @@ importFrom(purrr,map)
importFrom(purrr,map_chr)
importFrom(purrr,map_lgl)
importFrom(purrr,pluck)
importFrom(purrr,safely)
importFrom(rlang,":=")
importFrom(rlang,abort)
importFrom(rlang,has_name)
Expand Down
112 changes: 96 additions & 16 deletions R/bucket.R
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
bucket_checks <- function(bucket) {
stop_if_not(rlang::is_character(bucket), "bucket must be character")
stop_if_not(length(bucket) == 1, "length(bucket) != 1")
}

#' Check if an S3 bucket exists
#'
#' @export
Expand All @@ -13,8 +18,7 @@
#' aws_bucket_exists(bucket = "no-bucket")
#' }
aws_bucket_exists <- function(bucket) {
stop_if_not(rlang::is_character(bucket), "bucket must be character")
stop_if_not(length(bucket) == 1, "length(bucket) != 1")
bucket_checks(bucket)
res <- tryCatch(
{
env64$s3$head_bucket(Bucket = bucket)
Expand All @@ -37,8 +41,7 @@ aws_bucket_exists <- function(bucket) {
#' aws_bucket_create(bucket = "s64-test-2")
#' }
aws_bucket_create <- function(bucket, ...) {
stop_if_not(rlang::is_character(bucket), "bucket must be character")
stop_if_not(length(bucket) == 1, "length(bucket) != 1")
bucket_checks(bucket)
env64$s3$create_bucket(
Bucket = bucket,
CreateBucketConfiguration =
Expand Down Expand Up @@ -85,9 +88,7 @@ bucket_create_if_not <- function(bucket, force = FALSE) {
#' aws_buckets()
#' }
aws_bucket_delete <- function(bucket, force = FALSE, ...) {
stop_if_not(rlang::is_character(bucket), "bucket must be character")
stop_if_not(length(bucket) == 1, "length(bucket) != 1")
# TODO: add a package level option to override the prompt for adv. users
bucket_checks(bucket)
if (!force) {
if (yesno("Are you sure you want to delete {.strong {bucket}}?")) {
return(invisible())
Expand All @@ -97,6 +98,72 @@ aws_bucket_delete <- function(bucket, force = FALSE, ...) {
return(invisible())
}

#' Delete an S3 bucket
#'
#' Takes care of deleting bucket objects, so that the bucket itself
#' can be deleted cleanly
#'
#' @export
#' @importFrom purrr safely
#' @inherit aws_bucket_delete
#' @section What is magical:
#' - Exits early if bucket does not exist
#' - Checks for any objects in the bucket and deletes any present
#' - Deletes bucket after deleting objects
#' @family magicians
#' @examplesIf interactive()
#' # bucket does not exist
#' six_bucket_delete("notabucket")
#'
#' # bucket exists w/o objects
#' bucket <- random_string("bucket")
#' aws_bucket_create(bucket)
#' six_bucket_delete(bucket)
#'
#' # bucket exists w/ objects (files and directories with files)
#' bucket <- random_string("bucket")
#' aws_bucket_create(bucket)
#' demo_rds_file <- file.path(system.file(), "Meta/demo.rds")
#' links_file <- file.path(system.file(), "Meta/links.rds")
#' aws_file_upload(
#' c(demo_rds_file, links_file),
#' s3_path(bucket, c(basename(demo_rds_file), basename(links_file)))
#' )
#' aws_file_upload(
#' c(demo_rds_file, links_file),
#' s3_path(bucket, "newfolder",
#' c(basename(demo_rds_file), basename(links_file)))
#' )
#'
#' six_bucket_delete(bucket)
six_bucket_delete <- function(bucket, force = FALSE, ...) {
bucket_checks(bucket)
if (!aws_bucket_exists(bucket)) {
cli_warning("bucket {.strong {bucket}} does not exist; exiting")
return(invisible())
}
list_obs <- safely(aws_bucket_list_objects)
objects <- list_obs(bucket)
if (rlang::is_empty(objects$result)) {
cli_info("bucket {.strong {bucket}} has no objects")
} else {
cli_info(c(
"bucket {.strong {bucket}} has {length(objects$result$uri)} objects - ",
"deleting them now"
))
purrr::map(objects$result$uri, \(x) aws_file_delete(x))

# check for empty folders & delete thoes too
empties <- list_obs(bucket)
if (!rlang::is_empty(empties)) {
purrr::map(empties$result$uri, \(x) aws_file_delete(x))
}
}
cli_info("deleting bucket {.strong {bucket}}")
aws_bucket_delete(bucket, force, ...)
invisible()
}

#' Download an S3 bucket
#'
#' @export
Expand Down Expand Up @@ -182,9 +249,10 @@ aws_bucket_upload <- function(
#' List objects in an S3 bucket
#'
#' @export
#' @importFrom s3fs s3_dir_info
#' @autoglobal
#' @param bucket (character) bucket name. required
#' @param ... named parameters passed on to [s3fs::s3_dir_info()]
#' @param ... named parameters passed on to
#' [list_objects](https://www.paws-r-sdk.com/docs/s3_list_objects/)
#' @family buckets
#' @return if no objects found, an empty tibble. if tibble has rows each
#' is an S3 bucket, with 8 columns:
Expand All @@ -206,13 +274,25 @@ aws_bucket_upload <- function(
#' )
#' aws_bucket_list_objects(bucket = bucket_name)
aws_bucket_list_objects <- function(bucket, ...) {
# s3fs_creds_refresh()
out <- s3fs::s3_dir_info(bucket, ...)
if (is.data.frame(out) && NROW(out) > 0) {
as_tibble(out)
} else {
tibble()
}
out <- env64$s3$list_objects(bucket, ...)
if (rlang::is_empty(out$Contents)) return(tibble())
as_tibble(jsonlite::fromJSON(
jsonlite::toJSON(out$Contents, auto_unbox = TRUE),
flatten = TRUE
)) %>%
mutate(
bucket = bucket,
uri = glue("s3://{bucket}/{Key}"),
Size = fs::as_fs_bytes(Size),
LastModified = as_datetime(LastModified)
) %>%
rowwise() %>%
mutate(
type = if (endsWith(Key, "/")) "directory" else "file"
) %>%
ungroup() %>%
rename_with(tolower) %>%
select(bucket, key, uri, size, type, etag, lastmodified, storageclass)
}

#' List S3 buckets
Expand Down
8 changes: 6 additions & 2 deletions R/files.R
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,16 @@ aws_file_delete <- function(remote_path, ...) {

aws_file_delete_one <- function(one_path, ...) {
path_parsed <- path_s3_parse(one_path)
key <- if (nchar(path_parsed[[1]]$path)) {
trailing_slash <- grepl("/$", one_path)
key <- if (nzchar(path_parsed[[1]]$path)) {
file.path(path_parsed[[1]]$path, path_parsed[[1]]$file)
} else {
path_parsed[[1]]$file
}
env64$s3$delete_object(path_parsed[[1]]$bucket, key)
env64$s3$delete_object(
path_parsed[[1]]$bucket,
glue("{key}{ifelse(trailing_slash, '/', '')}")
)
invisible()
}

Expand Down
10 changes: 10 additions & 0 deletions R/globals.R
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ utils::globalVariables(c(
"service_map", # <billing_factory>
"acronym", # <billing_factory>
"Service", # <billing_factory>
"Size", # <aws_bucket_list_objects>
"LastModified", # <aws_bucket_list_objects>
"Key", # <aws_bucket_list_objects>
"key", # <aws_bucket_list_objects>
"uri", # <aws_bucket_list_objects>
"size", # <aws_bucket_list_objects>
"type", # <aws_bucket_list_objects>
"etag", # <aws_bucket_list_objects>
"lastmodified", # <aws_bucket_list_objects>
"storageclass", # <aws_bucket_list_objects>
"DBInstanceArn", # <aws_db_rds_list>
".", # <aws_group>
".", # <aws_policy>
Expand Down
1 change: 1 addition & 0 deletions _pkgdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ reference:
- six_bucket_change_user
- six_bucket_remove_user
- six_bucket_permissions
- six_bucket_delete
- six_user_creds
- group_policies
- title: Billing
Expand Down
3 changes: 2 additions & 1 deletion man/aws_bucket_list_objects.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion man/aws_buckets.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

76 changes: 76 additions & 0 deletions man/six_bucket_delete.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit f606486

Please sign in to comment.