lib/seira/pods.rb in seira-0.1.1 vs lib/seira/pods.rb in seira-0.1.2
- old
+ new
@@ -1,70 +1,151 @@
+require 'json'
+
module Seira
class Pods
- VALID_ACTIONS = %w[list delete logs top run].freeze
+ VALID_ACTIONS = %w[list delete logs top run connect].freeze
- attr_reader :app, :action, :key, :value, :context
+ attr_reader :app, :action, :args, :pod_name, :context
def initialize(app:, action:, args:, context:)
@app = app
@action = action
@context = context
- @key = args[0]
- @value = args[1]
+ @args = args
+ @pod_name = args[0]
end
def run
- # TODO: Some options: 'top', 'kill', 'delete', 'logs'
case action
when 'list'
run_list
when 'delete'
run_delete
when 'logs'
run_logs
when 'top'
run_top
+ when 'connect'
+ run_connect
when 'run'
run_run
else
fail "Unknown command encountered"
end
end
private
def run_list
- puts list_pods
+ puts `kubectl get pods --namespace=#{app} -o wide`
end
def run_delete
- puts `kubectl delete pod #{@key} --namespace=#{@app}`
+ puts `kubectl delete pod #{pod_name} --namespace=#{app}`
end
def run_logs
- puts `kubectl logs #{@key} --namespace=#{@app} -c #{@app}`
+ puts `kubectl logs #{pod_name} --namespace=#{app} -c #{app}`
end
def run_top
- puts `kubectl top pod #{@key} --namespace=#{@app} --containers`
+ puts `kubectl top pod #{pod_name} --namespace=#{app} --containers`
end
- def run_run
- pod_list = list_pods.split("\n")
- target_pod_type = "#{@app}-web"
- target_pod_options = pod_list.select { |pod| pod.include?(target_pod_type) }
+ def run_connect
+ # If a pod name is specified, connect to that pod; otherwise pick a random web pod
+ target_pod_name = pod_name || fetch_pods(app: app, tier: 'web').sample&.dig('metadata', 'name')
- if target_pod_options.count > 0
- target_pod = target_pod_options[0]
- pod_name = target_pod.split(" ")[0]
- puts pod_name
- system("kubectl exec -ti #{pod_name} --namespace=#{@app} -- bash")
+ if target_pod_name
+ connect_to_pod(target_pod_name)
else
- puts "Could not find web with name #{target_pod_type} to attach to"
+ puts "Could not find web pod to connect to"
end
end
- def list_pods
- `kubectl get pods --namespace=#{@app} -o wide`
+ def run_run
+ # Set defaults
+ tier = 'web'
+ clear_commands = false
+
+ # Loop through args and process any that aren't just the command to run
+ loop do
+ arg = args.first
+ if arg.nil?
+ puts 'Please specify a command to run'
+ exit(1)
+ end
+ break unless arg.start_with? '--'
+ if arg.start_with? '--tier='
+ tier = arg.split('=')[1]
+ elsif arg.start_with? '--clear-commands='
+ clear_commands = %w[true yes t y].include? arg.split('=')[1]
+ else
+ puts "Warning: Unrecognized argument #{arg}"
+ end
+ args.shift
+ end
+
+ # Any remaining args are the command to run
+ command = args.join(' ')
+
+ # Find a 'template' pod from the proper tier
+ template_pod = fetch_pods(app: app, tier: tier).first
+ if template_pod.nil?
+ puts "Unable to find #{tier} tier pod to copy config from"
+ exit(1)
+ end
+
+ # Use that template pod's configuration to create a new temporary pod
+ temp_name = "#{app}-temp-#{Random.unique_name}"
+ spec = template_pod['spec']
+ temp_pod = {
+ apiVersion: template_pod['apiVersion'],
+ kind: 'Pod',
+ spec: spec,
+ metadata: {
+ name: temp_name
+ }
+ }
+ spec['restartPolicy'] = 'Never'
+ if clear_commands
+ spec['containers'].each do |container|
+ container['command'] = ['bash', '-c', 'tail -f /dev/null']
+ end
+ end
+
+ puts "Creating temporary pod #{temp_name}"
+ unless system("kubectl --namespace=#{app} create -f - <<JSON\n#{temp_pod.to_json}\nJSON")
+ puts 'Failed to create pod'
+ exit(1)
+ end
+
+ # Check pod status until it's ready to connect to
+ print 'Waiting for pod to start...'
+ loop do
+ pod = JSON.parse(`kubectl --namespace=#{app} get pods/#{temp_name} -o json`)
+ break if pod['status']['phase'] == 'Running'
+ print '.'
+ sleep 1
+ end
+ print "\n"
+
+ # Connect to the pod, running the specified command
+ connect_to_pod(temp_name, command)
+
+ # Clean up
+ unless system("kubectl --namespace=#{app} delete pod #{temp_name}")
+ puts "Warning: failed to clean up pod #{temp_name}"
+ end
+ end
+
+ def fetch_pods(filters)
+ filter_string = filters.map { |k, v| "#{k}=#{v}" }.join(',')
+ JSON.parse(`kubectl get pods --namespace=#{app} -o json --selector=#{filter_string}`)['items']
+ end
+
+ def connect_to_pod(name, command = 'bash')
+ puts "Connecting to #{name}..."
+ system("kubectl exec -ti #{name} --namespace=#{app} -- #{command}")
end
end
end