-
Notifications
You must be signed in to change notification settings - Fork 90
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix to raise specific error when ssh user is not provided and root is used as default user. #466
Fix to raise specific error when ssh user is not provided and root is used as default user. #466
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for this PR!
I'm not sure this is the right place to put this check (but I'm new, so...). I've also made some other recommendation to make the ruby more idiomatic. Finally, I think this needs tests to show how it works in both the positive and exceptional cases.
lib/train/extras/command_wrapper.rb
Outdated
@@ -162,7 +162,13 @@ class CommandWrapper | |||
include_options WindowsCommand | |||
|
|||
def self.load(transport, options) | |||
if transport.os.unix? | |||
if transport.class == Train::Transports::SSH::Connection && !transport.connected? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty new so I'm not sure my analysis of train itself is all that accurate... That said:
It looks to me like it is transport.os.unix?
itself that is the method call that causes an actual connection to be established, so having this code before that call is ensuring that transport.connected?
is false.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for this PR!
I'm not sure this is the right place to put this check (but I'm new, so...). I've also made some other recommendation to make the ruby more idiomatic. Finally, I think this needs tests to show how it works in both the positive and exceptional cases.
@zenspider I was also not sure whether this the right place to do this. So I was debugging into the code more and also wanted some input so that I get right direction. I have removed this now and fixed in the os_common.rb file where the run_command is executing when we call transport.os.unix?
in command_wrapper.rb. So as this is the fix for ssh connection I am checking the class of the connection. Second as the command execution with root user for ssh connection results in standard output Please login as the user "azure" rather than the user "root"
I am doing string match. This looks to be a right fix as it raises appropriate error to user on root level. But Ia m open for suggestions. I am trying to understand the existing test suite so that I can add code coverage to this.
lib/train/extras/command_wrapper.rb
Outdated
if transport.os.unix? | ||
if transport.class == Train::Transports::SSH::Connection && !transport.connected? | ||
# Need to call run_command here to get the message and show it to user while raising exception | ||
msg = transport.run_command("whoami").stdout |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Presuming that we are in an error state, the use of whoami
is arbitrary (we might be trying to connect to windows or other systems). It might be clearer to use a command like "does_not_exist".
lib/train/extras/command_wrapper.rb
Outdated
msg = transport.run_command("whoami").stdout | ||
# this will throw the error as | ||
# Train::UserError: Ssh failed: Please login as the user "username" rather than the user "root". | ||
raise Train::UserError.new("Ssh failed: #{msg}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A better (more rubyish) way to say this is: raise Train::UserError, "Ssh failed: #{msg}"
lib/train/extras/command_wrapper.rb
Outdated
# this will throw the error as | ||
# Train::UserError: Ssh failed: Please login as the user "username" rather than the user "root". | ||
raise Train::UserError.new("Ssh failed: #{msg}") | ||
elsif transport.os.unix? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, my understanding of what this code is trying to do is to take the situation where the OS detection failed (because of auth/connection issues) but provide an error message that makes that more clear. I think it makes more sense from a design / logical flow perspective to push that down to a rescue on this method. IOW, react to the bad condition, rather than make it part of the normal checks:
def self.load(transport, options)
if transport.os.unix?
# ...
rescue Train::PlatformDetectionFailed => e
transport.valididate_connection # either raises or:
raise e
end
This would require valididate_connection
to be an abstract method (stubbed out in the superclass and raises if not overridden) on Transport::BaseConnection and on all supported Transports, which might be out of scope for this PR (not my call).
@@ -101,6 +101,11 @@ def login_command | |||
LoginCommand.new("ssh", args) | |||
end | |||
|
|||
def connected? | |||
return false if session.nil? | |||
session.exec!("whoami").match?(/Please login as the user/) ? false : true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any time you have a conditional whose branches evaluate to straight booleans is an opportunity to remove the conditional entirely. For example:
# original
return false if session.nil?
session.exec!("whoami").match?(/Please login as the user/) ? false : true
# abstract:
return false if x.nil?
x.y ? false : true
# avoid `nil?`:
return false if !x
x.y ? false : true
# combine the logic:
!x || x.y ? false : true
# flip the logic to put true first:
!(!x || x.y) ? true : false
# at this point: we don't need a conditional or the true/false:
!(!x || x.y)
# apply DeMorgan's Law to clean it up: !(a||b) -> !a&&!b
x && !x.y
# unabstract:
session && !session.exec!("whoami").match?(/Please login as the user/)
The final version actually reads like what it is trying to do: "If there is a session and running whoami doesn't match 'blah'".
I'd take it one step further and not use match
:
session && !session.exec!("whoami") =~ /Please login as the user/
# then push `!` down:
session && session.exec!("whoami") !~ /Please login as the user/
(but that is personal preference (but also bears out in benchmarks))
Also, instead of "Do not merge" there's a new PR type for drafts! It's in the pop-down in the button as you're submitting the PR. |
Thank you @zenspider for your valuable review comments. I am also new to the train connection code so definitely will have a look at all your suggestions and if require will connect with you if there are any blockers. |
Good to know 👍 |
@Vasu1105 btw... I'm in UTC-8 but I'm also a nocturne, so we can arrange to chat live if it is needed / helps. |
Sure. Thank you. |
5fec477
to
6b271fc
Compare
… ssh connection and it don't have access previlages Signed-off-by: Vasu1105 <vasundhara.jagdale@msystechnologies.com>
6b271fc
to
cb36939
Compare
Signed-off-by: Vasu1105 vasundhara.jagdale@msystechnologies.com
Description
When ssh user is not provided while doing Train connection it throws following error
This issue we come across when we trying to bootstrap the node using ssh connection in Chef as mentioned here chef/chef#8550
And while debugging realized that the fix should go in the Train
Following are the way to reproduce the issue using train.
Below is the trackback of the error
After giving the fix the output of the same on console:
And this is the output of fix when using with knife bootstrap
Related Issue
chef/chef#8550
Types of changes
Checklist: