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

Add SSHKit::Backend.current #319

Merged
merged 1 commit into from
Jan 22, 2016

Conversation

mattbrictson
Copy link
Member

This PR adds the SSHKit::Backend.current method that I originally introduced in a monkey patch in this Capistrano PR: capistrano/capistrano#1561

The point of SSHKit::Backend.current is to give code access to the current
Backend without having to pass around a reference to it. For example, consider
a `git` helper that executes a git command:

def git(*args)
  SSHKit::Backend.current.execute(:git, *args)
end

Thanks to `current`, we can use this git method wherever we are in an `on`
block, without having to pass a reference to `self`:

on release_roles(:all) do
  git "status"
end

Without the thread local, the same task would be much more awkward due to the
necessary passing of `self`:

def git(backend, *args)
  backend.execute(:git, *args)
end

on release_roles(:all) do
  git self, "status"
end

To someone not intimately familiar with SSHKit, what `self` in this context is
confusing and not obvious at all. It is because SSHKit uses `instance_exec`
within the `on` block that `self` magically transforms.

Better to avoid using `self` altogether and prefer the thread local.
leehambley added a commit that referenced this pull request Jan 22, 2016
@leehambley leehambley merged commit 6c81f53 into capistrano:master Jan 22, 2016
@leehambley
Copy link
Member

Small PRs are lovely <3

@robd
Copy link
Contributor

robd commented Jan 22, 2016

I'm very late to the party, so I realise that this is needed for stuff that is already merged in Capistrano, but I just wanted to understand a bit more about this.

I read up about this here, and I think it's a good idea to stop monkey patching Object, but I just wondered if a thread local is the only way of achieving this. I don't really know how capistrano holds onto it's SSHKit instance but would it be possible to allow the plugin API to provide the SSHKit backend instance explicitly to the plugins via a method, argument, yield param or something rather than depending on a thread local having to have been set by SSHKit? Maybe this is impossible without significant changes to everything, I cannot work it out!

My limited experience with thread locals is that they can be hard to reason about / test because they rely on things being set up at the right time.

Were you thinking of the public API for this to be SSHKit::Backend.current, or would the plugins access the thread local directly? I think it would be good to hide the thread local as an implementation detail and access only through SSHKit::Backend.current, which looks as if this might be what you are thinking of too.

@mattbrictson
Copy link
Member Author

I think it would be good to hide the thread local as an implementation detail and access only through SSHKit::Backend.current, which looks as if this might be what you are thinking of too.

Yes, in fact current is an implementation detail as well. Plugin authors will access the backend through a private backend helper method that hides the details.

See: https://github.com/capistrano/capistrano/blob/4f56f6914c200d695071eb4eb8d6a4838b3f0f3a/lib/capistrano/plugin.rb#L103

@robd
Copy link
Contributor

robd commented Jan 22, 2016

OK yeah I see - thanks for that link.

I see now in that same class, that normally plugin access through to SSHKit is provided via the on method.

In cases when you need to use SSHKit's backend outside of an on block

One thing I don't understand is if a plugin hasn't called the on method, how would the sshkit_backend thread local have been set? Is there somewhere else which sets up an SSHKit session (and the related state on the thread) before calling the plugin?

@mattbrictson
Copy link
Member Author

One thing I don't understand is if a plugin hasn't called the on method…

It's in the case where the on method is called in a different location. The SCM plugins, for example, have the on blocks defined in a .rake file, but within those blocks the tasks make use of helper methods that are defined somewhere else (i.e. in the plugin class).

So your .rake file might include:

task :check do
  on release_roles(:all) do
    plugin.git "status"
  end
end

And then in the plugin class:

def git(*args)
  backend.execute(:git, *args)
end

Without the backend helper (using the thread local), it is very awkward to use helper methods like these. You'd have to pass self, which because the backend uses instance_exec, is very confusing in practice.

See also my commit message here: mattbrictson@8032db6

@robd
Copy link
Contributor

robd commented Jan 22, 2016

Ah sorry - I somehow managed to miss that commit message which has all the info I needed - I understand now. Thanks for explaining it all again here.

jsonn pushed a commit to jsonn/pkgsrc that referenced this pull request Oct 17, 2016
## [1.11.3][] (2016-09-16)

  * Fix known_hosts caching to match on the entire hostlist
    [PR #364](capistrano/sshkit#364) @byroot

## [1.11.2][] (2016-07-29)

### Bug fixes

  * Fixed a crash occurring when `Host@keys` was set to a non-Enumerable.
    @xavierholt [PR #360](capistrano/sshkit#360)

## [1.11.1][] (2016-06-17)

### Bug fixes

  * Fixed a regression in 1.11.0 that would cause
    `ArgumentError: invalid option(s): known_hosts` in some older versions of
    net-ssh. @byroot [#357](capistrano/sshkit#357)

## [1.11.0][] (2016-06-14)

### Bug fixes

  * Fixed colorized output alignment in Logger::Pretty. @xavierholt
    [PR #349](capistrano/sshkit#349)
  * Fixed a bug that prevented nested `with` calls
    [#43](capistrano/sshkit#43)

### Other changes

  * Known hosts lookup optimization is now enabled by default. @byroot

## 1.10.0 (2016-04-22)

  * You can now opt-in to caching of SSH's known_hosts file for a speed boost
    when deploying to a large fleet of servers. Refer to the
    [README](https://github.com/capistrano/sshkit/tree/v1.10.0#known-hosts-caching) for
    details. We plan to turn this on by default in a future version of SSHKit.
    [PR #330](capistrano/sshkit#330) @byroot
  * SSHKit now explicitly closes its pooled SSH connections when Ruby exits;
    this fixes `zlib(finalizer): the stream was freed prematurely` warnings
    [PR #343](capistrano/sshkit#343) @mattbrictson
  * Allow command map entries (`SSHKit::CommandMap#[]`) to be Procs
    [PR #310](capistrano/sshkit#310)
    @mikz

## 1.9.0

**Refer to the 1.9.0.rc1 release notes for a full list of new features, fixes,
and potentially breaking changes since SSHKit 1.8.1.** There are no changes
since 1.9.0.rc1.

## 1.9.0.rc1

### Potentially breaking changes

  * The SSHKit DSL is no longer automatically included when you `require` it.
    **This means you  must now explicitly `include SSHKit::DSL`.**
    See [PR #219](capistrano/sshkit#219) for details.
    @beatrichartz
  * `SSHKit::Backend::Printer#test` now always returns true
    [PR #312](capistrano/sshkit#312) @mikz

### New features

  * `SSHKit::Formatter::Abstract` now accepts an optional Hash of options
    [PR #308](capistrano/sshkit#308) @mattbrictson
  * Add `SSHKit::Backend.current` so that Capistrano plugin authors can refactor
    helper methods and still have easy access to the currently-executing Backend
    without having to use global variables.
  * Add `SSHKit.config.default_runner` options that allows to override default command runner.
    This option also accepts a name of the custom runner class.
  * The ConnectionPool has been rewritten in this release to be more efficient
    and have a cleaner internal API. You can still completely disable the pool
    by setting `SSHKit::Backend::Netssh.pool.idle_timeout = 0`.
    @mattbrictson @byroot [PR #328](capistrano/sshkit#328)

### Bug fixes

  * make sure working directory for commands is properly cleared after `within` blocks
    [PR #307](capistrano/sshkit#307)
    @steved
  * display more accurate string for commands with spaces being output in `Formatter::Pretty`
    [PR #304](capistrano/sshkit#304)
    @steved
    [PR #319](capistrano/sshkit#319) @mattbrictson
  * Fix a race condition experienced in JRuby that could cause multi-server
    deploys to fail. [PR #322](capistrano/sshkit#322)
    @mattbrictson
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

Successfully merging this pull request may close these issues.

3 participants