diff --git a/DESCRIPTION b/DESCRIPTION index 0e414db..bb9a49b 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,12 +1,13 @@ Package: aws.ec2 Type: Package Title: Amazon Web Services EC2 Client Package -Version: 0.1.13 -Date: 2018-06-02 +Version: 0.1.13-0.42 +Date: 2018-06-26 Authors@R: c(person("Thomas J.", "Leeper", role = c("aut", "cre"), email = "thosjleeper@gmail.com", comment = c(ORCID = "0000-0003-4097-6326")), - person("Andrie", "de Vries", role = "ctb")) + person("Andrie", "de Vries", role = "ctb"), + person("Massimo", "Redaelli", role = "ctb")) Description: A simple client package for the Amazon Web Services ('AWS') Elastic Cloud Compute ('EC2') 'API'. License: GPL (>= 2) @@ -15,7 +16,8 @@ Imports: aws.signature (>= 0.4.0), httr, xml2, - base64enc + base64enc, + jsonlite Suggests: testthat, aws.ec2metadata, diff --git a/NAMESPACE b/NAMESPACE index 00ff741..c216b61 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -45,6 +45,7 @@ export(ec2HTTP) export(get_console_output) export(get_image_attr) export(get_instance_attr) +export(get_parameter) export(get_password_data) export(get_vpc_attr) export(import_keypair) @@ -78,6 +79,7 @@ importFrom(httr,headers) importFrom(httr,http_error) importFrom(httr,http_status) importFrom(httr,warn_for_status) +importFrom(jsonlite,fromJSON) importFrom(utils,head) importFrom(utils,tail) importFrom(xml2,as_list) diff --git a/NEWS.md b/NEWS.md index 4c14382..61e61ec 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,7 @@ +# aws.ec2 0.1.14 + +* Support for reading SSM parameters (#4) + # aws.ec2 0.1.13 * Merge major PR (#38) from Andrie de Vries. diff --git a/R/http.R b/R/http.R index bf0f9be..c557be3 100644 --- a/R/http.R +++ b/R/http.R @@ -9,6 +9,7 @@ #' @param secret A character string specifying an AWS Secret Key. See [aws.signature::locate_credentials()]. #' @param session_token Optionally, a character string specifying an AWS temporary Session Token to use in signing a request. See [aws.signature::locate_credentials()]. #' @param version A character string specifying an API version. Default is \dQuote{2015-10-01}. +#' @param service The AWS service (`ec2` or `ssm`). Defaults to `ec2`. #' @param clean_response A logical indicating whether to remove line breaks from the response. This is useful for returning a clean response object, but may not be appropriate if the XML-formatted API response contains meaningful linebreaks (e.g., in a keypair). #' @param \dots Additional arguments passed to [httr::GET()]. #' @return A list @@ -16,6 +17,7 @@ #' @importFrom aws.signature locate_credentials #' @importFrom httr add_headers headers content warn_for_status http_status http_error GET #' @importFrom xml2 read_xml as_list +#' @importFrom jsonlite fromJSON #' @export ec2HTTP <- function( @@ -28,6 +30,7 @@ ec2HTTP <- secret = NULL, session_token = NULL, version = "2016-11-15", + service = "ec2", clean_response = TRUE, ... ) { @@ -43,21 +46,21 @@ ec2HTTP <- query$DryRun <- tolower(as.character(dryrun)) } query$Version <- version - if (region == "us-east-1") { + if (region == "us-east-1" && service == "ec2") { url <- paste0("https://ec2.amazonaws.com") } else { - url <- paste0("https://ec2.",region,".amazonaws.com") + url <- paste0("https://",service,".",region,".amazonaws.com") } d_timestamp <- format(Sys.time(), "%Y%m%dT%H%M%SZ", tz = "UTC") Sig <- signature_v4_auth( datetime = d_timestamp, region = region, - service = "ec2", + service = service, verb = "GET", action = "/", query_args = query, canonical_headers = list( - host = if (region == "us-east-1") "ec2.amazonaws.com" else paste0("ec2.",region,".amazonaws.com"), + host = if (region == "us-east-1" && service == "ec2") paste0("ec2.amazonaws.com") else paste0(service, ".",region,".amazonaws.com"), `X-Amz-Date` = d_timestamp), request_body = "", key = key, @@ -83,8 +86,8 @@ ec2HTTP <- } if (http_error(r)) { tmp <- gsub("\n\\s*", "", content(r, "text")) - x <- try(as_list(read_xml(tmp)), silent = TRUE) - msg <- paste0(parse_errors(x), collapse = "\n") + x <- get(service, parse_response)(tmp) + msg <- paste0(get(service, parse_errors)(x), collapse = "\n") stop(msg, call. = FALSE) } else { if (isTRUE(clean_response)) { @@ -92,7 +95,7 @@ ec2HTTP <- } else { tmp <- content(r, "text") } - out <- try(as_list(read_xml(tmp)), silent = TRUE) + out <- get(service, parse_response)(tmp) if (inherits(out, "try-error")) { out <- structure(content(r, "text")) } @@ -101,10 +104,21 @@ ec2HTTP <- return(out) } -parse_errors <- function(error) { +parse_response = list() +parse_response$ec2 <- function(res) try(as_list(read_xml(res)), silent = TRUE) +parse_response$ssm <- function(res) try(fromJSON(res), silent = TRUE) + + +parse_errors = list() +parse_errors$ec2 <- function(error) { messages <- if (!is.null(error$Errors)) error$Errors else error$Response$Errors sapply(messages, function(z) { paste(z$Code[[1]], z$Message[[1]], sep = " : \n - ", collapse = "\n") }, USE.NAMES = FALSE) } +parse_errors$ssm <- function(error) { + messages <- if (!is.null(error$Error)) error$Error else error$Response$Error + paste0(messages$Code, " : ", messages$message) +} + diff --git a/R/ssm_parameter.R b/R/ssm_parameter.R new file mode 100644 index 0000000..1e74a1b --- /dev/null +++ b/R/ssm_parameter.R @@ -0,0 +1,22 @@ +#' gets a parameter from the parameter store +#' +#' @param name The parameter name +#' @param with_decryption Specifies if the parameter should be decripted. Defaults to \code{TRUE} +#' @param ... Additional arguments passed to \code{ec2HTTP} +#' +#' @return The value of the parameter +#' @export +get_parameter <- function(name, with_decryption=TRUE, ...) { + query <- list(Action = "GetParameter") + if (missing(name)) { + stop('Parameter name missing') + } + names(name) <- "Name" + + with_decryption <- if( with_decryption ) "true" else "false" + names(with_decryption) <- "WithDecryption" + + query <- c(query, name, with_decryption) + r <- ec2HTTP(version="2014-11-06", query = query, service="ssm", ...) + return(r$GetParameterResult$Parameter) +} \ No newline at end of file diff --git a/man/ec2HTTP.Rd b/man/ec2HTTP.Rd index c4110a7..e2e2a9a 100644 --- a/man/ec2HTTP.Rd +++ b/man/ec2HTTP.Rd @@ -8,7 +8,7 @@ ec2HTTP(query = list(), headers = list(), dryrun, verbose = getOption("AWS_VERBOSE_REQUEST", FALSE), region = Sys.getenv("AWS_DEFAULT_REGION", "us-east-1"), key = NULL, secret = NULL, session_token = NULL, version = "2016-11-15", - clean_response = TRUE, ...) + service = "ec2", clean_response = TRUE, ...) } \arguments{ \item{query}{A named list of query string parameters.} @@ -29,6 +29,8 @@ ec2HTTP(query = list(), headers = list(), dryrun, \item{version}{A character string specifying an API version. Default is \dQuote{2015-10-01}.} +\item{service}{The AWS service (\code{ec2} or \code{ssm}). Defaults to \code{ec2}.} + \item{clean_response}{A logical indicating whether to remove line breaks from the response. This is useful for returning a clean response object, but may not be appropriate if the XML-formatted API response contains meaningful linebreaks (e.g., in a keypair).} \item{\dots}{Additional arguments passed to \code{\link[httr:GET]{httr::GET()}}.} diff --git a/man/get_parameter.Rd b/man/get_parameter.Rd new file mode 100644 index 0000000..1107bb1 --- /dev/null +++ b/man/get_parameter.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ssm_parameter.R +\name{get_parameter} +\alias{get_parameter} +\title{gets a parameter from the parameter store} +\usage{ +get_parameter(name, with_decryption = TRUE, ...) +} +\arguments{ +\item{name}{The parameter name} + +\item{with_decryption}{Specifies if the parameter should be decripted. Defaults to \code{TRUE}} + +\item{...}{Additional arguments passed to \code{ec2HTTP}} +} +\value{ +The value of the parameter +} +\description{ +gets a parameter from the parameter store +}