ec2ssh 2.0.8 → 3.0.0.beta1

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.
@@ -0,0 +1,25 @@
1
+ require 'ec2ssh/dsl'
2
+
3
+ module Ec2ssh
4
+ module Command
5
+ class Base
6
+ attr_reader :cli
7
+
8
+ def initialize(cli)
9
+ @cli = cli
10
+ end
11
+
12
+ def dotfile
13
+ @dotfile ||= Ec2ssh::Dsl::Parser.parse_file(dotfile_path)
14
+ end
15
+
16
+ def ssh_config_path
17
+ cli.options.path || dotfile.path || "#{$ENV['HOME']}/.ssh/config"
18
+ end
19
+
20
+ def dotfile_path
21
+ cli.options.dotfile
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,72 @@
1
+ require 'ec2ssh/command'
2
+ require 'ec2ssh/ssh_config'
3
+ require 'ec2ssh/exceptions'
4
+
5
+ module Ec2ssh
6
+ module Command
7
+ class Init < Base
8
+ def initialize(cli)
9
+ super
10
+ end
11
+
12
+ def run
13
+ begin
14
+ init_ssh_config
15
+ rescue DotfileNotFound
16
+ init_dotfile
17
+ retry
18
+ end
19
+ end
20
+
21
+ def init_dotfile
22
+ if File.exist?(dotfile_path)
23
+ cli.yellow "Warning: #{dotfile_path} already exists."
24
+ return
25
+ end
26
+
27
+ write_dotfile_example
28
+
29
+ cli.green "Generated #{dotfile_path}"
30
+ cli.yellow "Please check and edit #{dotfile_path} before run `ec2ssh update`"
31
+ end
32
+
33
+ def write_dotfile_example
34
+ example = <<-DOTFILE
35
+ path '#{ENV['HOME']}/.ssh/config'
36
+ aws_keys(
37
+ default: {
38
+ access_key_id: ENV['AWS_ACCESS_KEY_ID'],
39
+ secret_access_key: ENV['AWS_SECRET_ACCESS_KEY']
40
+ },
41
+ # my_key1: { access_key_id: '...', secret_access_key: '...' }, ...
42
+ )
43
+ regions ENV['AWS_REGION'] || ENV['AMAZON_REGION'] || ENV['AWS_DEFAULT_REGION'] || 'us-east-1'
44
+ # Enable regions as you like
45
+ # regions *%w(ap-northeast-1 ap-southeast-1 ap-southeast-2 eu-west-1 sa-east-1 us-east-1 us-west-1 us-west-2)
46
+
47
+ # You can use methods of AWS::EC2::Instance.
48
+ # See http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/EC2/Instance.html
49
+ host_line <<END
50
+ Host <%= tags['Name'] %>.<%= availability_zone %>
51
+ HostName <%= dns_name || private_ip_address %>
52
+ END
53
+ DOTFILE
54
+
55
+ File.open(dotfile_path, 'w') {|f| f.write example }
56
+ end
57
+
58
+ def init_ssh_config
59
+ if ssh_config.mark_exist?
60
+ raise MarkAlreadyExists
61
+ else
62
+ ssh_config.append_mark!
63
+ cli.green "Added mark to #{ssh_config_path}"
64
+ end
65
+ end
66
+
67
+ def ssh_config
68
+ @ssh_config ||= SshConfig.new(ssh_config_path)
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,34 @@
1
+ require 'ec2ssh/exceptions'
2
+ require 'ec2ssh/command'
3
+ require 'ec2ssh/migrator'
4
+
5
+ module Ec2ssh
6
+ module Command
7
+ class Migrate < Base
8
+ def initialize(cli)
9
+ super
10
+ end
11
+
12
+ def run
13
+ migrator = Migrator.new dotfile_path
14
+ version = migrator.check_version
15
+ case version
16
+ when '2'
17
+ cli.red "Current dotfile version is #{version} (#{dotfile_path})"
18
+ new_dotfile_str = migrator.migrate_from_2
19
+ cli.red "Ec2ssh is migrating your dotfile to version 3 style as follows:"
20
+ cli.yellow new_dotfile_str
21
+ if cli.yes? "Are you sure to migrate #{dotfile_path} to version 3 style? (y/n)"
22
+ backup_path = migrator.backup!
23
+ puts "Creating backup as #{backup_path}"
24
+ migrator.replace! new_dotfile_str
25
+ cli.green 'Migrated successfully.'
26
+ end
27
+ when '3'
28
+ cli.green "Current dotfile version is #{version} (#{dotfile_path})"
29
+ cli.green 'Your dotfile is up-to-date.'
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,20 @@
1
+ require 'ec2ssh/exceptions'
2
+ require 'ec2ssh/command'
3
+ require 'ec2ssh/ssh_config'
4
+
5
+ module Ec2ssh
6
+ module Command
7
+ class Remove < Base
8
+ def initialize(cli)
9
+ super
10
+ end
11
+
12
+ def run
13
+ ssh_config = SshConfig.new(ssh_config_path)
14
+ raise MarkNotFound unless ssh_config.mark_exist?
15
+
16
+ ssh_config.replace! ""
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,39 @@
1
+ require 'ec2ssh/exceptions'
2
+ require 'ec2ssh/command'
3
+ require 'ec2ssh/ssh_config'
4
+ require 'ec2ssh/builder'
5
+ require 'ec2ssh/dsl'
6
+ require 'ec2ssh/migrator'
7
+
8
+ module Ec2ssh
9
+ module Command
10
+ class Update < Base
11
+ def initialize(cli)
12
+ super
13
+ end
14
+
15
+ def run
16
+ ssh_config = SshConfig.new(ssh_config_path)
17
+ raise MarkNotFound unless ssh_config.mark_exist?
18
+
19
+ ssh_config.parse!
20
+ lines = builder.build_host_lines
21
+ ssh_config_str = ssh_config.wrap lines
22
+ ssh_config.replace! ssh_config_str
23
+ cli.yellow ssh_config_str
24
+ end
25
+
26
+ def builder
27
+ @builder ||= Builder.new dsl
28
+ end
29
+
30
+ def dsl
31
+ @dsl ||= Ec2ssh::Dsl::Parser.parse File.read(dotfile_path)
32
+ end
33
+
34
+ def migrator
35
+ @migrator ||= Migrator.new dotfile_path
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,55 @@
1
+ require 'ec2ssh/exceptions'
2
+
3
+ module Ec2ssh
4
+ class Dsl
5
+ attr_reader :_result
6
+
7
+ def initialize
8
+ @_result = Container.new
9
+ end
10
+
11
+ def aws_keys(keys)
12
+ @_result.aws_keys = keys
13
+ end
14
+
15
+ def regions(*regions)
16
+ @_result.regions = regions
17
+ end
18
+
19
+ def host_line(erb)
20
+ @_result.host_line = erb
21
+ end
22
+
23
+ def reject(&block)
24
+ @_result.reject = block
25
+ end
26
+
27
+ def path(str)
28
+ @_result.path = str
29
+ end
30
+
31
+ class Container < Struct.new(*%i[
32
+ aws_keys
33
+ regions
34
+ host_line
35
+ reject
36
+ path
37
+ ])
38
+ end
39
+
40
+ module Parser
41
+ def self.parse(dsl_str)
42
+ dsl = Dsl.new
43
+ dsl.instance_eval dsl_str
44
+ dsl._result
45
+ rescue SyntaxError => e
46
+ raise DotfileSyntaxError, e.to_s
47
+ end
48
+
49
+ def self.parse_file(path)
50
+ raise DotfileNotFound, path.to_s unless File.exist?(path)
51
+ parse File.read(path)
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,38 @@
1
+ require 'aws-sdk'
2
+
3
+ module Ec2ssh
4
+ class Ec2Instances
5
+ attr_reader :ec2s, :aws_keys
6
+
7
+ def initialize(aws_keys, regions)
8
+ @aws_keys = aws_keys
9
+ @regions = regions
10
+ end
11
+
12
+ def make_ec2s
13
+ AWS.start_memoizing
14
+ _ec2s = {}
15
+ aws_keys.each do |name, key|
16
+ _ec2s[name] = {}
17
+ @regions.each do |region|
18
+ options = key.merge ec2_region: region
19
+ _ec2s[name][region] = AWS::EC2.new options
20
+ end
21
+ end
22
+ _ec2s
23
+ end
24
+
25
+ def ec2s
26
+ @ec2s ||= make_ec2s
27
+ end
28
+
29
+ def instances(key_name)
30
+ @regions.map {|region|
31
+ ec2s[key_name][region].instances.
32
+ filter('instance-state-name', 'running').
33
+ to_a.
34
+ sort_by {|ins| ins.tags['Name'] }
35
+ }.flatten
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,9 @@
1
+ module Ec2ssh
2
+ class DotfileNotFound < StandardError; end
3
+ class DotfileSyntaxError < StandardError; end
4
+ class ObsoleteDotfile < StandardError; end
5
+ class InvalidDotfile < StandardError; end
6
+ class MarkNotFound < StandardError; end
7
+ class MarkAlreadyExists < StandardError; end
8
+ class AwsKeyNotFound < StandardError; end
9
+ end
@@ -0,0 +1,74 @@
1
+ require 'yaml'
2
+ require 'stringio'
3
+ require 'ec2ssh/dsl'
4
+
5
+ module Ec2ssh
6
+ class Migrator
7
+ def initialize(dotfile_path)
8
+ @dotfile_path = dotfile_path
9
+ end
10
+
11
+ def dotfile_str
12
+ @dotfile_str ||= File.read(@dotfile_path)
13
+ end
14
+
15
+ def check_version
16
+ begin
17
+ hash = YAML.load dotfile_str
18
+ return '2' if hash.is_a?(Hash) && hash.keys.include?('aws_keys')
19
+ rescue Psych::SyntaxError
20
+ end
21
+
22
+ begin
23
+ Dsl::Parser.parse dotfile_str
24
+ return '3'
25
+ rescue DotfileSyntaxError
26
+ end
27
+
28
+ raise InvalidDotfile
29
+ end
30
+
31
+ def migrate_from_2
32
+ hash = YAML.load dotfile_str
33
+ out = StringIO.new
34
+
35
+ out.puts "path '#{hash['path']}'" if hash['path']
36
+
37
+ out.puts 'aws_keys('
38
+ out.puts hash['aws_keys'].map {|name, key|
39
+ " #{name}: { access_key_id: '#{key['access_key_id']}', secret_access_key: '#{key['secret_access_key']}' }"
40
+ }.join(",\n")
41
+ out.puts ')'
42
+
43
+ if hash['regions']
44
+ regions = hash['regions'].map{|r| "'#{r}'" }.join(', ')
45
+ out.puts "regions #{regions}"
46
+ end
47
+
48
+ out.puts <<-END
49
+
50
+ # You can use methods of AWS::EC2::Instance.
51
+ # See http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/EC2/Instance.html
52
+ host_line <<EOS
53
+ Host <%= tags['Name'] %>.<%= availability_zone %>
54
+ HostName <%= dns_name || private_ip_address %>
55
+ EOS
56
+ END
57
+
58
+ out.puts
59
+ out.puts dotfile_str.gsub(/^/m, '# ')
60
+
61
+ out.string
62
+ end
63
+
64
+ def replace!(new_dotfile_str)
65
+ File.open(@dotfile_path, 'w') {|f| f.write new_dotfile_str }
66
+ end
67
+
68
+ def backup!
69
+ backup_path = "#{@dotfile_path}.#{Time.now.strftime("%Y%m%d%H%M%S")}"
70
+ File.open(backup_path, 'w') {|f| f.write dotfile_str }
71
+ backup_path
72
+ end
73
+ end
74
+ end
@@ -8,9 +8,8 @@ module Ec2ssh
8
8
 
9
9
  attr_reader :path, :sections
10
10
 
11
- def initialize(path=nil, aws_key='default')
12
- @path = Pathname(path || "#{ENV['HOME']}/.ssh/config")
13
- @aws_key = aws_key
11
+ def initialize(path=nil)
12
+ @path = path || "#{ENV['HOME']}/.ssh/config"
14
13
  @sections = {}
15
14
  end
16
15
 
@@ -35,7 +34,7 @@ module Ec2ssh
35
34
 
36
35
  def append_mark!
37
36
  replace! ""
38
- open(@path, "a") do |f|
37
+ File.open(@path, "a") do |f|
39
38
  f.puts wrap("")
40
39
  end
41
40
  end
@@ -49,13 +48,13 @@ module Ec2ssh
49
48
  end
50
49
 
51
50
  def config_src
52
- @config_src ||= open(@path, "r") do |f|
51
+ @config_src ||= File.open(@path, "r") do |f|
53
52
  f.read
54
53
  end
55
54
  end
56
55
 
57
56
  def save!(str)
58
- open(@path, "w") do |f|
57
+ File.open(@path, "w") do |f|
59
58
  f.puts str
60
59
  end
61
60
  end
@@ -1,3 +1,3 @@
1
1
  module Ec2ssh
2
- VERSION = '2.0.8'
2
+ VERSION = '3.0.0.beta1'
3
3
  end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+ require 'ec2ssh/dsl'
3
+ require 'ec2ssh/builder'
4
+
5
+ describe Ec2ssh::Builder do
6
+ describe '#build_host_lines' do
7
+ let(:container) do
8
+ Ec2ssh::Dsl::Container.new.tap do |c|
9
+ c.aws_keys = {
10
+ key1: { access_key_id: 'KEY1', secret_access_key: 'SEC1' },
11
+ key2: { access_key_id: 'KEY2', secret_access_key: 'SEC2' }
12
+ }
13
+ c.host_line = "Host <%= tags['Name'] %>"
14
+ end
15
+ end
16
+
17
+ let(:builder) do
18
+ Ec2ssh::Builder.new(container).tap do |bldr|
19
+ allow(bldr).to receive(:ec2s) { ec2s }
20
+ end
21
+ end
22
+
23
+ let(:ec2s) do
24
+ double('ec2s', aws_keys: container.aws_keys).tap do |dbl|
25
+ allow(dbl).to receive(:instances) {|name| instances[name] }
26
+ end
27
+ end
28
+
29
+ let(:instances) do
30
+ {
31
+ key1: [
32
+ double('instance', tags: {'Name' => 'srv1'}),
33
+ double('instance', tags: {'Name' => 'srv2'})],
34
+ key2: [
35
+ double('instance', tags: {'Name' => 'srv3'}),
36
+ double('instance', tags: {'Name' => 'srv4'})]
37
+ }
38
+ end
39
+
40
+ it do
41
+ expect(builder.build_host_lines).to eq <<-END
42
+ # section: key1
43
+ Host srv1
44
+ Host srv2
45
+ # section: key2
46
+ Host srv3
47
+ Host srv4
48
+ END
49
+ end
50
+
51
+ context 'with #reject' do
52
+ before do
53
+ container.reject = lambda {|ins| ins.tags['Name'] == 'srv1' }
54
+ end
55
+
56
+ it do
57
+ expect(builder.build_host_lines).to eq <<-END
58
+ # section: key1
59
+ Host srv2
60
+ # section: key2
61
+ Host srv3
62
+ Host srv4
63
+ END
64
+ end
65
+ end
66
+ end
67
+ end