ecl 1.2.1 → 3.0.0.pre.alpha1
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 +5 -5
- data/.gitignore +0 -1
- data/Gemfile +1 -0
- data/Gemfile.lock +48 -0
- data/README.md +112 -89
- data/Rakefile +1 -0
- data/eclair.gemspec +7 -4
- data/exe/ecl +62 -4
- data/lib/eclair.rb +21 -18
- data/lib/eclair/color.rb +3 -0
- data/lib/eclair/config.rb +68 -21
- data/lib/eclair/grid.rb +242 -274
- data/lib/eclair/group_item.rb +33 -0
- data/lib/eclair/item.rb +42 -0
- data/lib/eclair/less_viewer.rb +2 -1
- data/lib/eclair/provider.rb +15 -0
- data/lib/eclair/providers/ec2.rb +2 -0
- data/lib/eclair/providers/ec2/ec2_group_item.rb +16 -0
- data/lib/eclair/providers/ec2/ec2_item.rb +136 -0
- data/lib/eclair/providers/ec2/ec2_provider.rb +118 -0
- data/lib/eclair/providers/gce.rb +2 -0
- data/lib/eclair/providers/gce/gce_group_item.rb +15 -0
- data/lib/eclair/providers/gce/gce_item.rb +117 -0
- data/lib/eclair/providers/gce/gce_provider.rb +34 -0
- data/lib/eclair/providers/k8s.rb +2 -0
- data/lib/eclair/providers/k8s/k8s_group_item.rb +15 -0
- data/lib/eclair/providers/k8s/k8s_item.rb +92 -0
- data/lib/eclair/providers/k8s/k8s_provider.rb +34 -0
- data/lib/eclair/version.rb +2 -1
- data/out.gif +0 -0
- data/templates/eclrc.template +81 -35
- metadata +60 -27
- data/bin/console +0 -10
- data/lib/eclair/cell.rb +0 -101
- data/lib/eclair/column.rb +0 -52
- data/lib/eclair/console.rb +0 -56
- data/lib/eclair/group.rb +0 -55
- data/lib/eclair/helpers/aws_helper.rb +0 -157
- data/lib/eclair/helpers/benchmark_helper.rb +0 -19
- data/lib/eclair/helpers/common_helper.rb +0 -24
- data/lib/eclair/instance.rb +0 -165
- data/lib/eclair/matcher.rb +0 -19
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Eclair
|
3
|
+
class GroupItem < Item
|
4
|
+
attr_reader :label
|
5
|
+
attr_accessor :visible
|
6
|
+
|
7
|
+
def initialize label, items
|
8
|
+
@label = label
|
9
|
+
@items = items
|
10
|
+
@visible = true
|
11
|
+
end
|
12
|
+
|
13
|
+
def toggle_select
|
14
|
+
if @items.all?(&:selected)
|
15
|
+
@items.each{|i| i.select(false) }
|
16
|
+
else
|
17
|
+
@items.each{|i| i.select(true) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def select state
|
22
|
+
@items.each{|i| i.select(state) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def length
|
26
|
+
@items.length
|
27
|
+
end
|
28
|
+
|
29
|
+
def color
|
30
|
+
[Curses::COLOR_WHITE, -1, Curses::A_BOLD]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/eclair/item.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "eclair/config"
|
3
|
+
|
4
|
+
module Eclair
|
5
|
+
class Item
|
6
|
+
include ConfigHelper
|
7
|
+
attr_accessor :selected, :visible
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@selected = false
|
11
|
+
@visible = true
|
12
|
+
end
|
13
|
+
|
14
|
+
def toggle_select
|
15
|
+
@selected = !@selected
|
16
|
+
end
|
17
|
+
|
18
|
+
def select state
|
19
|
+
@selected = state
|
20
|
+
end
|
21
|
+
|
22
|
+
def id
|
23
|
+
raise "Not Implemented"
|
24
|
+
end
|
25
|
+
|
26
|
+
def command
|
27
|
+
raise "Not Implemented"
|
28
|
+
end
|
29
|
+
|
30
|
+
def header
|
31
|
+
raise "Not Implemented"
|
32
|
+
end
|
33
|
+
|
34
|
+
def title
|
35
|
+
raise "Not Implemented"
|
36
|
+
end
|
37
|
+
|
38
|
+
def search_key
|
39
|
+
raise "Not Implemented"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/eclair/less_viewer.rb
CHANGED
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Eclair
|
3
|
+
module Provider
|
4
|
+
extend self
|
5
|
+
attr_accessor :items
|
6
|
+
|
7
|
+
def filter_items search_buffer
|
8
|
+
@items.select{ |item| item&.search_key&.include?(search_buffer.downcase) || item.selected }
|
9
|
+
end
|
10
|
+
|
11
|
+
def require_prepare
|
12
|
+
raise "Not prepared" unless @prepared
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "eclair/group_item"
|
3
|
+
|
4
|
+
module Eclair
|
5
|
+
class EC2GroupItem < GroupItem
|
6
|
+
def header
|
7
|
+
running = @items.count{|i| i.instance.state[:code] == 16}
|
8
|
+
all = @items.count
|
9
|
+
|
10
|
+
<<-EOS
|
11
|
+
Group #{label}
|
12
|
+
#{running} Instances Running / #{all} Instances Total
|
13
|
+
EOS
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'shellwords'
|
3
|
+
require "aws-sdk-ec2"
|
4
|
+
|
5
|
+
require "eclair/item"
|
6
|
+
require "eclair/providers/ec2/ec2_provider"
|
7
|
+
|
8
|
+
module Eclair
|
9
|
+
class EC2Item < Item
|
10
|
+
attr_reader :instance
|
11
|
+
|
12
|
+
def initialize instance
|
13
|
+
super()
|
14
|
+
@instance = instance
|
15
|
+
end
|
16
|
+
|
17
|
+
def id
|
18
|
+
@instance.id
|
19
|
+
end
|
20
|
+
|
21
|
+
def color
|
22
|
+
if @selected
|
23
|
+
[Curses::COLOR_YELLOW, -1, Curses::A_BOLD]
|
24
|
+
elsif !connectable?
|
25
|
+
[Curses::COLOR_BLACK, -1, Curses::A_BOLD]
|
26
|
+
else
|
27
|
+
[Curses::COLOR_WHITE, -1]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def command
|
32
|
+
hosts = [@instance.public_ip_address, @instance.private_ip_address].compact
|
33
|
+
ports = config.ssh_ports
|
34
|
+
ssh_options = config.ssh_options
|
35
|
+
ssh_command = config.ssh_command
|
36
|
+
username = config.ssh_username.call(image)
|
37
|
+
key_cmd = config.ssh_keys[@instance.key_name] ? "-i #{config.ssh_keys[@instance.key_name]}" : ""
|
38
|
+
format = config.exec_format
|
39
|
+
|
40
|
+
joined_cmd = hosts.map do |host|
|
41
|
+
ports.map do |port|
|
42
|
+
{
|
43
|
+
"{ssh_command}" => ssh_command,
|
44
|
+
"{ssh_options}" => ssh_options,
|
45
|
+
"{port}" => port,
|
46
|
+
"{ssh_key}" => key_cmd,
|
47
|
+
"{username}" => username,
|
48
|
+
"{host}" => host,
|
49
|
+
}.reduce(format) { |cmd,pair| cmd.sub(pair[0],pair[1].to_s) }
|
50
|
+
end
|
51
|
+
end.join(" || ")
|
52
|
+
# puts joined_cmd
|
53
|
+
"echo Attaching to #{Shellwords.escape(name)} \\[#{@instance.instance_id}\\] && #{joined_cmd}"
|
54
|
+
end
|
55
|
+
|
56
|
+
def header
|
57
|
+
<<-EOS
|
58
|
+
#{name} (#{instance_id}) [#{state[:name]}]
|
59
|
+
launched at #{launch_time.to_time}
|
60
|
+
EOS
|
61
|
+
end
|
62
|
+
|
63
|
+
def image
|
64
|
+
@image ||= provider.find_image_by_id(@instance.image_id)
|
65
|
+
end
|
66
|
+
|
67
|
+
def vpc
|
68
|
+
@vpc ||= provider.find_vpc_by_id(@instance.vpc_id)
|
69
|
+
end
|
70
|
+
|
71
|
+
def header
|
72
|
+
<<-EOS
|
73
|
+
#{name} (#{@instance.instance_id}) [#{@instance.state[:name]}]
|
74
|
+
launched at #{@instance.launch_time.to_time}
|
75
|
+
EOS
|
76
|
+
end
|
77
|
+
|
78
|
+
def label
|
79
|
+
" - #{name} [#{launched_at}]"
|
80
|
+
end
|
81
|
+
|
82
|
+
def info
|
83
|
+
{
|
84
|
+
instance: @instance,
|
85
|
+
image: provider.image_loaded? ? image : "ami info not loaded yet",
|
86
|
+
security_groups: provider.security_group_loaded? ? security_groups : "sg info not loaded yet",
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
def connectable?
|
91
|
+
![32, 48, 80].include?(@instance.state[:code])
|
92
|
+
end
|
93
|
+
|
94
|
+
def name
|
95
|
+
return @name if @name
|
96
|
+
begin
|
97
|
+
tag = @instance.tags.find{|t| t.key == "Name"}
|
98
|
+
@name = tag ? tag.value : "noname"
|
99
|
+
rescue
|
100
|
+
@name = "terminated"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def security_groups
|
105
|
+
@security_groups ||= @instance.security_groups.map{|sg| provider.find_security_group_by_id(sg.group_id)}
|
106
|
+
end
|
107
|
+
|
108
|
+
def search_key
|
109
|
+
name.downcase
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def launched_at
|
115
|
+
diff = Time.now - @instance.launch_time
|
116
|
+
{
|
117
|
+
"year" => 31557600,
|
118
|
+
"month" => 2592000,
|
119
|
+
"day" => 86400,
|
120
|
+
"hour" => 3600,
|
121
|
+
"minute" => 60,
|
122
|
+
"second" => 1
|
123
|
+
}.each do |unit,v|
|
124
|
+
if diff >= v
|
125
|
+
value = (diff/v).to_i
|
126
|
+
return "#{value} #{unit}#{value > 1 ? "s" : ""}"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
"now"
|
130
|
+
end
|
131
|
+
|
132
|
+
def provider
|
133
|
+
EC2Provider
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "aws-sdk-ec2"
|
3
|
+
require "eclair/provider"
|
4
|
+
require "eclair/providers/ec2/ec2_item"
|
5
|
+
require "eclair/providers/ec2/ec2_group_item"
|
6
|
+
|
7
|
+
module Eclair
|
8
|
+
module EC2Provider
|
9
|
+
extend Provider
|
10
|
+
extend self
|
11
|
+
|
12
|
+
def group_class
|
13
|
+
EC2GroupItem
|
14
|
+
end
|
15
|
+
|
16
|
+
def item_class
|
17
|
+
EC2Item
|
18
|
+
end
|
19
|
+
|
20
|
+
def prepare keyword
|
21
|
+
Thread.abort_on_exception = true
|
22
|
+
|
23
|
+
@instances ||= fetch_instances keyword
|
24
|
+
|
25
|
+
image_ids = @instances.map(&:image_id).uniq
|
26
|
+
@image_thread = Thread.new do
|
27
|
+
@images = fetch_images(image_ids)
|
28
|
+
@id_to_image = @images.map{|i| [i.image_id, i]}.to_h
|
29
|
+
end
|
30
|
+
|
31
|
+
@sg_thread = Thread.new do
|
32
|
+
@security_groups = fetch_security_groups
|
33
|
+
@id_to_sg = @security_groups.map{|i| [i.group_id, i]}.to_h
|
34
|
+
end
|
35
|
+
|
36
|
+
@vpc_thread = Thread.new do
|
37
|
+
@vpcs = fetch_vpcs
|
38
|
+
@id_to_vpc = @vpcs.map{|i| [i.vpc_id, i]}.to_h
|
39
|
+
end
|
40
|
+
|
41
|
+
@items = @instances.map{|i| EC2Item.new(i)}
|
42
|
+
|
43
|
+
@prepared = true
|
44
|
+
end
|
45
|
+
|
46
|
+
def image_loaded?
|
47
|
+
!@sg_thread.alive?
|
48
|
+
end
|
49
|
+
|
50
|
+
def images
|
51
|
+
@image_thread.join if @image_thread.alive?
|
52
|
+
@images
|
53
|
+
end
|
54
|
+
|
55
|
+
def find_image_by_id id
|
56
|
+
@image_thread.join if @image_thread.alive?
|
57
|
+
@id_to_image[id]
|
58
|
+
end
|
59
|
+
|
60
|
+
def security_group_loaded?
|
61
|
+
!@sg_thread.alive?
|
62
|
+
end
|
63
|
+
|
64
|
+
def security_groups
|
65
|
+
@sg_thread.join if @sg_thread.alive?
|
66
|
+
@security_groups
|
67
|
+
end
|
68
|
+
|
69
|
+
def find_security_group_by_id id
|
70
|
+
@sg_thread.join if @sg_thread.alive?
|
71
|
+
@id_to_sg[id]
|
72
|
+
end
|
73
|
+
|
74
|
+
def vpc_loaded?
|
75
|
+
!@vpc_thread.alive?
|
76
|
+
end
|
77
|
+
|
78
|
+
def vpcs
|
79
|
+
@vpc_thread.join if @vpc_thread.alive?
|
80
|
+
@vpcs
|
81
|
+
end
|
82
|
+
|
83
|
+
def find_vpc_by_id id
|
84
|
+
@vpc_thread.join if @vpc_thread.alive?
|
85
|
+
@id_to_vpc[id]
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def ec2_client
|
91
|
+
@ec2_client ||= Aws::EC2::Client.new
|
92
|
+
end
|
93
|
+
|
94
|
+
def fetch_instances keyword
|
95
|
+
filter = if keyword.empty? then {} else { filters: [{name: "tag:Name", values: ["*#{keyword}*"]}] } end
|
96
|
+
|
97
|
+
ec2_client.describe_instances(filter).map{ |resp|
|
98
|
+
resp.data.reservations.map do |rsv|
|
99
|
+
rsv.instances
|
100
|
+
end
|
101
|
+
}.flatten
|
102
|
+
end
|
103
|
+
|
104
|
+
def fetch_security_groups
|
105
|
+
ec2_client.describe_security_groups.map{ |resp|
|
106
|
+
resp.security_groups
|
107
|
+
}.flatten
|
108
|
+
end
|
109
|
+
|
110
|
+
def fetch_images image_ids
|
111
|
+
ec2_client.describe_images(image_ids: image_ids).images.flatten
|
112
|
+
end
|
113
|
+
|
114
|
+
def fetch_vpcs
|
115
|
+
ec2_client.describe_vpcs.map{|resp| resp.vpcs}.flatten
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'eclair/item'
|
3
|
+
require 'eclair/providers/gce/gce_provider'
|
4
|
+
require 'time'
|
5
|
+
|
6
|
+
module Eclair
|
7
|
+
class GCEItem < Item
|
8
|
+
attr_reader :instance
|
9
|
+
|
10
|
+
def initialize instance
|
11
|
+
super()
|
12
|
+
@instance = instance
|
13
|
+
end
|
14
|
+
|
15
|
+
def id
|
16
|
+
@instance["id"]
|
17
|
+
end
|
18
|
+
|
19
|
+
def color
|
20
|
+
if @selected
|
21
|
+
[Curses::COLOR_YELLOW, -1, Curses::A_BOLD]
|
22
|
+
elsif !connectable?
|
23
|
+
[Curses::COLOR_BLACK, -1, Curses::A_BOLD]
|
24
|
+
else
|
25
|
+
[Curses::COLOR_WHITE, -1]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def header
|
30
|
+
<<-EOS
|
31
|
+
#{name} (#{public_ip_address} #{private_ip_address})
|
32
|
+
launched at #{launch_time.to_time}
|
33
|
+
#{description}
|
34
|
+
EOS
|
35
|
+
end
|
36
|
+
|
37
|
+
def label
|
38
|
+
" - #{name} [#{launched_at}]"
|
39
|
+
end
|
40
|
+
|
41
|
+
def description
|
42
|
+
@instance["description"]
|
43
|
+
end
|
44
|
+
|
45
|
+
def name
|
46
|
+
@instance["name"]
|
47
|
+
end
|
48
|
+
|
49
|
+
def public_ip_address
|
50
|
+
@instance.dig("networkInterfaces", 0, "accessConfigs", 0, "natIP")
|
51
|
+
end
|
52
|
+
|
53
|
+
def private_ip_address
|
54
|
+
@instance.dig("networkInterfaces", 0, "networkIP")
|
55
|
+
end
|
56
|
+
|
57
|
+
def command
|
58
|
+
hosts = [private_ip_address, public_ip_address].compact
|
59
|
+
ports = config.ssh_ports
|
60
|
+
ssh_options = config.ssh_options
|
61
|
+
ssh_command = config.ssh_command
|
62
|
+
username = "ubuntu"
|
63
|
+
format = config.exec_format
|
64
|
+
|
65
|
+
joined_cmd = hosts.map do |host|
|
66
|
+
ports.map do |port|
|
67
|
+
{
|
68
|
+
"{ssh_command}" => ssh_command,
|
69
|
+
"{ssh_options}" => ssh_options,
|
70
|
+
"{port}" => port,
|
71
|
+
"{username}" => username,
|
72
|
+
"{host}" => host,
|
73
|
+
"{ssh_key}" => ""
|
74
|
+
}.reduce(format) { |cmd,pair| cmd.sub(pair[0],pair[1].to_s) }
|
75
|
+
end
|
76
|
+
end.join(" || ")
|
77
|
+
# puts joined_cmd
|
78
|
+
"echo Attaching to #{name} \\[#{name}\\] && #{joined_cmd}"
|
79
|
+
end
|
80
|
+
|
81
|
+
def connectable?
|
82
|
+
status == "RUNNING"
|
83
|
+
end
|
84
|
+
|
85
|
+
def search_key
|
86
|
+
name.downcase
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def status
|
92
|
+
@instance["status"]
|
93
|
+
end
|
94
|
+
|
95
|
+
def launch_time
|
96
|
+
Time.parse(@instance["creationTimestamp"]).localtime
|
97
|
+
end
|
98
|
+
|
99
|
+
def launched_at
|
100
|
+
diff = Time.now - launch_time
|
101
|
+
{
|
102
|
+
"year" => 31557600,
|
103
|
+
"month" => 2592000,
|
104
|
+
"day" => 86400,
|
105
|
+
"hour" => 3600,
|
106
|
+
"minute" => 60,
|
107
|
+
"second" => 1
|
108
|
+
}.each do |unit,v|
|
109
|
+
if diff >= v
|
110
|
+
value = (diff/v).to_i
|
111
|
+
return "#{value} #{unit}#{value > 1 ? "s" : ""}"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
"now"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|