seira 0.7.5 → 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/seira/cluster.rb +20 -2
- data/lib/seira/commands/teleport.rb +28 -0
- data/lib/seira/commands.rb +9 -0
- data/lib/seira/pods.rb +8 -3
- data/lib/seira/settings.rb +26 -0
- data/lib/seira/setup.rb +5 -0
- data/lib/seira/teleport/request.rb +31 -0
- data/lib/seira/teleport/role_requirements.rb +41 -0
- data/lib/seira/teleport/status.rb +58 -0
- data/lib/seira/teleport.rb +3 -0
- data/lib/seira/version.rb +1 -1
- data/lib/seira.rb +17 -0
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eb319292b2073d2caa5a7d526f70b7f6a707dfa8a7e8b7eba014d488c1ce50ee
|
4
|
+
data.tar.gz: b6dfc7dd336518acc1d510177d8eff2f02252351cece68e57c68d9019272d674
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 137620c27df4b7fb22580bb33249d8c6ea6b087f2203205b73f844eef456b20342af2f9c753264439e390ec9c82ad1e47dcda719885cd57ba2a980dbe9803ad9
|
7
|
+
data.tar.gz: f40b95e33ab48142deb3673b804507f7f9bf91e37b7a66827e92feb5afbdfaadf59032d6ea3d1848be956037037cd2f4e5d9910a8b442e0b86d34ff2a7b58cfe
|
data/lib/seira/cluster.rb
CHANGED
@@ -41,8 +41,26 @@ module Seira
|
|
41
41
|
cluster_metadata = settings.clusters[target_cluster]
|
42
42
|
|
43
43
|
puts("Switching to gcloud config of '#{target_cluster}' and kubernetes cluster of '#{cluster_metadata['cluster']}'") if verbose
|
44
|
-
|
45
|
-
|
44
|
+
unless system("gcloud config configurations activate #{target_cluster}")
|
45
|
+
puts("Try running `seira setup #{target_cluster}` if you have not yet configured this cluster.")
|
46
|
+
exit(1)
|
47
|
+
end
|
48
|
+
|
49
|
+
if settings.use_teleport?(target_cluster)
|
50
|
+
teleport_status = Seira::Teleport::Status.new
|
51
|
+
|
52
|
+
unless teleport_status.is_valid?
|
53
|
+
if teleport_status.active_cluster == settings.teleport_cluster(target_cluster)
|
54
|
+
exit(1) unless system("tsh login")
|
55
|
+
else
|
56
|
+
exit(1) unless system("tsh login --proxy=#{settings.teleport_cluster(target_cluster)}:443 --auth=#{settings.teleport_auth(target_cluster)}")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
exit(1) unless system("tsh kube login #{settings.teleport_kubernetes_cluster(target_cluster)}")
|
61
|
+
else
|
62
|
+
exit(1) unless system("kubectl config use-context #{cluster_metadata['cluster']}")
|
63
|
+
end
|
46
64
|
|
47
65
|
# If we haven't exited by now, it was successful
|
48
66
|
true
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Seira
|
2
|
+
module Commands
|
3
|
+
class Teleport
|
4
|
+
attr_reader :context, :command
|
5
|
+
|
6
|
+
def initialize(command, context: nil)
|
7
|
+
@command = command
|
8
|
+
@context = context
|
9
|
+
end
|
10
|
+
|
11
|
+
def invoke(clean_output: false, return_output: false)
|
12
|
+
puts "Calling: #{calculated_command.green}" unless clean_output
|
13
|
+
|
14
|
+
if return_output
|
15
|
+
`#{calculated_command}`
|
16
|
+
else
|
17
|
+
system(calculated_command)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def calculated_command
|
24
|
+
@calculated_command ||= "tsh #{command}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/seira/commands.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'seira/commands/kubectl'
|
2
2
|
require 'seira/commands/gcloud'
|
3
|
+
require 'seira/commands/teleport'
|
3
4
|
|
4
5
|
module Seira
|
5
6
|
module Commands
|
@@ -18,5 +19,13 @@ module Seira
|
|
18
19
|
def self.gcloud(command, context:, clean_output: false, format: :boolean)
|
19
20
|
Gcloud.new(command, context: context, clean_output: clean_output, format: format).invoke
|
20
21
|
end
|
22
|
+
|
23
|
+
def tsh(command, context:, clean_output: false, return_output: false)
|
24
|
+
Seira::Commands.tsh(command, context: context, clean_output: clean_output, return_output: return_output)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.tsh(command, context:, clean_output: false, return_output: false)
|
28
|
+
Teleport.new(command, context: context).invoke(clean_output: clean_output, return_output: return_output)
|
29
|
+
end
|
21
30
|
end
|
22
31
|
end
|
data/lib/seira/pods.rb
CHANGED
@@ -41,7 +41,12 @@ module Seira
|
|
41
41
|
def run_help
|
42
42
|
puts SUMMARY
|
43
43
|
puts "\n\n"
|
44
|
-
puts "
|
44
|
+
puts "Possible actions:\n\n"
|
45
|
+
puts "list: List all the pods (output mode wide)."
|
46
|
+
puts "delete: Delete the pod (positional arg)."
|
47
|
+
puts "logs: Log the output of the pod (provided via positional arg)."
|
48
|
+
puts "top: Print the current resource usage for the specified pod (optional positional arg) or all the pods (default) in the context's namespace."
|
49
|
+
puts "connect: Connect to, or run a command on (--command=), a running pod (positional arg or --pod=), or create a new dedicated pod (--dedicated). Can override tier (--tier=)"
|
45
50
|
end
|
46
51
|
|
47
52
|
def run_list
|
@@ -53,7 +58,7 @@ module Seira
|
|
53
58
|
end
|
54
59
|
|
55
60
|
def run_logs
|
56
|
-
kubectl("logs #{pod_name} -c #{app}")
|
61
|
+
kubectl("logs #{pod_name} -c #{app}", context: context)
|
57
62
|
end
|
58
63
|
|
59
64
|
def run_top
|
@@ -62,7 +67,7 @@ module Seira
|
|
62
67
|
|
63
68
|
def run_connect
|
64
69
|
tier = nil
|
65
|
-
pod_name =
|
70
|
+
pod_name = pod_name
|
66
71
|
dedicated = false
|
67
72
|
command = 'sh'
|
68
73
|
|
data/lib/seira/settings.rb
CHANGED
@@ -68,6 +68,32 @@ module Seira
|
|
68
68
|
settings['seira']['expected_environment_variable_during_deploys']
|
69
69
|
end
|
70
70
|
|
71
|
+
def use_teleport?(cluster)
|
72
|
+
teleport_enabled = settings['seira']['clusters'][cluster].dig('teleport', 'enabled')
|
73
|
+
|
74
|
+
return false if teleport_enabled.nil?
|
75
|
+
|
76
|
+
teleport_enabled || ENV['SEIRA_TELEPORT_ENABLED'] == 'true'
|
77
|
+
end
|
78
|
+
|
79
|
+
def teleport_cluster(cluster)
|
80
|
+
settings['seira']['clusters'][cluster].dig('teleport', 'cluster')
|
81
|
+
end
|
82
|
+
|
83
|
+
def teleport_auth(cluster)
|
84
|
+
settings['seira']['clusters'][cluster].dig('teleport', 'auth')
|
85
|
+
end
|
86
|
+
|
87
|
+
def teleport_kubernetes_cluster(cluster)
|
88
|
+
settings['seira']['clusters'][cluster].dig('teleport', 'kubernetes_cluster') || cluster
|
89
|
+
end
|
90
|
+
|
91
|
+
def teleport_role_requirements(cluster)
|
92
|
+
requirements = settings['seira']['clusters'][cluster].dig('teleport', 'role_requirements') || []
|
93
|
+
|
94
|
+
requirements.map { |r| Seira::Teleport::RoleRequirement.new(r) }
|
95
|
+
end
|
96
|
+
|
71
97
|
private
|
72
98
|
|
73
99
|
def parse_settings
|
data/lib/seira/setup.rb
CHANGED
@@ -121,6 +121,11 @@ module Seira
|
|
121
121
|
puts "Installing kubectl..."
|
122
122
|
system('gcloud components install kubectl')
|
123
123
|
end
|
124
|
+
|
125
|
+
puts "Making sure tsh is installed..."
|
126
|
+
unless system('tsh version &> /dev/null')
|
127
|
+
puts "Go to https://goteleport.com/docs/installation/ to install tsh.".yellow
|
128
|
+
end
|
124
129
|
end
|
125
130
|
|
126
131
|
def run_status
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Seira
|
2
|
+
module Teleport
|
3
|
+
class Request
|
4
|
+
def initialize(role, reviewer: nil, context: nil)
|
5
|
+
@role = role
|
6
|
+
@reviewer = reviewer
|
7
|
+
@context = context
|
8
|
+
end
|
9
|
+
|
10
|
+
def invoke
|
11
|
+
command.invoke
|
12
|
+
end
|
13
|
+
|
14
|
+
def cmd
|
15
|
+
cmd = "request create --roles '#{@role}'"
|
16
|
+
cmd += " --reason '#{reason}'"
|
17
|
+
cmd += " --reviewers #{@reviewer}" unless @reviewer.nil?
|
18
|
+
|
19
|
+
cmd
|
20
|
+
end
|
21
|
+
|
22
|
+
def reason
|
23
|
+
"Running: seira #{@context[:cluster]} #{@context[:app]} #{@context[:category]} #{@context[:action]}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def command
|
27
|
+
@command ||= Seira::Commands::Teleport.new(cmd, context: @context)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Seira
|
2
|
+
module Teleport
|
3
|
+
class RoleRequirement
|
4
|
+
include Comparable
|
5
|
+
|
6
|
+
attr_reader :commands, :category, :role
|
7
|
+
|
8
|
+
def initialize(requirement)
|
9
|
+
@requirement = requirement
|
10
|
+
@category = requirement['category']
|
11
|
+
@commands = requirement['commands']
|
12
|
+
@role = requirement['role']
|
13
|
+
end
|
14
|
+
|
15
|
+
def matches?(category, command)
|
16
|
+
@category == category && command_matches?(command)
|
17
|
+
end
|
18
|
+
|
19
|
+
def <=>(other)
|
20
|
+
raise ArgumentError, 'Cannot compare different categories' if category != other.category
|
21
|
+
|
22
|
+
if commands.nil?
|
23
|
+
return -1
|
24
|
+
elsif other.commands.nil?
|
25
|
+
return 1
|
26
|
+
end
|
27
|
+
|
28
|
+
commands.length <=> other.commands.length
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def command_matches?(command)
|
34
|
+
return false if command == 'help'
|
35
|
+
return true if @requirement['commands'].nil?
|
36
|
+
|
37
|
+
@requirement['commands'].include?(command)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'date'
|
3
|
+
require 'seira/commands/teleport'
|
4
|
+
|
5
|
+
module Seira
|
6
|
+
module Teleport
|
7
|
+
class Status
|
8
|
+
attr_reader :data
|
9
|
+
|
10
|
+
def initialize(data: nil)
|
11
|
+
@data = data
|
12
|
+
@data ||= get_status_json
|
13
|
+
end
|
14
|
+
|
15
|
+
def [](key)
|
16
|
+
data[key]
|
17
|
+
end
|
18
|
+
|
19
|
+
def active_cluster
|
20
|
+
data.dig('active', 'cluster')
|
21
|
+
end
|
22
|
+
|
23
|
+
def kubernetes_cluster
|
24
|
+
data.dig('active','kubernetes_cluster')
|
25
|
+
end
|
26
|
+
|
27
|
+
def roles
|
28
|
+
@roles ||= data.dig('active', 'roles')
|
29
|
+
end
|
30
|
+
|
31
|
+
def has_role?(role)
|
32
|
+
return false if roles.nil?
|
33
|
+
|
34
|
+
roles.include?(role)
|
35
|
+
end
|
36
|
+
|
37
|
+
def is_valid?
|
38
|
+
valid_until = data.dig('active', 'valid_until')
|
39
|
+
return false if valid_until.nil?
|
40
|
+
|
41
|
+
DateTime.parse(data.dig('active', 'valid_until')) > DateTime.now
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def get_status_json
|
47
|
+
cmd = Seira::Commands::Teleport.new('status -f json', context: :none)
|
48
|
+
|
49
|
+
out = cmd.invoke(clean_output: true, return_output: true)
|
50
|
+
if out == ''
|
51
|
+
{}
|
52
|
+
else
|
53
|
+
JSON.parse(cmd.invoke(clean_output: true, return_output: true))
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/seira/version.rb
CHANGED
data/lib/seira.rb
CHANGED
@@ -20,6 +20,7 @@ require 'seira/settings'
|
|
20
20
|
require 'seira/setup'
|
21
21
|
require 'seira/node_pools'
|
22
22
|
require 'seira/util/resource_renderer'
|
23
|
+
require 'seira/teleport'
|
23
24
|
|
24
25
|
# A base runner class that does base checks and then delegates the actual
|
25
26
|
# work for the command to a class in lib/seira folder.
|
@@ -118,6 +119,16 @@ module Seira
|
|
118
119
|
exit(1)
|
119
120
|
end
|
120
121
|
|
122
|
+
if settings.use_teleport?(cluster)
|
123
|
+
role_requirements = settings.teleport_role_requirements(cluster)
|
124
|
+
required_role = role_requirements.filter { |rr| rr.matches?(category, action) }.sort.first&.role
|
125
|
+
status = Seira::Teleport::Status.new
|
126
|
+
|
127
|
+
unless required_role.nil? || status.has_role?(required_role)
|
128
|
+
Seira::Teleport::Request.new(required_role, reviewer: ENV['TELEPORT_REVIEWER'], context: passed_context).invoke
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
121
132
|
if category == 'cluster'
|
122
133
|
perform_action_validation(klass: command_class, action: action)
|
123
134
|
command_class.new(action: action, args: args, context: passed_context, settings: settings).run
|
@@ -142,6 +153,7 @@ module Seira
|
|
142
153
|
region: settings.region_for_cluster(cluster),
|
143
154
|
zone: settings.zone_for_cluster(cluster),
|
144
155
|
app: app,
|
156
|
+
category: category,
|
145
157
|
action: action,
|
146
158
|
args: args
|
147
159
|
}
|
@@ -159,6 +171,11 @@ module Seira
|
|
159
171
|
exit(1)
|
160
172
|
end
|
161
173
|
|
174
|
+
unless system("tsh version > /dev/null 2>&1")
|
175
|
+
puts "Teleport CLI not installed properly. Please install `tsh` before using seira.".red
|
176
|
+
exit(1)
|
177
|
+
end
|
178
|
+
|
162
179
|
# The first arg must always be the cluster. This ensures commands are not run by
|
163
180
|
# accident on the wrong kubernetes cluster or gcloud project.
|
164
181
|
exit(1) unless Seira::Cluster.new(action: nil, args: nil, context: nil, settings: settings).switch(target_cluster: cluster, verbose: false)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: seira
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Scott Ringwelski
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-08-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: highline
|
@@ -123,6 +123,7 @@ files:
|
|
123
123
|
- lib/seira/commands.rb
|
124
124
|
- lib/seira/commands/gcloud.rb
|
125
125
|
- lib/seira/commands/kubectl.rb
|
126
|
+
- lib/seira/commands/teleport.rb
|
126
127
|
- lib/seira/config.rb
|
127
128
|
- lib/seira/db.rb
|
128
129
|
- lib/seira/db/create.rb
|
@@ -135,6 +136,10 @@ files:
|
|
135
136
|
- lib/seira/secrets.rb
|
136
137
|
- lib/seira/settings.rb
|
137
138
|
- lib/seira/setup.rb
|
139
|
+
- lib/seira/teleport.rb
|
140
|
+
- lib/seira/teleport/request.rb
|
141
|
+
- lib/seira/teleport/role_requirements.rb
|
142
|
+
- lib/seira/teleport/status.rb
|
138
143
|
- lib/seira/util/resource_renderer.rb
|
139
144
|
- lib/seira/version.rb
|
140
145
|
- resources/adjectives.txt
|