{"action":"created","comment":{"url":"https://api.github.com/repos/dcos/dcos/pulls/comments/89264245","pull_request_review_id":9809800,"id":89264245,"diff_hunk":"@@ -0,0 +1,198 @@\n+\"\"\"Working with azure effectively requires a few too many individual clients\n+with rather specific APIs. This module is intended to give the most basic functionality\n+to accomodate DC/OS testing\n+\"\"\"\n+import logging\n+import re\n+\n+import azure.common.credentials\n+from azure.mgmt.network import NetworkManagementClient\n+from azure.mgmt.resource.resources import ResourceManagementClient\n+from azure.mgmt.resource.resources.models import (DeploymentMode,\n+                                                  DeploymentProperties,\n+                                                  ResourceGroup, TemplateLink)\n+from msrestazure.azure_exceptions import CloudError\n+from retrying import retry\n+\n+from test_util.helpers import lazy_property\n+\n+\n+# Very noisy loggers that do not help much in debug mode\n+logging.getLogger(\"msrest\").setLevel(logging.INFO)\n+logging.getLogger(\"requests_oauthlib\").setLevel(logging.INFO)\n+log = logging.getLogger(__name__)\n+\n+# This interface is designed to only use a single deployment\n+# Being as the azure interface is based around resource groups, deriving\n+# deployment name from group names makes it easier to attach to creating deploys\n+DEPLOYMENT_NAME = '{}-Deployment'\n+\n+\n+class AzureWrapper:\n+    def __init__(self, subscription_id, tenant_id, client_id, client_secret):\n+        self.credentials = azure.common.credentials.ServicePrincipalCredentials(\n+            client_id=client_id,\n+            secret=client_secret,\n+            tenant=tenant_id)\n+        self.rmc = ResourceManagementClient(self.credentials, subscription_id)\n+        self.nmc = NetworkManagementClient(self.credentials, subscription_id)\n+\n+\n+class TemplateDeployment:\n+    def __init__(self, group_name, azure_wrapper):\n+        # each template deployment is a resource group\n+        self.group_name = group_name\n+        self.azure_wrapper = azure_wrapper\n+\n+    @classmethod\n+    def create(\n+            cls, azure_wrapper, template_uri, location, group_name,\n+            public_key, master_prefix, agent_prefix, admin_name, oauth_enabled,\n+            vm_size, agent_count, name_suffix, vm_diagnostics_enabled):\n+        \"\"\" Creates a new resource group and deploys the DC/OS template to it\n+        \"\"\"\n+        assert master_prefix != agent_prefix, 'Master and agents must have unique prefixs'\n+        assert re.match('^[a-z][a-z0-9-]{1,61}[a-z0-9]$', master_prefix),\\\n+            'Invalid master DNS prefix: {}'.format(master_prefix)\n+        assert re.match('^[a-z][a-z0-9-]{1,61}[a-z0-9]$', agent_prefix),\\\n+            'Invalid agent DNS prefix: {}'.format(agent_prefix)\n+\n+        deployment_name = DEPLOYMENT_NAME.format(group_name)\n+\n+        # Resource group must be created before validation can occur\n+        if azure_wrapper.rmc.resource_groups.check_existence(group_name):\n+            raise Exception(\"Group name already exists / taken: {}\".format(group_name))\n+        log.info('Starting resource group_creation')\n+        azure_wrapper.rmc.resource_groups.create_or_update(\n+            group_name,\n+            ResourceGroup(location=location))\n+\n+        template_params = {\n+            'sshRSAPublicKey': public_key,\n+            # must be lower or validation will fail\n+            'masterEndpointDNSNamePrefix': master_prefix.lower(),\n+            'agentEndpointDNSNamePrefix': agent_prefix.lower(),\n+            'linuxAdminUsername': admin_name,\n+            'agentVMSize': vm_size,\n+            'agentCount': agent_count,\n+            'nameSuffix': name_suffix,\n+            'oauthEnabled': repr(oauth_enabled).lower(),\n+            # oauth uses string, vm diagnostic uses bool\n+            'enableVMDiagnostics': vm_diagnostics_enabled}\n+        log.info('Provided template parameters: {}'.format(template_params))\n+        # add worthless required 'value' fields\n+        template_parameters = {k: {'value': v} for k, v in template_params.items()}\n+        deployment_properties = DeploymentProperties(\n+            template_link=TemplateLink(uri=template_uri),\n+            mode=DeploymentMode.incremental,\n+            parameters=template_parameters)\n+        # Use RPC against azure to validate the ARM template is well-formed\n+        log.info('Validating template deployment')\n+        try:\n+            result = azure_wrapper.rmc.deployments.validate(\n+                group_name, deployment_name, properties=deployment_properties)\n+            if result.error:\n+                for details in result.error.details:\n+                    log.error('{}: {}'.format(details.code, details.message))\n+                raise Exception(\"Template verification failed!\")","path":"test_util/azure.py","position":97,"original_position":97,"commit_id":"afee85ddb5b818313c09f00e8fdfbc61552e6657","original_commit_id":"afee85ddb5b818313c09f00e8fdfbc61552e6657","user":{"login":"cmaloney","id":751088,"avatar_url":"https://avatars.githubusercontent.com/u/751088?v=3","gravatar_id":"","url":"https://api.github.com/users/cmaloney","html_url":"https://github.com/cmaloney","followers_url":"https://api.github.com/users/cmaloney/followers","following_url":"https://api.github.com/users/cmaloney/following{/other_user}","gists_url":"https://api.github.com/users/cmaloney/gists{/gist_id}","starred_url":"https://api.github.com/users/cmaloney/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cmaloney/subscriptions","organizations_url":"https://api.github.com/users/cmaloney/orgs","repos_url":"https://api.github.com/users/cmaloney/repos","events_url":"https://api.github.com/users/cmaloney/events{/privacy}","received_events_url":"https://api.github.com/users/cmaloney/received_events","type":"User","site_admin":false},"body":"please include the details inside the exception. Having the exception without the details isn't very useful, esp. since they're right there.","created_at":"2016-11-23T07:11:36Z","updated_at":"2016-11-23T07:14:53Z","html_url":"https://github.com/dcos/dcos/pull/914#discussion_r89264245","pull_request_url":"https://api.github.com/repos/dcos/dcos/pulls/914","_links":{"self":{"href":"https://api.github.com/repos/dcos/dcos/pulls/comments/89264245"},"html":{"href":"https://github.com/dcos/dcos/pull/914#discussion_r89264245"},"pull_request":{"href":"https://api.github.com/repos/dcos/dcos/pulls/914"}}},"pull_request":{"url":"https://api.github.com/repos/dcos/dcos/pulls/914","id":91499054,"html_url":"https://github.com/dcos/dcos/pull/914","diff_url":"https://github.com/dcos/dcos/pull/914.diff","patch_url":"https://github.com/dcos/dcos/pull/914.patch","issue_url":"https://api.github.com/repos/dcos/dcos/issues/914","number":914,"state":"open","locked":false,"title":"Refactor azure","user":{"login":"mellenburg","id":14269783,"avatar_url":"https://avatars.githubusercontent.com/u/14269783?v=3","gravatar_id":"","url":"https://api.github.com/users/mellenburg","html_url":"https://github.com/mellenburg","followers_url":"https://api.github.com/users/mellenburg/followers","following_url":"https://api.github.com/users/mellenburg/following{/other_user}","gists_url":"https://api.github.com/users/mellenburg/gists{/gist_id}","starred_url":"https://api.github.com/users/mellenburg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mellenburg/subscriptions","organizations_url":"https://api.github.com/users/mellenburg/orgs","repos_url":"https://api.github.com/users/mellenburg/repos","events_url":"https://api.github.com/users/mellenburg/events{/privacy}","received_events_url":"https://api.github.com/users/mellenburg/received_events","type":"User","site_admin":false},"body":"Our azure test utility is currently only good for running an integration test. This commit will split out its functionality so we can eventually move towards a uniform dcos-cluster testing interface\n","created_at":"2016-10-29T19:20:12Z","updated_at":"2016-11-23T07:14:53Z","closed_at":null,"merged_at":null,"merge_commit_sha":"748d7cc47c5a467c6f59194a16ce8e5d9704a53b","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/dcos/dcos/pulls/914/commits","review_comments_url":"https://api.github.com/repos/dcos/dcos/pulls/914/comments","review_comment_url":"https://api.github.com/repos/dcos/dcos/pulls/comments{/number}","comments_url":"https://api.github.com/repos/dcos/dcos/issues/914/comments","statuses_url":"https://api.github.com/repos/dcos/dcos/statuses/afee85ddb5b818313c09f00e8fdfbc61552e6657","head":{"label":"mellenburg:refactor_azure","ref":"refactor_azure","sha":"afee85ddb5b818313c09f00e8fdfbc61552e6657","user":{"login":"mellenburg","id":14269783,"avatar_url":"https://avatars.githubusercontent.com/u/14269783?v=3","gravatar_id":"","url":"https://api.github.com/users/mellenburg","html_url":"https://github.com/mellenburg","followers_url":"https://api.github.com/users/mellenburg/followers","following_url":"https://api.github.com/users/mellenburg/following{/other_user}","gists_url":"https://api.github.com/users/mellenburg/gists{/gist_id}","starred_url":"https://api.github.com/users/mellenburg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mellenburg/subscriptions","organizations_url":"https://api.github.com/users/mellenburg/orgs","repos_url":"https://api.github.com/users/mellenburg/repos","events_url":"https://api.github.com/users/mellenburg/events{/privacy}","received_events_url":"https://api.github.com/users/mellenburg/received_events","type":"User","site_admin":false},"repo":{"id":56731971,"name":"dcos","full_name":"mellenburg/dcos","owner":{"login":"mellenburg","id":14269783,"avatar_url":"https://avatars.githubusercontent.com/u/14269783?v=3","gravatar_id":"","url":"https://api.github.com/users/mellenburg","html_url":"https://github.com/mellenburg","followers_url":"https://api.github.com/users/mellenburg/followers","following_url":"https://api.github.com/users/mellenburg/following{/other_user}","gists_url":"https://api.github.com/users/mellenburg/gists{/gist_id}","starred_url":"https://api.github.com/users/mellenburg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mellenburg/subscriptions","organizations_url":"https://api.github.com/users/mellenburg/orgs","repos_url":"https://api.github.com/users/mellenburg/repos","events_url":"https://api.github.com/users/mellenburg/events{/privacy}","received_events_url":"https://api.github.com/users/mellenburg/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/mellenburg/dcos","description":"DC/OS Build and Release tools","fork":true,"url":"https://api.github.com/repos/mellenburg/dcos","forks_url":"https://api.github.com/repos/mellenburg/dcos/forks","keys_url":"https://api.github.com/repos/mellenburg/dcos/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mellenburg/dcos/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mellenburg/dcos/teams","hooks_url":"https://api.github.com/repos/mellenburg/dcos/hooks","issue_events_url":"https://api.github.com/repos/mellenburg/dcos/issues/events{/number}","events_url":"https://api.github.com/repos/mellenburg/dcos/events","assignees_url":"https://api.github.com/repos/mellenburg/dcos/assignees{/user}","branches_url":"https://api.github.com/repos/mellenburg/dcos/branches{/branch}","tags_url":"https://api.github.com/repos/mellenburg/dcos/tags","blobs_url":"https://api.github.com/repos/mellenburg/dcos/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mellenburg/dcos/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mellenburg/dcos/git/refs{/sha}","trees_url":"https://api.github.com/repos/mellenburg/dcos/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mellenburg/dcos/statuses/{sha}","languages_url":"https://api.github.com/repos/mellenburg/dcos/languages","stargazers_url":"https://api.github.com/repos/mellenburg/dcos/stargazers","contributors_url":"https://api.github.com/repos/mellenburg/dcos/contributors","subscribers_url":"https://api.github.com/repos/mellenburg/dcos/subscribers","subscription_url":"https://api.github.com/repos/mellenburg/dcos/subscription","commits_url":"https://api.github.com/repos/mellenburg/dcos/commits{/sha}","git_commits_url":"https://api.github.com/repos/mellenburg/dcos/git/commits{/sha}","comments_url":"https://api.github.com/repos/mellenburg/dcos/comments{/number}","issue_comment_url":"https://api.github.com/repos/mellenburg/dcos/issues/comments{/number}","contents_url":"https://api.github.com/repos/mellenburg/dcos/contents/{+path}","compare_url":"https://api.github.com/repos/mellenburg/dcos/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mellenburg/dcos/merges","archive_url":"https://api.github.com/repos/mellenburg/dcos/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mellenburg/dcos/downloads","issues_url":"https://api.github.com/repos/mellenburg/dcos/issues{/number}","pulls_url":"https://api.github.com/repos/mellenburg/dcos/pulls{/number}","milestones_url":"https://api.github.com/repos/mellenburg/dcos/milestones{/number}","notifications_url":"https://api.github.com/repos/mellenburg/dcos/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mellenburg/dcos/labels{/name}","releases_url":"https://api.github.com/repos/mellenburg/dcos/releases{/id}","deployments_url":"https://api.github.com/repos/mellenburg/dcos/deployments","created_at":"2016-04-21T01:13:27Z","updated_at":"2016-10-26T01:11:32Z","pushed_at":"2016-11-23T02:08:06Z","git_url":"git://github.com/mellenburg/dcos.git","ssh_url":"git@github.com:mellenburg/dcos.git","clone_url":"https://github.com/mellenburg/dcos.git","svn_url":"https://github.com/mellenburg/dcos","homepage":"https://dcos.io","size":2226,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"dcos:master","ref":"master","sha":"449fbda168178cc8c30a8a0821f9cbc2edc715d1","user":{"login":"dcos","id":17648048,"avatar_url":"https://avatars.githubusercontent.com/u/17648048?v=3","gravatar_id":"","url":"https://api.github.com/users/dcos","html_url":"https://github.com/dcos","followers_url":"https://api.github.com/users/dcos/followers","following_url":"https://api.github.com/users/dcos/following{/other_user}","gists_url":"https://api.github.com/users/dcos/gists{/gist_id}","starred_url":"https://api.github.com/users/dcos/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dcos/subscriptions","organizations_url":"https://api.github.com/users/dcos/orgs","repos_url":"https://api.github.com/users/dcos/repos","events_url":"https://api.github.com/users/dcos/events{/privacy}","received_events_url":"https://api.github.com/users/dcos/received_events","type":"Organization","site_admin":false},"repo":{"id":56184050,"name":"dcos","full_name":"dcos/dcos","owner":{"login":"dcos","id":17648048,"avatar_url":"https://avatars.githubusercontent.com/u/17648048?v=3","gravatar_id":"","url":"https://api.github.com/users/dcos","html_url":"https://github.com/dcos","followers_url":"https://api.github.com/users/dcos/followers","following_url":"https://api.github.com/users/dcos/following{/other_user}","gists_url":"https://api.github.com/users/dcos/gists{/gist_id}","starred_url":"https://api.github.com/users/dcos/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dcos/subscriptions","organizations_url":"https://api.github.com/users/dcos/orgs","repos_url":"https://api.github.com/users/dcos/repos","events_url":"https://api.github.com/users/dcos/events{/privacy}","received_events_url":"https://api.github.com/users/dcos/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/dcos/dcos","description":"DC/OS Build and Release tools","fork":false,"url":"https://api.github.com/repos/dcos/dcos","forks_url":"https://api.github.com/repos/dcos/dcos/forks","keys_url":"https://api.github.com/repos/dcos/dcos/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dcos/dcos/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dcos/dcos/teams","hooks_url":"https://api.github.com/repos/dcos/dcos/hooks","issue_events_url":"https://api.github.com/repos/dcos/dcos/issues/events{/number}","events_url":"https://api.github.com/repos/dcos/dcos/events","assignees_url":"https://api.github.com/repos/dcos/dcos/assignees{/user}","branches_url":"https://api.github.com/repos/dcos/dcos/branches{/branch}","tags_url":"https://api.github.com/repos/dcos/dcos/tags","blobs_url":"https://api.github.com/repos/dcos/dcos/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dcos/dcos/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dcos/dcos/git/refs{/sha}","trees_url":"https://api.github.com/repos/dcos/dcos/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dcos/dcos/statuses/{sha}","languages_url":"https://api.github.com/repos/dcos/dcos/languages","stargazers_url":"https://api.github.com/repos/dcos/dcos/stargazers","contributors_url":"https://api.github.com/repos/dcos/dcos/contributors","subscribers_url":"https://api.github.com/repos/dcos/dcos/subscribers","subscription_url":"https://api.github.com/repos/dcos/dcos/subscription","commits_url":"https://api.github.com/repos/dcos/dcos/commits{/sha}","git_commits_url":"https://api.github.com/repos/dcos/dcos/git/commits{/sha}","comments_url":"https://api.github.com/repos/dcos/dcos/comments{/number}","issue_comment_url":"https://api.github.com/repos/dcos/dcos/issues/comments{/number}","contents_url":"https://api.github.com/repos/dcos/dcos/contents/{+path}","compare_url":"https://api.github.com/repos/dcos/dcos/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dcos/dcos/merges","archive_url":"https://api.github.com/repos/dcos/dcos/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dcos/dcos/downloads","issues_url":"https://api.github.com/repos/dcos/dcos/issues{/number}","pulls_url":"https://api.github.com/repos/dcos/dcos/pulls{/number}","milestones_url":"https://api.github.com/repos/dcos/dcos/milestones{/number}","notifications_url":"https://api.github.com/repos/dcos/dcos/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dcos/dcos/labels{/name}","releases_url":"https://api.github.com/repos/dcos/dcos/releases{/id}","deployments_url":"https://api.github.com/repos/dcos/dcos/deployments","created_at":"2016-04-13T20:31:58Z","updated_at":"2016-11-22T21:45:57Z","pushed_at":"2016-11-23T07:11:24Z","git_url":"git://github.com/dcos/dcos.git","ssh_url":"git@github.com:dcos/dcos.git","clone_url":"https://github.com/dcos/dcos.git","svn_url":"https://github.com/dcos/dcos","homepage":"https://dcos.io","size":3747,"stargazers_count":1118,"watchers_count":1118,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":187,"mirror_url":null,"open_issues_count":53,"forks":187,"open_issues":53,"watchers":1118,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/dcos/dcos/pulls/914"},"html":{"href":"https://github.com/dcos/dcos/pull/914"},"issue":{"href":"https://api.github.com/repos/dcos/dcos/issues/914"},"comments":{"href":"https://api.github.com/repos/dcos/dcos/issues/914/comments"},"review_comments":{"href":"https://api.github.com/repos/dcos/dcos/pulls/914/comments"},"review_comment":{"href":"https://api.github.com/repos/dcos/dcos/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/dcos/dcos/pulls/914/commits"},"statuses":{"href":"https://api.github.com/repos/dcos/dcos/statuses/afee85ddb5b818313c09f00e8fdfbc61552e6657"}}}}
{"actor":{"display_login":"cmaloney"}}