ec2ssh 2.0.8 → 3.0.0.beta1

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