Update for Ruby 2.5. Moderize configurability usage, update dependencies. v0.2.0
authorMahlon E. Smith <mahlon@martini.nu>
Thu, 12 Jul 2018 15:25:35 -0700
changeset 11 ffa70066522c
parent 10 7013280e62fa
child 12 61c5ec82378f
Update for Ruby 2.5. Moderize configurability usage, update dependencies.
.gems
.hgignore
.ruby-gemset
.ruby-version
.rvmrc
README.rdoc
Rakefile
lib/symphony/tasks/ssh.rb
lib/symphony/tasks/sshscript.rb
spec/symphony/tasks/sshscript_spec.rb
--- /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
--- 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
--- /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
--- /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
--- 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
-
-
-
-
--- 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
--- 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 <mahlon@martini.nu>', 'Michael Granger <ged@faeriemud.org>' ]
+	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|
--- 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
 
 
--- 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
 
 
--- 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