awssh 0.1.10 → 0.2.0

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
  SHA1:
3
- metadata.gz: fb9932336f3891ec3d4942cae574d6f1c9bd11f7
4
- data.tar.gz: 064bb17a62d25ad544b1130d9295b8f7958fb505
3
+ metadata.gz: 717e709f2882548951c708971b9c520865b43a90
4
+ data.tar.gz: dc34e939651504f2d726f4c6286ec7ce8e745300
5
5
  SHA512:
6
- metadata.gz: feda51cf7c4f3aa96180dd03593fdda9cdd4f40b93fdf02cc513ae39436b7ce4447251f137c62dc368fe469caac586951dfac1022f004fdb2e370c8afaa6d3b9
7
- data.tar.gz: 15ed1d0660d675f394cf6e25f94b29e0650c455ee7fbc13d194c906fa99b9c4d1a283c7a214b735d4572078785641c4557651e96b5a53b4885c08ad737f850b2
6
+ metadata.gz: 61ea4118362d29f203c5840042202222fb39de880040b2237b4d3cd38708a7e9933c28c9874858892697b1adc7b99d7fae720b27e5074c48d1b20c95a1d8aeba
7
+ data.tar.gz: ccff97b8024852e0b3636443529bb9136a5b8b8701c709134dfe1fe527d4bd1ff9bc8a41b3f0446dea830d50cf92b8ffe290bf4cc9abb78387ba618f0bd67da3
data/awssh.gemspec CHANGED
@@ -18,8 +18,8 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_dependency 'fog', '~> 1.23.0'
22
- spec.add_dependency 'dotenv', '~> 0.11.1'
21
+ spec.add_dependency 'fog', '~> 1.30.0'
22
+ spec.add_dependency 'dotenv', '~> 2.0.1'
23
23
  spec.add_dependency 'activesupport'
24
24
 
25
25
  spec.add_development_dependency "bundler", "~> 1.6"
data/bin/awssh CHANGED
@@ -5,4 +5,4 @@ require 'optparse'
5
5
  require 'active_support/all'
6
6
  require 'awssh'
7
7
 
8
- Awssh::Command.new(ARGV).connect
8
+ Awssh::Command.new(ARGV).run
@@ -0,0 +1,72 @@
1
+ require 'ostruct'
2
+
3
+ module Awssh
4
+ class Cache
5
+ class << self
6
+ def load(file)
7
+ @instance = new(file)
8
+ end
9
+
10
+ def instance
11
+ raise 'cache not loaded?' unless @instance.data
12
+ @instance
13
+ end
14
+ end
15
+
16
+ attr_reader :data
17
+
18
+ def initialize(file, expires)
19
+ if file
20
+ @file = File.expand_path(file)
21
+ else
22
+ @disabled = true
23
+ @file = nil
24
+ end
25
+ @expires = expires
26
+ @data = load
27
+ end
28
+
29
+ def write(key, value)
30
+ time = Time.now.to_i
31
+ data = {time: time, value: value}
32
+ @data[key] = data
33
+ save
34
+ end
35
+
36
+ def read(key)
37
+ @data[key][:value]
38
+ end
39
+
40
+ def fetch(key, force)
41
+ if force || @disabled
42
+ diff = Time.now.to_i
43
+ else
44
+ time = @data[key] ? @data[key][:time] : 0
45
+ diff = Time.now.to_i - time
46
+ end
47
+ if diff > @expires
48
+ value = yield
49
+ write(key, value)
50
+ return value
51
+ else
52
+ read(key)
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def load
59
+ return {} if @disabled
60
+ unless File.exists?(@file)
61
+ @data = {}
62
+ save
63
+ end
64
+ YAML.load_file(@file)
65
+ end
66
+
67
+ def save
68
+ return if @disabled
69
+ File.open(@file, "w+") {|f| f.write(@data.to_yaml)}
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,34 @@
1
+ module Awssh
2
+ class Cloud
3
+ class << self
4
+ def connect(key, secret, region)
5
+ @instance = new(key, secret, region)
6
+ end
7
+ def instance
8
+ raise "not connected?" unless @instance
9
+ @instance
10
+ end
11
+ end
12
+
13
+ def initialize(key, secret, region)
14
+ @key = key
15
+ @secret = secret
16
+ @region = region
17
+ @fog = Fog::Compute.new(provider: 'AWS', aws_access_key_id: @key, aws_secret_access_key: @secret, region: @region)
18
+ end
19
+
20
+ def servers
21
+ puts "requesting servers..."
22
+ list = @fog.servers.all({'instance-state-name' => 'running'})
23
+ list.inject([]) do |a, e|
24
+ a << {
25
+ id: e.id,
26
+ name: e.tags['Name'],
27
+ tags: e.tags.inject({}) {|h, e| (k,v) = e; h[k.downcase] = v.downcase; h},
28
+ private: e.private_ip_address,
29
+ public: e.public_ip_address,
30
+ }
31
+ end
32
+ end
33
+ end
34
+ end
data/lib/awssh/command.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'pp'
1
2
  module Awssh
2
3
  class Command
3
4
  def initialize(argv)
@@ -11,17 +12,15 @@ module Awssh
11
12
  @config = {
12
13
  multi: 'csshX',
13
14
  single: 'ssh',
14
- region: 'us-east-1',
15
15
  user: nil,
16
- key: 'AWS ACCESS KEY ID',
17
- secret: 'AWS SECRET ACCESS KEY',
18
- domain: 'example.com',
16
+ use_names: false,
19
17
  cache: '~/.awssh.cache',
20
18
  expires: 1.day
21
19
  }.stringify_keys
22
20
 
23
21
  @config_file = File.expand_path(@options[:config])
24
- @config.merge!(YAML.load_file(@config_file)||{}) if File.exists?(@config_file)
22
+ Awssh::Config.load(@config_file)
23
+ @config = Awssh::Config.data
25
24
 
26
25
  OptionParser.new do |opts|
27
26
  opts.banner = "Usage: awssh [options] [search terms]"
@@ -35,14 +34,22 @@ module Awssh
35
34
  opts.separator ' name !~ /term/'
36
35
  opts.separator ''
37
36
  opts.separator 'Options:'
37
+ opts.on('-c', "--config", "override config file (default: ~/.awssh)") do |c|
38
+ @options[:config] = c
39
+ end
38
40
  opts.on('-V', '--version', 'print version') do |v|
39
41
  puts "awssh version: #{Awssh::Version::STRING}"
40
42
  exit 0
41
43
  end
42
44
  opts.on('-i', '--init', 'initialize config') do |i|
43
- path = File.expand_path("~/.awssh")
44
- File.open(path, "w+") { |f| f.write @config.to_yaml }
45
- puts "created config file: #{path}"
45
+ path = File.expand_path(@options[:config])
46
+ puts "creating config file: #{path}"
47
+ if File.exists?(path)
48
+ backup = "#{path}.#{Time.now.to_i}"
49
+ puts "moving previous config to #{backup}"
50
+ FileUtils.mv(path, backup)
51
+ end
52
+ File.open(path, "w+") { |f| f.write Awssh::Config::DEFAULT }
46
53
  exit 0
47
54
  end
48
55
  opts.separator ''
@@ -59,134 +66,104 @@ module Awssh
59
66
  opts.separator ''
60
67
  opts.on('-U', '--update', 'just update the cache') do |u|
61
68
  @options[:update] = true
62
- get_servers
63
- exit 0
64
69
  end
65
70
  opts.on('--no-cache', 'disable cache for this run') do |u|
66
- @config['cache'] = false
71
+ @config.cache = false
67
72
  end
68
73
  opts.separator ''
69
74
  opts.on('-m', '--[no-]multi', 'connect to multiple servers') do |m|
70
75
  @options[:multi] = m
71
76
  end
72
- opts.on('-c', "--config", "override config file (default: ~/.awssh)") do |c|
73
- @options[:config] = c
74
- end
75
77
  opts.on('-u', '--user USER', 'override user setting') do |u|
76
- @config['user'] = u
78
+ @config.user = u
77
79
  end
78
80
  end.parse!(argv)
79
81
 
82
+ @cloud = Awssh::Cloud.connect(@config.key, @config.secret, @config.region)
83
+ @cache = Awssh::Cache.new(@config.cache, @config.expires||1.day)
80
84
  @search = argv
81
85
 
86
+ if @options[:update]
87
+ cache(:servers, true) { @cloud.servers }
88
+ exit 0
89
+ end
90
+
82
91
  if @options[:verbose]
83
- p @search
84
- p @options
85
- p @config
92
+ puts "options: #{@options.inspect}"
93
+ puts "config: #{@config.inspect}"
86
94
  end
87
95
  end
88
96
 
89
- def connect
90
- @servers = find_servers
91
-
92
- if @options[:verbose] || @options[:list]
93
- print_list
94
- end
95
- return if @options[:list]
97
+ def run
98
+ @servers = cache(:servers) { @cloud.servers }
99
+ search = Awssh::Search.new(@servers, @search)
100
+ list = search.filter
101
+ hosts = hosts(list)
96
102
 
97
- if @servers.count > 1 && !@options[:multi]
98
- print_list
99
- puts "more than one server found, and multi is false"
100
- puts "set the -m flag to connect to more than one matched server"
103
+ if hosts.count == 0
104
+ puts "no servers found."
101
105
  exit 1
102
106
  end
103
107
 
104
- if @servers.count == 0
105
- puts "no servers found"
108
+ multi_not_multi = (hosts.count > 1 && !@options[:multi])
109
+
110
+ if @options[:list] || @options[:verbose] || multi_not_multi
111
+ puts_hosts(hosts)
112
+ end
113
+ puts "#{hosts.count} servers found" if @options[:verbose]
114
+ exit 0 if @options[:list]
115
+ if multi_not_multi
116
+ puts "more than one server found and multi is false"
117
+ puts "use the -m flag to connect to multiple servers"
106
118
  exit 1
107
119
  end
108
120
 
109
- @command = get_command(@servers)
110
-
111
- puts "running: #{@command}"
112
- exec @command unless @options[:test]
113
- end
114
-
115
- private
116
-
117
- def print_list
118
- puts "found: #{@servers.count}"
119
- @servers.each do |s|
120
- puts "- #{s}"
121
- end
121
+ connect(hosts)
122
122
  end
123
123
 
124
- def find_servers
125
- servers = []
126
- get_servers.each do |n|
127
- fail = false
128
- @search.each do |v|
129
- if v =~ /^\^/
130
- fail = true if n =~ /#{v.gsub(/^\^/, '')}/
131
- else
132
- fail = true unless n =~ /#{v}/
133
- end
134
- end
135
- next if fail
136
- servers << n
124
+ def connect(hosts)
125
+ cmd = command(hosts)
126
+ if @options[:test] || @options[:verbose]
127
+ puts cmd
137
128
  end
138
- servers
129
+ exec(cmd) unless @options[:test]
139
130
  end
140
131
 
141
- def get_servers
142
- list = get_cache do
143
- server_names
132
+ def command(hosts)
133
+ if @options[:multi]
134
+ command = "#{@config.multi} #{hosts.map { |e| host(e) }.join(' ')}"
135
+ else
136
+ command = "#{@config.single} #{host(servers.first)}"
144
137
  end
145
- puts "total servers: #{list.count}" if @options[:verbose]
146
- list.sort
138
+ command
147
139
  end
148
140
 
149
- def get_cache
150
- if @config['cache']
151
- file = File.expand_path(@config['cache'])
152
- if File.exists?(file)
153
- unless @options[:update]
154
- if Time.now - File.mtime(file) < @config['expires']
155
- return YAML.load_file(file)
156
- end
157
- end
158
- end
159
- puts "updating cache ..."
160
- list = yield
161
- File.open(file, "w+") { |f| f.write list.to_yaml }
162
- return list
163
- end
164
- list = yield
165
- return list
141
+ def cache(key, force=!@config.cache, &block)
142
+ @cache.fetch(key, force, &block)
166
143
  end
167
144
 
168
- def server_names
169
- puts "requesting servers ..."
170
- @fog = Fog::Compute.new(provider: 'AWS', aws_access_key_id: @config['key'], aws_secret_access_key: @config['secret'], region: @config["region"])
171
- list = @fog.servers.all({'instance-state-name' => 'running'})
172
- list.inject([]) { |a, e| a << e.tags['Name'] || e.id }
145
+ def hosts(list)
146
+ list.map do |l|
147
+ (id,_) = l.split('||')
148
+ @servers.to_a.detect {|e| e[:id] == id}
149
+ end.compact.sort_by {|e| e[:name]}
173
150
  end
174
151
 
175
- def get_command(servers)
176
- if @options[:multi]
177
- command = "#{@config["multi"]} #{servers.map { |e| server_url(e) }.join(' ')}"
152
+ def host(host)
153
+ out = []
154
+ out << "#{@config.user}@" if @config.user
155
+ if @config.use_names
156
+ out << [host[:name], @config.domain].compact.join('.')
178
157
  else
179
- command = "#{@config["single"]} #{server_url(servers.first)}"
158
+ out << host[:private]
180
159
  end
181
- command
160
+ out.join('')
182
161
  end
183
162
 
184
- def server_url(server)
185
- out = []
186
- out << "#{@config["user"]}@" if @config["user"]
187
- out << server
188
- out << ".#{@config["domain"]}" if @config["domain"]
189
- out.join('')
163
+ def puts_hosts(hosts)
164
+ hosts.each do |host|
165
+ puts "%10s %-15s %s" % [host[:id], host[:private], host[:name]]
166
+ end
190
167
  end
191
168
  end
192
169
  end
@@ -0,0 +1,42 @@
1
+ require 'ostruct'
2
+
3
+ module Awssh
4
+ class Config
5
+ class << self
6
+ def load(file)
7
+ @instance = new(file)
8
+ end
9
+
10
+ def data
11
+ raise 'config not loaded?' unless @instance.data
12
+ @instance.data
13
+ end
14
+ end
15
+
16
+ attr_reader :data
17
+
18
+ def initialize(file)
19
+ @file = file
20
+ raise "config file does not exist: #{file}" unless File.exist?(file)
21
+ @data = OpenStruct.new(YAML.load_file(file))
22
+ end
23
+
24
+ DEFAULT = <<-EOF
25
+ ---
26
+ region: us-east-1 # AWS Region
27
+ key: AWS_ACCESS_KEY_ID # AWS access key id
28
+ secret: AWS_SECRET_ACCESS_KEY # AWS secret access key
29
+ multi: csshX # command to use when connecting to multiple servers
30
+ single: ssh # command to use when connecting to single server
31
+ #user: username # set user for connection to all servers
32
+ # this can be overridden on the command line
33
+ domain: example.com # if 'use_names' is set, this will be appended
34
+ # to names, leave blank if name is fully-qualified
35
+ use_names: false # if true, rather than connecting to IP's,
36
+ # connection strings will be created using Name
37
+ # tag and domain
38
+ cache: ~/.awssh.cache # the cache file, set to false to disable caching
39
+ expires: 86400 # cache expiration time in seconds
40
+ EOF
41
+ end
42
+ end
@@ -0,0 +1,49 @@
1
+ module Awssh
2
+ class Search
3
+ def initialize(servers, terms)
4
+ @db = db(servers)
5
+ @terms = convert(terms)
6
+ end
7
+
8
+ def filter
9
+ list = @db
10
+ @terms.each do |key, value, opts|
11
+ regex = key == 'name' ? /\sname:[^\s]*#{value}[^\s]*/ : /\s#{key}:#{value}/
12
+ puts "regex: #{regex}"
13
+ if opts[:inverse]
14
+ found = list.grep(regex)
15
+ list = list - found
16
+ else
17
+ list = list.grep(regex)
18
+ end
19
+ end
20
+ list
21
+ end
22
+
23
+ def convert(terms)
24
+ terms.inject([]) do |a, e|
25
+ opts = {}
26
+ term = e.downcase
27
+ if term =~ /\:/
28
+ (key, value) = term.split(':')
29
+ else
30
+ key = 'name'
31
+ value = term
32
+ if term =~ /^\^/
33
+ value = term.gsub(/^\^/, '')
34
+ key = "^#{k}"
35
+ end
36
+ end
37
+ if key =~ /^\^/
38
+ opts[:inverse] = true
39
+ key.gsub!(/^\^/)
40
+ end
41
+ a << [key, value, opts]
42
+ end
43
+ end
44
+
45
+ def db(servers)
46
+ servers.inject([]) { |a, s| a << "#{s[:id]}|| #{s[:tags].inject([]) { |a, e| a << e.join(':') }.join(' ')}" }
47
+ end
48
+ end
49
+ end
data/lib/awssh/version.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  module Awssh
2
2
  module Version
3
3
  MAJOR = 0
4
- MINOR = 1
5
- TINY = 10
4
+ MINOR = 2
5
+ TINY = 0
6
6
  TAG = nil
7
7
  LIST = [MAJOR, MINOR, TINY, TAG].compact
8
8
  STRING = LIST.join('.')
data/lib/awssh.rb CHANGED
@@ -1,2 +1,10 @@
1
- require "awssh/version"
2
- require "awssh/command"
1
+ require 'awssh/version'
2
+ require 'awssh/config'
3
+ require 'awssh/cloud'
4
+ require 'awssh/cache'
5
+ require 'awssh/search'
6
+ require 'awssh/command'
7
+
8
+ module Awssh
9
+
10
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: awssh
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.10
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shawn Catanzarite
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-22 00:00:00.000000000 Z
11
+ date: 2015-05-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fog
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 1.23.0
19
+ version: 1.30.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 1.23.0
26
+ version: 1.30.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: dotenv
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.11.1
33
+ version: 2.0.1
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.11.1
40
+ version: 2.0.1
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: activesupport
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -96,7 +96,11 @@ files:
96
96
  - awssh.gemspec
97
97
  - bin/awssh
98
98
  - lib/awssh.rb
99
+ - lib/awssh/cache.rb
100
+ - lib/awssh/cloud.rb
99
101
  - lib/awssh/command.rb
102
+ - lib/awssh/config.rb
103
+ - lib/awssh/search.rb
100
104
  - lib/awssh/version.rb
101
105
  homepage: ''
102
106
  licenses:
@@ -118,7 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
118
122
  version: '0'
119
123
  requirements: []
120
124
  rubyforge_project:
121
- rubygems_version: 2.2.2
125
+ rubygems_version: 2.4.7
122
126
  signing_key:
123
127
  specification_version: 4
124
128
  summary: aws ssh wrapper