awssh 0.1.10 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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