xlogin 0.12.11 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 072e11f01422b9a33d8d41fbd1af71fe322783ef00b920d22cdb5b13b048d610
4
- data.tar.gz: da9263bd37c56de30b903e83ceee3ca95c0105c5467e376d055114d25eb70c5e
3
+ metadata.gz: f6a3054375bed554e54f7d09096df9a5d0bf6e1e614db1b90ddec6c6091ee184
4
+ data.tar.gz: d5a2f79a8c1a2e233c267af0ba7c2b469125f88a3e011467251c8198d8990310
5
5
  SHA512:
6
- metadata.gz: 01ce8239c84dddbc92f722c7b12bbd1a9bf412467bf7b9e93a6fcf15cd8df2814ea778a3a51e1dcee3f83d1bc51524e81a13308a49a90b65ce3a8b91587122c6
7
- data.tar.gz: 0f40a544dcc8194abbd7896d7b5c39319907b65c501d9f3addf44d2486770c1fce4e93e642074799bade06d7e92943e9c65040933fc69bebbb064e4d4719e554
6
+ metadata.gz: '04915fd0733db1d164a83fab63745aef560c75a3d1d5739765f2933af75a3f3cd336e35e99ad589a51a9f5bc15d738e651878c36737e3598f40150db6caefef3'
7
+ data.tar.gz: 2739edd7f265bd8fc12a50ca04fca9bcd80788a75ba54484907c887e1c2b60e068b9b9f2e44f2b31f54566398ba3274b107781b888db90748752b29e2e3ee0e3
@@ -1,20 +1,21 @@
1
1
  $:.unshift File.dirname(__FILE__)
2
2
 
3
3
  require 'net/http'
4
+ require 'ostruct'
4
5
  require 'xlogin/factory'
5
6
  require 'xlogin/version'
6
7
 
7
8
  module Xlogin
8
9
 
9
- class SessionError < StandardError; end
10
- class TemplateError < StandardError; end
11
-
12
- class << self
13
-
14
- def factory
15
- @factory ||= Xlogin::Factory.instance
10
+ class Error < StandardError; end
11
+ class Settings < OpenStruct
12
+ def initialize(*args, &block)
13
+ super(*args, &block)
14
+ freeze
16
15
  end
16
+ end
17
17
 
18
+ class << self
18
19
  def list(*patterns)
19
20
  factory.list_inventory(*patterns)
20
21
  end
@@ -27,12 +28,14 @@ module Xlogin
27
28
  session = case args
28
29
  when Hash then factory.build(**args.merge(**opts))
29
30
  when String then factory.build_from_hostname(args, **opts)
30
- else return
31
+ else
32
+ raise SessionError.new("Invalid argument: '#{args}'")
31
33
  end
32
34
 
33
35
  return session unless block
34
36
  begin block.call(session) ensure session.close end
35
37
  end
38
+ alias_method :create, :get
36
39
 
37
40
  def get_pool(args, **opts, &block)
38
41
  pool = factory.build_pool(args, **opts)
@@ -40,13 +43,15 @@ module Xlogin
40
43
  return pool unless block
41
44
  block.call(pool)
42
45
  end
46
+ alias_method :create_pool, :get_pool
43
47
 
44
48
  def configure(&block)
45
49
  instance_eval(&block)
46
50
  end
47
51
 
48
- def assume_yes?
49
- @assume_yes == true
52
+ def settings
53
+ @settings ||= {}
54
+ Settings.new(@settings)
50
55
  end
51
56
 
52
57
  def generate_templates(dir)
@@ -56,54 +61,50 @@ module Xlogin
56
61
  end
57
62
 
58
63
  private
59
- def assume_yes(boolean = true, &block)
60
- @assume_yes = boolean == true || (block && block.call == true)
64
+ def factory
65
+ @factory ||= Xlogin::Factory.instance
61
66
  end
62
67
 
63
- def source(*sources, &block)
64
- unless block
65
- return sources.each do |path|
66
- raise SessionError.new("Inventory file not found: #{path}") unless File.exist?(path)
67
- factory.instance_eval(IO.read(path), path)
68
- end
68
+ def set(**opts)
69
+ @settings ||= {}
70
+ opts.each do |key, val|
71
+ val = val.call if val.kind_of?(Proc)
72
+ @settings.update(key.to_sym => val)
69
73
  end
74
+ end
70
75
 
71
- factory.instance_eval(&block)
76
+ def source(*sources, &block)
77
+ return factory.instance_eval(&block) if block
78
+
79
+ sources.each do |path|
80
+ raise Xlogin::Error.new("Inventory file not found: #{path}") unless File.exist?(path)
81
+ factory.instance_eval(IO.read(path), path)
82
+ end
72
83
  end
73
84
 
74
85
  def template(*templates, **opts, &block)
75
- unless block
76
- templates.each do |template|
77
- return template_url(template, **opts) if template =~ URI.regexp(['http', 'https'])
78
- raise TemplateError.new("Template file or directory not found: #{template}") unless File.exist?(template)
79
-
80
- files = [template] if File.file?(template)
81
- files = Dir.glob(File.join(template, '*.rb')) if File.directory?(template)
82
- files.each do |file|
83
- name = opts[:type] || File.basename(file, '.rb').scan(/\w+/).join('_')
84
- factory.set_template(name, IO.read(file))
85
- end
86
+ return factory.set_template(templates.shift, &block) if block && templates.size == 1
87
+
88
+ templates.each do |template|
89
+ return template_url(template, **opts) if template =~ URI.regexp(['http', 'https'])
90
+ raise Xlogin::Error.new("Template file or directory not found: #{template}") unless File.exist?(template)
91
+
92
+ paths = [template] if File.file?(template)
93
+ paths = Dir.glob(File.join(template, '*.rb')) if File.directory?(template)
94
+ paths.each do |path|
95
+ name = opts[:type] || File.basename(path, '.rb').scan(/\w+/).join('_')
96
+ factory.set_template(name, IO.read(path))
86
97
  end
87
98
  end
88
-
89
- name = opts[:type] || templates.first
90
- raise ArgumentError.new('Missing template name') unless name
91
- factory.set_template(name, &block)
92
99
  end
93
100
 
94
101
  def template_url(*template_urls, **opts)
95
- template_urls.compact.each do |url|
96
- uri = URI(url.to_s)
102
+ template_urls.each do |url|
103
+ uri = URI(url.to_s)
97
104
  name = opts[:type] || File.basename(uri.path, '.rb').scan(/\w+/).join('_')
98
- text = Net::HTTP.get(uri)
99
- if text =~ /\w+.rb$/
100
- uri.path = File.join(File.dirname(uri.path), text.lines.first.chomp)
101
- text = Net::HTTP.get(uri)
102
- end
103
- factory.set_template(name, text)
105
+ factory.set_template(name, Net::HTTP.get(uri))
104
106
  end
105
107
  end
106
-
107
108
  end
108
109
 
109
110
  end
@@ -1,9 +1,7 @@
1
1
  #! /usr/bin/env ruby
2
2
 
3
3
  require 'optparse'
4
- require 'ostruct'
5
4
  require 'parallel'
6
- require 'readline'
7
5
  require 'stringio'
8
6
 
9
7
  module Xlogin
@@ -13,112 +11,91 @@ module Xlogin
13
11
  DEFAULT_TEMPLATE_DIR = File.join(ENV['HOME'], '.xlogin.d')
14
12
 
15
13
  def self.run(args = ARGV)
16
- config = getopts(args)
17
- client = Xlogin::CLI.new
18
- client.method(config.task.first).call(config)
19
- end
20
-
21
- def self.getopts(args)
22
- config = OpenStruct.new(
23
- jobs: 1,
24
- task: [:tty, nil],
25
- assume_yes: false,
26
- inventory: DEFAULT_INVENTORY_FILE,
27
- template_dir: DEFAULT_TEMPLATE_DIR,
28
- )
29
-
30
- parser = OptionParser.new
31
- parser.banner = "#{File.basename($0)} HOST_PATTERN [Options]"
32
- parser.version = Xlogin::VERSION
33
-
34
- parser.on('-i PATH', '--inventory', String, 'The PATH to the inventory file (default: $HOME/.xloginrc).') { |v| config.inventory = v }
35
- parser.on('-T PATH', '--template', String, 'The PATH to the template dir (default: $HOME/.xlogin.d).') { |v| config.template_dir = v }
36
- parser.on('-L [DIRECTORY]', '--log-dir', String, 'The PATH to the log dir (default: $PWD).') { |v| config.logdir = v || '.' }
37
-
38
- parser.on('-l', '--list', TrueClass, 'List the inventory.') { |v| config.task = [:list, nil] }
39
- parser.on('-t', '--tty', TrueClass, 'Allocate a pseudo-tty.') { |v| config.task = [:tty, nil] }
40
- parser.on('-e COMMAND', '--exec', 'Execute commands and quit.') { |v| config.task = [:exec, v] }
41
-
42
- parser.on('-j NUM', '--jobs', Integer, 'The NUM of jobs to execute in parallel(default: 1).') { |v| config.jobs = v }
43
- parser.on('-E', '--enable', TrueClass, 'Try to gain enable priviledge.') { |v| config.enable = v }
44
- parser.on('-y', '--assume-yes', TrueClass, 'Automatically answer yes to prompts.') { |v| config.assume_yes = v }
45
-
46
- parser.parse!(args)
47
- Xlogin.configure do
48
- assume_yes(true)
49
- source(File.expand_path(config.inventory, ENV['PWD']))
50
- template(File.expand_path(config.template_dir, ENV['PWD']))
51
- end
52
-
53
- config.hosts = Xlogin.list(*args)
54
- raise "No host found: `#{args.join(', ')}`" if config.hosts.empty?
55
-
56
- return config
57
- rescue => e
58
- $stderr.puts e, '', parser
59
- exit 1
14
+ Xlogin::CLI.new.run(args)
60
15
  end
61
16
 
62
17
  def list(config)
63
- $stdout.puts config.hosts.map { |e| e[:name] }.sort.uniq
18
+ $stdout.puts Xlogin.list(*config[:pattern]).map { |e| e[:name] }.sort.uniq
64
19
  end
65
20
 
66
21
  def tty(config)
67
- Signal.trap(:INT) { exit 0 }
68
-
69
- config.hosts.each do |hostinfo|
70
- unless config.hosts.size == 1
71
- case resp = Readline.readline(">> #{hostinfo[:name]}(Y/n)? ", false).strip
72
- when /^y(es)?$/i, ''
73
- when /^n(o)?$/i then next
74
- else redo
75
- end
76
- end
77
-
78
- $stdout.puts "Trying #{hostinfo[:name]}...", "Escape character is '^]'."
79
- tty_config = OpenStruct.new(config.to_h.merge(jobs: 1, hosts: [hostinfo]))
22
+ info = Xlogin.list(*config[:pattern]).first
23
+ $stdout.puts "Trying #{info[:name]}...", "Escape character is '^]'."
80
24
 
81
- session, _ = exec(tty_config)
82
- session.interact!
83
- end
25
+ session, _ = exec(config.merge(jobs: 1))
26
+ session.interact!
84
27
  end
85
28
 
86
29
  def exec(config)
87
30
  Signal.trap(:INT) { exit 0 }
88
31
 
89
- max_width = config.hosts.map { |e| e[:name].length }.max
90
- Parallel.map(config.hosts, in_threads: config.jobs) do |hostinfo|
32
+ jobs = config[:jobs] || 1
33
+ hosts = Xlogin.list(*config[:pattern])
34
+ width = hosts.map { |e| e[:name].length }.max
35
+
36
+ Parallel.map(hosts, in_threads: jobs) do |info|
37
+ buffer = StringIO.new
38
+ prefix = "#{info[:name].to_s.ljust(width)} |"
91
39
  session = nil
92
- error = nil
93
40
 
94
41
  begin
95
- buffer = StringIO.new
96
-
97
42
  loggers = []
98
- loggers << ((config.jobs > 1)? buffer : $stdout)
99
- loggers << File.expand_path(File.join(config.logdir, "#{hostinfo[:name]}.log"), ENV['PWD']) if config.logdir
43
+ loggers << ((jobs > 1)? buffer : $stdout)
44
+ loggers << File.expand_path(File.join(config[:log_dir], "#{info[:name]}.log"), ENV['PWD']) if config[:log_dir]
100
45
 
101
- session = Xlogin.get(hostinfo.merge(log: loggers))
102
- session.enable(hostinfo[:enable]) if config.enable && hostinfo[:enable]
46
+ session = Xlogin.get(info.merge(log: loggers))
47
+ session.enable(info[:enable]) if config[:enable] && info[:enable]
103
48
 
104
- command_lines = ['', *config.task.last.to_s.split(';').map(&:strip)]
49
+ command_lines = ['', *config[:exec].to_s.split(';').map(&:strip)]
105
50
  command_lines.each { |line| session.cmd(line) }
106
- rescue => err
107
- error = err
108
- end
109
51
 
110
- if config.jobs > 1
111
- prefix = "#{hostinfo[:name].to_s.ljust(max_width)} |"
112
- output = buffer.string.lines.map { |line| prefix + line.chomp.gsub("\r", '') + "\n" }.join
113
- $stdout.print output
114
- $stderr.print prefix + "[Error] #{error}\n" if error
115
- else
116
- $stderr.print "[Error] #{error}\n" if error
52
+ buffer.string.lines.each { |line| $stdout.print prefix + line.gsub("\r", '') } if jobs > 1
53
+ rescue => e
54
+ buffer.string.lines.each { |line| $stdout.print prefix + line.gsub("\r", '') } if jobs > 1
55
+ raise e
117
56
  end
118
57
 
119
58
  session
120
59
  end
121
60
  end
122
61
 
62
+ def run(args)
63
+ config = Hash.new
64
+ config[:runner] = self.method(:tty)
65
+ config[:inventory] = DEFAULT_INVENTORY_FILE
66
+ config[:templates] = DEFAULT_TEMPLATE_DIR
67
+
68
+ parser = OptionParser.new
69
+ parser.banner = "#{File.basename($0)} HOST_PATTERN [Options]"
70
+ parser.version = Xlogin::VERSION
71
+
72
+ parser.on('-i PATH', '--inventory', String, 'The PATH to the inventory file (default: $HOME/.xloginrc).')
73
+ parser.on('-T PATH', '--template', String, 'The PATH to the template dir (default: $HOME/.xlogin.d).')
74
+ parser.on('-L [DIRECTORY]', '--log-dir', String, 'The PATH to the log dir (default: $PWD).') { |v| v || '.' }
75
+
76
+ parser.on('-t', '--tty', TrueClass, 'Allocate a pseudo-tty.') { |v| config[:runner] = self.method(:tty) }
77
+ parser.on('-l', '--list', TrueClass, 'List the inventory.') { |v| config[:runner] = self.method(:list) }
78
+ parser.on('-e COMMAND', '--exec', 'Execute commands and quit.') { |v| config[:runner] = self.method(:exec); v }
79
+
80
+ parser.on('-j NUM', '--jobs', Integer, 'The NUM of jobs to execute in parallel(default: 1).')
81
+ parser.on('-E', '--enable', TrueClass, 'Try to gain enable priviledge.')
82
+ parser.on('-y', '--assume-yes', TrueClass, 'Automatically answer yes to prompts.')
83
+
84
+ parser.parse!(args, into: config)
85
+ config[:pattern] = args
86
+
87
+ Xlogin.configure do
88
+ set assume_yes: true if config[:assume_yes]
89
+ source File.expand_path(config[:inventory], ENV['PWD'])
90
+ template File.expand_path(config[:templates], ENV['PWD'])
91
+ end
92
+ raise "No host found: `#{args.join(', ')}`" if Xlogin.list(*config[:pattern]).empty?
93
+
94
+ config[:runner].call(config)
95
+ rescue => e
96
+ $stderr.puts e, '', parser
97
+ exit 1
98
+ end
99
+
123
100
  end
124
101
  end
@@ -15,7 +15,7 @@ module Xlogin
15
15
 
16
16
  login = @methods.fetch(:login)
17
17
  delegate = @methods.fetch(:delegate)
18
- raise TemplateError.new("'login' and 'delegate' methods must be defined in the #{target_info[:type]} template.") unless login && delegate
18
+ raise Xlogin::Error.new("'login' and 'delegate' methods must be defined in the #{target_info[:type]} template.") unless login && delegate
19
19
 
20
20
  relay_uri = Addressable::URI.parse(uri.to_s)
21
21
  userinfo_cache = relay_uri.userinfo.dup
@@ -52,7 +52,7 @@ module Xlogin
52
52
 
53
53
  def build(type:, **opts)
54
54
  template = get_template(type)
55
- raise TemplateError.new("Template not found: '#{type}'") unless template
55
+ raise Xlogin::Error.new("Template not found: '#{type}'") unless template
56
56
 
57
57
  template.build(uri(opts), **opts)
58
58
  end
@@ -63,19 +63,17 @@ module Xlogin
63
63
 
64
64
  def build_from_hostname(args, **opts)
65
65
  hostinfo = get_inventory(args)
66
- raise SessionError.new("Host not found: '#{args}'") unless hostinfo
66
+ raise Xlogin::Error.new("Host not found: '#{args}'") unless hostinfo
67
67
 
68
68
  build(hostinfo.merge(name: args, **opts))
69
69
  end
70
70
 
71
- def method_missing(method_name, *args, &block)
72
- super unless args.size == 2 || args.size == 3 && URI.regexp =~ args[1]
71
+ def method_missing(method_name, *args, **opts, &block)
72
+ super unless args.size == 2 && URI.regexp =~ args[1]
73
73
 
74
74
  type = method_name.to_s.downcase
75
75
  name = args[0]
76
76
  uri = args[1]
77
- opts = args[2] || {}
78
-
79
77
  set_inventory(name, type: type, uri: uri, **opts)
80
78
  end
81
79
 
@@ -90,7 +88,7 @@ module Xlogin
90
88
 
91
89
  Addressable::URI.parse("#{scheme}://" + [userinfo, address].compact.join('@'))
92
90
  rescue
93
- raise SessionError.new("Invalid target - '#{opts}'")
91
+ raise Xlogin::Error.new("Invalid target - '#{opts}'")
94
92
  end
95
93
 
96
94
  end
@@ -1,3 +1,3 @@
1
1
  module Xlogin
2
- VERSION = "0.12.11"
2
+ VERSION = "0.13.1"
3
3
  end
@@ -29,6 +29,7 @@ Gem::Specification.new do |spec|
29
29
  spec.bindir = "bin"
30
30
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
31
31
  spec.require_paths = ["lib"]
32
+ spec.required_ruby_version = ">= 2.4.1"
32
33
 
33
34
  spec.add_dependency "net-telnet"
34
35
  spec.add_dependency "net-ssh"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xlogin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.11
4
+ version: 0.13.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - haccht
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-30 00:00:00.000000000 Z
11
+ date: 2019-09-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: net-telnet
@@ -187,7 +187,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
187
187
  requirements:
188
188
  - - ">="
189
189
  - !ruby/object:Gem::Version
190
- version: '0'
190
+ version: 2.4.1
191
191
  required_rubygems_version: !ruby/object:Gem::Requirement
192
192
  requirements:
193
193
  - - ">="