seira 0.7.4 → 0.8.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
  SHA256:
3
- metadata.gz: 57d3df41212e06d4182e97edc51ff948019596f2063696fee21327f6d79e7820
4
- data.tar.gz: a1530d5bfd8142524600d9e73e6346668a32ab9aef4326366e0d173be4850f94
3
+ metadata.gz: 72e2d8ac0c72c8e4c7689f6997b7c460011cbceaf921a5935196c2c354e57ac7
4
+ data.tar.gz: de987469fe8decf1a4a8ac4206857c6dd5a871547a4ee8875717fa4167cdc7ec
5
5
  SHA512:
6
- metadata.gz: c2c2e1d7e7f52832046653fcca340d1cb30addecbb2325979b02a822d0a38757344216dc8cbc363fc8f02ec01905d57fecbe713e467c2fa59c40184b0783e392
7
- data.tar.gz: 7a6cd149ff5338938ece13e6ff2c157c2e0145bf08f4af1974150f259765fc8fd2c5243f4780223799d02f2326626c0547a7062f63560afd942195dec8565003
6
+ metadata.gz: 36b4e0d1dde0dad8b7d9ae57306726516739184f90aeb0cb85657fc8b2b5b7ea93c08937ea8e2679be6c9ebccf0500b81f7e83e2764d6f6cdba06881e9f2b931
7
+ data.tar.gz: 032fa962c1631a249bd1a1b6a9e113e16b4f4198de3957afe60a8777441c695eada40cbc910d7734ce8d53d0e229623a5914a05f46d8f5554bafa1f7976f63e3
@@ -0,0 +1,24 @@
1
+ name: Ruby
2
+
3
+ on: push
4
+
5
+ jobs:
6
+ test:
7
+
8
+ runs-on: ubuntu-20.04
9
+
10
+ strategy:
11
+ matrix:
12
+ ruby-version: ['2.6.3', '2.7.2']
13
+
14
+ steps:
15
+ # Pin to this commit: v2
16
+ - uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
17
+ - name: Set up Ruby
18
+ # Pin to this commit, v1.82.0
19
+ uses: ruby/setup-ruby@5e4f0a10bfc39c97cd5358121291e27e5d97e09b
20
+ with:
21
+ ruby-version: ${{ matrix.ruby-version }}
22
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
23
+ - name: Run tests
24
+ run: bundle exec rake
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Seira
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/seira.svg)](https://badge.fury.io/rb/seira)
4
- [![Build Status](https://travis-ci.org/joinhandshake/seira.svg?branch=master)](https://travis-ci.org/joinhandshake/seira)
4
+ [![Build Status](https://github.com/joinhandshake/seira/actions/workflows/ruby.yml/badge.svg)](https://github.com/joinhandshake/seira/actions)
5
5
 
6
6
  An opinionated library for building applications on Kubernetes.
7
7
 
@@ -165,7 +165,7 @@ Pods can be listed and also exec'd into.
165
165
 
166
166
  `seira staging app-name pods list`
167
167
 
168
- `seira staging app-name pods run`
168
+ `seira staging app-name pods connect --pod=<POD-NAME>`
169
169
 
170
170
  ## Development
171
171
 
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
- exit(1) unless system("gcloud config configurations activate #{target_cluster}")
45
- exit(1) unless system("kubectl config use-context #{cluster_metadata['cluster']}")
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
@@ -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/jobs.rb CHANGED
@@ -57,7 +57,19 @@ module Seira
57
57
  def run_help
58
58
  puts SUMMARY
59
59
  puts "\n\n"
60
- puts "TODO"
60
+ puts <<~HELPTEXT
61
+ help: Display the help page.
62
+ list: List the currently running jobs in the given application namespace.
63
+ delete: Delete a stuck migration job via Kubernetes job 'seira cluster app delete job-name'.
64
+ run: Execute a given command to issue a schema migration.
65
+
66
+ options:
67
+ --template-file: Provide a job template file to use. (--template-file=template.yaml.erb)
68
+ --async: Wait for the job to finish before exiting.
69
+ --no_delete: Do not delete the Kubernetes job after it is finished.
70
+ --size: Use a predetermined resource sizing template for this job. (Default size: 1)
71
+
72
+ HELPTEXT
61
73
  end
62
74
 
63
75
  def run_list
@@ -75,6 +87,7 @@ module Seira
75
87
  async = false # Wait for job to finish before continuing.
76
88
  no_delete = false # Delete at end
77
89
  resource_hash = RESOURCE_SIZES['1']
90
+ template_file = "template.yaml"
78
91
 
79
92
  # Loop through args and process any that aren't just the command to run
80
93
  loop do
@@ -94,6 +107,8 @@ module Seira
94
107
  elsif arg.start_with?('--size=')
95
108
  size = arg.split('=')[1]
96
109
  resource_hash = RESOURCE_SIZES[size]
110
+ elsif arg.start_with?('--template-file=')
111
+ template_file = arg.split('=')[1]
97
112
  else
98
113
  puts "Warning: Unrecognized argument #{arg}"
99
114
  end
@@ -113,7 +128,7 @@ module Seira
113
128
 
114
129
  source = "kubernetes/#{context[:cluster]}/#{app}" # TODO: Move to method in app.rb
115
130
  Dir.mktmpdir do |destination|
116
- file_name = discover_job_template_file_name(source)
131
+ file_name = discover_job_template_file_name(source, template_file)
117
132
 
118
133
  FileUtils.mkdir_p destination # Create the nested directory
119
134
  FileUtils.copy_file "#{source}/jobs/#{file_name}", "#{destination}/#{file_name}"
@@ -174,11 +189,11 @@ module Seira
174
189
  end
175
190
  end
176
191
 
177
- def discover_job_template_file_name(source)
178
- if File.exist?("#{source}/jobs/template.yaml.erb")
179
- "template.yaml.erb"
192
+ def discover_job_template_file_name(source, template_file)
193
+ if File.exist?("#{source}/jobs/#{template_file}.erb")
194
+ "#{template_file}.erb"
180
195
  else
181
- "template.yaml"
196
+ template_file
182
197
  end
183
198
  end
184
199
  end
data/lib/seira/pods.rb CHANGED
@@ -31,8 +31,6 @@ module Seira
31
31
  run_top
32
32
  when 'connect'
33
33
  run_connect
34
- when 'run'
35
- run_run
36
34
  else
37
35
  fail "Unknown command encountered"
38
36
  end
@@ -43,7 +41,12 @@ module Seira
43
41
  def run_help
44
42
  puts SUMMARY
45
43
  puts "\n\n"
46
- puts "TODO"
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=)"
47
50
  end
48
51
 
49
52
  def run_list
@@ -55,7 +58,7 @@ module Seira
55
58
  end
56
59
 
57
60
  def run_logs
58
- kubectl("logs #{pod_name} -c #{app}")
61
+ kubectl("logs #{pod_name} -c #{app}", context: context)
59
62
  end
60
63
 
61
64
  def run_top
@@ -64,7 +67,7 @@ module Seira
64
67
 
65
68
  def run_connect
66
69
  tier = nil
67
- pod_name = nil
70
+ pod_name = pod_name
68
71
  dedicated = false
69
72
  command = 'sh'
70
73
 
data/lib/seira/random.rb CHANGED
@@ -70,7 +70,6 @@ module Seira
70
70
  bombay
71
71
  bongo
72
72
  bonobo
73
- booby
74
73
  budgerigar
75
74
  buffalo
76
75
  bulldog
@@ -120,7 +119,6 @@ module Seira
120
119
  dachshund
121
120
  dalmatian
122
121
  deer
123
- dhole
124
122
  dingo
125
123
  dinosaur
126
124
  discus
@@ -193,7 +191,6 @@ module Seira
193
191
  hippopotamus
194
192
  hornet
195
193
  horse
196
- human
197
194
  hummingbird
198
195
  hyena
199
196
  ibis
@@ -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
@@ -0,0 +1,3 @@
1
+ require 'seira/teleport/status'
2
+ require 'seira/teleport/role_requirements'
3
+ require 'seira/teleport/request'
data/lib/seira/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Seira
2
- VERSION = "0.7.4".freeze
2
+ VERSION = "0.8.0".freeze
3
3
  end
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,14 @@ module Seira
118
119
  exit(1)
119
120
  end
120
121
 
122
+ role_requirements = settings.teleport_role_requirements(cluster)
123
+ required_role = role_requirements.filter { |rr| rr.matches?(category, action) }.sort.first&.role
124
+ status = Seira::Teleport::Status.new
125
+
126
+ unless required_role.nil? || status.has_role?(required_role)
127
+ Seira::Teleport::Request.new(required_role, reviewer: ENV['TELEPORT_REVIEWER'], context: passed_context).invoke
128
+ end
129
+
121
130
  if category == 'cluster'
122
131
  perform_action_validation(klass: command_class, action: action)
123
132
  command_class.new(action: action, args: args, context: passed_context, settings: settings).run
@@ -142,6 +151,7 @@ module Seira
142
151
  region: settings.region_for_cluster(cluster),
143
152
  zone: settings.zone_for_cluster(cluster),
144
153
  app: app,
154
+ category: category,
145
155
  action: action,
146
156
  args: args
147
157
  }
@@ -159,6 +169,11 @@ module Seira
159
169
  exit(1)
160
170
  end
161
171
 
172
+ unless system("tsh version > /dev/null 2>&1")
173
+ puts "Teleport CLI not installed properly. Please install `tsh` before using seira.".red
174
+ exit(1)
175
+ end
176
+
162
177
  # The first arg must always be the cluster. This ensures commands are not run by
163
178
  # accident on the wrong kubernetes cluster or gcloud project.
164
179
  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.7.4
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott Ringwelski
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-02-19 00:00:00.000000000 Z
11
+ date: 2023-08-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: highline
@@ -103,12 +103,12 @@ extensions: []
103
103
  extra_rdoc_files: []
104
104
  files:
105
105
  - ".default-rubocop.yml"
106
+ - ".github/workflows/ruby.yml"
106
107
  - ".gitignore"
107
108
  - ".hound.yml"
108
109
  - ".rspec"
109
110
  - ".rubocop.yml"
110
111
  - ".ruby-version"
111
- - ".travis.yml"
112
112
  - Gemfile
113
113
  - LICENSE
114
114
  - README.md
@@ -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
@@ -143,7 +148,7 @@ homepage: https://github.com/joinhandshake/seira
143
148
  licenses:
144
149
  - MIT
145
150
  metadata: {}
146
- post_install_message:
151
+ post_install_message:
147
152
  rdoc_options: []
148
153
  require_paths:
149
154
  - lib
@@ -159,7 +164,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
159
164
  version: '0'
160
165
  requirements: []
161
166
  rubygems_version: 3.0.3
162
- signing_key:
167
+ signing_key:
163
168
  specification_version: 4
164
169
  summary: An opinionated library for building applications on Kubernetes.
165
170
  test_files: []
data/.travis.yml DELETED
@@ -1,7 +0,0 @@
1
- sudo: false
2
- language: ruby
3
- notifications:
4
- email: false
5
- rvm:
6
- - 2.5.3
7
- before_install: gem install bundler