Repository: Azure/azure-cli

Issue:
vm/vmss create: don't throw when tries to retrieve image to extract the plan information
The context is some images are still being staged and not yet published, hence image API won't go through. Instead of fail (therefore block the create), the suggested change is to warn. I am fine with it, as using market place image with plan is a minority scenario.
The code to wrap inside a try-catch is [here](https://github.com/Azure/azure-cli/blob/master/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/_validators.py#L160-L185) 

Discussion:
codecov-io: # [Codecov](https://codecov.io/gh/Azure/azure-cli/pull/4265?src=pr&el=h1) Report
> Merging [#4265](https://codecov.io/gh/Azure/azure-cli/pull/4265?src=pr&el=desc) into [master](https://codecov.io/gh/Azure/azure-cli/commit/85c0e4c8fb21e26a4984cad3b21a5e22a6a8b92b?src=pr&el=desc) will **increase** coverage by `<.01%`.
> The diff coverage is `86.66%`.

[![Impacted file tree graph](https://codecov.io/gh/Azure/azure-cli/pull/4265/graphs/tree.svg?src=pr&token=2pog0TKvF8&width=650&height=150)](https://codecov.io/gh/Azure/azure-cli/pull/4265?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #4265      +/-   ##
==========================================
+ Coverage   70.08%   70.09%   +<.01%     
==========================================
  Files         475      475              
  Lines       29636    29639       +3     
  Branches     4542     4542              
==========================================
+ Hits        20771    20774       +3     
- Misses       7419     7423       +4     
+ Partials     1446     1442       -4
```


| [Impacted Files](https://codecov.io/gh/Azure/azure-cli/pull/4265?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [...cli-vm/azure/cli/command\_modules/vm/\_validators.py](https://codecov.io/gh/Azure/azure-cli/pull/4265?src=pr&el=tree#diff-c3JjL2NvbW1hbmRfbW9kdWxlcy9henVyZS1jbGktdm0vYXp1cmUvY2xpL2NvbW1hbmRfbW9kdWxlcy92bS9fdmFsaWRhdG9ycy5weQ==) | `78.48% <86.66%> (+0.1%)` | :arrow_up: |
| [...dback/azure/cli/command\_modules/feedback/custom.py](https://codecov.io/gh/Azure/azure-cli/pull/4265?src=pr&el=tree#diff-c3JjL2NvbW1hbmRfbW9kdWxlcy9henVyZS1jbGktZmVlZGJhY2svYXp1cmUvY2xpL2NvbW1hbmRfbW9kdWxlcy9mZWVkYmFjay9jdXN0b20ucHk=) | `31.25% <0%> (ø)` | :arrow_up: |
| [...nent/azure/cli/command\_modules/component/custom.py](https://codecov.io/gh/Azure/azure-cli/pull/4265?src=pr&el=tree#diff-c3JjL2NvbW1hbmRfbW9kdWxlcy9henVyZS1jbGktY29tcG9uZW50L2F6dXJlL2NsaS9jb21tYW5kX21vZHVsZXMvY29tcG9uZW50L2N1c3RvbS5weQ==) | `16.26% <0%> (ø)` | :arrow_up: |
| [...li-cloud/azure/cli/command\_modules/cloud/custom.py](https://codecov.io/gh/Azure/azure-cli/pull/4265?src=pr&el=tree#diff-c3JjL2NvbW1hbmRfbW9kdWxlcy9henVyZS1jbGktY2xvdWQvYXp1cmUvY2xpL2NvbW1hbmRfbW9kdWxlcy9jbG91ZC9jdXN0b20ucHk=) | `13.79% <0%> (ø)` | :arrow_up: |
| [src/azure-cli-core/azure/cli/core/util.py](https://codecov.io/gh/Azure/azure-cli/pull/4265?src=pr&el=tree#diff-c3JjL2F6dXJlLWNsaS1jb3JlL2F6dXJlL2NsaS9jb3JlL3V0aWwucHk=) | `69.73% <0%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/Azure/azure-cli/pull/4265?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/Azure/azure-cli/pull/4265?src=pr&el=footer). Last update [85c0e4c...5cf8abf](https://codecov.io/gh/Azure/azure-cli/pull/4265?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/packaged_releases/debian/debian_build.sh b/packaged_releases/debian/debian_build.sh
index cac900add..1d59a1ffd 100644
--- a/packaged_releases/debian/debian_build.sh
+++ b/packaged_releases/debian/debian_build.sh
@@ -55,6 +55,9 @@ $source_dir/python_env/bin/pip3 install wheel
 for d in $source_dir/src/azure-cli $source_dir/src/azure-cli-core $source_dir/src/azure-cli-nspkg $source_dir/src/azure-cli-command_modules-nspkg $source_dir/src/command_modules/azure-cli-*/; do cd $d; $source_dir/python_env/bin/python3 setup.py bdist_wheel -d $tmp_pkg_dir; cd -; done;
 $source_dir/python_env/bin/pip3 install azure-cli --find-links $tmp_pkg_dir
 $source_dir/python_env/bin/pip3 install --force-reinstall --upgrade azure-nspkg azure-mgmt-nspkg
+# WORKAROUND: Newer versions of cryptography do not work on Bash on Windows / WSL - see https://github.com/Azure/azure-cli/issues/4154
+# If you *have* to use a newer version of cryptography in the future, verify that it works on WSL also.
+$source_dir/python_env/bin/pip3 install cryptography==2.0
 # Add the debian files
 mkdir $source_dir/debian
 # Create temp dir for the debian/ directory used for CLI build.
diff --git a/scripts/releaser/HISTORY.rst b/scripts/releaser/HISTORY.rst
index a89636c31..563fff31d 100644
--- a/scripts/releaser/HISTORY.rst
+++ b/scripts/releaser/HISTORY.rst
@@ -3,6 +3,11 @@
 Release History
 ===============
 
+0.1.3 (2017-08-18)
+++++++++++++++++++
+
+* Fix 'packaged release archive' creation step. We now clone the repo again after pushing tags so we can use the new tags.
+
 0.1.2 (2017-08-15)
 ++++++++++++++++++
 
diff --git a/scripts/releaser/release.py b/scripts/releaser/release.py
index 4e671b353..c65460c6e 100644
--- a/scripts/releaser/release.py
+++ b/scripts/releaser/release.py
@@ -365,5 +365,8 @@ if __name__ == "__main__":
     give_chance_to_cancel('Create GitHub releases and tags')
     run_create_github_release(release_commitish_list, release_assets_dir_map)
     give_chance_to_cancel('Create Packaged Release archive')
+    # We need to clone the repo again as we've now pushed the git tags and we need them to create the packaged release.
+    # (we could do 'git pull' but this is easier and uses a clean directory just to be safe)
+    cli_repo_dir = set_up_cli_repo_dir()
     run_create_packaged_release(cli_repo_dir)
     print_status('Done.')
diff --git a/src/command_modules/azure-cli-vm/HISTORY.rst b/src/command_modules/azure-cli-vm/HISTORY.rst
index ad0fe2f37..0f9a6ccf6 100644
--- a/src/command_modules/azure-cli-vm/HISTORY.rst
+++ b/src/command_modules/azure-cli-vm/HISTORY.rst
@@ -4,6 +4,7 @@ Release History
 ===============
 unreleased
 +++++++++++++++++++
+* `vm/vmss create`: fix issue where the command would throw an error if unable to extract plan information from an image. 
 * `vmss create`: fix a crash when create a scaleset with an internal LB
 * `vm availability-set create`: Fix issue where --no-wait argument did not work.
 
diff --git a/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/_validators.py b/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/_validators.py
index f1de4fb21..8e9abcc1d 100644
--- a/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/_validators.py
+++ b/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/_validators.py
@@ -157,32 +157,36 @@ def _parse_image_argument(namespace):
         namespace.os_offer = urn_match.group(2)
         namespace.os_sku = urn_match.group(3)
         namespace.os_version = urn_match.group(4)
-        compute_client = _compute_client_factory()
-        if namespace.os_version.lower() == 'latest':
-            top_one = compute_client.virtual_machine_images.list(namespace.location,
-                                                                 namespace.os_publisher,
-                                                                 namespace.os_offer,
-                                                                 namespace.os_sku,
-                                                                 top=1,
-                                                                 orderby='name desc')
-            if not top_one:
-                raise CLIError("Can't resolve the vesion of '{}'".format(namespace.image))
-
-            image_version = top_one[0].name
-        else:
-            image_version = namespace.os_version
-
-        image = compute_client.virtual_machine_images.get(namespace.location,
-                                                          namespace.os_publisher,
-                                                          namespace.os_offer,
-                                                          namespace.os_sku,
-                                                          image_version)
-
-        # pylint: disable=no-member
-        if image.plan:
-            namespace.plan_name = image.plan.name
-            namespace.plan_product = image.plan.product
-            namespace.plan_publisher = image.plan.publisher
+        try:
+            compute_client = _compute_client_factory()
+            if namespace.os_version.lower() == 'latest':
+                top_one = compute_client.virtual_machine_images.list(namespace.location,
+                                                                     namespace.os_publisher,
+                                                                     namespace.os_offer,
+                                                                     namespace.os_sku,
+                                                                     top=1,
+                                                                     orderby='name desc')
+                if not top_one:
+                    raise CLIError("Can't resolve the vesion of '{}'".format(namespace.image))
+
+                image_version = top_one[0].name
+            else:
+                image_version = namespace.os_version
+
+            image = compute_client.virtual_machine_images.get(namespace.location,
+                                                              namespace.os_publisher,
+                                                              namespace.os_offer,
+                                                              namespace.os_sku,
+                                                              image_version)
+
+            # pylint: disable=no-member
+            if image.plan:
+                namespace.plan_name = image.plan.name
+                namespace.plan_product = image.plan.product
+                namespace.plan_publisher = image.plan.publisher
+        except CloudError as ex:
+            logger.warning("Querying the image of '%s' failed for an error '%s'. Configuring plan settings "
+                           "will be skipped", namespace.image, ex.message)
         return 'urn'
 
     # 4 - check if a fully-qualified ID (assumes it is an image ID)


Test patch:
diff --git a/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/tests/test_vm_actions.py b/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/tests/test_vm_actions.py
index 068c40722..0475ec9e3 100644
--- a/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/tests/test_vm_actions.py
+++ b/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/tests/test_vm_actions.py
@@ -8,6 +8,7 @@ import tempfile
 import unittest
 import mock
 
+from msrestazure.azure_exceptions import CloudError
 from azure.cli.core.util import CLIError
 from azure.cli.core.commands.validators import DefaultStr
 from azure.cli.core.keys import is_valid_ssh_rsa_public_key
@@ -172,6 +173,29 @@ class TestActions(unittest.TestCase):
         self.assertEqual('product1', np.plan_product)
         self.assertEqual('publisher1', np.plan_publisher)
 
+    @mock.patch('azure.cli.command_modules.vm._validators._compute_client_factory', autospec=True)
+    @mock.patch('azure.cli.command_modules.vm._validators.logger.warning', autospec=True)
+    def test_parse_staging_image_argument(self, logger_mock, client_factory_mock):
+        compute_client = mock.MagicMock()
+        resp = mock.MagicMock()
+        resp.status_code = 404
+        resp.text = '{"Message": "Not Found"}'
+
+        compute_client.virtual_machine_images.get.side_effect = CloudError(resp, error='image not found')
+        client_factory_mock.return_value = compute_client
+
+        np = mock.MagicMock()
+        np.location = 'some region'
+        np.image = 'publisher1:offer1:sku1:1.0.0'
+
+        # action
+        _parse_image_argument(np)
+
+        # assert
+        logger_mock.assert_called_with("Querying the image of '%s' failed for an error '%s'. "
+                                       "Configuring plan settings will be skipped", 'publisher1:offer1:sku1:1.0.0',
+                                       'image not found')
+
     def test_get_next_subnet_addr_suffix(self):
         result = _get_next_subnet_addr_suffix('10.0.0.0/16', '10.0.0.0/24', 24)
         self.assertEqual(result, '10.0.1.0/24')