From 875c71cb3a254263407d86f46e9fcfd8970805cd Mon Sep 17 00:00:00 2001 From: Hong Ooi Date: Wed, 13 May 2020 20:07:48 +1000 Subject: [PATCH] Handle extended resource fields (#9) closes #8 --- DESCRIPTION | 2 +- NEWS.md | 4 ++ R/az_resgroup.R | 16 ++++---- R/az_resource.R | 63 +++++++++++++++++++------------- R/az_template.R | 28 +++++++------- R/utils.R | 12 +++--- tests/testthat/test04_resource.R | 27 +++++++++++++- 7 files changed, 96 insertions(+), 56 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index dabcc36..365dd5e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: AzureRMR Title: Interface to 'Azure Resource Manager' -Version: 2.3.2 +Version: 2.3.2.9000 Authors@R: c( person("Hong", "Ooi", , "hongooi@microsoft.com", role = c("aut", "cre")), person("Microsoft", role="cph") diff --git a/NEWS.md b/NEWS.md index dba2fc8..15779b2 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,7 @@ +# AzureRMR 2.3.2.9000 + +- Allow for extra resource type-specific fields beyond those mentioned in the Resource Manager documentation. In particular, virtual machines and managed disks may have a `zones` field containing the availability zones. + # AzureRMR 2.3.2 - Add `do_operation` method for the Resource Manager login client, allowing arbitrary operations at the top-level scope. diff --git a/R/az_resgroup.R b/R/az_resgroup.R index 209c8d9..fe12f29 100644 --- a/R/az_resgroup.R +++ b/R/az_resgroup.R @@ -302,7 +302,7 @@ private=list( } else { - private$validate_parms(parms) + # private$validate_parms(parms) self$name <- parms$name } parms @@ -311,17 +311,17 @@ private=list( init_and_create=function(name, ...) { parms <- modifyList(list(...), list(name=name)) - private$validate_parms(parms) + # private$validate_parms(parms) self$name <- name private$rg_op(body=parms, encode="json", http_verb="PUT") }, - validate_parms=function(parms) - { - required_names <- c("location", "name") - optional_names <- c("id", "managedBy", "tags", "properties", "type") - validate_object_names(names(parms), required_names, optional_names) - }, + # validate_parms=function(parms) + # { + # required_names <- c("location", "name") + # optional_names <- c("id", "managedBy", "tags", "properties", "type") + # validate_object_names(names(parms), required_names, optional_names) + # }, rg_op=function(op="", ...) { diff --git a/R/az_resource.R b/R/az_resource.R index c3d3e07..6d989f9 100644 --- a/R/az_resource.R +++ b/R/az_resource.R @@ -130,6 +130,7 @@ public=list( tags=NULL, token=NULL, etag=NULL, + ext=list(), # constructor overloads: # 1. deploy resource: resgroup, {provider, path}|type, name, ... @@ -163,6 +164,7 @@ public=list( self$sku <- parms$sku self$tags <- parms$tags self$etag <- parms$etag + self$ext <- get_extended_resource_fields(parms) NULL }, @@ -240,9 +242,9 @@ public=list( update=function(..., options=list()) { parms <- list(...) - private$validate_update_parms(names(parms)) + # private$validate_update_parms(names(parms)) private$res_op( - body=jsonlite::toJSON(parms, auto_unbox=TRUE, digits=22), + body=jsonlite::toJSON(parms, auto_unbox=TRUE, digits=22, null="null"), options=options, encode="raw", http_verb="PATCH" @@ -326,8 +328,8 @@ private=list( init_from_parms=function(parms) { # allow list(NULL) as special case for creating an empty object - if(!identical(parms, list(NULL))) - private$validate_response_parms(parms) + # if(!identical(parms, list(NULL))) + # private$validate_response_parms(parms) parms }, @@ -344,7 +346,7 @@ private=list( if(length(properties) == 1 && is.character(properties[[1]]) && jsonlite::validate(properties[[1]])) properties <- jsonlite::fromJSON(properties[[1]], simplifyVector=FALSE) - private$validate_deploy_parms(properties) + # private$validate_deploy_parms(properties) private$res_op(body=properties, encode="json", http_verb="PUT") # do we wait until resource has finished provisioning? @@ -379,29 +381,29 @@ private=list( } }, - validate_deploy_parms=function(parms) - { - required_names <- character(0) - optional_names <- - c("identity", "kind", "location", "managedBy", "plan", "properties", "sku", "tags", "etag") - validate_object_names(names(parms), required_names, optional_names) - }, + # validate_deploy_parms=function(parms) + # { + # required_names <- character(0) + # optional_names <- + # c("identity", "kind", "location", "managedBy", "plan", "properties", "sku", "tags", "etag") + # validate_object_names(names(parms), required_names, optional_names) + # }, - validate_response_parms=function(parms) - { - required_names <- c("id", "name", "type") - optional_names <- - c("identity", "kind", "location", "managedBy", "plan", "properties", "sku", "tags", "etag") - validate_object_names(names(parms), required_names, optional_names) - }, + # validate_response_parms=function(parms) + # { + # required_names <- c("id", "name", "type") + # optional_names <- + # c("identity", "kind", "location", "managedBy", "plan", "properties", "sku", "tags", "etag") + # validate_object_names(names(parms), required_names, optional_names) + # }, - validate_update_parms=function(parms) - { - required_names <- character(0) - optional_names <- - c("identity", "kind", "location", "managedBy", "plan", "properties", "sku", "tags", "etag") - validate_object_names(names(parms), required_names, optional_names) - }, + # validate_update_parms=function(parms) + # { + # required_names <- character(0) + # optional_names <- + # c("identity", "kind", "location", "managedBy", "plan", "properties", "sku", "tags", "etag") + # validate_object_names(names(parms), required_names, optional_names) + # }, res_op=function(op="", ..., api_version=private$api_version) { @@ -413,3 +415,12 @@ private=list( call_azure_rm(self$token, self$subscription, op, ..., api_version=api_version) } )) + + +get_extended_resource_fields <- function(res_fields) +{ + known_fields <- c("id", "name", "type", "identity", "kind", "location", "managedBy", + "plan", "properties", "sku", "tags", "etag") + nms <- names(res_fields) + res_fields[!(nms %in% known_fields)] +} diff --git a/R/az_template.R b/R/az_template.R index 819bb3a..2aeb92c 100644 --- a/R/az_template.R +++ b/R/az_template.R @@ -178,7 +178,7 @@ private=list( init_from_parms=function(parms) { - private$validate_response_parms(parms) + # private$validate_response_parms(parms) self$name <- parms$name parms }, @@ -193,7 +193,7 @@ private=list( mode="Incremental" ) properties <- modifyList(default_properties, list(...)) - private$validate_deploy_parms(properties) + # private$validate_deploy_parms(properties) # rather than working with R objects, convert to JSON and do text munging # this allows adding template/params that are already JSON text without conversion roundtrip @@ -260,19 +260,19 @@ private=list( parms }, - validate_response_parms=function(parms) - { - required_names <- c("name") - optional_names <- c("id", "properties") - validate_object_names(names(parms), required_names, optional_names) - }, + # validate_response_parms=function(parms) + # { + # required_names <- c("name") + # optional_names <- c("id", "properties") + # validate_object_names(names(parms), required_names, optional_names) + # }, - validate_deploy_parms=function(parms) - { - required_names <- c("debugSetting", "mode") - optional_names <- c("onErrorDeployment") - validate_object_names(names(parms), required_names, optional_names) - }, + # validate_deploy_parms=function(parms) + # { + # required_names <- c("debugSetting", "mode") + # optional_names <- c("onErrorDeployment") + # validate_object_names(names(parms), required_names, optional_names) + # }, # delete resources that were created (which may not be the same as resources that are required) free_resources=function() diff --git a/R/utils.R b/R/utils.R index 9195f16..d355f76 100644 --- a/R/utils.R +++ b/R/utils.R @@ -78,12 +78,12 @@ get_paged_list <- function(lst, token, next_link_name="nextLink", value_name="va # check that 1) all required names are present; 2) optional names may be present; 3) no other names are present -validate_object_names <- function(x, required, optional=character(0)) -{ - valid <- all(required %in% x) && all(x %in% c(required, optional)) - if(!valid) - stop("Invalid object names") -} +# validate_object_names <- function(x, required, optional=character(0)) +# { +# valid <- all(required %in% x) && all(x %in% c(required, optional)) +# if(!valid) +# stop("Invalid object names") +# } # handle different behaviour of file_path on Windows/Linux wrt trailing / diff --git a/tests/testthat/test04_resource.R b/tests/testthat/test04_resource.R index 5fde16e..dc7f8fa 100644 --- a/tests/testthat/test04_resource.R +++ b/tests/testthat/test04_resource.R @@ -17,8 +17,8 @@ rg <- az_rm$ test_that("Resource methods work", { - restype <- "Microsoft.Storage/storageAccounts" # storage account resource + restype <- "Microsoft.Storage/storageAccounts" resname <- paste(sample(letters, 20, replace=TRUE), collapse="") expect_false(rg$resource_exists(type="foo/bar", name="randomname")) @@ -78,4 +78,29 @@ test_that("Resource methods work", expect_true(is(res2, "az_resource") && !is_empty(res2$properties)) }) +test_that("Extended resource fields works", +{ + # managed disk resource + restype <- "Microsoft.Compute/disks" + resname <- paste(sample(letters, 20, replace=TRUE), collapse="") + + res <- rg$create_resource(type=restype, name=resname, + properties=list( + creationData=list(createOption="empty"), + diskSizeGB=500, + osType="" + ), + sku=list(name="Standard_LRS"), + zones=list(1) + ) + + expect_true(rg$resource_exists(type=restype, name=resname)) + expect_is(res, "az_resource") + + expect_false(is_empty(res$ext)) + + reslst <- rg$list_resources() + expect_true(is.list(reslst) && all(sapply(reslst, is_resource))) +}) + rg$delete(confirm=FALSE)