Conversation
| content = string_or_file | ||
| if os.path.exists(string_or_file): | ||
| logger.info('Use existing SSH public key file: %s', string_or_file) | ||
| logger.info('Reusing existing SSH public key from %s', string_or_file) |
There was a problem hiding this comment.
I would leave the original logging msg, at this point, the CLI only knows the file exists and the key has not been verified yet.
There was a problem hiding this comment.
Okay, I will leave the original logging message.
8a4968f to
ebc2b43
Compare
williexu
left a comment
There was a problem hiding this comment.
Some comments, also check CI
There was a problem hiding this comment.
You only want to catch a FileNotFoundError right?
It may be better to do the existence check like you do with the public key.
There was a problem hiding this comment.
Okay, I will change this to check the path instead
|
I made the requested changes, can someone please look at the version numbers and make sure they look ok |
There was a problem hiding this comment.
Maybe use the absolute namespace.
There was a problem hiding this comment.
Put this in the tearDown otherwise, it is not executed when the test fails.
|
So is it okay to merge this? |
|
Please address my comments. |
|
@adewaleo you might need to rebase your branch, check the changes for the PR |
…" is run and id_rsa exists but id_rsa.pub does not exist.
38b88b6 to
7c67fdb
Compare
| public_key_filepath, pub_ssh_dir) | ||
|
|
||
| return public_key | ||
| except IOError: |
There was a problem hiding this comment.
FileNotFoundError, IOError spans too many scenarios
There was a problem hiding this comment.
Unfortunately FileNotFoundError is not defined in Python 2. IOError handles the case that the file isn't found and that the file is unreadable for some other reason.
There was a problem hiding this comment.
I wonder if I should check if the file exists and handle the IOError separately?
There was a problem hiding this comment.
You want different behaviors for a non-existing public file vs other unreadable file errors(no permission, password-protected, etc) correct?
In that case, you need to differentiate between the two; if FileNotFound is not available in Python2, the best option seems to simply check for existence.
There was a problem hiding this comment.
I think I have addressed your comments, thanks for the feedback.
| try: | ||
| with open(public_key_filepath, 'r') as public_key_file: | ||
| public_key = public_key_file.read() | ||
| pub_ssh_dir, _ = os.path.split(public_key_filepath) |
There was a problem hiding this comment.
Since the basename is not used here just use os.path.dirname
https://docs.python.org/3/library/os.path.html?highlight=os%20path%20dirname#os.path.dirname
| key = paramiko.RSAKey(filename=private_key_filepath) | ||
| logger.warning("Private SSH key file '%s' found in dir '%s'." | ||
| " Generating public key file '%s'", | ||
| private_key_filepath, ssh_dir, public_key_filepath) |
There was a problem hiding this comment.
What's the fallback here when it fails to load the private key?
| cls.key.write_private_key(keyOutput) | ||
|
|
||
| cls.private_key = keyOutput.getvalue() | ||
| cls.public_key = '{} {}'.format(cls.key.get_name(), cls.key.get_base64()) |
There was a problem hiding this comment.
Why not just leave it in the setUp?
| if os.path.isfile(fp): | ||
| os.remove(fp) | ||
| else: | ||
| shutil.rmtree(fp) |
There was a problem hiding this comment.
Way too complicated. One setUp tearDown pair and execute the same logic for each test method. There is no need to share the construction between test methods.
| self.assertTrue(os.path.isfile(public_key_file4)) | ||
|
|
||
| # delete temporary directory and its files | ||
| shutil.rmtree(temp_dir_name) |
There was a problem hiding this comment.
Throw it in self.AddCleanup - https://docs.python.org/3/library/unittest.html?highlight=unittest%20addcleanup#unittest.TestCase.addCleanup
There was a problem hiding this comment.
I think I have addressed your comments, thanks for the feedback.
yugangw-msft
left a comment
There was a problem hiding this comment.
LGTM overall. Thanks for the change! I added a few comments, hope they help you remove some code.
| import paramiko | ||
| from paramiko.ssh_exception import PasswordRequiredException, SSHException | ||
|
|
||
| if os.path.isfile(public_key_filepath): |
There was a problem hiding this comment.
We probably don't need this as the caller is supposed to check the public key file not existing and then invoke this. Check out the code here
There was a problem hiding this comment.
I hear you Yugang, but I was asked by @williexu to update this method independent of the current caller's behavior. I imagine this is to make sure the method doesn't fail if a future caller of this method fails to make the check.
There was a problem hiding this comment.
I am fine with this idea, but then you also need to remove the redundant file check in VM and ACS modules. I thought we can do step by step through the next PR as this piece is pretty important so risk is a concern, but up to you.
src/azure-cli-core/HISTORY.rst
Outdated
| ++++++ | ||
| * Minor fixes. | ||
| * Fixed issue where `az vm create --generate-ssh-keys` overwrites private key | ||
| file if public key file is missing. (#4725, #6790) |
There was a problem hiding this comment.
Nice catch! Will update!
| def _create_new_temp_key_file(self, key_data, suffix=""): | ||
| with tempfile.NamedTemporaryFile(mode='w', dir=self._tempdirName, delete=False, suffix=suffix) as f: | ||
| f.write(key_data) | ||
| file_path = f.name |
There was a problem hiding this comment.
you can just do return f.name
| # delete temporary directory to be used for temp files. | ||
| shutil.rmtree(self._tempdirName) | ||
|
|
||
| def test_public_key_file_exists(self): |
There was a problem hiding this comment.
suggest name like test_when_public_key_file_exists? also this test can removed though as generate_ssh_keys should only be invoked when public key file doesn't exist
There was a problem hiding this comment.
I will update the test names.
I agree that the user should not call generate_ssh_keys even though they have a valid public key file. But I think it would still be good to handle and test this situation just in case this method is called by a future caller that fails to check this case. Again, I believe I was asked to handle all the different cases of public / private key being present or not present. ,
| new_private_key = f.read() | ||
| self.assertEqual(self.private_key, new_private_key) | ||
|
|
||
| def test_public_key_file_exists_no_permissions(self): |
There was a problem hiding this comment.
again, we don't need this test
There was a problem hiding this comment.
If you would like me to remove this test, I can. Although @troydai asked me to test different cornercases that come to mind.
| with self.assertRaises(CLIError): | ||
| generate_ssh_keys("", public_key_path) | ||
|
|
||
| def test_private_key_file_exists_no_permissions(self): |
There was a problem hiding this comment.
suggest test_error_raised_when_private_key_file_exists_no_permissions
| public_key_path = private_key_path + ".pub" | ||
| generate_ssh_keys(private_key_path, public_key_path) | ||
|
|
||
| def test_private_key_file_exists_encrypted(self): |
There was a problem hiding this comment.
same: test_error_raised_when_private_key_file_exists_no_permissions
There was a problem hiding this comment.
will update, thanks
| public_key_path = private_key_path + ".pub" | ||
| generate_ssh_keys(private_key_path, public_key_path) | ||
|
|
||
| def test_private_key_file_exists(self): |
There was a problem hiding this comment.
suggest: test_generate_public_key_from_existing_private_key_file
| private_key = f.read() | ||
| self.assertEqual(self.private_key, private_key) | ||
|
|
||
| def test_private_key_file_new(self): |
There was a problem hiding this comment.
suggest: `test_generate_new_private_key_files
| # Check that public key returned is same as public key in public key path | ||
| with open(public_key_path, 'r') as f: | ||
| public_key = f.read() | ||
| self.assertEqual(public_key, new_public_key) |
There was a problem hiding this comment.
Just a question: so you are sure that the keys generated at different time in the same machine would be the same?
There was a problem hiding this comment.
So, this method asserts that the new_public_key returned by generate_ssh_keys is the same as the one that the method wrote to the public key file it created under public_key_path.
There was a problem hiding this comment.
Thanks for all the feedback, btw.
30209f3 to
4983dc5
Compare
|
So I undid my last commit. Someone should let me know if it looks good so I can merge. |
williexu
left a comment
There was a problem hiding this comment.
Looks good for the most part, just some small requested changes.
src/azure-cli-core/HISTORY.rst
Outdated
| @@ -6,6 +6,8 @@ Release History | |||
| 2.0.46 | |||
| ++++++ | |||
| * Minor fixes. | |||
There was a problem hiding this comment.
Replace the Minor fixes entry
| os.chmod(private_key_filepath, 0o600) | ||
| if os.path.isfile(private_key_filepath): | ||
| # try to use existing private key if it exists. | ||
|
|
| logger.warning("Private SSH key file '%s' found in dir '%s'. " | ||
| "Generating new Public key file '%s'", | ||
| private_key_filepath, ssh_dir, public_key_filepath) | ||
|
|
There was a problem hiding this comment.
Okay, I just addressed these comments.
|
Merge when ci is green |
Fixes #4725 and #6780 by using
id_rsaas the private key file (if it exists) to generate a new public key instead of generating a new private-public key pair (which overwritesid-rsa).I tested this fix by deleting my public key and running
az vm create ... --ssh-key-gen.A new public key was generated in
id_rsa.puband I was able to ssh into the newly created VM. I was able to create another vm using the existing keys pair and ssh into it.Please let me know your thoughts on this PR.
This checklist is used to make sure that common guidelines for a pull request are followed.
The PR has modified HISTORY.rst describing any customer-facing, functional changes. Note that this does not include changes only to help content. (see Modifying change log).
I adhere to the Command Guidelines.