Skip to content
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

Interfaces for 2.0 (WinRM and Shell) #115

Closed
maxlinc opened this issue Jan 2, 2015 · 3 comments
Closed

Interfaces for 2.0 (WinRM and Shell) #115

maxlinc opened this issue Jan 2, 2015 · 3 comments

Comments

@maxlinc
Copy link
Contributor

maxlinc commented Jan 2, 2015

This is a proposal for 2.0, though it could be possible to add new interfaces in 1.4.x as long as the old interfaces are still around (possibly deprecated).

I think it'd be nice to add Shell and Command classes along with Output (added in #103). I hinted towards this in #114 (selecting FileManagers) and #103 (comparing w/ mixlib-shellout), so figured I'd write up an actual proposal.

The WinRM class would represent a WinRM connection. Once you have a connection you can create objects that will interact with that session, like winrm.file_manager (see example in #114) or winrm.shell:

winrm = WinRM.new(connection_opts)
default = winrm.shell
ps = winrm.shell(:powershell)
cmd = winrm.shell(:cmd)
wql = winrm.shell(:wql)
bash = winrm.shell(:bash) # Seems odd, but could be useful for WinRM->Cygwin

The default shell could be a generic or "multi-shell" for people that let's you switch back and forth between different shells, closer to backwards-compatible with the current interface. So these would all be equivalent:

# Option 1 - generic shell w/ cmd, powershell and wql commands
winrm.shell.powerscript(command)
# Option 2 - dedicated PowerShell
ps = winrm.shell(:powerscript)
ps.run(command)

However, since the Shell represents a session I think the second approach is a better usage pattern:

  1. It would make it possible to implement shell-specific methods (on WinRM::Shell subclasses like WinRM::PowerShell), like PowerShell#import_module.
  2. It could provide better session management. Hopefully instantiating a new WinRM::PowerShell would behave just like launching a new PowerShell window - variable assignments and imported modules will be remembered across commands within a session, but not across sessions:
session1 = winrm.shell(:powershell)
session2 = winrm.shell(:powershell)
session2.run('$foo = "bar"')
session1.run('Write-Host $foo') # nothing
session2.run('Write-Host $foo') # "bar"

session1.import_module(WinRM::PowerShellModules::Cryptography)
# re-usable powershell code shipped
# as a ruby module as a separate gem!
# 
# Maybe even `session1.include(WinRM::PowerShellModules::Cryptography)`,
# so it could add both PowerShell modules and Ruby methods to the shell.
command = 'Encrypt-String "Something to encrypt" "P@ssw0rd"'
session1.run(command) # works
session2.run(command) # Encrypt-String not found...

In the example above, imagine that shell.import_module(WinRM::Extensions::Cryptography) imports this module into the powershell session.

I think all of this has a few advantages:

  • WinRM.new is easier to remember than WinRM:: WinRMWebService.new
  • You can add WinRM.new and leave WinRM:: WinRMWebService.new as a (possibly deprecated) backwards-compatible interface.
  • Allows custom shells, like Bash/CSH/ZSH for Cygwin, or Chocolatey.
  • A base Shell class with CmdShell, PowerShell, and WQLShell subclasses will be easier to read than having WQL code in WinRM::WinRMWebService.
  • The #shell method could allow for shell-specific features (like PowerShell#import_module in the example above).
@maxlinc
Copy link
Contributor Author

maxlinc commented Jan 2, 2015

Custom shell example that would make me 😄

require "winrm/extensions/chocolatey"

chocolatey = winrm.shell(:chocolatey)
chocolatey.install_chocolatey unless chocolatey.installed?
chocolatey.install("git")

if chocolatey.installed? "ruby"
  ruby_version = chocolatey.version("ruby")
  puts "Time to upgrade Ruby?" unless ruby_version.found != ruby_version.latest
else
  chocolatey.install("ruby -x86")
  chocolatey.install("ruby2.devkit -x86")
end

Of course, in this case you may be better off with doing that all in PowerShell, but there's cases where something like that could be useful.

@sneal
Copy link
Member

sneal commented Jan 2, 2015

👍 breaking apart WinRMWebService into multiple classes.

I like the idea of being able to open a PowerShell session and call run multiple times, however it might present some challenges getting the output back. Currently if you start a cmd/powershell process via WinRM and leave it running, the call to get the output will never finish. This is why in rwinrm we have a secondary thread that constantly polls for output from the WinRM service.

@mwrock
Copy link
Member

mwrock commented Jan 13, 2017

this was fixed via #191

@mwrock mwrock closed this as completed Jan 13, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants