From b7dc1ce0f7612e16ad008c98a823ecbcd6938ea1 Mon Sep 17 00:00:00 2001 From: AdarShaked <59121240+AdarShaked@users.noreply.github.com> Date: Sun, 23 May 2021 12:46:55 +0300 Subject: [PATCH] Rd 626 support create if missing (#190) * add create if missing for webapp and plan * use resource factory to check if resource exist * use function in order to save common info in runtime props * remove logs * use function to save external resource details * fix plan tests and add external resouce and create if missing tests * add genereate cloud error function, add tests for webapp with create if missing and refactor tests structure * fix plan delete * use function to get azure config and create loadbalancer backend puul resource in sdk * implement get load balancer backend pool in sdk * add create if missing for load balancer backend pool * remove uneccesary inputs from example blueprint * flake8 * add loadbalancer probe sdk * fix load balancer probe bug and add create if missing to lb probes * remove comments * fix lb rule bug * add create if missing for loadbalancer rule * add create if missing for inbound nat rule * flake8 fixes * Rd 626 support create if missing neteork resources (#191) * some refactoring for the network resources * fix subnet params bug * Rd 626 support create if missing storage resources (#192) * add file share sdk and task * clean storage account * more refactoring for storage account * change mapping * add delete operation for fileshare * add storage account runtime property * Rd 626 support create if missing compute resources (#193) * add create if missing for managed cluster * add operation for aks * map aks store kubeconfig operation * delete comments * delete one more comment * bump version --- CHANGELOG.txt | 1 + azure_sdk/resources/app_service/plan.py | 14 +- azure_sdk/resources/app_service/web_app.py | 14 +- azure_sdk/resources/network/load_balancer.py | 96 +++++++ azure_sdk/resources/storage/file_share.py | 57 ++++ cloudify_azure/constants.py | 2 + cloudify_azure/decorators.py | 159 +++++++---- cloudify_azure/resources/app_service/plan.py | 50 +--- .../resources/app_service/publishing_user.py | 8 +- .../resources/app_service/webapp.py | 51 +--- .../resources/compute/availabilityset.py | 20 +- .../resources/compute/managed_cluster.py | 87 +++--- .../resources/network/loadbalancer.py | 256 +++++++----------- .../resources/network/networkinterfacecard.py | 36 +-- .../resources/network/networksecuritygroup.py | 22 +- .../resources/network/networksecurityrule.py | 21 +- .../resources/network/publicipaddress.py | 28 +- cloudify_azure/resources/network/route.py | 21 +- .../resources/network/routetable.py | 21 +- cloudify_azure/resources/network/subnet.py | 81 +++--- .../resources/network/virtualnetwork.py | 21 +- cloudify_azure/resources/storage/file.py | 175 +++++------- .../resources/storage/storageaccount.py | 20 +- cloudify_azure/tests/resources/__init__.py | 9 + .../tests/resources/test_app_service.py | 192 ++++++++----- .../tests/resources/test_deployment.py | 29 +- .../tests/resources/test_managed_cluster.py | 135 +++++++-- .../tests/resources/test_network.py | 20 +- cloudify_azure/utils.py | 25 +- .../app_service/app-sample-blueprint.yaml | 43 +-- plugin.yaml | 44 +-- 31 files changed, 926 insertions(+), 832 deletions(-) create mode 100644 azure_sdk/resources/storage/file_share.py diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 500a6e44..f4f4fc36 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,4 @@ +3.3.0: Add create_if_missing for all azure resources. 3.2.1: Fix bug, fail if trying to use external resource that doesn't exist. 3.2.0: Add pull operation for Azure ARM deployment. 3.1.0: Support create if missing/use if exists logic. diff --git a/azure_sdk/resources/app_service/plan.py b/azure_sdk/resources/app_service/plan.py index 0f7d266e..1b20d276 100644 --- a/azure_sdk/resources/app_service/plan.py +++ b/azure_sdk/resources/app_service/plan.py @@ -32,11 +32,12 @@ def get(self, group_name, plan_name): self.logger.info("Get plan...{0}".format(plan_name)) plan = self.client.app_service_plans.get( resource_group_name=group_name, - name=plan_name - ).as_dict() - self.logger.info( - 'Get plan result: {0}'.format( - utils.secure_logging_content(plan))) + name=plan_name) + if plan: + plan = plan.as_dict() + self.logger.info( + 'Get plan result: {0}'.format( + utils.secure_logging_content(plan))) return plan def create_or_update(self, group_name, plan_name, params): @@ -56,10 +57,9 @@ def create_or_update(self, group_name, plan_name, params): def delete(self, group_name, plan_name): self.logger.info("Deleting plan...{0}".format(plan_name)) - delete_async_operation = self.client.app_service_plans.delete( + self.client.app_service_plans.delete( resource_group_name=group_name, name=plan_name ) - delete_async_operation.wait() self.logger.debug( 'Deleted plan {0}'.format(plan_name)) diff --git a/azure_sdk/resources/app_service/web_app.py b/azure_sdk/resources/app_service/web_app.py index 87077421..dcc8533d 100644 --- a/azure_sdk/resources/app_service/web_app.py +++ b/azure_sdk/resources/app_service/web_app.py @@ -32,11 +32,12 @@ def get(self, group_name, web_app_name): self.logger.info("Get web_app...{0}".format(web_app_name)) web_app = self.client.web_apps.get( resource_group_name=group_name, - name=web_app_name - ).as_dict() - self.logger.info( - 'Get web_app result: {0}'.format( - utils.secure_logging_content(web_app))) + name=web_app_name) + if web_app: + web_app = web_app.as_dict() + self.logger.info( + 'Get web_app result: {0}'.format( + utils.secure_logging_content(web_app))) return web_app def create_or_update(self, group_name, web_app_name, params): @@ -55,10 +56,9 @@ def create_or_update(self, group_name, web_app_name, params): def delete(self, group_name, web_app_name): self.logger.info("Deleting web_app...{0}".format(web_app_name)) - delete_async_operation = self.client.web_apps.delete( + self.client.web_apps.delete( resource_group_name=group_name, name=web_app_name ) - delete_async_operation.wait() self.logger.debug( 'Deleted web_app {0}'.format(web_app_name)) diff --git a/azure_sdk/resources/network/load_balancer.py b/azure_sdk/resources/network/load_balancer.py index 74520c55..16ecf827 100644 --- a/azure_sdk/resources/network/load_balancer.py +++ b/azure_sdk/resources/network/load_balancer.py @@ -68,3 +68,99 @@ def delete(self, group_name, load_balancer_name): delete_async_operation.wait() self.logger.debug( 'Deleted load_balancer {0}'.format(load_balancer_name)) + + +class LoadBalancerBackendAddressPool(AzureResource): + + def __init__(self, azure_config, logger, + api_version=constants.API_VER_NETWORK_LB_BACKEND_PROBES): + super(LoadBalancerBackendAddressPool, self).__init__(azure_config) + self.logger = logger + self.client = \ + NetworkManagementClient(self.credentials, self.subscription_id, + api_version=api_version) + + def get(self, group_name, load_balancer_name, backend_address_pool_name): + self.logger.info("Get load balancer backend address pool...{0}".format( + backend_address_pool_name)) + backend_pool = self.client.load_balancer_backend_address_pools.get( + resource_group_name=group_name, + load_balancer_name=load_balancer_name, + backend_address_pool_name=backend_address_pool_name).as_dict() + self.logger.info( + "Get load balancer backend address pool: {0}".format( + utils.secure_logging_content(backend_pool)) + ) + return backend_pool + + +class LoadBalancerProbe(AzureResource): + + def __init__(self, azure_config, logger, + api_version=constants.API_VER_NETWORK_LB_BACKEND_PROBES): + super(LoadBalancerProbe, self).__init__(azure_config) + self.logger = logger + self.client = \ + NetworkManagementClient(self.credentials, self.subscription_id, + api_version=api_version) + + def get(self, group_name, load_balancer_name, probe_name): + self.logger.info("Get load balancer probe...{0}".format( + probe_name)) + probe = self.client.load_balancer_probes.get( + resource_group_name=group_name, + load_balancer_name=load_balancer_name, + probe_name=probe_name).as_dict() + self.logger.info( + "Get load balancer probe result: {0}".format( + utils.secure_logging_content(probe)) + ) + return probe + + +class LoadBalancerLoadBalancingRule(AzureResource): + + def __init__(self, azure_config, logger, + api_version=constants.API_VER_NETWORK_LB_BACKEND_PROBES): + super(LoadBalancerLoadBalancingRule, self).__init__(azure_config) + self.logger = logger + self.client = \ + NetworkManagementClient(self.credentials, self.subscription_id, + api_version=api_version) + + def get(self, group_name, load_balancer_name, load_balancing_rule_name): + self.logger.info("Get load balancer rule...{0}".format( + load_balancing_rule_name)) + rule = self.client.load_balancer_load_balancing_rules.get( + resource_group_name=group_name, + load_balancer_name=load_balancer_name, + load_balancing_rule_name=load_balancing_rule_name).as_dict() + self.logger.info( + "Get load balancer rule result: {0}".format( + utils.secure_logging_content(rule)) + ) + return rule + + +class LoadBalancerInboundNatRule(AzureResource): + + def __init__(self, azure_config, logger, + api_version=constants.API_VER_NETWORK_LB_BACKEND_PROBES): + super(LoadBalancerInboundNatRule, self).__init__(azure_config) + self.logger = logger + self.client = \ + NetworkManagementClient(self.credentials, self.subscription_id, + api_version=api_version) + + def get(self, group_name, load_balancer_name, inbound_nat_rule_name): + self.logger.info("Get load balancer inbound nat rule...{0}".format( + inbound_nat_rule_name)) + rule = self.client.inbound_nat_rules.get( + resource_group_name=group_name, + load_balancer_name=load_balancer_name, + inbound_nat_rule_name=inbound_nat_rule_name).as_dict() + self.logger.info( + "Get load balancer inbound nat rule result: {0}".format( + utils.secure_logging_content(rule)) + ) + return rule diff --git a/azure_sdk/resources/storage/file_share.py b/azure_sdk/resources/storage/file_share.py new file mode 100644 index 00000000..8d86a3c7 --- /dev/null +++ b/azure_sdk/resources/storage/file_share.py @@ -0,0 +1,57 @@ +from azure.mgmt.storage import StorageManagementClient + +from cloudify_azure import (constants, utils) +from azure_sdk.common import AzureResource + + +class FileShare(AzureResource): + + def __init__(self, azure_config, logger, + api_version=constants.API_VER_STORAGE_FILE_SHARE): + super(FileShare, self).__init__(azure_config) + self.logger = logger + self.client = \ + StorageManagementClient(self.credentials, self.subscription_id, + api_version=api_version) + + def get(self, group_name, account_name, share_name): + self.logger.info("Get File Share...{0}".format(share_name)) + file_share = self.client.file_shares.get( + resource_group_name=group_name, + account_name=account_name, + share_name=share_name + ).as_dict() + self.logger.info( + 'Get File Share result: {0}'.format( + utils.secure_logging_content(file_share))) + return file_share + + def create(self, + group_name, + account_name, + share_name, + metadata=None, + share_quota=None): + self.logger.info( + "Create File Share...{0}".format(share_name)) + file_share = self.client.file_shares.create( + resource_group_name=group_name, + account_name=account_name, + share_name=share_name, + metadata=metadata, + share_quota=share_quota, + ).as_dict() + self.logger.info( + 'Create File Share result : {0}'.format( + utils.secure_logging_content(file_share))) + return file_share + + def delete(self, group_name, account_name, share_name): + self.logger.info("Deleting File Share...{0}".format(share_name)) + self.client.file_shares.delete( + resource_group_name=group_name, + account_name=account_name, + share_name=share_name + ) + self.logger.debug( + 'Deleted File Share {0}'.format(share_name)) diff --git a/cloudify_azure/constants.py b/cloudify_azure/constants.py index 57be0cd0..c255b20e 100644 --- a/cloudify_azure/constants.py +++ b/cloudify_azure/constants.py @@ -40,7 +40,9 @@ # Each service has its own API version independent of any other services API_VER_RESOURCES = '2017-05-10' API_VER_STORAGE = '2015-06-15' +API_VER_STORAGE_FILE_SHARE = '2019-06-01' API_VER_NETWORK = '2016-09-01' +API_VER_NETWORK_LB_BACKEND_PROBES = '2020-03-01' API_VER_COMPUTE = '2016-03-30' API_VER_STORAGE_BLOB = '2015-12-11' API_VER_CONTAINER = '2017-07-01' diff --git a/cloudify_azure/decorators.py b/cloudify_azure/decorators.py index 93aa78b5..34b19dfa 100644 --- a/cloudify_azure/decorators.py +++ b/cloudify_azure/decorators.py @@ -27,11 +27,18 @@ from azure_sdk.resources.network.subnet import Subnet from azure_sdk.resources.deployment import Deployment from azure_sdk.resources.resource_group import ResourceGroup +from azure_sdk.resources.storage.file_share import FileShare from azure_sdk.resources.storage.storage_account import StorageAccount from azure_sdk.resources.network.network_security_rule \ import NetworkSecurityRule from azure_sdk.resources.compute.virtual_machine_extension \ import VirtualMachineExtension +from azure_sdk.resources.network.load_balancer import \ + (LoadBalancerProbe, + LoadBalancerInboundNatRule, + LoadBalancerLoadBalancingRule, + LoadBalancerBackendAddressPool + ) def sa_name_generator(): @@ -40,12 +47,20 @@ def sa_name_generator(): string.ascii_lowercase + string.digits) for i in range(3, 24)) +def file_share_name_generator(): + """Generates a unique File Share resource name""" + return ''.join(random.choice(string.ascii_lowercase + string.digits) + for i in range(random.randint(24, 63))) + + def get_unique_name(resource, resource_group_name, name, **kwargs): if not name: for _ in range(0, 15): # special naming handling if isinstance(resource, StorageAccount): name = sa_name_generator() + elif isinstance(resource, FileShare): + name = file_share_name_generator() else: name = "{0}".format(uuid4()) try: @@ -69,6 +84,15 @@ def get_unique_name(resource, resource_group_name, name, **kwargs): elif isinstance(resource, NetworkSecurityRule): nsg_name = kwargs['nsg_name'] result = resource.get(resource_group_name, nsg_name, name) + elif isinstance(resource, (LoadBalancerBackendAddressPool, + LoadBalancerLoadBalancingRule, + LoadBalancerInboundNatRule, + LoadBalancerProbe)): + lb_name = kwargs['lb_name'] + result = resource.get(resource_group_name, lb_name, name) + elif isinstance(resource, FileShare): + sa_name = kwargs['sa_name'] + result = resource.get(resource_group_name, sa_name, name) else: result = resource.get(resource_group_name, name) if result: # found a resource with same name @@ -142,6 +166,23 @@ def wrapper_inner(*args, **kwargs): resource=resource, resource_group_name=resource_group_name, name=name) + elif isinstance(resource, (LoadBalancerBackendAddressPool, + LoadBalancerLoadBalancingRule, + LoadBalancerInboundNatRule, + LoadBalancerProbe)): + lb_name = utils.get_load_balancer(ctx) + name = get_unique_name( + resource=resource, + resource_group_name=resource_group_name, + name=name, + lb_name=lb_name) + elif isinstance(resource, FileShare): + sa_name = utils.get_storage_account(ctx) + name = get_unique_name( + resource=resource, + resource_group_name=resource_group_name, + name=name, + sa_name=sa_name) else: name = get_unique_name( resource=resource, @@ -163,47 +204,11 @@ def wrapper_outer(func): def wrapper_inner(*args, **kwargs): ctx = kwargs['ctx'] name = utils.get_resource_name(ctx) - try: - # check if azure_config is given and if the resource - # is external or not - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated " - "please use client_config, " - "in later version it will be removed") - resource = resource_class_name(azure_config, ctx.logger) - if not isinstance(resource, ResourceGroup): - resource_group_name = utils.get_resource_group(ctx) - # handle speical cases - # resource_group - if isinstance(resource, ResourceGroup): - exists = resource.get(name) - elif isinstance(resource, Deployment): - exists = resource.get(resource_group_name, name) - # virtual_machine_extension - elif isinstance(resource, VirtualMachineExtension): - vm_name = \ - ctx.node.properties.get('virtual_machine_name') - exists = resource.get(resource_group_name, vm_name, name) - # subnet - elif isinstance(resource, Subnet): - vnet_name = utils.get_virtual_network(ctx) - exists = resource.get(resource_group_name, vnet_name, name) - # route - elif isinstance(resource, Route): - rtbl_name = utils.get_route_table(ctx) - exists = resource.get(resource_group_name, rtbl_name, name) - # network_security_rule - elif isinstance(resource, NetworkSecurityRule): - nsg_name = utils.get_network_security_group(ctx) - exists = resource.get(resource_group_name, nsg_name, name) - else: - exists = resource.get(resource_group_name, name) - except CloudError: - exists = None - + # check if azure_config is given and if the resource + # is external or not + azure_config = utils.get_client_config(ctx.node.properties) + resource_factory = ResourceGetter(ctx, azure_config, name) + exists = resource_factory.get_resource(resource_class_name) # There is now a good idea whether the desired resource exists. # Now find out if it is expected and if it does or doesn't. expected = ctx.node.properties.get( @@ -228,14 +233,16 @@ def wrapper_inner(*args, **kwargs): resource_class_name, name)) elif use_existing and not (create_op and arm_deployment): ctx.logger.info("Using external resource") - ctx.instance.runtime_properties['resource'] = exists - ctx.instance.runtime_properties['resource_id'] = exists.get( - "id", "") + utils.save_common_info_in_runtime_properties( + resource_group_name=resource_factory.resource_group_name, + resource_name=name, + resource_get_create_result=exists) return elif not create and not (create_op and arm_deployment): raise cfy_exc.NonRecoverableError( - "Can't use non-existing {0} '{1}'.".format( - resource_class_name, name)) + "Can't use non-existing," + " or resource already exist but configuration is not to" + " use it: {0} '{1}'.".format(resource_class_name, name)) elif create and exists and ctx.workflow_id not in \ ['update', 'execute_operation']: ctx.logger.warn("Resource with name {0} exists".format(name)) @@ -247,3 +254,63 @@ def wrapper_inner(*args, **kwargs): return func(*args, **kwargs) return wrapper_inner return wrapper_outer + + +class ResourceGetter(object): + + def __init__(self, ctx, azure_config, resource_name): + self.azure_config = azure_config + self.ctx = ctx + self.name = resource_name + self.resource_group_name = None + + def get_resource(self, resource_class_name): + try: + resource = resource_class_name(self.azure_config, self.ctx.logger) + if not isinstance(resource, ResourceGroup): + resource_group_name = utils.get_resource_group(self.ctx) + self.resource_group_name = resource_group_name + # handle speical cases + # resource_group + if isinstance(resource, ResourceGroup): + exists = resource.get(self.name) + self.resource_group_name = self.name + elif isinstance(resource, Deployment): + exists = resource.get(resource_group_name, self.name) + # virtual_machine_extension + elif isinstance(resource, VirtualMachineExtension): + vm_name = \ + self.ctx.node.properties.get('virtual_machine_name') + exists = resource.get(resource_group_name, vm_name, self.name) + # subnet + elif isinstance(resource, Subnet): + vnet_name = utils.get_virtual_network(self.ctx) + exists = resource.get(resource_group_name, vnet_name, + self.name) + # route + elif isinstance(resource, Route): + rtbl_name = utils.get_route_table(self.ctx) + exists = resource.get(resource_group_name, rtbl_name, + self.name) + # network_security_rule + elif isinstance(resource, NetworkSecurityRule): + nsg_name = utils.get_network_security_group(self.ctx) + exists = resource.get(resource_group_name, nsg_name, self.name) + # load_balancer_backend_address_pool + elif isinstance(resource, (LoadBalancerBackendAddressPool, + LoadBalancerLoadBalancingRule, + LoadBalancerInboundNatRule, + LoadBalancerProbe)): + lb_name = utils.get_load_balancer(self.ctx) + exists = resource.get(resource_group_name, + lb_name, + self.name) + # file share + elif isinstance(resource, FileShare): + sa_name = utils.get_storage_account(self.ctx) + exists = resource.get(resource_group_name, sa_name, self.name) + else: + exists = resource.get(resource_group_name, self.name) + except CloudError: + exists = None + return exists diff --git a/cloudify_azure/resources/app_service/plan.py b/cloudify_azure/resources/app_service/plan.py index ebc4f23e..545e41c3 100644 --- a/cloudify_azure/resources/app_service/plan.py +++ b/cloudify_azure/resources/app_service/plan.py @@ -18,59 +18,37 @@ from msrestazure.azure_exceptions import CloudError from cloudify_azure import (constants, utils) +from cloudify_azure.decorators import with_azure_resource from azure_sdk.resources.app_service.plan import ServicePlan @operation(resumable=True) +@with_azure_resource(ServicePlan) def create(ctx, resource_group, name, plan_details, **kwargs): - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) api_version = \ ctx.node.properties.get('api_version', constants.API_VER_APP_SERVICE) plan = ServicePlan(azure_config, ctx.logger, api_version) try: - result = plan.get(resource_group, name) - if ctx.node.properties.get('use_external_resource', False): - ctx.logger.info("Using external resource") - else: - ctx.logger.info("Resource with name {0} exists".format(name)) - return - except CloudError: # Customized error message will be avialable on PY3 - if ctx.node.properties.get('use_external_resource', False): - raise cfy_exc.NonRecoverableError( - "Can't use non-existing plan '{0}'.".format(name)) - else: - try: - result = \ - plan.create_or_update(resource_group, name, plan_details) - except CloudError as cr: - raise cfy_exc.NonRecoverableError( - "create plan '{0}' " - "failed with this error : {1}".format(name, - cr.message) + result = \ + plan.create_or_update(resource_group, name, plan_details) + except CloudError as cr: + raise cfy_exc.NonRecoverableError( + "create plan '{0}' " + "failed with this error : {1}".format(name, + cr.message) ) - - ctx.instance.runtime_properties['resource_group'] = resource_group - ctx.instance.runtime_properties['resource'] = result - ctx.instance.runtime_properties['resource_id'] = result.get("id", "") - ctx.instance.runtime_properties['name'] = name + utils.save_common_info_in_runtime_properties(resource_group, + name, + result) @operation(resumable=True) def delete(ctx, **kwargs): if ctx.node.properties.get('use_external_resource', False): return - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) resource_group = ctx.instance.runtime_properties.get('resource_group') name = ctx.instance.runtime_properties.get('name') api_version = \ diff --git a/cloudify_azure/resources/app_service/publishing_user.py b/cloudify_azure/resources/app_service/publishing_user.py index 0296ae14..fc7683f1 100644 --- a/cloudify_azure/resources/app_service/publishing_user.py +++ b/cloudify_azure/resources/app_service/publishing_user.py @@ -20,17 +20,13 @@ from cloudify_azure import constants +from cloudify_azure import utils from azure_sdk.resources.app_service.publishing_user import PublishingUser @operation(resumable=True) def set_user(ctx, user_details, **kwargs): - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) api_version = \ ctx.node.properties.get('api_version', constants.API_VER_APP_SERVICE) publishing_user = PublishingUser(azure_config, ctx.logger, api_version) diff --git a/cloudify_azure/resources/app_service/webapp.py b/cloudify_azure/resources/app_service/webapp.py index 1beb23bb..6bf8abf5 100644 --- a/cloudify_azure/resources/app_service/webapp.py +++ b/cloudify_azure/resources/app_service/webapp.py @@ -19,59 +19,36 @@ from cloudify.decorators import operation from cloudify_azure import (constants, utils) +from cloudify_azure.decorators import with_azure_resource from azure_sdk.resources.app_service.web_app import WebApp @operation(resumable=True) +@with_azure_resource(WebApp) def create(ctx, resource_group, name, app_config, **kwargs): - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) api_version = \ ctx.node.properties.get('api_version', constants.API_VER_APP_SERVICE) web_app = WebApp(azure_config, ctx.logger, api_version) try: - result = web_app.get(resource_group, name) - if ctx.node.properties.get('use_external_resource', False): - ctx.logger.info("Using external resource") - else: - ctx.logger.info("Resource with name {0} exists".format(name)) - return - except CloudError: - if ctx.node.properties.get('use_external_resource', False): - raise cfy_exc.NonRecoverableError( - "Can't use non-existing web_app '{0}'.".format(name)) - else: - try: - result = \ - web_app.create_or_update(resource_group, name, app_config) - except CloudError as cr: - raise cfy_exc.NonRecoverableError( - "create web_app '{0}' " - "failed with this error : {1}".format(name, - cr.message) - ) - - ctx.instance.runtime_properties['resource_group'] = resource_group - ctx.instance.runtime_properties['resource'] = result - ctx.instance.runtime_properties['resource_id'] = result.get("id", "") - ctx.instance.runtime_properties['name'] = name + result = \ + web_app.create_or_update(resource_group, name, app_config) + except CloudError as cr: + raise cfy_exc.NonRecoverableError( + "create web_app '{0} failed with this error : " + "{1}".format(name, cr.message) + ) + utils.save_common_info_in_runtime_properties(resource_group, + name, + result) @operation(resumable=True) def delete(ctx, **kwargs): if ctx.node.properties.get('use_external_resource', False): return - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) resource_group = ctx.instance.runtime_properties.get('resource_group') name = ctx.instance.runtime_properties.get('name') api_version = \ diff --git a/cloudify_azure/resources/compute/availabilityset.py b/cloudify_azure/resources/compute/availabilityset.py index f659c005..1097e863 100644 --- a/cloudify_azure/resources/compute/availabilityset.py +++ b/cloudify_azure/resources/compute/availabilityset.py @@ -31,12 +31,7 @@ @decorators.with_azure_resource(AvailabilitySet) def create(ctx, **_): """Uses an existing, or creates a new, Availability Set""" - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) name = utils.get_resource_name(ctx) resource_group_name = utils.get_resource_group(ctx) resource_config = ctx.node.properties.get('resource_config') @@ -65,9 +60,9 @@ def create(ctx, **_): cr.message) ) - ctx.instance.runtime_properties['resource_group'] = resource_group_name - ctx.instance.runtime_properties['resource'] = result - ctx.instance.runtime_properties['resource_id'] = result.get("id", "") + utils.save_common_info_in_runtime_properties(resource_group_name, + name, + result) @operation(resumable=True) @@ -75,12 +70,7 @@ def delete(ctx, **_): """Deletes a Availability Set""" if ctx.node.properties.get('use_external_resource', False): return - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) resource_group_name = utils.get_resource_group(ctx) name = ctx.instance.runtime_properties.get('name') api_version = \ diff --git a/cloudify_azure/resources/compute/managed_cluster.py b/cloudify_azure/resources/compute/managed_cluster.py index b5f6da03..fa2d5ea9 100644 --- a/cloudify_azure/resources/compute/managed_cluster.py +++ b/cloudify_azure/resources/compute/managed_cluster.py @@ -21,81 +21,64 @@ from cloudify.decorators import operation from cloudify import exceptions as cfy_exc -from cloudify_azure import (constants, utils) +from cloudify_azure import (constants, utils, decorators) from azure_sdk.resources.compute.managed_cluster import ManagedCluster -@operation(resumable=True) -def create(ctx, resource_group, cluster_name, resource_config, **kwargs): - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") - store_kube_config_in_runtime = \ - ctx.node.properties.get('store_kube_config_in_runtime') +def get_manged_cluster_interface(ctx): + azure_config = utils.get_client_config(ctx.node.properties) api_version = \ ctx.node.properties.get('api_version', constants.API_VER_MANAGED_CLUSTER) - managed_cluster = ManagedCluster(azure_config, ctx.logger, api_version) + return ManagedCluster(azure_config, ctx.logger, api_version) + + +@operation(resumable=True) +@decorators.with_azure_resource(ManagedCluster) +def create(ctx, resource_group, cluster_name, resource_config, **_): + managed_cluster = get_manged_cluster_interface(ctx) resource_config_payload = {} resource_config_payload = \ utils.handle_resource_config_params(resource_config_payload, resource_config) try: - result = managed_cluster.get(resource_group, cluster_name) - if ctx.node.properties.get('use_external_resource', False): - ctx.logger.info("Using external resource") - else: - ctx.logger.info("Resource with name {0} exists".format( - cluster_name)) - return - except CloudError: - if ctx.node.properties.get('use_external_resource', False): - raise cfy_exc.NonRecoverableError( - "Can't use non-existing managed_cluster '{0}'.".format( - cluster_name)) - else: - try: - result = \ - managed_cluster.create_or_update(resource_group, - cluster_name, - resource_config_payload) - except CloudError as cr: - raise cfy_exc.NonRecoverableError( - "create managed_cluster '{0}' " - "failed with this error : {1}".format(cluster_name, - cr.message) - ) + result = \ + managed_cluster.create_or_update(resource_group, + cluster_name, + resource_config_payload) + except CloudError as cr: + raise cfy_exc.NonRecoverableError( + "create managed_cluster '{0}' " + "failed with this error : {1}".format(cluster_name, + cr.message) + ) + + utils.save_common_info_in_runtime_properties(resource_group, + cluster_name, + result) - ctx.instance.runtime_properties['resource_group'] = resource_group - ctx.instance.runtime_properties['resource'] = result - ctx.instance.runtime_properties['resource_id'] = result.get("id", "") - ctx.instance.runtime_properties['name'] = cluster_name + +@operation(resumable=True) +def store_kubeconf_if_needed(ctx): + resource_group = utils.get_resource_group(ctx) + name = utils.get_resource_name(ctx) + managed_cluster = get_manged_cluster_interface(ctx) + store_kube_config_in_runtime = \ + ctx.node.properties.get('store_kube_config_in_runtime') if store_kube_config_in_runtime: ctx.instance.runtime_properties['kubeconf'] = \ yaml.load(base64.b64decode(managed_cluster.get_admin_kubeconf( - resource_group, cluster_name))) + resource_group, name))) @operation(resumable=True) -def delete(ctx, **kwargs): +def delete(ctx, **_): if ctx.node.properties.get('use_external_resource', False): return - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") resource_group = ctx.instance.runtime_properties.get('resource_group') name = ctx.instance.runtime_properties.get('name') - api_version = \ - ctx.node.properties.get('api_version', - constants.API_VER_MANAGED_CLUSTER) - managed_cluster = ManagedCluster(azure_config, ctx.logger, api_version) + managed_cluster = get_manged_cluster_interface(ctx) try: managed_cluster.get(resource_group, name) except CloudError: diff --git a/cloudify_azure/resources/network/loadbalancer.py b/cloudify_azure/resources/network/loadbalancer.py index 94f2077f..27144c60 100644 --- a/cloudify_azure/resources/network/loadbalancer.py +++ b/cloudify_azure/resources/network/loadbalancer.py @@ -26,11 +26,17 @@ from cloudify_azure import (constants, decorators, utils) from cloudify_azure.resources.network.ipconfiguration \ import get_ip_configurations -from azure_sdk.resources.network.load_balancer import LoadBalancer from azure_sdk.resources.network.network_interface_card \ import NetworkInterfaceCard from azure_sdk.resources.network.public_ip_address \ import PublicIPAddress +from azure_sdk.resources.network.load_balancer import\ + (LoadBalancer, + LoadBalancerProbe, + LoadBalancerInboundNatRule, + LoadBalancerLoadBalancingRule, + LoadBalancerBackendAddressPool) + LB_ADDRPOOLS_KEY = 'load_balancer_backend_address_pools' @@ -83,12 +89,7 @@ def configure(ctx, **_): if ip_cfg.get('subnet'): del ip_cfg['subnet'] # Create a resource (if necessary) - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) name = ctx.instance.runtime_properties.get('name') resource_group_name = utils.get_resource_group(ctx) api_version = \ @@ -122,10 +123,9 @@ def configure(ctx, **_): cr.message) ) - ctx.instance.runtime_properties['resource_group'] = resource_group_name - ctx.instance.runtime_properties['resource'] = result - ctx.instance.runtime_properties['resource_id'] = result.get("id", "") - ctx.instance.runtime_properties['name'] = name + utils.save_common_info_in_runtime_properties(resource_group_name, + name, + result) for fe_ipc_data in result.get('frontend_ip_configurations', list()): ctx.instance.runtime_properties['ip'] = \ @@ -145,12 +145,7 @@ def configure(ctx, **_): def delete(ctx, **_): """Deletes a Load Balancer""" # Delete the resource - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) resource_group_name = utils.get_resource_group(ctx) name = ctx.instance.runtime_properties.get('name') api_version = \ @@ -176,17 +171,12 @@ def delete(ctx, **_): def attach_ip_configuration(ctx, **_): """Generates a usable UUID for the NIC's IP Configuration""" # Generate the IPConfiguration's name - azure_config = ctx.source.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.source.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.source.node.properties) resource_group_name = \ ctx.source.instance.runtime_properties.get('resource_group') load_balancer_name = ctx.source.instance.runtime_properties.get('name') load_balancer = LoadBalancer(azure_config, ctx.logger) - ip_configuration_name = ctx.target.node.properties['name'] + ip_configuration_name = ctx.target.node.properties.get('name') ip_configuration_name = \ get_unique_lb_prop_name(load_balancer, resource_group_name, load_balancer_name, @@ -196,6 +186,8 @@ def attach_ip_configuration(ctx, **_): @operation(resumable=True) +@decorators.with_generate_name(LoadBalancerBackendAddressPool) +@decorators.with_azure_resource(LoadBalancerBackendAddressPool) def create_backend_pool(ctx, **_): """Uses an existing, or creates a new, Load Balancer Backend Pool""" # Check if invalid external resource @@ -204,25 +196,13 @@ def create_backend_pool(ctx, **_): raise cfy_exc.NonRecoverableError( '"use_external_resource" specified without a resource "name"') # Generate a name if it doesn't exist - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) resource_group_name = utils.get_resource_group(ctx) - load_balancer_name = ctx.node.properties.get('load_balancer_name') or \ - utils.get_resource_name_ref(constants.REL_CONTAINED_IN_LB, - 'load_balancer_name', - _ctx=ctx) + load_balancer_name = utils.get_load_balancer(ctx, + constants.REL_CONTAINED_IN_LB) load_balancer = LoadBalancer(azure_config, ctx.logger) - backend_pool_name = ctx.node.properties.get('name') - backend_pool_name = \ - get_unique_lb_prop_name(load_balancer, resource_group_name, - load_balancer_name, - "backend_address_pools", - backend_pool_name) - ctx.instance.runtime_properties['name'] = backend_pool_name + backend_pool_name = utils.get_resource_name(ctx) + # Get an interface to the Load Balancer lb_rel = utils.get_relationship_by_type( ctx.instance.relationships, @@ -235,7 +215,8 @@ def create_backend_pool(ctx, **_): 'name': backend_pool_name, }) load_balancer_params = { - 'backend_address_pools': lb_pools + 'backend_address_pools': lb_pools, + 'location': lb_rel.target.node.properties.get('location') } # clean empty values from params load_balancer_params = \ @@ -244,16 +225,17 @@ def create_backend_pool(ctx, **_): result = load_balancer.create_or_update(resource_group_name, load_balancer_name, load_balancer_params) - for item in result.get("backend_address_pools"): - if item.get("name") == backend_pool_name: - ctx.instance.runtime_properties['resource_id'] = item.get("id") - ctx.instance.runtime_properties['resource'] = item except CloudError as cr: raise cfy_exc.NonRecoverableError( "create backend_pool '{0}' " "failed with this error : {1}".format(backend_pool_name, cr.message) ) + for item in result.get("backend_address_pools"): + if item.get("name") == backend_pool_name: + ctx.instance.runtime_properties['resource_id'] = item.get("id") + ctx.instance.runtime_properties['resource'] = item + ctx.instance.runtime_properties["resource_group"] = resource_group_name @operation(resumable=True) @@ -262,12 +244,7 @@ def delete_backend_pool(ctx, **_): if ctx.node.properties.get('use_external_resource', False): return # Get an interface to the Load Balancer - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) resource_group_name = utils.get_resource_group(ctx) lb_rel = utils.get_relationship_by_type( ctx.instance.relationships, @@ -283,7 +260,8 @@ def delete_backend_pool(ctx, **_): del lb_pools[idx] # Update the Load Balancer with the new pool list lb_params = { - 'backend_address_pools': lb_pools + 'backend_address_pools': lb_pools, + 'location': lb_rel.target.node.properties.get('location') } try: load_balancer.create_or_update(resource_group_name, lb_name, lb_params) @@ -296,6 +274,8 @@ def delete_backend_pool(ctx, **_): @operation(resumable=True) +@decorators.with_generate_name(LoadBalancerProbe) +@decorators.with_azure_resource(LoadBalancerProbe) def create_probe(ctx, **_): """Uses an existing, or creates a new, Load Balancer Probe""" # Check if invalid external resource @@ -304,22 +284,10 @@ def create_probe(ctx, **_): raise cfy_exc.NonRecoverableError( '"use_external_resource" specified without a resource "name"') # Generate a name if it doesn't exist - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) resource_group_name = utils.get_resource_group(ctx) - load_balancer_name = ctx.node.properties.get('load_balancer_name') or \ - utils.get_resource_name_ref(constants.REL_CONTAINED_IN_LB, - 'load_balancer_name', - _ctx=ctx) load_balancer = LoadBalancer(azure_config, ctx.logger) - probe_name = ctx.node.properties.get('name') - probe_name = \ - get_unique_lb_prop_name(load_balancer, resource_group_name, - load_balancer_name, "probes", probe_name) + probe_name = utils.get_resource_name(ctx) ctx.instance.runtime_properties['name'] = probe_name # Get an interface to the Load Balancer lb_rel = utils.get_relationship_by_type( @@ -329,16 +297,17 @@ def create_probe(ctx, **_): # Get the existing probes lb_data = load_balancer.get(resource_group_name, lb_name) lb_probes = lb_data.get('probes', list()) - lb_probes.append({ - 'name': probe_name, - }) - lb_probes = \ - utils.handle_resource_config_params(lb_probes, - ctx.node.properties.get( - 'resource_config', {})) + lb_probe = \ + utils.handle_resource_config_params({ + 'name': probe_name, + }, + ctx.node.properties.get( + 'resource_config', {})) + lb_probes.append(lb_probe) # Update the Load Balancer with the new probe lb_params = { - 'probes': lb_probes + 'probes': lb_probes, + 'location': lb_rel.target.node.properties.get('location') } # clean empty values from params lb_params = \ @@ -346,16 +315,17 @@ def create_probe(ctx, **_): try: result = load_balancer.create_or_update(resource_group_name, lb_name, lb_params) - for item in result.get("probes"): - if item.get("name") == probe_name: - ctx.instance.runtime_properties['resource_id'] = item.get("id") - ctx.instance.runtime_properties['resource'] = item except CloudError as cr: raise cfy_exc.NonRecoverableError( "create probe '{0}' " "failed with this error : {1}".format(probe_name, cr.message) ) + for item in result.get("probes"): + if item.get("name") == probe_name: + ctx.instance.runtime_properties['resource_id'] = item.get("id") + ctx.instance.runtime_properties['resource'] = item + ctx.instance.runtime_properties["resource_group"] = resource_group_name @operation(resumable=True) @@ -364,12 +334,7 @@ def delete_probe(ctx, **_): if ctx.node.properties.get('use_external_resource', False): return # Get an interface to the Load Balancer - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) resource_group_name = utils.get_resource_group(ctx) lb_rel = utils.get_relationship_by_type( ctx.instance.relationships, @@ -385,7 +350,8 @@ def delete_probe(ctx, **_): del lb_probes[idx] # Update the Load Balancer with the new probes list lb_params = { - 'probes': lb_probes + 'probes': lb_probes, + 'location': lb_rel.target.node.properties.get('location') } try: load_balancer.create_or_update(resource_group_name, lb_name, lb_params) @@ -398,6 +364,8 @@ def delete_probe(ctx, **_): @operation(resumable=True) +@decorators.with_generate_name(LoadBalancerInboundNatRule) +@decorators.with_azure_resource(LoadBalancerInboundNatRule) def create_incoming_nat_rule(ctx, **_): """Uses an existing, or creates a new, Load Balancer Incoming NAT Rule""" # Check if invalid external resource @@ -406,23 +374,10 @@ def create_incoming_nat_rule(ctx, **_): raise cfy_exc.NonRecoverableError( '"use_external_resource" specified without a resource "name"') # Generate a name if it doesn't exist - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) resource_group_name = utils.get_resource_group(ctx) - load_balancer_name = ctx.node.properties.get('load_balancer_name') or \ - utils.get_resource_name_ref(constants.REL_CONTAINED_IN_LB, - 'load_balancer_name', - _ctx=ctx) load_balancer = LoadBalancer(azure_config, ctx.logger) - incoming_nat_rule_name = ctx.node.properties.get('name') - incoming_nat_rule_name = \ - get_unique_lb_prop_name(load_balancer, resource_group_name, - load_balancer_name, "inbound_nat_rules", - incoming_nat_rule_name) + incoming_nat_rule_name = utils.get_resource_name(ctx) ctx.instance.runtime_properties['name'] = incoming_nat_rule_name # Get an interface to the Load Balancer lb_rel = utils.get_relationship_by_type( @@ -444,19 +399,19 @@ def create_incoming_nat_rule(ctx, **_): if constants.REL_CONNECTED_TO_IPC in rel.type_hierarchy: lb_fe_ipc_id = \ rel.target.instance.runtime_properties.get('resource_id') - lb_rules.append({ + lb_rule = utils.handle_resource_config_params({ 'name': incoming_nat_rule_name, 'frontend_ip_configuration': { 'id': lb_fe_ipc_id } - }) - lb_rules = \ - utils.handle_resource_config_params(lb_rules, - ctx.node.properties.get( - 'resource_config', {})) + }, + ctx.node.properties.get( + 'resource_config', {})) + lb_rules.append(lb_rule) # Update the Load Balancer with the new NAT rule lb_params = { - 'inbound_nat_rules': lb_rules + 'inbound_nat_rules': lb_rules, + 'location': lb_rel.target.node.properties.get('location') } # clean empty values from params lb_params = \ @@ -482,12 +437,7 @@ def delete_incoming_nat_rule(ctx, **_): if ctx.node.properties.get('use_external_resource', False): return # Get an interface to the Load Balancer - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) resource_group_name = utils.get_resource_group(ctx) lb_rel = utils.get_relationship_by_type( ctx.instance.relationships, @@ -516,6 +466,8 @@ def delete_incoming_nat_rule(ctx, **_): @operation(resumable=True) +@decorators.with_generate_name(LoadBalancerLoadBalancingRule) +@decorators.with_azure_resource(LoadBalancerLoadBalancingRule) def create_rule(ctx, **_): """Uses an existing, or creates a new, Load Balancer Rule""" # Check if invalid external resource @@ -524,23 +476,9 @@ def create_rule(ctx, **_): raise cfy_exc.NonRecoverableError( '"use_external_resource" specified without a resource "name"') # Generate a name if it doesn't exist - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) resource_group_name = utils.get_resource_group(ctx) - load_balancer_name = ctx.node.properties.get('load_balancer_name') or \ - utils.get_resource_name_ref(constants.REL_CONTAINED_IN_LB, - 'load_balancer_name', - _ctx=ctx) - load_balancer = LoadBalancer(azure_config, ctx.logger) - lb_rule_name = ctx.node.properties.get('name') - lb_rule_name = \ - get_unique_lb_prop_name(load_balancer, resource_group_name, - load_balancer_name, "load_balancing_rules", - lb_rule_name) + lb_rule_name = utils.get_resource_name(ctx) ctx.instance.runtime_properties['name'] = lb_rule_name # Get an interface to the Load Balancer lb_rel = utils.get_relationship_by_type( @@ -583,15 +521,20 @@ def create_rule(ctx, **_): rel.target.instance.runtime_properties.get('resource_id') # Get the existing Load Balancer Rules lb_rules = lb_data.get('load_balancing_rules', list()) - lb_rules.append({ - 'name': lb_rule_name, - 'frontend_ip_configuration': {'id': lb_fe_ipc_id}, - 'backend_address_pool': {'id': lb_be_pool_id}, - 'probe': {'id': lb_probe_id} - }) + lb_rule = \ + utils.handle_resource_config_params({ + 'name': lb_rule_name, + 'frontend_ip_configuration': {'id': lb_fe_ipc_id}, + 'backend_address_pool': {'id': lb_be_pool_id}, + 'probe': {'id': lb_probe_id} + }, + ctx.node.properties.get( + 'resource_config', {})) + lb_rules.append(lb_rule) # Update the Load Balancer with the new rule lb_params = { - 'load_balancing_rules': lb_rules + 'load_balancing_rules': lb_rules, + 'location': lb_rel.target.node.properties.get('location') } # clean empty values from params lb_params = \ @@ -599,16 +542,18 @@ def create_rule(ctx, **_): try: result = load_balancer.create_or_update(resource_group_name, lb_name, lb_params) - for item in result.get("load_balancing_rules"): - if item.get("name") == lb_rule_name: - ctx.instance.runtime_properties['resource_id'] = item.get("id") - ctx.instance.runtime_properties['resource'] = item + except CloudError as cr: raise cfy_exc.NonRecoverableError( "create load_balancing_rules '{0}' " "failed with this error : {1}".format(lb_rule_name, cr.message) ) + for item in result.get("load_balancing_rules"): + if item.get("name") == lb_rule_name: + ctx.instance.runtime_properties['resource_id'] = item.get("id") + ctx.instance.runtime_properties['resource'] = item + ctx.instance.runtime_properties["resource_group"] = resource_group_name @operation(resumable=True) @@ -620,12 +565,7 @@ def delete_rule(ctx, **_): if ctx.node.properties.get('use_external_resource', False): return # Get an interface to the Load Balancer - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) resource_group_name = utils.get_resource_group(ctx) lb_rel = utils.get_relationship_by_type( ctx.instance.relationships, @@ -662,12 +602,7 @@ def attach_nic_to_backend_pool(ctx, **_): # Get the ID of the Backend Pool be_pool_id = {'id': ctx.target.instance.runtime_properties['resource_id']} # Get an interface to the Network Interface Card - azure_config = ctx.source.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.source.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.source.node.properties) resource_group_name = utils.get_resource_group(ctx.source) name = ctx.source.instance.runtime_properties['name'] network_interface_card = NetworkInterfaceCard(azure_config, ctx.logger) @@ -681,7 +616,8 @@ def attach_nic_to_backend_pool(ctx, **_): nic_ip_cfgs[ip_idx][LB_ADDRPOOLS_KEY] = nic_pools # Update the NIC IPConfigurations nic_params = { - 'ip_configurations': nic_ip_cfgs + 'ip_configurations': nic_ip_cfgs, + 'location': ctx.source.node.properties.get('location') } try: network_interface_card.create_or_update(resource_group_name, name, @@ -703,13 +639,8 @@ def detach_nic_from_backend_pool(ctx, **_): # Get the ID of the Backend Pool be_pool_id = {'id': ctx.target.instance.runtime_properties['resource_id']} # Get an interface to the Network Interface Card - azure_config = ctx.source.node.properties['azure_config'] - if not azure_config.get("subscription_id"): - azure_config = ctx.source.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") - resource_group_name = ctx.source.node.properties['resource_group_name'] + azure_config = utils.get_client_config(ctx.source.node.properties) + resource_group_name = utils.get_resource_group(ctx.source) name = ctx.source.instance.runtime_properties['name'] network_interface_card = NetworkInterfaceCard(azure_config, ctx.logger) # Get the existing NIC IPConfigurations @@ -725,7 +656,8 @@ def detach_nic_from_backend_pool(ctx, **_): nic_ip_cfgs[ip_idx][LB_ADDRPOOLS_KEY] = nic_pools # Update the NIC IPConfigurations nic_params = { - 'ip_configurations': nic_ip_cfgs + 'ip_configurations': nic_ip_cfgs, + 'location': ctx.source.node.properties.get('location') } try: network_interface_card.create_or_update(resource_group_name, name, diff --git a/cloudify_azure/resources/network/networkinterfacecard.py b/cloudify_azure/resources/network/networkinterfacecard.py index 335a481f..36efdfb9 100644 --- a/cloudify_azure/resources/network/networkinterfacecard.py +++ b/cloudify_azure/resources/network/networkinterfacecard.py @@ -97,12 +97,7 @@ def configure(ctx, **_): operation creates the object """ # Create a resource (if necessary) - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) name = ctx.instance.runtime_properties.get('name') resource_group_name = utils.get_resource_group(ctx) api_version = \ @@ -147,10 +142,10 @@ def configure(ctx, **_): cr.message) ) - ctx.instance.runtime_properties['resource_group'] = resource_group_name - ctx.instance.runtime_properties['resource'] = result - ctx.instance.runtime_properties['resource_id'] = result.get("id", "") - ctx.instance.runtime_properties['name'] = name + utils.save_common_info_in_runtime_properties( + resource_group_name=resource_group_name, + resource_name=name, + resource_get_create_result=result) @operation(resumable=True) @@ -159,12 +154,7 @@ def start(ctx, **_): Stores NIC IPs in runtime properties. """ - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) name = ctx.instance.runtime_properties.get('name') resource_group_name = utils.get_resource_group(ctx) api_version = \ @@ -212,12 +202,7 @@ def start(ctx, **_): def delete(ctx, **_): """Deletes a Network Interface Card""" # Delete the resource - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) resource_group_name = utils.get_resource_group(ctx) name = ctx.instance.runtime_properties.get('name') api_version = \ @@ -244,12 +229,7 @@ def delete(ctx, **_): def attach_ip_configuration(ctx, **_): """Generates a usable UUID for the NIC's IP Configuration""" # Generate the IPConfiguration's name - azure_config = ctx.source.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.source.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.source.node.properties) resource_group_name = utils.get_resource_group(ctx.source) nic_name = ctx.source.instance.runtime_properties.get('name') network_interface_card = NetworkInterfaceCard(azure_config, ctx.logger) diff --git a/cloudify_azure/resources/network/networksecuritygroup.py b/cloudify_azure/resources/network/networksecuritygroup.py index 25125b2c..d4f65e59 100644 --- a/cloudify_azure/resources/network/networksecuritygroup.py +++ b/cloudify_azure/resources/network/networksecuritygroup.py @@ -33,12 +33,7 @@ def create(ctx, **_): """Uses an existing, or creates a new, Network Security Group""" # Create a resource (if necessary) - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) name = utils.get_resource_name(ctx) resource_group_name = utils.get_resource_group(ctx) nsg_params = { @@ -69,10 +64,10 @@ def create(ctx, **_): "failed with this error : {1}".format(name, cr.message) ) - - ctx.instance.runtime_properties['resource_group'] = resource_group_name - ctx.instance.runtime_properties['resource'] = result - ctx.instance.runtime_properties['resource_id'] = result.get("id", "") + utils.save_common_info_in_runtime_properties( + resource_group_name=resource_group_name, + resource_name=name, + resource_get_create_result=result) @operation(resumable=True) @@ -81,12 +76,7 @@ def delete(ctx, **_): # Delete the resource if ctx.node.properties.get('use_external_resource', False): return - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) resource_group_name = utils.get_resource_group(ctx) name = ctx.instance.runtime_properties.get('name') api_version = \ diff --git a/cloudify_azure/resources/network/networksecurityrule.py b/cloudify_azure/resources/network/networksecurityrule.py index 488551f4..944bb182 100644 --- a/cloudify_azure/resources/network/networksecurityrule.py +++ b/cloudify_azure/resources/network/networksecurityrule.py @@ -34,12 +34,7 @@ def create(ctx, **_): """Uses an existing, or creates a new, Network Security Rule""" # Create a resource (if necessary) - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) name = utils.get_resource_name(ctx) resource_group_name = utils.get_resource_group(ctx) nsg_name = utils.get_network_security_group(ctx) @@ -68,10 +63,11 @@ def create(ctx, **_): cr.message) ) - ctx.instance.runtime_properties['resource_group'] = resource_group_name ctx.instance.runtime_properties['network_security_group'] = nsg_name - ctx.instance.runtime_properties['resource'] = result - ctx.instance.runtime_properties['resource_id'] = result.get("id", "") + utils.save_common_info_in_runtime_properties( + resource_group_name=resource_group_name, + resource_name=name, + resource_get_create_result=result) @operation(resumable=True) @@ -80,12 +76,7 @@ def delete(ctx, **_): # Delete the resource if ctx.node.properties.get('use_external_resource', False): return - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) resource_group_name = utils.get_resource_group(ctx) nsg_name = ctx.instance.runtime_properties.get('network_security_group') name = ctx.instance.runtime_properties.get('name') diff --git a/cloudify_azure/resources/network/publicipaddress.py b/cloudify_azure/resources/network/publicipaddress.py index 90bbb8ac..e28d8caa 100644 --- a/cloudify_azure/resources/network/publicipaddress.py +++ b/cloudify_azure/resources/network/publicipaddress.py @@ -34,12 +34,7 @@ def create(ctx, **_): """Uses an existing, or creates a new, Public IP Address""" # Create a resource (if necessary) - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) name = utils.get_resource_name(ctx) resource_group_name = utils.get_resource_group(ctx) public_ip_address_params = { @@ -77,20 +72,16 @@ def create(ctx, **_): cr.message) ) - ctx.instance.runtime_properties['resource_group'] = resource_group_name - ctx.instance.runtime_properties['resource'] = result - ctx.instance.runtime_properties['resource_id'] = result.get("id", "") + utils.save_common_info_in_runtime_properties( + resource_group_name=resource_group_name, + resource_name=name, + resource_get_create_result=result) @operation(resumable=True) def start(ctx, **_): """Update IP runtime property""" - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) name = ctx.instance.runtime_properties.get('name') resource_group_name = utils.get_resource_group(ctx) api_version = \ @@ -111,12 +102,7 @@ def delete(ctx, **_): # Delete the resource if ctx.node.properties.get('use_external_resource', False): return - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) resource_group_name = utils.get_resource_group(ctx) name = ctx.instance.runtime_properties.get('name') api_version = \ diff --git a/cloudify_azure/resources/network/route.py b/cloudify_azure/resources/network/route.py index a06cff85..b7d6a3e3 100644 --- a/cloudify_azure/resources/network/route.py +++ b/cloudify_azure/resources/network/route.py @@ -32,12 +32,7 @@ def create(ctx, **_): """Uses an existing, or creates a new, Route""" # Create a resource (if necessary) - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) name = utils.get_resource_name(ctx) resource_group_name = utils.get_resource_group(ctx) route_table_name = utils.get_route_table(ctx) @@ -66,10 +61,11 @@ def create(ctx, **_): cr.message) ) - ctx.instance.runtime_properties['resource_group'] = resource_group_name ctx.instance.runtime_properties['route_table'] = route_table_name - ctx.instance.runtime_properties['resource'] = result - ctx.instance.runtime_properties['resource_id'] = result.get("id", "") + utils.save_common_info_in_runtime_properties( + resource_group_name=resource_group_name, + resource_name=name, + resource_get_create_result=result) @operation(resumable=True) @@ -78,12 +74,7 @@ def delete(ctx, **_): # Delete the resource if ctx.node.properties.get('use_external_resource', False): return - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) resource_group_name = utils.get_resource_group(ctx) route_table_name = ctx.instance.runtime_properties.get('route_table') name = ctx.instance.runtime_properties.get('name') diff --git a/cloudify_azure/resources/network/routetable.py b/cloudify_azure/resources/network/routetable.py index 5516a7a7..98ace32e 100644 --- a/cloudify_azure/resources/network/routetable.py +++ b/cloudify_azure/resources/network/routetable.py @@ -32,12 +32,7 @@ def create(ctx, **_): """Uses an existing, or creates a new, Route Table""" # Create a resource (if necessary) - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) name = utils.get_resource_name(ctx) resource_group_name = utils.get_resource_group(ctx) rtbl_params = { @@ -65,9 +60,10 @@ def create(ctx, **_): cr.message) ) - ctx.instance.runtime_properties['resource_group'] = resource_group_name - ctx.instance.runtime_properties['resource'] = result - ctx.instance.runtime_properties['resource_id'] = result.get("id", "") + utils.save_common_info_in_runtime_properties( + resource_group_name=resource_group_name, + resource_name=name, + resource_get_create_result=result) @operation(resumable=True) @@ -76,12 +72,7 @@ def delete(ctx, **_): # Delete the resource if ctx.node.properties.get('use_external_resource', False): return - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) resource_group_name = utils.get_resource_group(ctx) name = ctx.instance.runtime_properties.get('name') api_version = \ diff --git a/cloudify_azure/resources/network/subnet.py b/cloudify_azure/resources/network/subnet.py index 26b9f953..6fdef13f 100644 --- a/cloudify_azure/resources/network/subnet.py +++ b/cloudify_azure/resources/network/subnet.py @@ -32,12 +32,7 @@ def create(ctx, **_): """Uses an existing, or creates a new, Subnet""" # Create a resource (if necessary) - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) name = utils.get_resource_name(ctx) resource_group_name = utils.get_resource_group(ctx) vnet_name = utils.get_virtual_network(ctx) @@ -65,10 +60,11 @@ def create(ctx, **_): cr.message) ) - ctx.instance.runtime_properties['resource_group'] = resource_group_name ctx.instance.runtime_properties['virtual_network'] = vnet_name - ctx.instance.runtime_properties['resource'] = result - ctx.instance.runtime_properties['resource_id'] = result.get("id", "") + utils.save_common_info_in_runtime_properties( + resource_group_name=resource_group_name, + resource_name=name, + resource_get_create_result=result) @operation(resumable=True) @@ -77,12 +73,7 @@ def delete(ctx, **_): # Delete the resource if ctx.node.properties.get('use_external_resource', False): return - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) resource_group_name = utils.get_resource_group(ctx) vnet_name = ctx.instance.runtime_properties.get('virtual_network') name = ctx.instance.runtime_properties.get('name') @@ -110,18 +101,17 @@ def attach_network_security_group(ctx, **_): """Attaches a Network Security Group (source) to the Subnet (target)""" nsg_id = ctx.source.instance.runtime_properties.get("resource_id", "") # Attach - azure_config = ctx.target.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.target.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.target.node.properties) resource_group_name = utils.get_resource_group(ctx.target) vnet_name = ctx.target.instance.runtime_properties.get('virtual_network') name = ctx.target.instance.runtime_properties.get('name') - subnet_params = { + subnet_params = \ + utils.handle_resource_config_params({}, + ctx.target.node.properties.get( + 'resource_config', {})) + subnet_params.update({ 'network_security_group': {'id': nsg_id} - } + }) subnet = Subnet(azure_config, ctx.logger) try: subnet.create_or_update(resource_group_name, @@ -140,18 +130,17 @@ def attach_network_security_group(ctx, **_): def detach_network_security_group(ctx, **_): """Detaches a Network Security Group to the Subnet""" # Detach - azure_config = ctx.target.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.target.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.target.node.properties) resource_group_name = utils.get_resource_group(ctx.target) vnet_name = ctx.target.instance.runtime_properties.get('virtual_network') name = ctx.target.instance.runtime_properties.get('name') - subnet_params = { + subnet_params = \ + utils.handle_resource_config_params({}, + ctx.target.node.properties.get( + 'resource_config', {})) + subnet_params.update({ 'network_security_group': None - } + }) subnet = Subnet(azure_config, ctx.logger) try: subnet.create_or_update(resource_group_name, @@ -171,20 +160,19 @@ def attach_route_table(ctx, **_): """Attaches a Route Table (source) to the Subnet (target)""" rtbl_id = ctx.source.instance.runtime_properties.get("resource_id", "") # Attach - azure_config = ctx.target.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.target.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.target.node.properties) resource_group_name = utils.get_resource_group(ctx.target) vnet_name = ctx.target.instance.runtime_properties.get('virtual_network') name = ctx.target.instance.runtime_properties.get('name') - subnet_params = { + subnet_params = \ + utils.handle_resource_config_params({}, + ctx.target.node.properties.get( + 'resource_config', {})) + subnet_params.update({ 'route_table': { 'id': rtbl_id } - } + }) subnet = Subnet(azure_config, ctx.logger) try: subnet.create_or_update(resource_group_name, @@ -203,18 +191,17 @@ def attach_route_table(ctx, **_): def detach_route_table(ctx, **_): """Detaches a Route Table to the Subnet""" # Detach - azure_config = ctx.target.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.target.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.target.node.properties) resource_group_name = utils.get_resource_group(ctx.target) vnet_name = ctx.target.instance.runtime_properties.get('virtual_network') name = ctx.target.instance.runtime_properties.get('name') - subnet_params = { + subnet_params = \ + utils.handle_resource_config_params({}, + ctx.target.node.properties.get( + 'resource_config', {})) + subnet_params.update({ 'route_table': None - } + }) subnet = Subnet(azure_config, ctx.logger) try: subnet.create_or_update(resource_group_name, diff --git a/cloudify_azure/resources/network/virtualnetwork.py b/cloudify_azure/resources/network/virtualnetwork.py index 9a27f37d..cc05adc9 100644 --- a/cloudify_azure/resources/network/virtualnetwork.py +++ b/cloudify_azure/resources/network/virtualnetwork.py @@ -32,12 +32,7 @@ def create(ctx, **_): """Uses an existing, or creates a new, Virtual Network""" # Create a resource (if necessary) - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) name = utils.get_resource_name(ctx) resource_group_name = utils.get_resource_group(ctx) vnet_params = { @@ -65,9 +60,10 @@ def create(ctx, **_): cr.message) ) - ctx.instance.runtime_properties['resource_group'] = resource_group_name - ctx.instance.runtime_properties['resource'] = result - ctx.instance.runtime_properties['resource_id'] = result.get("id", "") + utils.save_common_info_in_runtime_properties( + resource_group_name=resource_group_name, + resource_name=name, + resource_get_create_result=result) @operation(resumable=True) @@ -75,12 +71,7 @@ def delete(ctx, **_): """Deletes a Virtual Network""" if ctx.node.properties.get('use_external_resource', False): return - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) resource_group_name = utils.get_resource_group(ctx) name = ctx.instance.runtime_properties.get('name') api_version = \ diff --git a/cloudify_azure/resources/storage/file.py b/cloudify_azure/resources/storage/file.py index 4cc92f79..f69409cd 100644 --- a/cloudify_azure/resources/storage/file.py +++ b/cloudify_azure/resources/storage/file.py @@ -17,134 +17,99 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Microsoft Azure Storage File Share interface """ -import random -import string -from azure.storage.common.cloudstorageaccount import CloudStorageAccount +from msrestazure.azure_exceptions import CloudError from cloudify.decorators import operation from cloudify.exceptions import (RecoverableError, NonRecoverableError) -from cloudify_azure import (constants, utils) +from azure_sdk.resources.storage.file_share import FileShare from azure_sdk.resources.storage.storage_account import StorageAccount - - -def file_share_name_generator(): - """Generates a unique File Share resource name""" - return ''.join(random.choice(string.lowercase + string.digits) - for i in range(random.randint(24, 63))) - - -def file_share_exists(ctx, filesvc, share_name): - """ - Checks if a File Share already exists - - :rtype: `azure.storage.file.models.Share` or `None` - :returns: Azure File Share object if the File Share - exists or None if it does not - """ - ctx.logger.debug('Checking if File Share "{0}" exists' - .format(share_name)) - try: - props = filesvc.get_share_properties(share_name) - ctx.logger.debug('File Share "{0}" exists' - .format(share_name)) - return props - except Exception: - ctx.logger.debug('File Share "{0}" does not exist' - .format(share_name)) - return None +from cloudify_azure import (constants, utils, decorators) @operation(resumable=True) -def create_file_share(ctx, **_): +@decorators.with_generate_name(FileShare) +@decorators.with_azure_resource(FileShare) +def create(ctx, **_): """Creates an Azure File Share""" - # Get resource config values - res_cfg = ctx.node.properties.get("resource_config", {}) share_name = utils.get_resource_name(ctx) + res_cfg = ctx.node.properties.get("resource_config", {}) metadata = res_cfg.get('metadata') - quota = res_cfg.get('quota') - fail_on_exist = res_cfg.get('fail_on_exist', False) - # Check if invalid external resource + share_quota = res_cfg.get('quota') if ctx.node.properties.get('use_external_resource', False) and \ - not share_name: + not share_name: raise NonRecoverableError( '"use_external_resource" specified without a resource "name"') - # Get the storage account - storage_account = utils.get_parent( - ctx.instance, - rel_type=constants.REL_CONTAINED_IN_SA - ) + storage_account = utils.get_storage_account(ctx) resource_group_name = utils.get_resource_group(ctx) - storage_account_name = utils.get_resource_name(_ctx=storage_account) - azure_config = ctx.node.properties.get("azure_config") - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") - # Get the storage account keys + azure_config = utils.get_client_config(ctx.node.properties) + keys = StorageAccount(azure_config, ctx.logger).list_keys( - resource_group_name, storage_account_name) + resource_group_name, storage_account) if not keys or not keys.get("key1"): raise RecoverableError( 'StorageAccount reported no usable authentication keys') # Get an interface to the Storage Account storage_account_key = keys.get("key1") - storageacct = CloudStorageAccount( - account_name=storage_account_name, - account_key=storage_account_key) - # Get an interface to the File Service - filesvc = storageacct.create_file_service() - if ctx.node.properties.get('use_external_resource', False): - # Attempt to use an existing File Share (if specified) - ctx.logger.debug('Checking for existing File Share "{0}"' - .format(share_name)) - try: - share = filesvc.get_share_properties(share_name) - metadata = share.get('metadata', dict()) - quota = share.get('properties', dict()).get('quota') - created = False - except Exception as ex: - ctx.logger.error('File Share "{0}" does not exist and ' - '"use_external_resource" is set to true' - .format(share_name)) - raise NonRecoverableError(ex) - else: - # Generate a new File Share name if needed - if not share_name: - ctx.logger.info('Generating a new File Share name') - for _ in range(0, 10): - tmpname = file_share_name_generator() - if not file_share_exists(ctx, filesvc, tmpname): - share_name = tmpname - break - # Handle name error - if not share_name: - raise NonRecoverableError( - 'Error generating a new File Share name. Failed ' - 'after 10 tries.') - # Attempt to create the File Share - ctx.logger.debug('Creating File Share "{0}"'.format(share_name)) - created = filesvc.create_share( - share_name=share_name, - metadata=metadata, - quota=quota, - fail_on_exist=False) - if not created: - ctx.logger.warn('File Share already exists') - if fail_on_exist: - raise NonRecoverableError( - 'File Share already exists in the storage account and ' - '"fail_on_exist" set to True') - # Set run-time properties - ctx.instance.runtime_properties['name'] = share_name - ctx.instance.runtime_properties['quota'] = quota + # Get an interface to the File Share + + api_version = \ + ctx.node.properties.get('api_version', + constants.API_VER_STORAGE_FILE_SHARE) + + file_share = FileShare(azure_config, ctx.logger, api_version) + try: + result = \ + file_share.create(resource_group_name, + storage_account, + share_name, + metadata, + share_quota) + except CloudError as cr: + raise NonRecoverableError( + "create file share '{0}' " + "failed with this error : {1}".format(share_name, + cr.message) + ) + ctx.instance.runtime_properties['quota'] = share_quota ctx.instance.runtime_properties['metadata'] = metadata - ctx.instance.runtime_properties['created'] = created - ctx.instance.runtime_properties['storage_account'] = storage_account_name - ctx.instance.runtime_properties['username'] = storage_account_name + ctx.instance.runtime_properties['storage_account'] = storage_account + ctx.instance.runtime_properties['username'] = storage_account ctx.instance.runtime_properties['password'] = storage_account_key ctx.instance.runtime_properties['uri'] = '{0}.file.{1}/{2}'.format( - storage_account_name, constants.CONN_STORAGE_FILE_ENDPOINT, share_name + storage_account, constants.CONN_STORAGE_ENDPOINT, share_name ) + utils.save_common_info_in_runtime_properties( + resource_group_name=resource_group_name, + resource_name=share_name, + resource_get_create_result=result) + + +@operation(resumable=True) +def delete(ctx, **_): + """Deletes a File Share""" + # Delete the resource + if ctx.node.properties.get('use_external_resource', False): + return + azure_config = utils.get_client_config(ctx.node.properties) + resource_group_name = utils.get_resource_group(ctx) + share_name = utils.get_resource_name(ctx) + storage_account = ctx.instance.runtime_properties.get( + 'storage_account') or utils.get_storage_account(ctx) + api_version = \ + ctx.node.properties.get('api_version', + constants.API_VER_STORAGE_FILE_SHARE) + + file_share = FileShare(azure_config, ctx.logger, api_version) + try: + + file_share.delete(resource_group_name, + storage_account, + share_name) + utils.runtime_properties_cleanup(ctx) + except CloudError as cr: + raise NonRecoverableError( + "delete file share '{0}' " + "failed with this error : {1}".format(share_name, + cr.message)) diff --git a/cloudify_azure/resources/storage/storageaccount.py b/cloudify_azure/resources/storage/storageaccount.py index 91aea912..8eb6a480 100644 --- a/cloudify_azure/resources/storage/storageaccount.py +++ b/cloudify_azure/resources/storage/storageaccount.py @@ -32,11 +32,7 @@ def create(ctx, **_): """Uses an existing, or creates a new, Storage Account""" # Generate a resource name (if needed) - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.info("azure_config id deprecated please use client_config") + azure_config = utils.get_client_config(ctx.node.properties) name = utils.get_resource_name(ctx) resource_group_name = utils.get_resource_group(ctx) api_version = \ @@ -78,9 +74,10 @@ def create(ctx, **_): cr.message) ) - ctx.instance.runtime_properties['resource_group'] = resource_group_name - ctx.instance.runtime_properties['resource'] = result - ctx.instance.runtime_properties['resource_id'] = result.get("id", "") + utils.save_common_info_in_runtime_properties( + resource_group_name=resource_group_name, + resource_name=name, + resource_get_create_result=result) @operation(resumable=True) @@ -89,12 +86,7 @@ def delete(ctx, **_): # Delete the resource if ctx.node.properties.get('use_external_resource', False): return - azure_config = ctx.node.properties.get('azure_config') - if not azure_config.get("subscription_id"): - azure_config = ctx.node.properties.get('client_config') - else: - ctx.logger.warn("azure_config is deprecated please use client_config, " - "in later version it will be removed") + azure_config = utils.get_client_config(ctx.node.properties) resource_group_name = utils.get_resource_group(ctx) name = ctx.instance.runtime_properties.get('name') api_version = \ diff --git a/cloudify_azure/tests/resources/__init__.py b/cloudify_azure/tests/resources/__init__.py index afb94fad..e4c26aee 100644 --- a/cloudify_azure/tests/resources/__init__.py +++ b/cloudify_azure/tests/resources/__init__.py @@ -12,3 +12,12 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +import requests +from msrestazure.azure_exceptions import CloudError + + +def compose_not_found_cloud_error(): + response = requests.Response() + response.status_code = 404 + message = 'resource not found' + return CloudError(response, message) diff --git a/cloudify_azure/tests/resources/test_app_service.py b/cloudify_azure/tests/resources/test_app_service.py index 244df6be..2259e416 100644 --- a/cloudify_azure/tests/resources/test_app_service.py +++ b/cloudify_azure/tests/resources/test_app_service.py @@ -17,17 +17,16 @@ from msrestazure.azure_exceptions import CloudError +from cloudify.state import current_ctx from cloudify import mocks as cfy_mocks from cloudify import exceptions as cfy_exc from cloudify_azure.resources.app_service import plan, webapp, publishing_user +from . import compose_not_found_cloud_error -@mock.patch('azure_sdk.common.ServicePrincipalCredentials') -@mock.patch('azure_sdk.resources.app_service.' - 'plan.WebSiteManagementClient') -class PlanTest(unittest.TestCase): +class AppServiceTestBase(unittest.TestCase): def _get_mock_context_for_run(self): operation = {'name': 'cloudify.interfaces.lifecycle.mock'} fake_ctx = cfy_mocks.MockCloudifyContext(operation=operation) @@ -47,6 +46,7 @@ def _get_mock_context_for_run(self): def setUp(self): self.fake_ctx, self.node, self.instance = \ self._get_mock_context_for_run() + current_ctx.set(self.fake_ctx) self.dummy_azure_credentials = { 'client_id': 'dummy', 'client_secret': 'dummy', @@ -54,21 +54,33 @@ def setUp(self): 'tenant_id': 'dummy' } - def test_create(self, client, credentials): + def _set_azure_config_rg_and_name(self, resource_group, name): self.node.properties['azure_config'] = self.dummy_azure_credentials + self.node.properties['resource_group'] = resource_group + self.node.properties['name'] = name + + +@mock.patch('azure_sdk.common.ServicePrincipalCredentials') +@mock.patch('azure_sdk.resources.app_service.' + 'plan.WebSiteManagementClient') +class PlanTest(AppServiceTestBase): + + def test_create(self, client, credentials): resource_group = 'sample_resource_group' name = 'plan_name' plan_details = { 'key': 'value' } - response = requests.Response() - response.status_code = 404 - message = 'resource not found' - client().app_service_plans.get.side_effect = \ - CloudError(response, message) + self._set_azure_config_rg_and_name(resource_group=resource_group, + name=name) + err = compose_not_found_cloud_error() + client().app_service_plans.get.side_effect = err with mock.patch('cloudify_azure.utils.secure_logging_content', mock.Mock()): - plan.create(self.fake_ctx, resource_group, name, plan_details) + plan.create(ctx=self.fake_ctx, + resource_group=resource_group, + name=name, + plan_details=plan_details) client().app_service_plans.get.assert_called_with( resource_group_name=resource_group, name=name @@ -79,23 +91,58 @@ def test_create(self, client, credentials): app_service_plan=plan_details ) - def test_create_already_exists(self, client, credentials): - self.node.properties['azure_config'] = self.dummy_azure_credentials + def test_create_use_external_resource(self, client, credentials): resource_group = 'sample_resource_group' name = 'plan_name' plan_details = { 'key': 'value' } + self._set_azure_config_rg_and_name(resource_group=resource_group, + name=name) + self.node.properties['use_external_resource'] = True client().app_service_plans.get.return_value = mock.Mock() with mock.patch('cloudify_azure.utils.secure_logging_content', mock.Mock()): - plan.create(self.fake_ctx, resource_group, name, plan_details) + plan.create(ctx=self.fake_ctx, + resource_group=resource_group, + name=name, + plan_details=plan_details) client().app_service_plans.get.assert_called_with( resource_group_name=resource_group, name=name ) client().app_service_plans.create_or_update.assert_not_called() + def test_create_use_external_resource_create_if_missing(self, + client, + credentials): + resource_group = 'sample_resource_group' + name = 'plan_name' + plan_details = { + 'key': 'value' + } + self._set_azure_config_rg_and_name(resource_group=resource_group, + name=name) + self.node.properties['use_external_resource'] = True + self.node.properties['create_if_missing'] = True + err = compose_not_found_cloud_error() + client().app_service_plans.get.side_effect = err + with mock.patch('cloudify_azure.utils.secure_logging_content', + mock.Mock()): + plan.create(ctx=self.fake_ctx, + resource_group=resource_group, + name=name, + plan_details=plan_details) + client().app_service_plans.get.assert_called_with( + resource_group_name=resource_group, + name=name + ) + client().app_service_plans.create_or_update.assert_called_with( + resource_group_name=resource_group, + name=name, + app_service_plan=plan_details + ) + def test_delete(self, client, credentials): self.node.properties['azure_config'] = self.dummy_azure_credentials resource_group = 'sample_resource_group' @@ -130,31 +177,7 @@ def test_delete_do_not_exist(self, client, credentials): @mock.patch('azure_sdk.common.ServicePrincipalCredentials') @mock.patch('azure_sdk.resources.app_service.' 'web_app.WebSiteManagementClient') -class WebAppTest(unittest.TestCase): - - def _get_mock_context_for_run(self): - fake_ctx = cfy_mocks.MockCloudifyContext() - instance = mock.Mock() - instance.runtime_properties = {} - fake_ctx._instance = instance - node = mock.Mock() - fake_ctx._node = node - node.properties = {} - node.runtime_properties = {} - fake_ctx.get_resource = mock.MagicMock( - return_value="" - ) - return fake_ctx, node, instance - - def setUp(self): - self.fake_ctx, self.node, self.instance = \ - self._get_mock_context_for_run() - self.dummy_azure_credentials = { - 'client_id': 'dummy', - 'client_secret': 'dummy', - 'subscription_id': 'dummy', - 'tenant_id': 'dummy' - } +class WebAppTest(AppServiceTestBase): def test_create(self, client, credentials): self.node.properties['azure_config'] = self.dummy_azure_credentials @@ -163,14 +186,16 @@ def test_create(self, client, credentials): app_details = { 'key': 'value' } - response = requests.Response() - response.status_code = 404 - message = 'resource not found' - client().web_apps.get.side_effect = \ - CloudError(response, message) + self._set_azure_config_rg_and_name(resource_group=resource_group, + name=name) + err = compose_not_found_cloud_error() + client().web_apps.get.side_effect = err with mock.patch('cloudify_azure.utils.secure_logging_content', mock.Mock()): - webapp.create(self.fake_ctx, resource_group, name, app_details) + webapp.create(ctx=self.fake_ctx, + resource_group=resource_group, + name=name, + app_config=app_details) client().web_apps.get.assert_called_with( resource_group_name=resource_group, name=name @@ -190,23 +215,68 @@ def test_create(self, client, credentials): resource_group ) - def test_create_already_exists(self, client, credentials): - self.node.properties['azure_config'] = self.dummy_azure_credentials + def test_create_use_external_resource(self, client, credentials): resource_group = 'sample_resource_group' name = 'app_name' app_details = { 'key': 'value' } + self._set_azure_config_rg_and_name(resource_group=resource_group, + name=name) + self.node.properties['use_external_resource'] = True client().web_apps.get.return_value = mock.Mock() with mock.patch('cloudify_azure.utils.secure_logging_content', mock.Mock()): - webapp.create(self.fake_ctx, resource_group, name, app_details) + webapp.create(ctx=self.fake_ctx, + resource_group=resource_group, + name=name, + app_config=app_details) client().web_apps.get.assert_called_with( resource_group_name=resource_group, name=name ) client().web_apps.create_or_update.assert_not_called() + def test_create_use_external_resource_create_if_missing(self, + client, + credentials): + self.node.properties['azure_config'] = self.dummy_azure_credentials + resource_group = 'sample_resource_group' + name = 'app_name' + app_details = { + 'key': 'value' + } + self._set_azure_config_rg_and_name(resource_group=resource_group, + name=name) + self.node.properties['use_external_resource'] = True + self.node.properties['create_if_missing'] = True + err = compose_not_found_cloud_error() + client().web_apps.get.side_effect = err + with mock.patch('cloudify_azure.utils.secure_logging_content', + mock.Mock()): + webapp.create(ctx=self.fake_ctx, + resource_group=resource_group, + name=name, + app_config=app_details) + client().web_apps.get.assert_called_with( + resource_group_name=resource_group, + name=name + ) + client().web_apps.create_or_update.assert_called_with( + resource_group_name=resource_group, + name=name, + site_envelope=app_details + ) + self.assertEquals( + self.fake_ctx.instance.runtime_properties.get("name"), + name + ) + self.assertEquals( + self.fake_ctx.instance.runtime_properties.get( + "resource_group"), + resource_group + ) + def test_delete(self, client, credentials): self.node.properties['azure_config'] = self.dummy_azure_credentials resource_group = 'sample_resource_group' @@ -241,31 +311,7 @@ def test_delete_do_not_exist(self, client, credentials): @mock.patch('azure_sdk.common.ServicePrincipalCredentials') @mock.patch('azure_sdk.resources.app_service.' 'publishing_user.WebSiteManagementClient') -class PublishingUserTest(unittest.TestCase): - - def _get_mock_context_for_run(self): - fake_ctx = cfy_mocks.MockCloudifyContext() - instance = mock.Mock() - instance.runtime_properties = {} - fake_ctx._instance = instance - node = mock.Mock() - fake_ctx._node = node - node.properties = {} - node.runtime_properties = {} - fake_ctx.get_resource = mock.MagicMock( - return_value="" - ) - return fake_ctx, node, instance - - def setUp(self): - self.fake_ctx, self.node, self.instance = \ - self._get_mock_context_for_run() - self.dummy_azure_credentials = { - 'client_id': 'dummy', - 'client_secret': 'dummy', - 'subscription_id': 'dummy', - 'tenant_id': 'dummy' - } +class PublishingUserTest(AppServiceTestBase): def test_set_user(self, client, credentials): self.node.properties['azure_config'] = self.dummy_azure_credentials diff --git a/cloudify_azure/tests/resources/test_deployment.py b/cloudify_azure/tests/resources/test_deployment.py index ecad5fae..603c3794 100644 --- a/cloudify_azure/tests/resources/test_deployment.py +++ b/cloudify_azure/tests/resources/test_deployment.py @@ -13,11 +13,9 @@ # limitations under the License. import json import mock -import requests import tempfile import unittest -from msrestazure.azure_exceptions import CloudError from azure.mgmt.resource.resources.models import DeploymentMode from azure.mgmt.resource.resources.v2019_10_01.models import \ DeploymentProperties, DeploymentWhatIfProperties @@ -30,6 +28,7 @@ from cloudify.state import current_ctx +from . import compose_not_found_cloud_error from cloudify_azure.resources import deployment from cloudify_azure.resources.deployment import STATE, IS_DRIFTED @@ -100,12 +99,6 @@ def setUp(self): 'tenant_id': 'dummy' } - def _not_found_cloud_error(self): - response = requests.Response() - response.status_code = 404 - message = 'resource not found' - return CloudError(response, message) - def test_create(self, rg_client, deployment_client, credentials): self.node.properties['azure_config'] = self.dummy_azure_credentials resource_group = 'sample_deployment' @@ -129,7 +122,7 @@ def test_create(self, rg_client, deployment_client, credentials): template=deployment_params['template'], parameters=deployment_params['parameters']) - err = self._not_found_cloud_error() + err = compose_not_found_cloud_error() rg_client().resource_groups.get.side_effect = err deployment_client().deployments.get.side_effect = err with mock.patch('cloudify_azure.utils.secure_logging_content', @@ -211,7 +204,7 @@ def test_create_without_template(self, rg_client, deployment_client, resource_group = 'sample_deployment' self.node.properties['name'] = resource_group self.node.properties['resource_group_name'] = resource_group - err = self._not_found_cloud_error() + err = compose_not_found_cloud_error() rg_client().resource_groups.get.side_effect = err deployment_client().deployments.get.side_effect = err with mock.patch('cloudify_azure.utils.secure_logging_content', @@ -228,7 +221,7 @@ def test_create_with_template_string(self, rg_client, deployment_client, resource_group = TEST_RESOURCE_GROUP_NAME self.node.properties['name'] = resource_group self.node.properties['resource_group_name'] = resource_group - err = self._not_found_cloud_error() + err = compose_not_found_cloud_error() rg_client().resource_groups.get.side_effect = err deployment_client().deployments.get.side_effect = err deployment_properties = DeploymentProperties( @@ -272,7 +265,7 @@ def test_create_with_template_file(self, rg_client, deployment_client, self.node.properties['template_file'] = fock.name self.fake_ctx.get_resource.return_value = open(fock.name).read() - err = self._not_found_cloud_error() + err = compose_not_found_cloud_error() rg_client().resource_groups.get.side_effect = err deployment_client().deployments.get.side_effect = err deployment_properties = DeploymentProperties( @@ -318,13 +311,9 @@ def test_delete_do_not_exist(self, rg_client, deployment_client, self.node.properties['azure_config'] = self.dummy_azure_credentials resource_group = TEST_RESOURCE_GROUP_NAME self.instance.runtime_properties['name'] = resource_group - response = requests.Response() - response.status_code = 404 - message = 'resource not found' - rg_client().resource_groups.get.side_effect = \ - CloudError(response, message) - deployment_client().deployments.get.side_effect = \ - CloudError(response, message) + err = compose_not_found_cloud_error() + rg_client().resource_groups.get.side_effect = err + deployment_client().deployments.get.side_effect = err with mock.patch('cloudify_azure.utils.secure_logging_content', mock.Mock()): deployment.delete(ctx=self.fake_ctx) @@ -352,7 +341,7 @@ def test_pull_no_resource_group(self, rg_client, deployment_client, self.node.properties['client_config'] = self.dummy_azure_credentials self.instance.runtime_properties['name'] = TEST_RESOURCE_GROUP_NAME rg_client().resource_groups.get.side_effect = \ - self._not_found_cloud_error() + compose_not_found_cloud_error() deployment.pull(ctx=self.fake_ctx) self.assertEquals(self.fake_ctx.instance.runtime_properties[STATE], []) self.assertEquals( diff --git a/cloudify_azure/tests/resources/test_managed_cluster.py b/cloudify_azure/tests/resources/test_managed_cluster.py index 88bb6fd3..7b5a829b 100644 --- a/cloudify_azure/tests/resources/test_managed_cluster.py +++ b/cloudify_azure/tests/resources/test_managed_cluster.py @@ -13,12 +13,12 @@ # limitations under the License. import mock import unittest -import requests - -from msrestazure.azure_exceptions import CloudError +from cloudify.state import current_ctx from cloudify import mocks as cfy_mocks +from cloudify.exceptions import NonRecoverableError +from . import compose_not_found_cloud_error from cloudify_azure.resources.compute import managed_cluster from cloudify_azure.utils import handle_resource_config_params @@ -53,12 +53,68 @@ def setUp(self): 'subscription_id': 'dummy', 'tenant_id': 'dummy' } + current_ctx.set(self.fake_ctx) def test_create(self, client, credentials): self.node.properties['azure_config'] = self.dummy_azure_credentials resource_group = 'sample_resource_group' cluster_name = 'mc_name' + self.node.properties['name'] = cluster_name + self.node.properties['resource_group'] = resource_group + self.node.properties['store_kube_config_in_runtime'] = False + managed_cluster_config = { + 'network_profile': None, + 'addon_profiles': None, + 'windows_profile': None, + 'dns_prefix': 'dummy-dns', + 'linux_profile': None, + 'agent_pool_profiles': None, + 'service_principal_profile': None, + 'location': 'westus', + 'enable_rbac': True, + 'kubernetes_version': None, + 'tags': None + } + cluster_payload = {} + cluster_payload = \ + handle_resource_config_params(cluster_payload, + managed_cluster_config) + err = compose_not_found_cloud_error() + client().managed_clusters.get.side_effect = err + with mock.patch('cloudify_azure.utils.secure_logging_content', + mock.Mock()): + managed_cluster.create(ctx=self.fake_ctx, + resource_group=resource_group, + cluster_name=cluster_name, + resource_config=managed_cluster_config) + client().managed_clusters.get.assert_called_with( + resource_group_name=resource_group, + resource_name=cluster_name, + ) + client().managed_clusters.create_or_update.assert_called_with( + resource_group_name=resource_group, + resource_name=cluster_name, + parameters=cluster_payload + ) + self.assertEquals( + self.fake_ctx.instance.runtime_properties.get("name"), + cluster_name + ) + self.assertEquals( + self.fake_ctx.instance.runtime_properties.get( + "resource_group"), + resource_group + ) + + def test_create_if_missing(self, client, credentials): + self.node.properties['azure_config'] = self.dummy_azure_credentials + resource_group = 'sample_resource_group' + cluster_name = 'mc_name' + self.node.properties['name'] = cluster_name + self.node.properties['resource_group'] = resource_group self.node.properties['store_kube_config_in_runtime'] = False + self.node.properties['use_external_resource'] = True + self.node.properties['create_if_missing'] = True managed_cluster_config = { 'network_profile': None, 'addon_profiles': None, @@ -76,15 +132,14 @@ def test_create(self, client, credentials): cluster_payload = \ handle_resource_config_params(cluster_payload, managed_cluster_config) - response = requests.Response() - response.status_code = 404 - message = 'resource not found' - client().managed_clusters.get.side_effect = \ - CloudError(response, message) + err = compose_not_found_cloud_error() + client().managed_clusters.get.side_effect = err with mock.patch('cloudify_azure.utils.secure_logging_content', mock.Mock()): - managed_cluster.create(self.fake_ctx, resource_group, - cluster_name, managed_cluster_config) + managed_cluster.create(ctx=self.fake_ctx, + resource_group=resource_group, + cluster_name=cluster_name, + resource_config=managed_cluster_config) client().managed_clusters.get.assert_called_with( resource_group_name=resource_group, resource_name=cluster_name, @@ -104,10 +159,55 @@ def test_create(self, client, credentials): resource_group ) - def test_create_already_exists(self, client, credentials): + def test_create_already_exists_but_not_using_external_resource( + self, + client, + credentials): + self.node.properties['azure_config'] = self.dummy_azure_credentials + resource_group = 'sample_resource_group' + cluster_name = 'mc_name' + self.node.properties['name'] = cluster_name + self.node.properties['resource_group'] = resource_group + self.node.properties['store_kube_config_in_runtime'] = False + managed_cluster_config = { + 'network_profile': None, + 'addon_profiles': None, + 'windows_profile': None, + 'dns_prefix': 'dummy-dns', + 'linux_profile': None, + 'agent_pool_profiles': None, + 'service_principal_profile': None, + 'location': 'westus', + 'enable_rbac': True, + 'kubernetes_version': None, + 'tags': None + } + client().managed_clusters.get.return_value = mock.Mock() + with mock.patch('cloudify_azure.utils.secure_logging_content', + mock.Mock()): + with self.assertRaisesRegexp( + NonRecoverableError, + 'resource already exist but configuration is ' + 'not to use it'): + managed_cluster.create(ctx=self.fake_ctx, + resource_group=resource_group, + cluster_name=cluster_name, + resource_config=managed_cluster_config) + client().managed_clusters.get.assert_called_with( + resource_group_name=resource_group, + resource_name=cluster_name, + ) + client().managed_clusters.create_or_update.assert_not_called() + + def test_create_already_exists_and_use_external_resource(self, + client, + credentials): self.node.properties['azure_config'] = self.dummy_azure_credentials resource_group = 'sample_resource_group' cluster_name = 'mc_name' + self.node.properties['name'] = cluster_name + self.node.properties['resource_group'] = resource_group + self.node.properties['use_external_resource'] = True self.node.properties['store_kube_config_in_runtime'] = False managed_cluster_config = { 'network_profile': None, @@ -125,8 +225,10 @@ def test_create_already_exists(self, client, credentials): client().managed_clusters.get.return_value = mock.Mock() with mock.patch('cloudify_azure.utils.secure_logging_content', mock.Mock()): - managed_cluster.create(self.fake_ctx, resource_group, - cluster_name, managed_cluster_config) + managed_cluster.create(ctx=self.fake_ctx, + resource_group=resource_group, + cluster_name=cluster_name, + resource_config=managed_cluster_config) client().managed_clusters.get.assert_called_with( resource_group_name=resource_group, resource_name=cluster_name, @@ -153,11 +255,8 @@ def test_delete_do_not_exist(self, client, credentials): cluster_name = 'mc_name' self.instance.runtime_properties['resource_group'] = resource_group self.instance.runtime_properties['name'] = cluster_name - response = requests.Response() - response.status_code = 404 - message = 'resource not found' - client().managed_clusters.get.side_effect = \ - CloudError(response, message) + err = compose_not_found_cloud_error() + client().managed_clusters.get.side_effect = err with mock.patch('cloudify_azure.utils.secure_logging_content', mock.Mock()): managed_cluster.delete(self.fake_ctx) diff --git a/cloudify_azure/tests/resources/test_network.py b/cloudify_azure/tests/resources/test_network.py index cdc7152d..051fc652 100644 --- a/cloudify_azure/tests/resources/test_network.py +++ b/cloudify_azure/tests/resources/test_network.py @@ -13,12 +13,11 @@ # limitations under the License. import mock import unittest -import requests - -from msrestazure.azure_exceptions import CloudError +from cloudify.state import current_ctx from cloudify import mocks as cfy_mocks +from . import compose_not_found_cloud_error from cloudify_azure.resources.network import virtualnetwork @@ -52,6 +51,7 @@ def setUp(self): 'subscription_id': 'dummy', 'tenant_id': 'dummy' } + current_ctx.set(self.fake_ctx) def test_create(self, client, credentials): self.node.properties['azure_config'] = self.dummy_azure_credentials @@ -67,11 +67,8 @@ def test_create(self, client, credentials): 'location': self.node.properties.get('location'), 'tags': self.node.properties.get('tags') } - response = requests.Response() - response.status_code = 404 - message = 'resource not found' - client().virtual_networks.get.side_effect = \ - CloudError(response, message) + err = compose_not_found_cloud_error() + client().virtual_networks.get.side_effect = err with mock.patch('cloudify_azure.utils.secure_logging_content', mock.Mock()): virtualnetwork.create(ctx=self.fake_ctx) @@ -135,11 +132,8 @@ def test_delete_do_not_exist(self, client, credentials): vnet_name = 'sample_vnet' self.instance.runtime_properties['resource_group'] = resource_group self.instance.runtime_properties['name'] = vnet_name - response = requests.Response() - response.status_code = 404 - message = 'resource not found' - client().virtual_networks.get.side_effect = \ - CloudError(response, message) + err = compose_not_found_cloud_error() + client().virtual_networks.get.side_effect = err with mock.patch('cloudify_azure.utils.secure_logging_content', mock.Mock()): virtualnetwork.delete(ctx=self.fake_ctx) diff --git a/cloudify_azure/utils.py b/cloudify_azure/utils.py index b9038639..2798d05c 100644 --- a/cloudify_azure/utils.py +++ b/cloudify_azure/utils.py @@ -94,6 +94,7 @@ def get_resource_group(_ctx=ctx): """ return _ctx.node.properties.get('resource_group_name') or \ + _ctx.node.properties.get('resource_group') or \ _ctx.instance.runtime_properties.get('resource_group') or \ get_ancestor_name(_ctx.instance, constants.REL_CONTAINED_IN_RG) @@ -137,8 +138,18 @@ def get_network_security_group(_ctx=ctx, :rtype: string """ return _ctx.node.properties.get('network_security_group_name') or \ - get_ancestor_name( - _ctx.instance, rel_type) + get_ancestor_name(_ctx.instance, rel_type) + + +def get_load_balancer(_ctx=ctx, + rel_type=constants.REL_CONTAINED_IN_LB): + return ctx.node.properties.get('load_balancer_name') or \ + get_ancestor_name(_ctx.instance, rel_type) + + +def get_storage_account(_ctx=ctx, rel_type=constants.REL_CONTAINED_IN_SA): + return ctx.node.properties.get('storage_account_name') or \ + get_ancestor_name(_ctx.instance, rel_type) def get_retry_after(_ctx=ctx): @@ -432,3 +443,13 @@ def check_if_resource_exists(resource, resource_group_name, name=None): return resource.get(resource_group_name) except CloudError: return + + +def save_common_info_in_runtime_properties(resource_group_name, + resource_name, + resource_get_create_result): + ctx.instance.runtime_properties['resource_group'] = resource_group_name + ctx.instance.runtime_properties['resource'] = resource_get_create_result + ctx.instance.runtime_properties['resource_id'] = \ + resource_get_create_result.get("id", "") + ctx.instance.runtime_properties['name'] = resource_name diff --git a/examples/app_service/app-sample-blueprint.yaml b/examples/app_service/app-sample-blueprint.yaml index 675a8b82..4885f378 100644 --- a/examples/app_service/app-sample-blueprint.yaml +++ b/examples/app_service/app-sample-blueprint.yaml @@ -5,8 +5,8 @@ description: > on an existing host. imports: - - http://www.getcloudify.org/spec/cloudify/3.4/types.yaml - - ../../plugin.yaml + - https://cloudify.co/spec/cloudify/5.1.0/types.yaml + - plugin:cloudify-azure-plugin inputs: resource_prefix: @@ -15,30 +15,10 @@ inputs: resource_suffix: default: 10 - # Azure account information - subscription_id: - type: string - required: false - - tenant_id: - type: string - required: false - - client_id: - type: string - required: false - - client_secret: - type: string - required: false - location: type: string required: true - default: eastus - - password: - type: string + default: eastus2 plan_name: type: string @@ -50,14 +30,14 @@ inputs: web_app_name: type: string - default: test-app + default: cloudify-test-app dsl_definitions: azure_config: &azure_config - subscription_id: { get_input: subscription_id } - tenant_id: { get_input: tenant_id } - client_id: { get_input: client_id } - client_secret: { get_input: client_secret } + subscription_id: { get_secret: subscription_id } + tenant_id: { get_secret: tenant_id } + client_id: { get_secret: client_id } + client_secret: { get_secret: client_secret } plan_info: &plan_info location: { get_input: location } @@ -68,18 +48,19 @@ dsl_definitions: app_config: &app_config location: { get_input: location } - server_farm_id: sample-plan + server_farm_id: { get_attribute:[plan, resource_id] } site_config: scm_type: LocalGit python_version: 3.6 user: &user name: sample-user - user_name: sample-user + publishing_user_name: sample-user publishing_password: STR0NG-test-Pa$$& location: { get_input: location } node_templates: + user: type: cloudify.azure.nodes.PublishingUser properties: @@ -98,7 +79,7 @@ node_templates: properties: name: { get_input: plan_name } resource_group: { get_input: resource_group_name } - sku: *plan_info + plan_details: *plan_info azure_config: *azure_config relationships: - type: cloudify.relationships.depends_on diff --git a/plugin.yaml b/plugin.yaml index ecb7c1b2..537e6002 100644 --- a/plugin.yaml +++ b/plugin.yaml @@ -5,9 +5,9 @@ plugins: azure: executor: central_deployment_agent - source: https://github.com/cloudify-cosmo/cloudify-azure-plugin/archive/3.2.1.zip + source: https://github.com/cloudify-cosmo/cloudify-azure-plugin/archive/3.3.0.zip package_name: cloudify-azure-plugin - package_version: '3.2.1' + package_version: '3.3.0' data_types: cloudify.datatypes.azure.Config: @@ -198,12 +198,12 @@ data_types: description: > Quote, in GiB, for the maximum size of the file share required: false - fail_on_exist: - description: > - If true, causes the operation to raise a NonRecoverableError if - the file share already exists. If false, issues a warning and - continues execution. - default: false +# fail_on_exist: +# description: > +# If true, causes the operation to raise a NonRecoverableError if +# the file share already exists. If false, issues a warning and +# continues execution. +# default: false cloudify.datatypes.azure.network.VirtualNetworkConfig: description: > @@ -649,6 +649,13 @@ node_types: description: > Specifies the supported Azure location for the resource required: false + storage_account_name: + type: string + description: > + Name of the storage account that the existing resource belongs to + (this is only useful when not using a relationship between a resource + node and a storage account node) + required: false retry_after: description: > Overrides the Azure-specified "retry_after" response. This property @@ -663,7 +670,8 @@ node_types: required: false interfaces: cloudify.interfaces.lifecycle: - create: azure.cloudify_azure.resources.storage.file.create_file_share + create: azure.cloudify_azure.resources.storage.file.create + delete: azure.cloudify_azure.resources.storage.file.delete cloudify.azure.nodes.network.VirtualNetwork: derived_from: cloudify.nodes.Root @@ -1599,11 +1607,11 @@ node_types: resource_group: required: true type: string - sku: - type: string + plan_details: required: false description: > - SKU details, including location, name, capacity and tier + Plan details, including location, SKU, kind etc. + https://docs.microsoft.com/en-us/python/api/azure-mgmt-web/azure.mgmt.web.v2019_08_01.models.appserviceplan?view=azure-python interfaces: cloudify.interfaces.lifecycle: create: @@ -1613,8 +1621,8 @@ node_types: default: { get_property: [SELF, name]} resource_group: default: { get_property: [SELF, resource_group]} - sku: - default: { get_property: [SELF, sku]} + plan_details: + default: { get_property: [SELF, plan_details]} azure_config: default: { get_property: [SELF, azure_config]} client_config: @@ -1626,6 +1634,7 @@ node_types: derived_from: cloudify.nodes.Root properties: <<: *common_properties + <<: *existing_resources resource_group: required: true type: string @@ -1684,10 +1693,11 @@ node_types: cloudify.azure.nodes.compute.ManagedCluster: derived_from: cloudify.nodes.Root properties: + <<: *existing_resources resource_group: required: true type: string - cluster_name: + name: required: true type: string resource_config: @@ -1720,13 +1730,15 @@ node_types: resource_group: default: { get_property: [SELF, resource_group]} cluster_name: - default: { get_property: [SELF, cluster_name]} + default: { get_property: [SELF, name]} resource_config: default: { get_property: [SELF, resource_config]} azure_config: default: { get_property: [SELF, azure_config]} client_config: default: { get_property: [SELF, client_config]} + configure: + implementation: azure.cloudify_azure.resources.compute.managed_cluster.store_kubeconf_if_needed delete: implementation: azure.cloudify_azure.resources.compute.managed_cluster.delete