kubernetes-cli 0.3.0 → 0.4.0

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 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