cloud-sh 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/exe/kubetail ADDED
@@ -0,0 +1,357 @@
1
+ #!/bin/bash
2
+
3
+ # copied from https://raw.githubusercontent.com/johanhaleby/kubetail/master/kubetail (SHA: b13267a6d25b364eeb636060f426c7eba4e28a39)
4
+
5
+ KUBECTL_BIN=${KUBECTL_BIN:-"kubectl"}
6
+
7
+ readonly PROGNAME=$(basename $0)
8
+
9
+ calculate_default_namespace() {
10
+ local config_namespace=$(${KUBECTL_BIN} config view --minify --output 'jsonpath={..namespace}')
11
+ echo "${KUBETAIL_NAMESPACE:-${config_namespace:-default}}"
12
+ }
13
+
14
+ default_previous="${KUBETAIL_PREVIOUS:-false}"
15
+ default_since="${KUBETAIL_SINCE:-10s}"
16
+ default_namespace=$(calculate_default_namespace)
17
+ default_follow="${KUBETAIL_FOLLOW:-true}"
18
+ default_line_buffered="${KUBETAIL_LINE_BUFFERED:-}"
19
+ default_colored_output="${KUBETAIL_COLORED_OUTPUT:-line}"
20
+ default_timestamps="${KUBETAIL_TIMESTAMPS:-}"
21
+ default_jq_selector="${KUBETAIL_JQ_SELECTOR:-}"
22
+ default_skip_colors="${KUBETAIL_SKIP_COLORS:-7,8}"
23
+ default_tail="${KUBETAIL_TAIL:--1}"
24
+ default_show_color_index="${KUBETAIL_SHOW_COLOR_INDEX:-false}"
25
+
26
+ namespace="${default_namespace}"
27
+ follow="${default_follow}"
28
+ line_buffered="${default_line_buffered}"
29
+ colored_output="${default_colored_output}"
30
+ timestamps="${default_timestamps}"
31
+ jq_selector="${default_jq_selector}"
32
+ skip_colors="${default_skip_colors}"
33
+ tail="${default_tail}"
34
+ show_color_index="${default_show_color_index}"
35
+
36
+ if [[ ${1} != -* ]]
37
+ then
38
+ pod="${1}"
39
+ fi
40
+ containers=()
41
+ selector=()
42
+ regex='substring'
43
+ previous="${default_previous}"
44
+ since="${default_since}"
45
+ version="1.6.11-SNAPSHOT"
46
+ dryrun=false
47
+ cluster=""
48
+ namespace_arg="-n ${default_namespace}"
49
+
50
+ usage="${PROGNAME} <search term> [-h] [-c] [-n] [-t] [-l] [-d] [-p] [-s] [-b] [-k] [-v] [-r] [-i] -- tail multiple Kubernetes pod logs at the same time
51
+
52
+ where:
53
+ -h, --help Show this help text
54
+ -c, --container The name of the container to tail in the pod (if multiple containers are defined in the pod).
55
+ Defaults to all containers in the pod. Can be used multiple times.
56
+ -t, --context The k8s context. ex. int1-context. Relies on ~/.kube/config for the contexts.
57
+ -l, --selector Label selector. If used the pod name is ignored.
58
+ -n, --namespace The Kubernetes namespace where the pods are located (defaults to \"${default_namespace}\")
59
+ -f, --follow Specify if the logs should be streamed. (true|false) Defaults to ${default_follow}.
60
+ -d, --dry-run Print the names of the matched pods and containers, then exit.
61
+ -p, --previous Return logs for the previous instances of the pods, if available. (true|false) Defaults to ${default_previous}.
62
+ -s, --since Only return logs newer than a relative duration like 5s, 2m, or 3h. Defaults to ${default_since}.
63
+ -b, --line-buffered This flags indicates to use line-buffered. Defaults to false.
64
+ -e, --regex The type of name matching to use (regex|substring)
65
+ -j, --jq If your output is json - use this jq-selector to parse it.
66
+ example: --jq \".logger + \\\" \\\" + .message\"
67
+ -k, --colored-output Use colored output (pod|line|false).
68
+ pod = only color pod name, line = color entire line, false = don't use any colors.
69
+ Defaults to ${default_colored_output}.
70
+ -z, --skip-colors Comma-separated list of colors to not use in output
71
+ If you have green foreground on black, this will skip dark grey and some greens -z 2,8,10
72
+ Defaults to: ${default_skip_colors}
73
+ --timestamps Show timestamps for each log line
74
+ --tail Lines of recent log file to display. Defaults to ${default_tail}, showing all log lines.
75
+ -v, --version Prints the kubetail version
76
+ -r, --cluster The name of the kubeconfig cluster to use.
77
+ -i, --show-color-index Show the color index before the pod name prefix that is shown before each log line.
78
+ Normally only the pod name is added as a prefix before each line, for example \"[app-5b7ff6cbcd-bjv8n]\",
79
+ but if \"show-color-index\" is true then color index is added as well: \"[1:app-5b7ff6cbcd-bjv8n]\".
80
+ This is useful if you have color blindness or if you want to know which colors to exclude (see \"--skip-colors\").
81
+ Defaults to ${default_show_color_index}.
82
+
83
+ examples:
84
+ ${PROGNAME} my-pod-v1
85
+ ${PROGNAME} my-pod-v1 -c my-container
86
+ ${PROGNAME} my-pod-v1 -t int1-context -c my-container
87
+ ${PROGNAME} '(service|consumer|thing)' -e regex
88
+ ${PROGNAME} -l service=my-service
89
+ ${PROGNAME} --selector service=my-service --since 10m
90
+ ${PROGNAME} --tail 1"
91
+
92
+ if [ "$#" -ne 0 ]; then
93
+ while [ "$#" -gt 0 ]
94
+ do
95
+ case "$1" in
96
+ -h|--help)
97
+ echo "$usage"
98
+ exit 0
99
+ ;;
100
+ -v|--version)
101
+ echo "$version"
102
+ exit 0
103
+ ;;
104
+ -c|--container)
105
+ containers+=("$2")
106
+ ;;
107
+ -e|--regex)
108
+ regex="regex"
109
+ ;;
110
+ -t|--context)
111
+ context="$2"
112
+ ;;
113
+ -r|--cluster)
114
+ cluster="--cluster $2"
115
+ ;;
116
+ -l|--selector)
117
+ selector=(--selector "$2")
118
+ pod=""
119
+ ;;
120
+ -d|--dry-run)
121
+ dryrun=true
122
+ ;;
123
+ -p|--previous)
124
+ previous=true
125
+ ;;
126
+ -s|--since)
127
+ if [ -z "$2" ]; then
128
+ since="${default_since}"
129
+ else
130
+ since="$2"
131
+ fi
132
+ ;;
133
+ -n|--namespace)
134
+ if [ -z "$2" ]; then
135
+ # using namespace from context
136
+ :
137
+ else
138
+ namespace_arg="--namespace $2"
139
+ fi
140
+ ;;
141
+ -f|--follow)
142
+ if [ "$2" = "false" ]; then
143
+ follow="false"
144
+ fi
145
+ ;;
146
+ -b|--line-buffered)
147
+ if [ "$2" = "true" ]; then
148
+ line_buffered="| grep - --line-buffered"
149
+ fi
150
+ ;;
151
+ -k|--colored-output)
152
+ if [ -z "$2" ]; then
153
+ colored_output="${default_colored_output}"
154
+ else
155
+ colored_output="$2"
156
+ fi
157
+ ;;
158
+ -j|--jq)
159
+ if [ -z "$2" ]; then
160
+ jq_selector="${default_jq_selector}"
161
+ else
162
+ jq_selector="$2"
163
+ fi
164
+ ;;
165
+ -z|--skip-colors)
166
+ if [ -z "$2" ]; then
167
+ skip_colors="${default_skip_colors}"
168
+ else
169
+ skip_colors="$2"
170
+ fi
171
+ ;;
172
+ --timestamps)
173
+ if [ "$2" = "false" ]; then
174
+ timestamps="$1=$2"
175
+ else
176
+ timestamps="$1"
177
+ fi
178
+ ;;
179
+ --tail)
180
+ if [ -z "$2" ]; then
181
+ tail="${default_tail}"
182
+ else
183
+ tail="$2"
184
+ fi
185
+ ;;
186
+ -i|--show-color-index)
187
+ if [ -z "$2" ]; then
188
+ show_color_index="${default_show_color_index}"
189
+ else
190
+ show_color_index="$2"
191
+ fi
192
+ ;;
193
+ --)
194
+ break
195
+ ;;
196
+ -*)
197
+ echo "Invalid option '$1'. Use --help to see the valid options" >&2
198
+ exit 1
199
+ ;;
200
+ # an option argument, continue
201
+ *) ;;
202
+ esac
203
+ shift
204
+ done
205
+ else
206
+ echo "$usage"
207
+ exit 1
208
+ fi
209
+
210
+ # Join function that supports a multi-character separator (copied from http://stackoverflow.com/a/23673883/398441)
211
+ function join() {
212
+ # $1 is return variable name
213
+ # $2 is sep
214
+ # $3... are the elements to join
215
+ local retname=$1 sep=$2 ret=$3
216
+ shift 3 || shift $(($#))
217
+ printf -v "$retname" "%s" "$ret${@/#/$sep}"
218
+ }
219
+
220
+ # Check if pod query contains a comma and we've not specified "regex" explicitly,
221
+ # if so we convert the pod query string into a regex that matches all pods seperated by the comma
222
+ if [[ "${pod}" = *","* ]] && [ ! "${regex}" == 'regex' ]; then
223
+
224
+ # Split the supplied query string (in variable pod) by comma into an array named "pods_to_match"
225
+ IFS=',' read -r -a pods_to_match <<< "${pod}"
226
+
227
+ # Join all pod names into a string with ".*|.*" as delimiter
228
+ join pod ".*|.*" "${pods_to_match[@]}"
229
+
230
+ # Prepend and initial ".*" and and append the last ".*"
231
+ pod=".*${pod}.*"
232
+
233
+ # Force the use of regex matching
234
+ regex='regex'
235
+ fi
236
+
237
+ grep_matcher=''
238
+ if [ "${regex}" == 'regex' ]; then
239
+ echo "Using regex '${pod}' to match pods"
240
+ grep_matcher='-E'
241
+ fi
242
+
243
+ # Get all pods matching the input and put them in an array. If no input then all pods are matched.
244
+ matching_pods=(`${KUBECTL_BIN} get pods ${context:+--context=${context}} "${selector[@]}" ${namespace_arg} ${cluster} --output=jsonpath='{.items[*].metadata.name}' | xargs -n1 | grep --color=never $grep_matcher "${pod}"`)
245
+ matching_pods_size=${#matching_pods[@]}
246
+
247
+ if [ ${matching_pods_size} -eq 0 ]; then
248
+ echo "No pod exists that matches ${pod}"
249
+ exit 1
250
+ fi
251
+
252
+ color_end=$(tput sgr0)
253
+
254
+ # Wrap all pod names in the "kubectl logs <name> -f=true/false" command
255
+ display_names_preview=()
256
+ pod_logs_commands=()
257
+ i=0
258
+ color_index=0
259
+
260
+ function next_col {
261
+ potential_col=$(($1+1))
262
+ [[ $skip_colors =~ (^|,)$potential_col($|,) ]] && echo `next_col $potential_col` || echo $potential_col
263
+ }
264
+
265
+ # Allows for more colors, this is useful if one tails a lot pods
266
+ if [ ${colored_output} != "false" ]; then
267
+ export TERM=xterm-256color
268
+ fi
269
+
270
+ # Function that kills all kubectl processes that are started by kubetail in the background
271
+ function kill_kubectl_processes {
272
+ kill 0
273
+ }
274
+
275
+ # Invoke the "kill_kubectl_processes" function when the script is stopped (including ctrl+c)
276
+ # Note that "INT" is not used because if, for example, kubectl cannot find a container
277
+ # (for example when running "kubetail something -c non_matching")
278
+ trap kill_kubectl_processes EXIT
279
+
280
+ # Putting all needed values in a variable so that multiple requests to Kubernetes api can be avoided, thus making it faster
281
+ all_pods_containers=$(echo -e `${KUBECTL_BIN} get pods ${namespace_arg} ${context:+--context=${context}} --output=jsonpath="{range .items[*]}{.metadata.name} {.spec['containers', 'initContainers'][*].name} \n{end}"`)
282
+
283
+
284
+ for pod in ${matching_pods[@]}; do
285
+ if [ ${#containers[@]} -eq 0 ]; then
286
+ pod_containers=($(echo -e "$all_pods_containers" | grep $pod | cut -d ' ' -f2- | xargs -n1))
287
+ else
288
+ pod_containers=("${containers[@]}")
289
+ fi
290
+
291
+ for container in ${pod_containers[@]}; do
292
+ if [ ${colored_output} == "false" ] || [ ${matching_pods_size} -eq 1 -a ${#pod_containers[@]} -eq 1 ]; then
293
+ color_start=$(tput sgr0)
294
+ color_index_prefix=""
295
+ else
296
+ color_index=`next_col $color_index`
297
+ color_start=$(tput setaf $color_index)
298
+ color_index_prefix=`if [ ${show_color_index} == "true" ]; then echo "${color_index}:"; else echo ""; fi`
299
+ fi
300
+
301
+ if [ ${#pod_containers[@]} -eq 1 ]; then
302
+ display_name="${pod}"
303
+ else
304
+ display_name="${pod} ${container}"
305
+ fi
306
+
307
+ if [ ${colored_output} == "false" ]; then
308
+ display_names_preview+=("${display_name}")
309
+ else
310
+ display_names_preview+=("$color_index_prefix${color_start}${display_name}${color_end}")
311
+ fi
312
+
313
+ if [ ${colored_output} == "false" ]; then
314
+ colored_line="[${display_name}] \$REPLY"
315
+ elif [ ${colored_output} == "pod" ]; then
316
+ colored_line="${color_start}[${color_end}${color_index_prefix}${color_start}${display_name}]${color_end} \$REPLY"
317
+ else
318
+ # color_index_prefix=`if [ ${show_color_index} == "true" ]; then echo "${color_index}:"; else echo ""; fi`
319
+ colored_line="${color_start}[${color_end}${color_index_prefix}${color_start}${display_name}] \$REPLY ${color_end}"
320
+ fi
321
+
322
+ kubectl_cmd="${KUBECTL_BIN} ${context:+--context=${context}} logs ${pod} ${container} -f=${follow} --previous=${previous} --since=${since} --tail=${tail} ${namespace_arg} ${cluster}"
323
+ colorify_lines_cmd="while read -r; do echo \"$colored_line\" | tail -n +1; done"
324
+ if [ "z" == "z$jq_selector" ]; then
325
+ logs_commands+=("${kubectl_cmd} ${timestamps} | ${colorify_lines_cmd}");
326
+ else
327
+ logs_commands+=("${kubectl_cmd} | jq --unbuffered -r -R --stream '. as \$line | try (fromjson | $jq_selector) catch \$line' | ${colorify_lines_cmd}");
328
+ fi
329
+
330
+ # There are only 11 usable colors
331
+ i=$(( ($i+1)%13 ))
332
+ done
333
+ done
334
+
335
+ # Preview pod colors
336
+ echo "Will tail ${#display_names_preview[@]} logs..."
337
+ for preview in "${display_names_preview[@]}"; do
338
+ echo "$preview"
339
+ done
340
+
341
+ if [[ ${dryrun} == true ]];
342
+ then
343
+ exit 0
344
+ fi
345
+
346
+ # Join all log commands into one string separated by " & "
347
+ join command_to_tail " & " "${logs_commands[@]}"
348
+
349
+ # Aggregate all logs and print to stdout
350
+ # Note that tail +1f doesn't work on some Linux distributions so we use this slightly longer alternative
351
+ # Note that if --follow=false, then the tail command should also not be followed
352
+ tail_follow_command="-f"
353
+ if [[ ${follow} == false ]];
354
+ then
355
+ tail_follow_command=""
356
+ fi
357
+ tail ${tail_follow_command} -n +1 <( eval "${command_to_tail}" ) $line_buffered
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cloud
4
+ module Sh
5
+ class Cli
6
+ extend GLI::App
7
+ subcommand_option_handling :normal
8
+ arguments :strict
9
+ program_desc "Cloud shell helpers"
10
+
11
+ desc "Refresh aliases"
12
+ command :refresh do |c|
13
+ c.action do |global_options, options, args|
14
+ Cloud::Sh::Commands::Refresh.execute(global_options, options, args)
15
+ end
16
+ end
17
+
18
+ # desc "SSH Into machines"
19
+ # arg_name 'hostname_or_ip'
20
+ # command :ssh do |c|
21
+ # c.action do |global_options, options, args|
22
+ # Cloud::Commands::Ssh.execute(global_options, options, args)
23
+ # end
24
+ # end
25
+
26
+ # desc "DB Commands"
27
+ # command :db do |c|
28
+ # c.desc "Open a cli to to the db"
29
+ # c.arg "database_name_or_url"
30
+ # c.command :cli do |sc|
31
+ # sc.flag :cli, desc: "DB CLI tool to use (psql, mysql, mycli)", required: false, default_value: "auto", arg_name: "cli"
32
+ # sc.action do |global_options, options, args|
33
+ # Cloud::Commands::Db::Cli.execute(global_options, options, args)
34
+ # end
35
+ # end
36
+
37
+ # c.desc "Dump the database content to a sql file"
38
+ # c.arg "database_name_or_url"
39
+ # c.command :dump do |sc|
40
+ # sc.action do |global_options, options, args|
41
+ # Cloud::Commands::Db::Dump.execute(global_options, options, args)
42
+ # end
43
+ # end
44
+ # end
45
+
46
+ desc "K8S Commands"
47
+ command :k8s do |c|
48
+ c.desc "Open a shell in a container"
49
+ c.command :exec do |sc|
50
+ sc.flag :context, desc: "K8S Context", required: true, arg_name: "context"
51
+ sc.flag :namespace, desc: "K8S Namespace", required: true, arg_name: "namespace"
52
+ sc.flag :pod, desc: "K8S Pod (prefix)", required: true, arg_name: "pod"
53
+ sc.flag :cmd, desc: "Shell / Command to execute", required: false, arg_name: "cmd", default_value: "bash"
54
+ sc.action do |global_options, options, args|
55
+ Cloud::Sh::Commands::K8sExec.execute(global_options, options, args)
56
+ end
57
+ end
58
+
59
+ c.desc "Tail output for a pod"
60
+ c.command :tail do |sc|
61
+ sc.flag :context, desc: "K8S Context", required: true, arg_name: "context"
62
+ sc.flag :namespace, desc: "K8S Namespace", required: true, arg_name: "namespace"
63
+ sc.flag :pod, desc: "K8S Pod (prefix)", required: true, arg_name: "pod", default_value: "all"
64
+ sc.flag :tail, desc: "Number of lines to tail initially", required: false, arg_name: "tail", default_value: "10"
65
+ sc.action do |global_options, options, args|
66
+ puts [global_options, options, args].inspect
67
+ Cloud::Sh::Commands::K8sTail.execute(global_options, options, args)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cloud
4
+ module Sh
5
+ module Commands
6
+ class Base
7
+ include Cloud::Sh::Helpers::Commands
8
+
9
+ attr_reader :options, :args
10
+
11
+ def self.execute(global_options, options, args)
12
+ new(options: global_options.merge(options), args: args).execute
13
+ rescue Exception => e
14
+ puts e.backtrace.join("\n")
15
+ puts e.inspect
16
+ end
17
+
18
+ def initialize(options:, args:)
19
+ @options = options
20
+ @args = args
21
+ end
22
+
23
+ def execute
24
+ raise NotImplementedError
25
+ end
26
+
27
+ def config
28
+ Cloud::Sh.config
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cloud/sh/commands/base"
4
+
5
+ module Cloud
6
+ module Sh
7
+ module Commands
8
+ class K8sExec < Base
9
+ def execute
10
+ pod = kubectl.context(options[:context]).namespace(options[:namespace]).get.pod.no_headers(true).map(:name).find do |pod|
11
+ pod.name.start_with?(options[:pod])
12
+ end
13
+ raise "Cannot find pod." unless pod
14
+ command = kubectl.context(options[:context]).namespace(options[:namespace]).exec.with(pod.name).stdin(true).tty(true).with(options[:cmd])
15
+ puts "Command: #{command}\n"
16
+ command.replace_current_process
17
+ end
18
+
19
+ def kubectl
20
+ command_chain("kubectl").kubeconfig(Cloud::Sh::Providers::DigitalOcean.kube_config)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ if false
28
+ #!/usr/bin/env ruby
29
+
30
+ require "optparse"
31
+
32
+ context = ARGV[0]
33
+ namespace = ARGV[1]
34
+ pod = ARGV[2]
35
+
36
+ pod_index = 1
37
+ container_name = nil
38
+ command = "bash"
39
+
40
+ OptionParser.new do |opts|
41
+ opts.banner = "Usage: k8s-#{context}-#{namespace}-tail-#{pod} [options]"
42
+ opts.on("-p","--pod [POD_INDEX]", Integer, "Pod index to show (default: 1)") do |v,c|
43
+ pod_index = v
44
+ end
45
+ opts.on("-c","--container [CONTAINER_NAME]", String, "Container name (default: first one found)") do |v|
46
+ container_name = v
47
+ end
48
+ opts.on("-x","--command [COMMAND]", String, "Command to execute (default: bash)") do |v|
49
+ command = v
50
+ end
51
+ end.parse(ARGV[3..100])
52
+
53
+ pods = `kubectl --context #{context} --namespace #{namespace} get pod -o name | cut -f2 -d/ | egrep -e "^#{pod}"`.split("\n").map(&:strip)
54
+ if pods.size == 0
55
+ puts "No pods found."
56
+ exit
57
+ end
58
+
59
+ if pod_index <= 0 || pod_index.to_i > pods.size
60
+ puts "Unrecognized pod index. Run the command again with a valid pod number: "
61
+ pods.each_with_index { |p, i| puts "#{i + 1}. #{p}" }
62
+ puts "\nExample: k8s-#{context}-#{namespace}-tail-#{pod} --pod 1"
63
+ exit
64
+ end
65
+ pod_name = pods[pod_index.to_i - 1]
66
+
67
+
68
+ containers = `kubectl --context #{context} --namespace #{namespace} get pod #{pod_name} -o jsonpath='{.spec.containers[*].name}'`.split(" ")
69
+ container_name ||= containers.first
70
+ unless containers.include?(container_name)
71
+ puts "Unrecognized container name. Available options: #{containers.join(", ")}"
72
+ puts "\nExample: k8s-#{context}-#{namespace}-tail-#{pod} --pod #{pod_index} --container #{containers.first}"
73
+ exit
74
+ end
75
+
76
+ puts "Context: #{context}"
77
+ puts "Namespace: #{namespace}"
78
+ puts "Pod: #{pod_name} (Options: #{pods.map.with_index { |p, i| "#{i + 1} - #{p}" }.join(", ")})"
79
+ puts "Container: #{container_name} (Options: #{containers.join(", ")})"
80
+ puts "Command: #{command}"
81
+
82
+ puts ""
83
+
84
+ cmd = "kubectl --context #{context} --namespace #{namespace} exec #{pod_name} -c #{container_name} -t -i #{command}"
85
+ puts "Running: #{cmd}"
86
+ puts ""
87
+ exec cmd
88
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cloud/sh/commands/base"
4
+
5
+ module Cloud
6
+ module Sh
7
+ module Commands
8
+ class K8sTail < Base
9
+ def execute
10
+ puts "Command: #{command}"
11
+ puts "Env: #{env.inspect}\n"
12
+ exec env, command
13
+ end
14
+
15
+ def command
16
+ [
17
+ exe,
18
+ "^" + (options[:pod] == "all" ? "." : options[:pod]),
19
+ "--context #{options[:context]}",
20
+ "--namespace #{options[:namespace]}",
21
+ "--regex",
22
+ "--tail #{options[:tail]}",
23
+ "--since 240h",
24
+ "--colored-output pod",
25
+ "--follow true"
26
+ ].join(" ")
27
+ end
28
+
29
+ def exe
30
+ File.expand_path("../../../../exe/vendor/kubetail", __dir__)
31
+ end
32
+
33
+ def env
34
+ {
35
+ "KUBECONFIG" => Cloud::Sh::Providers::DigitalOcean.kube_config
36
+ }
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end