# HG changeset patch # User Mahlon E. Smith # Date 1531434335 25200 # Node ID ffa70066522cf73480a69be104d1d113773068d3 # Parent 7013280e62fa43cd2cb3c55e04ca4fbca43285c6 Update for Ruby 2.5. Moderize configurability usage, update dependencies. diff -r 7013280e62fa -r ffa70066522c .gems --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.gems Thu Jul 12 15:25:35 2018 -0700 @@ -0,0 +1,8 @@ +configurability -v3.2.0 +symphony -v0.11.1 +inversion -v1.1.1 +net-ssh -v5.0.2 +net-sftp -v2.1.2 +rspec -v3.7.0 +simplecov -v0.16.1 +pry -v0.11.3 diff -r 7013280e62fa -r ffa70066522c .hgignore --- a/.hgignore Wed May 28 11:45:41 2014 -0700 +++ b/.hgignore Thu Jul 12 15:25:35 2018 -0700 @@ -7,4 +7,4 @@ ^ChangeLog$ ^docs/ ^coverage/ - +Session.vim diff -r 7013280e62fa -r ffa70066522c .ruby-gemset --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.ruby-gemset Thu Jul 12 15:25:35 2018 -0700 @@ -0,0 +1,1 @@ +symphony-ssh diff -r 7013280e62fa -r ffa70066522c .ruby-version --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.ruby-version Thu Jul 12 15:25:35 2018 -0700 @@ -0,0 +1,1 @@ +2.5 diff -r 7013280e62fa -r ffa70066522c .rvmrc --- a/.rvmrc Wed May 28 11:45:41 2014 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -#!/usr/bin/env bash - -# This is an RVM Project .rvmrc file, used to automatically load the ruby -# development environment upon cd'ing into the directory - -environment_id="2.0.0@symphony-ssh" - -if [[ -d "${rvm_path:-$HOME/.rvm}/environments" \ - && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]; then - echo "Using ${environment_id}" - . "${rvm_path:-$HOME/.rvm}/environments/$environment_id" - - if [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]]; then - . "${rvm_path:-$HOME/.rvm}/hooks/after_use" - fi -else - # If the environment file has not yet been created, use the RVM CLI to select. - if ! rvm --create use "$environment_id" - then - echo "Failed to create RVM environment '${environment_id}'." - exit 1 - fi -fi - -filename=".rvm.gems" -if [[ -s "$filename" ]]; then - rvm gemset import "$filename" -fi - - - - diff -r 7013280e62fa -r ffa70066522c README.rdoc --- a/README.rdoc Wed May 28 11:45:41 2014 -0700 +++ b/README.rdoc Thu Jul 12 15:25:35 2018 -0700 @@ -22,22 +22,26 @@ Configurability[https://rubygems.org/gems/configurability] to determine behavior. The configuration is a YAML[http://www.yaml.org/] file. - symphony_ssh: - path: /usr/bin/ssh - user: root - key: /path/to/a/private_key.rsa - opts: - - -e - - none - - -T - - -x - - -o - - CheckHostIP=no' - - -o - - BatchMode=yes' - - -o - - StrictHostKeyChecking=no + symphony: + ssh: + path: /usr/bin/ssh + user: root + key: /path/to/a/private_key.rsa + opts: + - -e + - none + - -T + - -x + - -o + - CheckHostIP=no' + - -o + - BatchMode=yes' + - -o + - StrictHostKeyChecking=no +**NOTE**: If you've upgrade from a version pre 0.2.0, the +Configurability path has changed from `symphony_ssh`, to an `ssh` key +under the `symphony` top level. === path @@ -47,7 +51,7 @@ === user The default user to connect to remote hosts with. This can be -changes per connection in the AMQP payload. +changed per connection in the AMQP payload. === key @@ -82,7 +86,7 @@ == License -Copyright (c) 2014, Mahlon E. Smith and Michael Granger +Copyright (c) 2014-2018, Mahlon E. Smith and Michael Granger All rights reserved. Redistribution and use in source and binary forms, with or without diff -r 7013280e62fa -r ffa70066522c Rakefile --- a/Rakefile Wed May 28 11:45:41 2014 -0700 +++ b/Rakefile Thu Jul 12 15:25:35 2018 -0700 @@ -25,14 +25,14 @@ require 'rubygems' require 'rubygems/package_task' spec = Gem::Specification.new do |s| - s.email = 'mahlon@martini.nu' s.homepage = 'http://projects.martini.nu/ruby-modules' - s.authors = [ 'Mahlon E. Smith ', 'Michael Granger ' ] + s.authors = [ 'Mahlon E. Smith', 'Michael Granger' ] + s.email = [ 'mahlon@martini.nu', 'ged@faeriemud.org' ] s.platform = Gem::Platform::RUBY s.summary = "Base classes for using Symphony with ssh." s.name = 'symphony-ssh' - s.version = '0.1.1' - s.license = 'BSD' + s.version = '0.2.0' + s.license = 'BSD-3-Clause' s.has_rdoc = true s.require_path = 'lib' s.bindir = 'bin' @@ -48,12 +48,14 @@ s.required_rubygems_version = '>= 2.0.3' s.required_ruby_version = '>= 2.0.0' - s.add_dependency 'symphony', '~> 0.6' - s.add_dependency 'net-ssh', '~> 2.9' + s.add_dependency 'configurability', '~> 3.2' + s.add_dependency 'symphony', '~> 0.11' + s.add_dependency 'inversion', '~> 1.1' + s.add_dependency 'net-ssh', '~> 5.0' s.add_dependency 'net-sftp', '~> 2.1' - s.add_development_dependency 'rspec', '~> 3.0' - s.add_development_dependency 'simplecov', '~> 0.8' + s.add_development_dependency 'rspec', '~> 3.7' + s.add_development_dependency 'simplecov', '~> 0.16' end Gem::PackageTask.new( spec ) do |pkg| diff -r 7013280e62fa -r ffa70066522c lib/symphony/tasks/ssh.rb --- a/lib/symphony/tasks/ssh.rb Wed May 28 11:45:41 2014 -0700 +++ b/lib/symphony/tasks/ssh.rb Thu Jul 12 15:25:35 2018 -0700 @@ -23,7 +23,7 @@ ### key: (optional) The path to an SSH private key ### ### -### Additionally, this class responds to the 'symphony_ssh' configurability +### Additionally, this class responds to the 'symphony.ssh' configurability ### key. Currently, you can set the 'path' argument, which is the ### full path to the local ssh binary (defaults to '/usr/bin/ssh') and ### override the default ssh user, key, and client opts. @@ -47,53 +47,42 @@ ### class Symphony::Task::SSH < Symphony::Task extend Configurability - config_key :symphony_ssh - # SSH default options. + # The default set of ssh command line flags. # - CONFIG_DEFAULTS = { - :path => '/usr/bin/ssh', - :opts => [ - '-e', 'none', - '-T', - '-x', - '-q', - '-o', 'CheckHostIP=no', - '-o', 'BatchMode=yes', - '-o', 'StrictHostKeyChecking=no' - ], - :user => 'root', - :key => nil - } + DEFAULT_SSH_OPTS = %w[ + -e none + -T + -x + -q + -o CheckHostIP=no + -o BatchMode=yes + -o StrictHostKeyChecking=no + ] # SSH "informative" stdout output that should be cleaned from the # command output. SSH_CLEANUP = %r/Warning: no access to tty|Thus no job control in this shell/ - class << self + + # Configurability API + # + configurability( :symphony__ssh ) do + # The full path to the ssh binary. - attr_reader :path + setting :path, default: '/usr/bin/ssh' - # A default set of ssh client options when connecting + # The default user to use when connecting. + setting :user, default: 'root' + + # A default Array of ssh client options when connecting # to remote hosts. - attr_reader :opts - - # The default user to use when connecting. If unset, 'root' is used. - attr_reader :user + setting :opts, default: DEFAULT_SSH_OPTS do |val| + Array( val ) + end # An absolute path to a password-free ssh private key. - attr_reader :key - end - - ### Configurability API. - ### - def self::configure( config=nil ) - config = Symphony::Task::SSH.defaults.merge( config || {} ) - @path = config.delete( :path ) - @opts = config.delete( :opts ) - @user = config.delete( :user ) - @key = config.delete( :key ) - super + setting :key end diff -r 7013280e62fa -r ffa70066522c lib/symphony/tasks/sshscript.rb --- a/lib/symphony/tasks/sshscript.rb Wed May 28 11:45:41 2014 -0700 +++ b/lib/symphony/tasks/sshscript.rb Thu Jul 12 15:25:35 2018 -0700 @@ -1,12 +1,13 @@ #!/usr/bin/env ruby # vim: set nosta noet ts=4 sw=4: +require 'securerandom' require 'net/ssh' require 'net/sftp' -require 'tmpdir' require 'inversion' require 'symphony' require 'symphony/task' +require 'symphony/tasks/ssh' ### A base class for connecting to a remote host, then uploading and @@ -26,9 +27,10 @@ ### key: (optional) The path to an SSH private key ### attributes: (optional) Additional data to attach to the template ### nocleanup: (optional) Leave the remote script after execution? (default to false) +### tempdir: (optional) The destination temp directory. (defaults to /tmp) ### ### -### Additionally, this class responds to the 'symphony_ssh' configurability +### Additionally, this class responds to the 'symphony.ssh' configurability ### key. Currently, you can override the default ssh user and private key. ### ### Textual output of the command is stored in the @output instance variable. @@ -49,53 +51,27 @@ ### end ### class Symphony::Task::SSHScript < Symphony::Task - extend Configurability - config_key :symphony_ssh # Template config # TEMPLATE_OPTS = { - :ignore_unknown_tags => false, - :on_render_error => :propagate, - :strip_tag_lines => true + ignore_unknown_tags: false, + on_render_error: :propagate, + strip_tag_lines: true } # The defaults to use when connecting via SSH # DEFAULT_SSH_OPTIONS = { - :auth_methods => [ 'publickey' ], - :compression => true, - :config => false, - :keys_only => true, - :paranoid => false, - :global_known_hosts_file => '/dev/null', - :user_known_hosts_file => '/dev/null' - } - - # SSH default options. - # - CONFIG_DEFAULTS = { - :user => 'root', - :key => nil + auth_methods: [ 'publickey' ], + compression: true, + config: false, + keys_only: true, + verify_host_key: :never, + global_known_hosts_file: '/dev/null', + user_known_hosts_file: '/dev/null' } - class << self - # The default user to use when connecting. If unset, 'root' is used. - attr_reader :user - - # An absolute path to a password-free ssh private key. - attr_reader :key - end - - ### Configurability API. - ### - def self::configure( config=nil ) - config = Symphony::Task::SSHScript.defaults.merge( config || {} ) - @user = config.delete( :user ) - @key = config.delete( :key ) - super - end - ### Perform the ssh connection, render the template, send it, and ### execute it. @@ -104,20 +80,21 @@ template = payload[ 'template' ] attributes = payload[ 'attributes' ] || {} port = payload[ 'port' ] || 22 - user = payload[ 'user' ] || Symphony::Task::SSHScript.user - key = payload[ 'key' ] || Symphony::Task::SSHScript.key + user = payload[ 'user' ] || Symphony::Task::SSH.user + key = payload[ 'key' ] || Symphony::Task::SSH.key nocleanup = payload[ 'nocleanup' ] + tempdir = payload[ 'tempdir' ] || '/tmp' raise ArgumentError, "Missing required option 'template'" unless template - raise ArgumentError, "Missing required option 'host'" unless payload[ 'host' ] + raise ArgumentError, "Missing required option 'host'" unless payload[ 'host' ] - remote_filename = self.make_remote_filename( template ) + remote_filename = self.make_remote_filename( template, tempdir ) source = self.generate_script( template, attributes ) - ssh_options = DEFAULT_SSH_OPTIONS.merge( :port => port, :keys => [key] ) + ssh_options = DEFAULT_SSH_OPTIONS.merge( port: port, keys: Array(key) ) ssh_options.merge!( - :logger => Loggability[ Net::SSH ], - :verbose => :debug + logger: Loggability[ Net::SSH ], + verbose: :debug ) if payload[ 'debug' ] Net::SSH.start( payload['host'], user, ssh_options ) do |conn| @@ -141,11 +118,15 @@ ### Generate a unique filename for the script on the remote host, ### based on +template+ name. ### - def make_remote_filename( template ) + def make_remote_filename( template, tempdir="/tmp" ) basename = File.basename( template, File.extname(template) ) - tmpname = Dir::Tmpname.make_tmpname( basename, rand(10000) ) + tmpname = "%s/%s-%s" % [ + tempdir, + basename, + SecureRandom.hex( 6 ) + ] - return "/tmp/#{tmpname}" + return tmpname end diff -r 7013280e62fa -r ffa70066522c spec/symphony/tasks/sshscript_spec.rb --- a/spec/symphony/tasks/sshscript_spec.rb Wed May 28 11:45:41 2014 -0700 +++ b/spec/symphony/tasks/sshscript_spec.rb Thu Jul 12 15:25:35 2018 -0700 @@ -1,17 +1,30 @@ require_relative '../../helpers' +require 'symphony/tasks/ssh' require 'symphony/tasks/sshscript' context Symphony::Task::SSHScript do before( :each ) do - described_class.configure( + Symphony::Task::SSH.configure( key: '/tmp/sekrit.rsa', user: 'symphony' ) end - it_should_behave_like "an object with Configurability" + + describe 'utility' do + + it "can generate an appropriate tempfile name" do + instance = Class.new( described_class ).new( 'queue' ) + tmpname = instance.send( :make_remote_filename, "fancy-script.tmpl" ) + expect( tmpname ).to match( %r|^/tmp/fancy-script-[[:xdigit:]]{6}| ) + + tmpname = instance.send( :make_remote_filename, "fancy-script.tmpl", "/var/tmp" ) + expect( tmpname ).to match( %r|/var/tmp/fancy-script-[[:xdigit:]]{6}| ) + end + end + describe 'subclassed' do let( :instance ) { Class.new(described_class).new('queue') } @@ -30,7 +43,7 @@ before( :each ) do allow( Inversion::Template ).to receive( :load ).and_return( template ) - allow( Dir::Tmpname ).to receive( :make_tmpname ).and_return( "script_temp" ) + allow( instance ).to receive( :make_remote_filename ).and_return( "/tmp/script_temp" ) end it "aborts if there is no template in the payload" do