kubernetes-cli 0.3.0 → 0.4.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: 44f3e261b827758d975c3e51c00f9319ac280c25592fd50d5980418de7fc4a46
4
- data.tar.gz: ce69cc4e1c0a898e4baad326c14fd15baeebc18f39416157986a992a7e40596f
3
+ metadata.gz: b93bf8e3f620c17f2781d2eb5f2748f970982a854d0aa622d3313b1d74104d3b
4
+ data.tar.gz: 9c7d170e2ab873b66cfbf6a12532260e3442781330c9e96363e472d354ff997a
5
5
  SHA512:
6
- metadata.gz: 73a7bff727ab4a7f5b42c3b1f1b4d6fd95d2176667978fe454b308c8fc78219eacfeb451ec2bffc50b42f6e8d7d895656ab282c888b48bb9aa0c3ded65fa945d
7
- data.tar.gz: 931ebe19d76a237f4e357ec0cac817221a52ba96e53630bfd3f76c669096b50694a52dc27f70bcbc7404ece859249592b433169d795f21736970afa02c2dd3ea
6
+ metadata.gz: 0156747413340bab08b571bf13db212d0a8397ceb0b733fbc152c7835080f203400f6f1554829c74dab4fe1b743b3f3eda3d2e11950896f01da6b9bedf5bc3ca
7
+ data.tar.gz: c21786be60e403d60d2c39fd7be371a7043539412000e9cff2ea197e37092fe38a2bc621a6413266aabd0b8633656375056ce46745ac65726eb08c3e31cd14e3
data/CHANGELOG.md CHANGED
@@ -1,3 +1,14 @@
1
+ ## 0.4.0
2
+ * Add `#version` method to get k8s client/server version info.
3
+ * Add Sorbet type definitions.
4
+ * Add tests.
5
+
6
+ ## 0.3.2
7
+ * Add missing require statement.
8
+
9
+ ## 0.3.1
10
+ * Fix issue restricting Docker CLI output.
11
+
1
12
  ## 0.3.0
2
13
  * Add ability to redirect kubectl's stdout and stderr streams.
3
14
 
data/Gemfile CHANGED
@@ -6,10 +6,21 @@ gemspec
6
6
  # See: https://github.com/rubygems/rubygems/issues/3646
7
7
  gem 'kubectl-rb'
8
8
 
9
+ group :test do
10
+ gem 'rspec'
11
+ gem 'kind-rb', '~> 0.1'
12
+ gem 'kube-dsl', '~> 0.6'
13
+ end
14
+
9
15
  group :development do
10
- gem 'rake'
16
+ gem 'curdle', '~> 1.0'
17
+
18
+ # lock to same version as kuby-core
19
+ gem 'sorbet', '= 0.5.6433'
20
+ gem 'parlour', '~> 6.0'
11
21
  end
12
22
 
13
- group :test do
14
- gem 'rspec'
23
+ group :development, :test do
24
+ gem 'pry-byebug'
25
+ gem 'rake'
15
26
  end
data/README.md ADDED
@@ -0,0 +1,49 @@
1
+ ## kubernetes-cli
2
+
3
+ ![Unit Tests](https://github.com/getkuby/kuby-core/actions/workflows/unit_tests.yml/badge.svg?branch=master)
4
+ ![Integration Tests](https://github.com/getkuby/kuby-core/actions/workflows/integration_tests.yml/badge.svg?branch=master)
5
+
6
+ A Ruby wrapper around the Kubernetes CLI.
7
+
8
+ ### Usage
9
+
10
+ Create a new instance by passing the path to your Kube config (usually ~/.kube/config) and optionally the path to the kubectl executable (by default, the executable path comes from the [kubectl-rb gem](https://github.com/getkuby/kubectl-rb)).
11
+
12
+ ```ruby
13
+ cli = KubernetesCLI(File.join(ENV['HOME'], '.kube', 'config'))
14
+ ```
15
+
16
+ ### Available Methods
17
+
18
+ - `annotate`
19
+ - `api_resources`
20
+ - `apply`
21
+ - `apply_uri`
22
+ - `current_context`
23
+ - `delete_object`
24
+ - `delete_objects`
25
+ - `exec_cmd`
26
+ - `executable`
27
+ - `get_object`
28
+ - `get_objects`
29
+ - `kubeconfig_path`
30
+ - `last_status`
31
+ - `logtail`
32
+ - `patch_object`
33
+ - `restart_deployment`
34
+ - `run_cmd`
35
+ - `system_cmd`
36
+
37
+ Please see the source code for available options.
38
+
39
+ ## Running Tests
40
+
41
+ `bundle exec rspec` should do the trick. Requires that you have Docker installed.
42
+
43
+ ## License
44
+
45
+ Licensed under the MIT license. See LICENSE for details.
46
+
47
+ ## Authors
48
+
49
+ * Cameron C. Dutro: http://github.com/camertron
data/Rakefile CHANGED
@@ -2,9 +2,11 @@ require 'bundler'
2
2
  require 'rspec/core/rake_task'
3
3
  require 'rubygems/package_task'
4
4
 
5
+ require 'sorbet-runtime'
5
6
  require 'kubernetes-cli'
7
+ require 'curdle'
6
8
 
7
- Bundler::GemHelper.install_tasks
9
+ Curdle::Tasks.install
8
10
 
9
11
  task default: :spec
10
12
 
@@ -11,8 +11,8 @@ Gem::Specification.new do |s|
11
11
 
12
12
  s.description = s.summary = 'Ruby wrapper around the Kubernetes CLI.'
13
13
 
14
- s.add_dependency 'kubectl-rb', '~> 0.1'
14
+ s.add_dependency 'kubectl-rb', '~> 0.2'
15
15
 
16
16
  s.require_path = 'lib'
17
- s.files = Dir['{lib,spec,vendor}/**/*', 'Gemfile', 'LICENSE', 'CHANGELOG.md', 'README.md', 'Rakefile', 'kubernetes-cli.gemspec']
17
+ s.files = Dir['{lib,spec,rbi}/**/*', 'Gemfile', 'LICENSE', 'CHANGELOG.md', 'README.md', 'Rakefile', 'kubernetes-cli.gemspec']
18
18
  end
@@ -1,3 +1,5 @@
1
+ # typed: ignore
2
+
1
3
  class KubernetesCLI
2
- VERSION = '0.3.0'
4
+ VERSION = '0.4.0'.freeze
3
5
  end
@@ -1,64 +1,161 @@
1
+ # typed: strict
2
+
3
+ require 'json'
1
4
  require 'kubectl-rb'
2
5
  require 'open3'
6
+ require 'shellwords'
3
7
  require 'stringio'
4
8
 
5
9
  class KubernetesCLI
10
+ # extend T::Sig
11
+
6
12
  class KubernetesError < StandardError; end
7
13
 
8
14
  class InvalidResourceError < KubernetesError
9
- attr_accessor :resource
15
+ # extend T::Sig
16
+
17
+ # T::Sig::WithoutRuntime.sig { returns(T.nilable(::KubeDSL::DSLObject)) }
18
+ attr_reader :resource
19
+
20
+ # T::Sig::WithoutRuntime.sig { params(resource: ::KubeDSL::DSLObject).returns(::KubeDSL::DSLObject) }
21
+ attr_writer :resource
22
+
23
+ # T::Sig::WithoutRuntime.sig { params(args: T.untyped).void }
24
+ def initialize(*args)
25
+ # @resource = T.let(@resource, T.nilable(::KubeDSL::DSLObject))
26
+ super
27
+ end
10
28
  end
11
29
 
12
30
  class InvalidResourceUriError < KubernetesError
13
- attr_accessor :resource_uri
31
+ # extend T::Sig
32
+
33
+ # T::Sig::WithoutRuntime.sig { returns(T.nilable(String)) }
34
+ attr_reader :resource_uri
35
+
36
+ # T::Sig::WithoutRuntime.sig { params(resource_uri: String).returns(String) }
37
+ attr_writer :resource_uri
38
+
39
+ # T::Sig::WithoutRuntime.sig { params(args: T.untyped).void }
40
+ def initialize(*args)
41
+ # @resource_uri = T.let(@resource_uri, T.nilable(String))
42
+ super
43
+ end
14
44
  end
15
45
 
16
46
  class GetResourceError < KubernetesError; end
47
+ class DeleteResourceError < KubernetesError; end
48
+ class PatchResourceError < KubernetesError; end
49
+ class AnnotateResourceError < KubernetesError; end
50
+ class GetVersionError < KubernetesError; end
17
51
 
18
52
  STATUS_KEY = :kubernetes_cli_last_status
19
53
  STDOUT_KEY = :kubernetes_cli_stdout
20
54
  STDERR_KEY = :kubernetes_cli_stderr
21
55
 
22
- attr_reader :kubeconfig_path, :executable
56
+ # T::Sig::WithoutRuntime.sig { returns(String) }
57
+ attr_reader :kubeconfig_path
58
+
59
+ # T::Sig::WithoutRuntime.sig { returns(String) }
60
+ attr_reader :executable
23
61
 
62
+ # BeforeCallback = T.type_alias { T.proc.params(cmd: T::Array[String]).void }
63
+ # AfterCallback = T.type_alias do
64
+ # T.proc.params(cmd: T::Array[String], last_status: Process::Status).void
65
+ # end
66
+
67
+ # T::Sig::WithoutRuntime.sig { params(kubeconfig_path: String, executable: String).void }
24
68
  def initialize(kubeconfig_path, executable = KubectlRb.executable)
25
69
  @kubeconfig_path = kubeconfig_path
26
70
  @executable = executable
27
71
  @before_execute = []
28
72
  @after_execute = []
73
+ # @env = T.let(@env, T.nilable(T::Hash[String, String]))
29
74
  end
30
75
 
76
+ # T::Sig::WithoutRuntime.sig { params(block: BeforeCallback).void }
31
77
  def before_execute(&block)
32
78
  @before_execute << block
33
79
  end
34
80
 
81
+ # T::Sig::WithoutRuntime.sig { params(block: AfterCallback).void }
35
82
  def after_execute(&block)
36
83
  @after_execute << block
37
84
  end
38
85
 
86
+ # T::Sig::WithoutRuntime.sig { returns(T.nilable(Process::Status)) }
39
87
  def last_status
40
88
  Thread.current[STATUS_KEY]
41
89
  end
42
90
 
91
+ # T::Sig::WithoutRuntime.sig { params(block: T.proc.params(last_status: Process::Status).void).void }
92
+ def with_last_status(&block)
93
+ block.call(last_status)
94
+ end
95
+
96
+ # T::Sig::WithoutRuntime.sig { params(block: T.proc.params(last_status: Process::Status).void).void }
97
+ def on_last_status_failure(&block)
98
+ with_last_status do |ls|
99
+ block.call(ls) unless ls.success?
100
+ end
101
+ end
102
+
103
+ # T::Sig::WithoutRuntime.sig { returns(T::Hash[T.untyped, T.untyped]) }
104
+ def version
105
+ cmd = [executable, '--kubeconfig', kubeconfig_path, 'version', '-o', 'json']
106
+ result = backticks(cmd)
107
+
108
+ on_last_status_failure do |last_status|
109
+ raise GetVersionError, "couldn't get version info: "\
110
+ "kubectl exited with status code #{last_status.exitstatus}"
111
+ end
112
+
113
+ JSON.parse(result)
114
+ end
115
+
116
+ # T::Sig::WithoutRuntime.sig { params(cmd: T.any(String, T::Array[String])).void }
43
117
  def run_cmd(cmd)
44
118
  cmd = [executable, '--kubeconfig', kubeconfig_path, *Array(cmd)]
45
119
  execc(cmd)
46
120
  end
47
121
 
48
- def exec_cmd(container_cmd, namespace, pod, tty = true)
122
+ # T::Sig::WithoutRuntime.sig {
123
+ # params(
124
+ # container_cmd: T.any(String, T::Array[String]),
125
+ # namespace: String,
126
+ # pod: String,
127
+ # tty: T::Boolean,
128
+ # container: T.nilable(String),
129
+ # out_file: T.nilable(String)
130
+ # ).void
131
+ # }
132
+ def exec_cmd(container_cmd, namespace, pod, tty = true, container = nil, out_file = nil)
49
133
  cmd = [executable, '--kubeconfig', kubeconfig_path, '-n', namespace, 'exec']
50
134
  cmd += ['-it'] if tty
135
+ cmd += ['-c', container] if container
51
136
  cmd += [pod, '--', *Array(container_cmd)]
137
+ cmd += ['>', out_file] if out_file
52
138
  execc(cmd)
53
139
  end
54
140
 
55
- def system_cmd(container_cmd, namespace, pod, tty = true)
141
+ # T::Sig::WithoutRuntime.sig {
142
+ # params(
143
+ # container_cmd: T.any(String, T::Array[String]),
144
+ # namespace: String,
145
+ # pod: String,
146
+ # tty: T::Boolean,
147
+ # container: T.nilable(String)
148
+ # ).void
149
+ # }
150
+ def system_cmd(container_cmd, namespace, pod, tty = true, container = nil)
56
151
  cmd = [executable, '--kubeconfig', kubeconfig_path, '-n', namespace, 'exec']
57
152
  cmd += ['-it'] if tty
153
+ cmd += ['-c', container] if container
58
154
  cmd += [pod, '--', *Array(container_cmd)]
59
155
  systemm(cmd)
60
156
  end
61
157
 
158
+ # T::Sig::WithoutRuntime.sig { params(res: ::KubeDSL::DSLObject, dry_run: T::Boolean).void }
62
159
  def apply(res, dry_run: false)
63
160
  cmd = [executable, '--kubeconfig', kubeconfig_path, 'apply', '--validate']
64
161
  cmd << '--dry-run=client' if dry_run
@@ -68,8 +165,8 @@ class KubernetesCLI
68
165
  stdin.puts(res.to_resource.to_yaml)
69
166
  end
70
167
 
71
- unless last_status.success?
72
- err = InvalidResourceError.new("Could not apply #{res.kind_sym.to_s.humanize.downcase} "\
168
+ on_last_status_failure do |last_status|
169
+ err = InvalidResourceError.new("Could not apply #{res.kind_sym} "\
73
170
  "'#{res.metadata.name}': kubectl exited with status code #{last_status.exitstatus}"
74
171
  )
75
172
 
@@ -78,13 +175,14 @@ class KubernetesCLI
78
175
  end
79
176
  end
80
177
 
178
+ # T::Sig::WithoutRuntime.sig { params(uri: String, dry_run: T::Boolean).void }
81
179
  def apply_uri(uri, dry_run: false)
82
180
  cmd = [executable, '--kubeconfig', kubeconfig_path, 'apply', '--validate']
83
181
  cmd << '--dry-run=client' if dry_run
84
182
  cmd += ['-f', uri]
85
183
  systemm(cmd)
86
184
 
87
- unless last_status.success?
185
+ on_last_status_failure do |last_status|
88
186
  err = InvalidResourceUriError.new("Could not apply #{uri}: "\
89
187
  "kubectl exited with status code #{last_status.exitstatus}"
90
188
  )
@@ -94,29 +192,48 @@ class KubernetesCLI
94
192
  end
95
193
  end
96
194
 
97
- def get_object(type, namespace, name = nil, match_labels = {})
98
- cmd = [executable, '--kubeconfig', kubeconfig_path, '-n', namespace]
195
+ # T::Sig::WithoutRuntime.sig {
196
+ # params(
197
+ # type: String,
198
+ # namespace: String,
199
+ # name: String
200
+ # ).returns(
201
+ # T::Hash[String, T.untyped]
202
+ # )
203
+ # }
204
+ def get_object(type, namespace, name)
205
+ cmd = [executable, '--kubeconfig', kubeconfig_path]
206
+ cmd += ['-n', namespace] if namespace
99
207
  cmd += ['get', type, name]
100
-
101
- unless match_labels.empty?
102
- cmd += ['--selector', match_labels.map { |key, value| "#{key}=#{value}" }.join(',')]
103
- end
104
-
105
208
  cmd += ['-o', 'json']
106
209
 
107
210
  result = backticks(cmd)
108
211
 
109
- unless last_status.success?
110
- raise GetResourceError, "couldn't get resources of type '#{type}' "\
212
+ on_last_status_failure do |last_status|
213
+ raise GetResourceError, "couldn't get resource of type '#{type}' named '#{name}' "\
111
214
  "in namespace #{namespace}: kubectl exited with status code #{last_status.exitstatus}"
112
215
  end
113
216
 
114
217
  JSON.parse(result)
115
218
  end
116
219
 
220
+ # T::Sig::WithoutRuntime.sig {
221
+ # params(
222
+ # type: String,
223
+ # namespace: T.any(String, Symbol),
224
+ # match_labels: T::Hash[String, String]
225
+ # ).returns(
226
+ # T::Array[T.untyped]
227
+ # )
228
+ # }
117
229
  def get_objects(type, namespace, match_labels = {})
118
- cmd = [executable, '--kubeconfig', kubeconfig_path, '-n', namespace]
119
- cmd += ['get', type]
230
+ cmd = [executable, '--kubeconfig', kubeconfig_path, 'get', type]
231
+
232
+ if namespace == :all
233
+ cmd << '--all-namespaces'
234
+ elsif namespace
235
+ cmd += ['-n', namespace.to_s]
236
+ end
120
237
 
121
238
  unless match_labels.empty?
122
239
  cmd += ['--selector', match_labels.map { |key, value| "#{key}=#{value}" }.join(',')]
@@ -126,7 +243,7 @@ class KubernetesCLI
126
243
 
127
244
  result = backticks(cmd)
128
245
 
129
- unless last_status.success?
246
+ on_last_status_failure do |last_status|
130
247
  raise GetResourceError, "couldn't get resources of type '#{type}' "\
131
248
  "in namespace #{namespace}: kubectl exited with status code #{last_status.exitstatus}"
132
249
  end
@@ -134,6 +251,89 @@ class KubernetesCLI
134
251
  JSON.parse(result)['items']
135
252
  end
136
253
 
254
+ # T::Sig::WithoutRuntime.sig {
255
+ # params(
256
+ # type: String,
257
+ # namespace: String,
258
+ # name: String
259
+ # ).void
260
+ # }
261
+ def delete_object(type, namespace, name)
262
+ cmd = [executable, '--kubeconfig', kubeconfig_path]
263
+ cmd += ['-n', namespace] if namespace
264
+ cmd += ['delete', type, name]
265
+
266
+ systemm(cmd)
267
+
268
+ on_last_status_failure do |last_status|
269
+ raise DeleteResourceError, "couldn't delete resource of type '#{type}' named '#{name}' "\
270
+ "in namespace #{namespace}: kubectl exited with status code #{last_status.exitstatus}"
271
+ end
272
+ end
273
+
274
+ # T::Sig::WithoutRuntime.sig {
275
+ # params(
276
+ # type: String,
277
+ # namespace: T.any(String, Symbol),
278
+ # match_labels: T::Hash[String, String]
279
+ # ).void
280
+ # }
281
+ def delete_objects(type, namespace, match_labels = {})
282
+ cmd = [executable, '--kubeconfig', kubeconfig_path]
283
+
284
+ if namespace == :all
285
+ cmd << '--all-namespaces'
286
+ elsif namespace
287
+ cmd += ['-n', namespace.to_s]
288
+ end
289
+
290
+ cmd += ['delete', type]
291
+
292
+ unless match_labels.empty?
293
+ cmd += ['--selector', match_labels.map { |key, value| "#{key}=#{value}" }.join(',')]
294
+ end
295
+
296
+ systemm(cmd)
297
+
298
+ on_last_status_failure do |last_status|
299
+ raise DeleteResourceError, "couldn't delete resources of type '#{type}' "\
300
+ "in namespace #{namespace}: kubectl exited with status code #{last_status.exitstatus}"
301
+ end
302
+ end
303
+
304
+ # T::Sig::WithoutRuntime.sig {
305
+ # params(
306
+ # type: String,
307
+ # namespace: String,
308
+ # name: String,
309
+ # patch_data: String,
310
+ # patch_type: String
311
+ # ).void
312
+ # }
313
+ def patch_object(type, namespace, name, patch_data, patch_type = 'merge')
314
+ cmd = [executable, '--kubeconfig', kubeconfig_path]
315
+ cmd += ['-n', namespace] if namespace
316
+ cmd += ['patch', type, name]
317
+ cmd += ['-p', Shellwords.shellescape(patch_data)]
318
+ cmd += ['--type', patch_type]
319
+
320
+ systemm(cmd)
321
+
322
+ on_last_status_failure do |last_status|
323
+ raise PatchResourceError, "couldn't patch resource of type '#{type}' named '#{name}' "\
324
+ "in namespace #{namespace}: kubectl exited with status code #{last_status.exitstatus}"
325
+ end
326
+ end
327
+
328
+ # T::Sig::WithoutRuntime.sig {
329
+ # params(
330
+ # type: String,
331
+ # namespace: String,
332
+ # name: String,
333
+ # annotations: T::Hash[String, String],
334
+ # overwrite: T::Boolean
335
+ # ).void
336
+ # }
137
337
  def annotate(type, namespace, name, annotations, overwrite: true)
138
338
  cmd = [
139
339
  executable,
@@ -151,12 +351,19 @@ class KubernetesCLI
151
351
 
152
352
  systemm(cmd)
153
353
 
154
- unless last_status.success?
155
- raise KubernetesError, "could not annotate resource '#{name}': kubectl "\
354
+ on_last_status_failure do |last_status|
355
+ raise AnnotateResourceError, "could not annotate resource '#{name}': kubectl "\
156
356
  "exited with status code #{last_status.exitstatus}"
157
357
  end
158
358
  end
159
359
 
360
+ # T::Sig::WithoutRuntime.sig {
361
+ # params(
362
+ # namespace: String,
363
+ # selector: T::Hash[String, String],
364
+ # follow: T::Boolean
365
+ # ).void
366
+ # }
160
367
  def logtail(namespace, selector, follow: true)
161
368
  cmd = [executable, '--kubeconfig', kubeconfig_path, '-n', namespace, 'logs']
162
369
  cmd << '-f' if follow
@@ -165,16 +372,18 @@ class KubernetesCLI
165
372
  execc(cmd)
166
373
  end
167
374
 
375
+ # T::Sig::WithoutRuntime.sig { returns(String) }
168
376
  def current_context
169
377
  cmd = [executable, '--kubeconfig', kubeconfig_path, 'config', 'current-context']
170
378
  backticks(cmd).strip
171
379
  end
172
380
 
381
+ # T::Sig::WithoutRuntime.sig { returns(String) }
173
382
  def api_resources
174
383
  cmd = [executable, '--kubeconfig', kubeconfig_path, 'api-resources']
175
384
  result = backticks(cmd)
176
385
 
177
- unless last_status.success?
386
+ on_last_status_failure do |last_status|
178
387
  raise KubernetesError, 'could not fetch API resources: kubectl exited with '\
179
388
  "status code #{last_status.exitstatus}. #{result}"
180
389
  end
@@ -182,6 +391,7 @@ class KubernetesCLI
182
391
  result
183
392
  end
184
393
 
394
+ # T::Sig::WithoutRuntime.sig { params(namespace: String, deployment: String).void }
185
395
  def restart_deployment(namespace, deployment)
186
396
  cmd = [
187
397
  executable,
@@ -192,13 +402,14 @@ class KubernetesCLI
192
402
 
193
403
  systemm(cmd)
194
404
 
195
- unless last_status.success?
405
+ on_last_status_failure do |last_status|
196
406
  raise KubernetesError, 'could not restart deployment: kubectl exited with '\
197
407
  "status code #{last_status.exitstatus}"
198
408
  end
199
409
  end
200
410
 
201
- def with_pipes(out = STDOUT, err = STDERR)
411
+ # T::Sig::WithoutRuntime.sig { params(out: T.any(StringIO, IO), err: T.any(StringIO, IO), block: T.proc.void).void }
412
+ def with_pipes(out = STDOUT, err = STDERR, &block)
202
413
  previous_stdout = self.stdout
203
414
  previous_stderr = self.stderr
204
415
  self.stdout = out
@@ -209,39 +420,66 @@ class KubernetesCLI
209
420
  self.stderr = previous_stderr
210
421
  end
211
422
 
423
+ # T::Sig::WithoutRuntime.sig { returns(T.any(StringIO, IO)) }
212
424
  def stdout
213
425
  Thread.current[STDOUT_KEY] || STDOUT
214
426
  end
215
427
 
428
+ # T::Sig::WithoutRuntime.sig { params(new_stdout: T.nilable(T.any(StringIO, IO))).void }
216
429
  def stdout=(new_stdout)
217
430
  Thread.current[STDOUT_KEY] = new_stdout
218
431
  end
219
432
 
433
+ # T::Sig::WithoutRuntime.sig { returns(T.any(StringIO, IO)) }
220
434
  def stderr
221
435
  Thread.current[STDERR_KEY] || STDERR
222
436
  end
223
437
 
438
+ # T::Sig::WithoutRuntime.sig { params(new_stderr: T.nilable(T.any(StringIO, IO))).void }
224
439
  def stderr=(new_stderr)
225
440
  Thread.current[STDERR_KEY] = new_stderr
226
441
  end
227
442
 
228
443
  private
229
444
 
445
+ # T::Sig::WithoutRuntime.sig { returns(T::Hash[String, String]) }
230
446
  def env
231
447
  @env ||= {}
232
448
  end
233
449
 
450
+ # T::Sig::WithoutRuntime.sig { returns(T::Array[String]) }
234
451
  def base_cmd
235
452
  [executable, '--kubeconfig', kubeconfig_path]
236
453
  end
237
454
 
455
+ # T::Sig::WithoutRuntime.sig { params(cmd: T::Array[String]).void }
238
456
  def execc(cmd)
239
457
  run_before_callbacks(cmd)
240
458
  cmd_s = cmd.join(' ')
241
459
  exec(cmd_s)
242
460
  end
243
461
 
462
+ # T::Sig::WithoutRuntime.sig { params(cmd: T::Array[String]).void }
244
463
  def systemm(cmd)
464
+ if stdout == STDOUT && stderr == STDERR
465
+ systemm_default(cmd)
466
+ else
467
+ systemm_open3(cmd)
468
+ end
469
+ end
470
+
471
+ # T::Sig::WithoutRuntime.sig { params(cmd: T::Array[String]).void }
472
+ def systemm_default(cmd)
473
+ run_before_callbacks(cmd)
474
+ cmd_s = cmd.join(' ')
475
+ system(cmd_s).tap do
476
+ self.last_status = $?
477
+ run_after_callbacks(cmd)
478
+ end
479
+ end
480
+
481
+ # T::Sig::WithoutRuntime.sig { params(cmd: T::Array[String]).void }
482
+ def systemm_open3(cmd)
245
483
  run_before_callbacks(cmd)
246
484
  cmd_s = cmd.join(' ')
247
485
 
@@ -267,7 +505,27 @@ class KubernetesCLI
267
505
  end
268
506
  end
269
507
 
508
+ # T::Sig::WithoutRuntime.sig { params(cmd: T::Array[String]).returns(String) }
270
509
  def backticks(cmd)
510
+ if stdout == STDOUT && stderr == STDERR
511
+ backticks_default(cmd)
512
+ else
513
+ backticks_open3(cmd)
514
+ end
515
+ end
516
+
517
+ # T::Sig::WithoutRuntime.sig { params(cmd: T::Array[String]).returns(String) }
518
+ def backticks_default(cmd)
519
+ run_before_callbacks(cmd)
520
+ cmd_s = cmd.join(' ')
521
+ `#{cmd_s}`.tap do
522
+ self.last_status = $?
523
+ run_after_callbacks(cmd)
524
+ end
525
+ end
526
+
527
+ # T::Sig::WithoutRuntime.sig { params(cmd: T::Array[String]).returns(String) }
528
+ def backticks_open3(cmd)
271
529
  run_before_callbacks(cmd)
272
530
  cmd_s = cmd.join(' ')
273
531
  result = StringIO.new
@@ -296,10 +554,20 @@ class KubernetesCLI
296
554
  result.string
297
555
  end
298
556
 
557
+ # T::Sig::WithoutRuntime.sig {
558
+ # params(
559
+ # env: T::Hash[String, String],
560
+ # cmd: T::Array[String],
561
+ # opts: T::Hash[Symbol, T.untyped],
562
+ # block: T.proc.params(p_stdin: IO).void
563
+ # ).void
564
+ # }
299
565
  def open3_w(env, cmd, opts = {}, &block)
300
566
  run_before_callbacks(cmd)
301
567
  cmd_s = cmd.join(' ')
302
568
 
569
+ # unsafes here b/c popen3 takes an optional first argument hash
570
+ # with environment variables, which confuses the sh*t out of sorbet
303
571
  Open3.popen3(env, cmd_s, opts) do |p_stdin, p_stdout, p_stderr, wait_thread|
304
572
  Thread.new(stdout) do |t_stdout|
305
573
  begin
@@ -315,23 +583,26 @@ class KubernetesCLI
315
583
  end
316
584
  end
317
585
 
318
- yield(p_stdin).tap do
319
- p_stdin.close
320
- self.last_status = wait_thread.value
321
- run_after_callbacks(cmd)
322
- wait_thread.join
323
- end
586
+ yield(p_stdin)
587
+
588
+ p_stdin.close
589
+ self.last_status = wait_thread.value
590
+ run_after_callbacks(cmd)
591
+ wait_thread.join
324
592
  end
325
593
  end
326
594
 
595
+ # T::Sig::WithoutRuntime.sig { params(cmd: T::Array[String]).void }
327
596
  def run_before_callbacks(cmd)
328
597
  @before_execute.each { |cb| cb.call(cmd) }
329
598
  end
330
599
 
600
+ # T::Sig::WithoutRuntime.sig { params(cmd: T::Array[String]).void }
331
601
  def run_after_callbacks(cmd)
332
602
  @after_execute.each { |cb| cb.call(cmd, last_status) }
333
603
  end
334
604
 
605
+ # T::Sig::WithoutRuntime.sig { params(status: Process::Status).void }
335
606
  def last_status=(status)
336
607
  Thread.current[STATUS_KEY] = status
337
608
  end