autossh 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 422069342456b2404cae7a17b2c5d295107406b2
4
+ data.tar.gz: 10a10355f20583451dcb018d657afb8ead2c7e9a
5
+ SHA512:
6
+ metadata.gz: fe533461dd980868e15f4fadd8ed0eaf91b3b2eb8f9ed95d64e083aa8042f6e2fd7f91e01e471e8c00e79bdec68d947c9843f17457de83147951cfa277433967
7
+ data.tar.gz: 0daa89f3800cb89908093fe642e303bd1e62c386307540beeb253c8e91b69cd02164a0e3ede23a0d1bcd6edf963f2aa5e6a876422d50378b0c9b8718cc355925
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ .idea/
2
+ .assh-*
3
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+ gem 'colorize'
3
+ gem 'aws-sdk'
data/Gemfile.lock ADDED
@@ -0,0 +1,20 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ aws-sdk (1.54.0)
5
+ aws-sdk-v1 (= 1.54.0)
6
+ aws-sdk-v1 (1.54.0)
7
+ json (~> 1.4)
8
+ nokogiri (>= 1.4.4)
9
+ colorize (0.7.3)
10
+ json (1.8.1)
11
+ mini_portile (0.6.0)
12
+ nokogiri (1.6.3.1)
13
+ mini_portile (= 0.6.0)
14
+
15
+ PLATFORMS
16
+ ruby
17
+
18
+ DEPENDENCIES
19
+ aws-sdk
20
+ colorize
data/README.md ADDED
@@ -0,0 +1,30 @@
1
+ Available on Rubygems at https://rubygems.org/gems/autossh
2
+
3
+ Github repository at https://github.com/paul-ridgway/autossh
4
+
5
+ To Install
6
+ ==========
7
+
8
+ 1. Clone the repository
9
+
10
+ 2. Ensure aash is available on the PATH:
11
+
12
+ ln -s /full/path/to/assh /usr/local/bin/assh
13
+
14
+
15
+ To do
16
+ =====
17
+
18
+ 1. Example configurations
19
+
20
+ 2. Safely handle no config (make dir, etc)
21
+
22
+ 3. Validate config files
23
+
24
+ 4. Gemify?
25
+
26
+ 5. Command line override config path (to test examples, and other uses)
27
+
28
+ 6. Decent logger/log levels, etc
29
+
30
+ 7. Tests
@@ -0,0 +1,6 @@
1
+ _foo()
2
+ {
3
+ local cur=${COMP_WORDS[COMP_CWORD]}
4
+ COMPREPLY=( $(compgen -W "alpha beta bar baz" -- $cur) )
5
+ }
6
+ complete -F _foo foo
data/autossh.gemspec ADDED
@@ -0,0 +1,15 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'autossh'
3
+ s.version = '0.0.3'
4
+ s.date = '2015-02-01'
5
+ s.summary = "AutoSSH"
6
+ s.description = "An SSH connection helper"
7
+ s.authors = ["Paul Ridgway"]
8
+ s.email = 'myself@paulridgway.co.uk'
9
+ s.homepage = 'https://github.com/paul-ridgway/autossh'
10
+
11
+ s.files = `git ls-files`.split("\n")
12
+ s.test_files = `git ls-files -- test/*`.split("\n")
13
+
14
+ s.executables << 'assh'
15
+ end
data/bin/assh ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require 'autossh'
3
+
4
+ Assh::Command.new(ARGV).execute!
@@ -0,0 +1,28 @@
1
+ ---
2
+
3
+ Includes: [example_include.yml, example_aws.yml, random.yml]
4
+
5
+ Port: 22
6
+ IdentityFile: ~/.ssh/id_rsa
7
+
8
+ Groups:
9
+
10
+ - Group: Home
11
+ Port: 22
12
+ User: other
13
+ Hosts:
14
+ - Host: nas
15
+ HostName: 192.168.1.1
16
+
17
+ - Group: Personal
18
+ Port: 22
19
+ User: me
20
+ Hosts:
21
+ - Host: a1-vm
22
+ HostName: 1a-vm.example.com
23
+ - Host: 2b-vm
24
+ HostName: 2b-vm.example.com
25
+ - Host: 3c-vm
26
+ HostName: 3c-vm.example.com
27
+ - Host: 4d
28
+ HostName: 4d.example.com
@@ -0,0 +1,8 @@
1
+ ---
2
+
3
+ Provider: aws
4
+
5
+ NameMatch: (.*?\..*?)\.
6
+
7
+ AccessKeyId: someKey
8
+ SecretAccessKey: someSecret
@@ -0,0 +1,43 @@
1
+ ---
2
+
3
+ Groups:
4
+ - Group: Blade Custer
5
+ Port: 2212
6
+ User: bladeuser
7
+ IdentityFile: ~/.ssh/blade_key
8
+ StrictHostKeyChecking: no
9
+ UserKnownHostsFile: /dev/null
10
+ LogLevel: quiet
11
+ Hosts:
12
+ - Host: node-01
13
+ HostName: 10.10.11.101
14
+ - Host: node-02
15
+ HostName: 10.10.11.102
16
+ - Host: node-03
17
+ HostName: 10.10.11.103
18
+ - Host: node-04
19
+ HostName: 10.10.11.104
20
+ - Host: node-05
21
+ HostName: 10.10.11.105
22
+ - Host: node-06
23
+ HostName: 10.10.11.106
24
+ - Host: node-07
25
+ HostName: 10.10.11.107
26
+ - Host: node-08
27
+ HostName: 10.10.11.108
28
+ - Host: node-09
29
+ HostName: 10.10.11.109
30
+ - Host: node-10
31
+ HostName: 10.10.11.110
32
+ - Host: node-11
33
+ HostName: 10.10.11.111
34
+ - Host: node-12
35
+ HostName: 10.10.11.112
36
+ - Host: node-13
37
+ HostName: 10.10.11.113
38
+ - Host: node-14
39
+ HostName: 10.10.11.114
40
+ - Host: node-15
41
+ HostName: 10.10.11.115
42
+ - Host: node-16
43
+ HostName: 10.10.11.116
@@ -0,0 +1,11 @@
1
+ ---
2
+
3
+ Provider: Random
4
+
5
+ Groups:
6
+ - Group: Test
7
+ User: paul
8
+ IdentityFile: ~/Dropbox/.ssh/floow/id_rsa
9
+ Hosts:
10
+ - Host: test
11
+ HostName: 10.10.10.9
data/lib/autossh.rb ADDED
@@ -0,0 +1,24 @@
1
+ require 'rubygems'
2
+ require 'colorize'
3
+
4
+ require_relative 'helpers'
5
+ require_relative 'configuration'
6
+ require_relative 'provider'
7
+ require_relative 'providers/file_provider'
8
+ require_relative 'providers/random_provider'
9
+ require_relative 'providers/aws_provider'
10
+ require_relative 'command'
11
+ require_relative 'generator'
12
+ require_relative 'host'
13
+
14
+ module Assh
15
+
16
+ ROOT = File.expand_path('~/.autossh')
17
+
18
+ GENERATED_CONFIG = "#{ROOT}/.assh-generated-config"
19
+ CONFIG_CACHE = "#{ROOT}/.assh-config-cache"
20
+ CONFIG_CACHE_AT = "#{ROOT}/.assh-config-cached-at"
21
+
22
+ VERSION = '0.0.3'
23
+
24
+ end
data/lib/command.rb ADDED
@@ -0,0 +1,77 @@
1
+ module Assh
2
+
3
+ class Command
4
+
5
+ def initialize(argv)
6
+ @args = argv
7
+ parse_arguments!
8
+
9
+ @configuration = Configuration.new
10
+
11
+ @generator = Generator.new
12
+
13
+ Provider.verbose! if @cmd_generate
14
+
15
+ if @configuration.needs_generating? || @cmd_generate
16
+ puts "Refreshing..."
17
+ Provider.load_configuration!(@configuration, 'config.yml')
18
+ @configuration.save_cache!
19
+ else
20
+ @configuration.load_cache!
21
+ end
22
+
23
+ end
24
+
25
+ def execute!
26
+ if @cmd_ls
27
+ puts "Host List (cached at #{Time.at(@configuration.generated_at)})".green
28
+ max_name = 0
29
+ @configuration.groups.each do |name, hosts|
30
+ hosts.each do |name, host|
31
+ max_name = name.size if name.size > max_name
32
+ end
33
+ end
34
+ @configuration.groups.each do |name, hosts|
35
+ puts " #{name.yellow}"
36
+ hosts = Hash[hosts.sort]
37
+ hosts.each do |name, host|
38
+ padded_name = name + (" " * (max_name - name.size))
39
+ puts " #{padded_name.light_white} #{"-".green} #{host.address.cyan}"
40
+ end
41
+ end
42
+ elsif @cmd_generate
43
+ puts "Generating config in #{@generator.output_file}"
44
+ generate!
45
+ puts "Done"
46
+ else
47
+ generate!
48
+ execute_default!
49
+ end
50
+ end
51
+
52
+ private
53
+ def parse_arguments!
54
+ return unless @args.size > 0
55
+ first_arg = @args[0].downcase
56
+ if first_arg == 'ls'
57
+ @cmd_ls = true
58
+ elsif first_arg == 'generate'
59
+ @cmd_generate = true
60
+ end
61
+ end
62
+
63
+ def generate!
64
+ #Generates up-to-date config
65
+ @generator.run!(@configuration.hosts)
66
+ end
67
+
68
+ def execute_default!
69
+ # Generates safe args and replaces assh process with ssh call
70
+ args = @args.map { |x| x.include?(' ') ? "\"#{x}\"" : x }.join(' ')
71
+ command = "ssh -F #{Assh::GENERATED_CONFIG} #{args}"
72
+ exec(command)
73
+ end
74
+
75
+ end
76
+
77
+ end
@@ -0,0 +1,55 @@
1
+ module Assh
2
+
3
+ class Configuration
4
+
5
+ attr_accessor :hosts, :groups
6
+
7
+ def initialize
8
+ @hosts = {}
9
+ @groups = {}
10
+ end
11
+
12
+ def add_host(group_name, host_name, host)
13
+ puts "WARNING: Duplicate host named: #{name}. Only latest host is included" if @hosts[host_name]
14
+
15
+ @groups[group_name] = {} unless @groups.has_key?(group_name)
16
+ @groups[group_name][host_name] = host
17
+ @hosts[host_name] = host
18
+ end
19
+
20
+ #TODO: Move generated-at to here and call config-cached-at
21
+
22
+ def save_cache!(config_file = File.expand_path(Assh::CONFIG_CACHE),
23
+ timestamp_file = File.expand_path(Assh::CONFIG_CACHE_AT))
24
+ cache = {
25
+ hosts: @hosts,
26
+ groups: @groups
27
+ }
28
+ File.open(config_file, 'w') { |f| f << cache.to_yaml }
29
+ File.open(timestamp_file, 'w') { |f| f << current_time }
30
+
31
+ end
32
+
33
+ def load_cache!(config_file = File.expand_path(Assh::CONFIG_CACHE))
34
+ cache = YAML::load_file(config_file)
35
+ @hosts = cache[:hosts]
36
+ @groups = cache[:groups]
37
+ end
38
+
39
+ def needs_generating?(timestamp_file = File.expand_path(Assh::CONFIG_CACHE_AT))
40
+ (current_time - generated_at(timestamp_file)) > 72000
41
+ end
42
+
43
+ def generated_at(timestamp_file = File.expand_path(Assh::CONFIG_CACHE_AT))
44
+ return File.read(timestamp_file).to_i if File.exists?(timestamp_file)
45
+ 0
46
+ end
47
+
48
+ private
49
+ def current_time
50
+ Time.now.to_i
51
+ end
52
+
53
+ end
54
+
55
+ end
data/lib/generator.rb ADDED
@@ -0,0 +1,20 @@
1
+ module Assh
2
+ class Generator
3
+
4
+ attr_accessor :output_file
5
+
6
+ def initialize(output_file = File.expand_path(Assh::GENERATED_CONFIG))
7
+ @output_file = output_file
8
+ end
9
+
10
+ def run!(hosts)
11
+ File.open(@output_file, 'w') do |f|
12
+ hosts.each do |_, host|
13
+ f << host.to_ssh_config
14
+ end
15
+ end
16
+ end
17
+
18
+ end
19
+
20
+ end
data/lib/helpers.rb ADDED
@@ -0,0 +1,11 @@
1
+ class String
2
+ def underscore
3
+ self.gsub(/::/, '/').
4
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
5
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
6
+ tr(" ", "-").
7
+ tr("(", "").
8
+ tr(")", "").
9
+ downcase
10
+ end
11
+ end
data/lib/host.rb ADDED
@@ -0,0 +1,46 @@
1
+ require_relative 'providers/file_provider'
2
+
3
+ module Assh
4
+
5
+ class Host
6
+
7
+ PORT_KEY = "Port"
8
+ HOST_KEY = Assh::FileProvider::HOST_KEY
9
+ HOSTNAME_KEY = "HostName"
10
+ HOSTS_KEY = Assh::FileProvider::HOSTS_KEY
11
+ GROUP_KEY = Assh::FileProvider::GROUP_KEY
12
+ GROUPS_KEY = Assh::FileProvider::GROUPS_KEY
13
+
14
+
15
+ attr_accessor :name, :hostname, :port
16
+
17
+ def initialize(params)
18
+ raise 'Unexpected params type' unless params.is_a? Hash
19
+ @name = params.delete(HOST_KEY)
20
+ @hostname = params.delete(HOSTNAME_KEY)
21
+ @port = params.delete(PORT_KEY) || 22
22
+ @params = params
23
+ end
24
+
25
+ def address
26
+ "#{hostname}:#{port}"
27
+ end
28
+
29
+ def to_s
30
+ "#{name} - #{hostname}:#{port}"
31
+ end
32
+
33
+ def to_ssh_config
34
+ f = ""
35
+ f << "Host #{name}\n"
36
+ f << " Hostname #{hostname}\n"
37
+ f << " Port #{port}\n"
38
+ @params.each do |k,v|
39
+ f << " #{k} #{v}\n"
40
+ end
41
+ f
42
+ end
43
+
44
+ end
45
+
46
+ end
data/lib/provider.rb ADDED
@@ -0,0 +1,61 @@
1
+ module Assh
2
+
3
+ class Provider
4
+
5
+ @@verbose = false
6
+
7
+ @@providers ||= {}
8
+
9
+ def initialize(configuration)
10
+ @configuration = configuration
11
+ end
12
+
13
+ def self.load_configuration!(configuration, file)
14
+ status "Loading: #{file}"
15
+
16
+ config = YAML::load_file(File.expand_path(file, Assh::ROOT))
17
+
18
+ includes = config.delete(Assh::FileProvider::INCLUDES_KEY) || []
19
+
20
+ provider_name = (config.delete('Provider') || 'file').downcase
21
+ provider_class = @@providers[provider_name]
22
+ raise "Provider not found: #{provider_name}" unless provider_class
23
+
24
+ provider = provider_class.new(configuration)
25
+ provider.parse_config!(config)
26
+
27
+ includes.each do |inc|
28
+ Provider.load_configuration!(configuration, File.expand_path(inc, ROOT))
29
+ end
30
+
31
+ provider
32
+ end
33
+
34
+ def parse_config!(config)
35
+ raise 'parse_config! must be overridden'
36
+ end
37
+
38
+ def add_host(group_name, host_name, host)
39
+ @configuration.add_host(group_name, host_name, host)
40
+ end
41
+
42
+ def self.status(message = "", inline = false)
43
+ return unless @@verbose
44
+ if inline
45
+ print message
46
+ else
47
+ puts message
48
+ end
49
+ end
50
+
51
+ def self.verbose!
52
+ @@verbose = true
53
+ end
54
+
55
+ def self.register_provider!(name, clazz)
56
+ @@providers[name] = clazz
57
+ end
58
+
59
+ end
60
+
61
+ end
@@ -0,0 +1,67 @@
1
+ require 'aws-sdk'
2
+
3
+ module Assh
4
+
5
+ class AwsProvider < Assh::Provider
6
+
7
+ def initialize(configuration)
8
+ super(configuration)
9
+ end
10
+
11
+ def parse_config!(config)
12
+ return unless config
13
+ raise 'Unexpected config type' unless config.is_a? Hash
14
+
15
+ name_match = config.delete 'NameMatch'
16
+ access_key_id = config.delete 'AccessKeyId'
17
+ secret_access_key = config.delete 'SecretAccessKey'
18
+
19
+ AWS.config(
20
+ access_key_id: access_key_id,
21
+ secret_access_key: secret_access_key)
22
+
23
+
24
+ Provider::status "Downloading regions list..."
25
+
26
+ regions = AWS.ec2.regions.map { |r| r.name }
27
+
28
+ Provider::status "Downloading instances..."
29
+
30
+ regions.each do |region|
31
+
32
+ ec2 = AWS::EC2.new(
33
+ access_key_id: access_key_id,
34
+ secret_access_key: secret_access_key,
35
+ region: region)
36
+
37
+ hosts = {}
38
+ ec2.instances.each do |i|
39
+ Provider::status '.', true
40
+ name = (i.tags.Name || i.instance_id).underscore
41
+ ip = i.public_ip_address
42
+ hosts[name] = ip
43
+ end
44
+
45
+ hosts.each do |name, ip|
46
+
47
+ if name_match
48
+ m = Regexp.new(name_match).match(name)
49
+ if m
50
+ name = m.captures[0]
51
+ end
52
+ end
53
+
54
+ add_host(region, name, Host.new(config.merge({'Host' => name, 'HostName' => ip})))
55
+ end
56
+
57
+ end
58
+
59
+ Provider::status
60
+
61
+ end
62
+
63
+ end
64
+
65
+ end
66
+
67
+ Assh::Provider.register_provider!("aws", Assh::AwsProvider)
@@ -0,0 +1,54 @@
1
+ require_relative '../provider'
2
+ require 'yaml'
3
+
4
+ module Assh
5
+
6
+ class FileProvider < Provider
7
+
8
+ ROOT = File.expand_path('~/.autossh')
9
+
10
+ INCLUDES_KEY = "Includes"
11
+ HOST_KEY = "Host"
12
+ HOSTS_KEY = "Hosts"
13
+ GROUP_KEY = "Group"
14
+ GROUPS_KEY = "Groups"
15
+
16
+ def initialize(configuration)
17
+ super(configuration)
18
+ end
19
+
20
+ def parse_config!(config)
21
+ return unless config
22
+ raise 'Unexpected config type' unless config.is_a? Hash
23
+
24
+ groups = config.delete(GROUPS_KEY)
25
+
26
+ if hosts = config.delete(HOSTS_KEY)
27
+ groups << {GROUP_KEY => 'ungrouped', HOSTS_KEY => hosts}
28
+ end
29
+
30
+ groups.each { |g| parse_group!(config, g) }
31
+ end
32
+
33
+ private
34
+ def parse_group!(parent_config, group)
35
+ return unless group
36
+ parent_config ||= {}
37
+ name = group.delete(GROUP_KEY)
38
+ hosts = group.delete(HOSTS_KEY)
39
+ hosts.each { |h| parse_host!(name, parent_config.merge(group), h) }
40
+ end
41
+
42
+ def parse_host!(group_name, parent_config, host)
43
+ return unless host
44
+ parent_config ||= {}
45
+ name = host[HOST_KEY]
46
+
47
+ add_host(group_name, name, Host.new(parent_config.merge(host)))
48
+ end
49
+
50
+ end
51
+
52
+ end
53
+
54
+ Assh::Provider.register_provider!("file", Assh::FileProvider)
@@ -0,0 +1,23 @@
1
+ require_relative '../provider'
2
+ require 'yaml'
3
+
4
+ module Assh
5
+
6
+ class RandomProvider < Provider
7
+
8
+ def initialize(configuration)
9
+ super(configuration)
10
+ end
11
+
12
+ def parse_config!(config)
13
+ return unless config
14
+ raise 'Unexpected config type' unless config.is_a? Hash
15
+
16
+ add_host('Random', 'rnd', Host.new({'Host' => 'rnd', 'HostName' => '123.1.2.3'}))
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+
23
+ Assh::Provider.register_provider!("random", Assh::RandomProvider)
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: autossh
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Paul Ridgway
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-01 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: An SSH connection helper
14
+ email: myself@paulridgway.co.uk
15
+ executables:
16
+ - assh
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".gitignore"
21
+ - Gemfile
22
+ - Gemfile.lock
23
+ - README.md
24
+ - assh_completion.sh
25
+ - autossh.gemspec
26
+ - bin/assh
27
+ - examples/config.yml
28
+ - examples/example_aws.yml
29
+ - examples/example_include.yml
30
+ - examples/random.yml
31
+ - lib/autossh.rb
32
+ - lib/command.rb
33
+ - lib/configuration.rb
34
+ - lib/generator.rb
35
+ - lib/helpers.rb
36
+ - lib/host.rb
37
+ - lib/provider.rb
38
+ - lib/providers/aws_provider.rb
39
+ - lib/providers/file_provider.rb
40
+ - lib/providers/random_provider.rb
41
+ homepage: https://github.com/paul-ridgway/autossh
42
+ licenses: []
43
+ metadata: {}
44
+ post_install_message:
45
+ rdoc_options: []
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements: []
59
+ rubyforge_project:
60
+ rubygems_version: 2.4.5
61
+ signing_key:
62
+ specification_version: 4
63
+ summary: AutoSSH
64
+ test_files: []