Skip to content

Commit b55f1e3

Browse files
authored
Merge pull request #138 from thercast/spaces
Add support for basic digital ocean spaces API
2 parents ccb2296 + ceed414 commit b55f1e3

File tree

6 files changed

+269
-1
lines changed

6 files changed

+269
-1
lines changed

DESCRIPTION

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ Imports:
2323
httr (>= 1.2.0),
2424
jsonlite (>= 1.1),
2525
magrittr,
26-
yaml
26+
yaml,
27+
aws.s3 (>= 0.3.3)
2728
Suggests:
2829
roxygen2 (>= 6.0.1),
2930
testthat,

NAMESPACE

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ S3method(as.snapshot,character)
1919
S3method(as.snapshot,list)
2020
S3method(as.snapshot,numeric)
2121
S3method(as.snapshot,snapshot)
22+
S3method(as.space,character)
23+
S3method(as.space,space)
2224
S3method(as.sshkey,character)
2325
S3method(as.sshkey,list)
2426
S3method(as.sshkey,numeric)
@@ -46,11 +48,13 @@ S3method(print,domain_record)
4648
S3method(print,droplet)
4749
S3method(print,image)
4850
S3method(print,snapshot)
51+
S3method(print,space)
4952
S3method(print,sshkey)
5053
S3method(print,tag)
5154
S3method(print,volume)
5255
S3method(print,volume_snapshot)
5356
S3method(summary,droplet)
57+
S3method(summary,space)
5458
export("%>%")
5559
export(account)
5660
export(action)
@@ -61,6 +65,7 @@ export(as.domain_record)
6165
export(as.droplet)
6266
export(as.image)
6367
export(as.snapshot)
68+
export(as.space)
6469
export(as.sshkey)
6570
export(as.tag)
6671
export(as.volume)
@@ -159,6 +164,8 @@ export(sizes)
159164
export(snapshot)
160165
export(snapshot_delete)
161166
export(snapshots)
167+
export(space_create)
168+
export(spaces)
162169
export(standardise_keys)
163170
export(tag)
164171
export(tag_create)
@@ -178,6 +185,9 @@ export(volume_resize)
178185
export(volume_snapshot_create)
179186
export(volume_snapshots)
180187
export(volumes)
188+
importFrom(aws.s3,bucketlist)
189+
importFrom(aws.s3,get_bucket)
190+
importFrom(aws.s3,put_bucket)
181191
importFrom(httr,GET)
182192
importFrom(httr,HEAD)
183193
importFrom(httr,POST)

R/spaces.R

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
spaces_base <- "nyc3.digitaloceanspaces.com"
2+
3+
check_space_access <- function(spaces_key) {
4+
tmp <- ifelse(is.null(spaces_key),
5+
Sys.getenv("DO_SPACES_ACCESS_KEY"),
6+
spaces_key)
7+
if (tmp == "") {
8+
stop("Need a digital ocean spaces access key defined in your session",
9+
call. = FALSE)
10+
} else {
11+
tmp
12+
}
13+
}
14+
15+
check_space_secret <- function(spaces_secret) {
16+
tmp <- ifelse(is.null(spaces_secret),
17+
Sys.getenv("DO_SPACES_SECRET_KEY"),
18+
spaces_secret)
19+
if (tmp == "") {
20+
stop("Need a digital ocean spaces secret key defined in your session",
21+
call. = FALSE)
22+
} else {
23+
tmp
24+
}
25+
}
26+
27+
#' @param x Object to coerce to a space
28+
#' @export
29+
#' @rdname spaces
30+
as.space <- function(x) UseMethod("as.space")
31+
#' @export
32+
as.space.space <- function(x) x
33+
#' @export
34+
as.space.character <- function(x) spaces()[[x]]
35+
36+
#' @export
37+
print.space <- function(x, ...) {
38+
cat("<space>", x$Name, "\n", sep = "")
39+
cat(" Created at: ", x$CreationDate, "\n")
40+
}
41+
42+
#' @export
43+
summary.space <- function(object, ...) {
44+
space_info <- space_info(name = object$Name, ...)
45+
46+
# obtain total size used by space
47+
size <- space_size(space_info)
48+
49+
# obtain number of files in space
50+
n_files <- space_files(space_info)
51+
52+
cat("<space_detail>", object$Name, "\n", sep = "")
53+
cat(" Size (GB): ", size, "\n", sep = "")
54+
cat(" Files: ", n_files, "\n", sep = "")
55+
cat(" Created at: ", object$CreationDate, "\n", sep = "")
56+
}
57+
58+
#' Spaces storage operations
59+
#'
60+
#' \describe{
61+
#' \item{spaces}{Retrieve all spaces in your digital ocean account}
62+
#' \item{space_create}{Create a new space}
63+
#' }
64+
#'
65+
#' @param name (character) Space name.
66+
#' @param spaces_key (character) String containing a spaces access key. If
67+
#' missing, defaults to value stored in an environment variable
68+
#' \code{DO_SPACES_ACCESS_KEY}.
69+
#' @param spaces_secret (character) String containing the secret associated
70+
#' with the spaces key. If missing, defaults to value stored in an environment
71+
#' variable \code{DO_SPACES_SECRET_KEY}.
72+
#' @param ... Additional arguments passed down to \code{\link[aws.s3]{bucketlist}},
73+
#' \code{\link[aws.s3]{get_bucket}}, \code{\link[aws.s3]{put_bucket}} functions
74+
#' from the \code{aws.s3} package.
75+
#' @examples \dontrun{
76+
#' # list spaces
77+
#' spaces()
78+
#'
79+
#' # obtain spaces as a list of space objects
80+
#' res <- spaces()
81+
#'
82+
#' # print space summary using a space object
83+
#' summary(res[['my_space_name']])
84+
#'
85+
#' # create a new space
86+
#' space_create('new_space_name')
87+
#' }
88+
89+
#' @importFrom aws.s3 bucketlist
90+
#' @keywords internal
91+
spaces_GET <- function(spaces_key = NULL, spaces_secret = NULL, ...) {
92+
93+
spaces_key <- check_space_access(spaces_key)
94+
spaces_secret <- check_space_secret(spaces_secret)
95+
96+
res <- aws.s3::s3HTTP(verb = "GET",
97+
region = NULL,
98+
key = spaces_key,
99+
secret = spaces_secret,
100+
base_url = spaces_base,
101+
...)
102+
103+
return(res)
104+
}
105+
106+
#' @export
107+
#' @rdname spaces
108+
spaces <- function(spaces_key = NULL, spaces_secret = NULL, ...) {
109+
res <- spaces_GET(spaces_key = spaces_key, spaces_secret = spaces_secret, ...)
110+
111+
# when only one space is present, res$Buckets only contains the Name and
112+
# CreationDate. If more than one space is present, then each space will
113+
# have a Bucket list object with the Name and CreationDate
114+
if (identical(names(res$Buckets), c("Name", "CreationDate"))) {
115+
res$Buckets <- list(
116+
Bucket = list(
117+
Name = res$Buckets$Name,
118+
CreationDate = res$Buckets$CreationDate
119+
)
120+
)
121+
}
122+
sp <- lapply(res$Buckets, structure, class = "space")
123+
setNames(sp, vapply(res$Buckets, function(x) x$Name, character(1)))
124+
}
125+
126+
#' @importFrom aws.s3 get_bucket
127+
#' @keywords internal
128+
space_info <- function(name, spaces_key = NULL, spaces_secret = NULL, ...) {
129+
if (is.null(name)) stop("Please specify the space name")
130+
spaces_key <- check_space_access(spaces_key)
131+
spaces_secret <- check_space_secret(spaces_secret)
132+
133+
space_info <- get_bucket(name,
134+
region = NULL,
135+
check_region = FALSE,
136+
key = spaces_key,
137+
secret = spaces_secret,
138+
base_url = spaces_base,
139+
max = Inf,
140+
...)
141+
142+
return(space_info)
143+
}
144+
145+
space_size <- function(space_info) {
146+
# grab the sizes from each file (unit is bytes)
147+
sizes <- vapply(space_info, function(x) x$Size, numeric(1))
148+
149+
# compute total size (convert to gb)
150+
sum(sizes) * 1e-09
151+
}
152+
153+
space_files <- function(space_info) {
154+
# remove entries with size 0 (those are nested directories)
155+
length(lapply(space_info, function(x) x[x$Size > 0]))
156+
}
157+
158+
#' Create a new space
159+
#' @importFrom aws.s3 put_bucket
160+
#' @export
161+
#' @rdname spaces
162+
space_create <- function(name, spaces_key = NULL, spaces_secret = NULL, ...) {
163+
if (is.null(name)) stop("Please specify the space name")
164+
spaces_key <- check_space_access(spaces_key)
165+
spaces_secret <- check_space_secret(spaces_secret)
166+
167+
res <- put_bucket(name,
168+
region = NULL,
169+
key = spaces_key,
170+
secret = spaces_secret,
171+
base_url = spaces_base,
172+
...)
173+
174+
if (res) message(sprintf("New space %s created successfully", name))
175+
}

man/spaces.Rd

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/spaces_GET.Rd

Lines changed: 44 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/test-space.R

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# tests for spaces
2+
context("space")
3+
4+
test_that("fails well with no input", {
5+
expect_error(space_info(), "argument \"name\" is missing")
6+
})
7+
8+
test_that("key checks fail when not defined in environment", {
9+
skip_on_cran()
10+
11+
Sys.unsetenv("DO_SPACES_ACCESS_KEY")
12+
expect_error(check_space_access(spaces_key = NULL),
13+
"Need a digital ocean spaces access key defined in your session")
14+
15+
Sys.unsetenv("DO_SPACES_SECRET_KEY")
16+
expect_error(check_space_secret(spaces_secret = NULL),
17+
"Need a digital ocean spaces secret key defined in your session")
18+
})

0 commit comments

Comments
 (0)