Skip to content

Commit ce1294d

Browse files
properly handle exec failures
1 parent e395845 commit ce1294d

8 files changed

Lines changed: 62 additions & 14 deletions

File tree

assets/src/components/cluster/containers/ContainerShell.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,9 @@ export function Shell({
5656
}) {
5757
const { command, setCommand, defaultCommand, isDefault } = useCommand(null)
5858

59-
const cluster = clusterId ?? ''
60-
const room = `pod:${cluster}:${namespace}:${name}:${container}`
59+
const room = clusterId
60+
? `pod:${clusterId}:${namespace}:${name}:${container}`
61+
: `pod:${namespace}:${name}:${container}`
6162

6263
return (
6364
<Flex

assets/src/components/workbenches/workbench/WorkbenchJobsSearch.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,10 @@ function WorkbenchJobSearchResultRow({
181181
}}
182182
onClick={(e) => e.stopPropagation()}
183183
>
184-
<PRsModalIcon prs={prs} />
184+
<PRsModalIcon
185+
prs={prs}
186+
type="tertiary"
187+
/>
185188
<JobConclusionIcon result={job.result} />
186189
<RunStatusIcon
187190
fullColor

assets/src/components/workbenches/workbench/WorkbenchJobsTable.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,10 @@ export function WorkbenchJobActionsRow({
323323
job={job}
324324
fillLevel={chipFillLevel}
325325
/>
326-
<PRsModalIcon prs={prs} />
326+
<PRsModalIcon
327+
prs={prs}
328+
type="tertiary"
329+
/>
327330
<JobEvalBadge job={job} />
328331
<JobConclusionIcon result={job.result} />
329332
<WorkbenchQueuedPromptChip

lib/console/ai/tools/workbench/infrastructure/kube_shell.ex

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -139,19 +139,10 @@ defmodule Console.AI.Tools.Workbench.KubeShell do
139139

140140
defp wait_for_result() do
141141
receive do
142-
{:exec_status, status} -> parse_exec_status(status)
142+
{:exec_status, status} -> PodExec.parse_exec_status(status)
143143
:exec_stream_closed -> :ok
144144
after
145145
@timeout -> {:error, "shell command timed out after 30 minutes"}
146146
end
147147
end
148-
149-
defp parse_exec_status(status) do
150-
case Jason.decode(status) do
151-
{:ok, %{"status" => "Success"}} -> :ok
152-
{:ok, %{"message" => message}} when is_binary(message) -> {:error, message}
153-
{:ok, response} -> {:error, "unexpected Kubernetes exec status: #{inspect(response)}"}
154-
{:error, _} -> {:error, "invalid Kubernetes exec status: #{status}"}
155-
end
156-
end
157148
end

lib/console/kubernetes/pod_exec.ex

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,15 @@ defmodule Console.Kubernetes.PodExec do
5757
WebSockex.send_frame(client, {:binary, <<4>> <> resize})
5858
end
5959

60+
def parse_exec_status(status) do
61+
case Jason.decode(status) do
62+
{:ok, %{"status" => "Success"}} -> :ok
63+
{:ok, %{"message" => message}} when is_binary(message) -> {:error, message}
64+
{:ok, response} -> {:error, "unexpected Kubernetes exec status: #{inspect(response)}"}
65+
{:error, _} -> {:error, "invalid Kubernetes exec status: #{status}"}
66+
end
67+
end
68+
6069
defp deliver_frame(<<1, frame::binary>>, pid),
6170
do: send_frame(pid, frame)
6271
defp deliver_frame(<<2, frame::binary>>, pid),

lib/console_web/channels/shell_channel.ex

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,15 @@ defmodule ConsoleWeb.ShellChannel do
4040
{:noreply, socket}
4141
end
4242

43+
def handle_info({:exec_status, status}, socket) do
44+
case PodExec.parse_exec_status(status) do
45+
:ok -> :ok
46+
{:error, message} -> push(socket, "stdo", %{message: "\r\nError: #{message}\r\n"})
47+
end
48+
49+
{:noreply, socket}
50+
end
51+
4352
def handle_in("command", %{"cmd" => cmd}, socket) do
4453
PodExec.command(socket.assigns.wss_pid, fmt_cmd(cmd))
4554
{:reply, :ok, socket}

test/console/kubernetes/pod_exec_test.exs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,21 @@ defmodule Console.Kubernetes.PodExecTest do
7676
end
7777
end
7878

79+
describe "parse_exec_status/1" do
80+
test "returns successful Kubernetes exec statuses" do
81+
assert PodExec.parse_exec_status(~s({"status":"Success"})) == :ok
82+
end
83+
84+
test "returns Kubernetes exec error messages" do
85+
assert PodExec.parse_exec_status(~s({"status":"Failure","message":"shell not found"})) ==
86+
{:error, "shell not found"}
87+
end
88+
89+
test "returns an error for invalid Kubernetes exec statuses" do
90+
assert PodExec.parse_exec_status("not json") == {:error, "invalid Kubernetes exec status: not json"}
91+
end
92+
end
93+
7994
defp command_args(url) do
8095
query_pairs(url)
8196
|> Enum.flat_map(fn

test/console_web/channels/shell_channel_test.exs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,23 @@ defmodule ConsoleWeb.ShellChannelTest do
5656
assert_push "stdo", %{message: "echo 'hello world'"}
5757
end
5858

59+
test "displays Kubernetes exec failures in the terminal" do
60+
user = insert(:user)
61+
role = insert(:role, repositories: ["*"], permissions: %{operate: true})
62+
insert(:role_binding, role: role, user: user)
63+
64+
url = PodExec.exec_url("ns", "n", "c")
65+
expect(PodExec, :start_link, fn ^url, _ -> {:ok, :pid} end)
66+
67+
{:ok, socket} = mk_socket(user)
68+
{:ok, _, socket} = subscribe_and_join(socket, "pod:ns:n:c", %{})
69+
70+
status = ~s({"status":"Failure","message":"shell not found"})
71+
send(socket.channel_pid, {:exec_status, status})
72+
73+
assert_push "stdo", %{message: "\r\nError: shell not found\r\n"}
74+
end
75+
5976
@tag :skip
6077
test "those without access cannot shell into pods" do
6178
user = insert(:user)

0 commit comments

Comments
 (0)