Deployment and lifecycle management for Nix
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

sower: fix async deployment tests generating errors

+71 -48
+45 -41
apps/sower/lib/sower/orchestration.ex
··· 959 959 960 960 %Agent{} = agent -> 961 961 request_id = SowerClient.Sid.generate("request") 962 - process_deployment(request_id, [subscription], agent, opts) 962 + {:ok, request_id, _task} = process_deployment(request_id, [subscription], agent, opts) 963 + {:ok, request_id} 963 964 end 964 965 end 965 966 ··· 1013 1014 """ 1014 1015 def handle_deployment_request(payload, agent) do 1015 1016 with {:ok, request} <- SowerClient.Orchestration.DeploymentRequest.cast(payload), 1016 - {:ok, subscriptions} <- validate_deployment_request(request, agent.id) do 1017 - process_deployment(request.request_id, subscriptions, agent, force: request.force) 1017 + {:ok, subscriptions} <- validate_deployment_request(request, agent.id), 1018 + {:ok, request_id, task} <- 1019 + process_deployment(request.request_id, subscriptions, agent, force: request.force) do 1020 + {:ok, request_id, task} 1018 1021 end 1019 1022 end 1020 1023 ··· 1043 1046 results back to agent via channel. Validation happens synchronously before 1044 1047 task spawn. 1045 1048 1046 - Returns {:ok, request_id} if async task starts successfully, 1047 - {:error, reason} if validation fails. 1049 + Returns {:ok, request_id, task} where task is the async %Task{}. 1050 + Returns {:error, reason} if validation fails. 1048 1051 1049 1052 ## Examples 1050 1053 1051 - iex> process_deployment(request_id, subscriptions, agent) 1052 - {:ok, "request_123"} 1054 + iex> {:ok, request_id, task} = process_deployment(request_id, subscriptions, agent) 1055 + iex> Task.await(task) 1053 1056 1054 1057 """ 1055 1058 def process_deployment(request_id, subscriptions, %Agent{} = agent, opts \\ []) do 1056 - Task.Supervisor.start_child(Sower.TaskSupervisor, fn -> 1057 - Repo.put_org_id(agent.org_id) 1059 + task = 1060 + Task.Supervisor.async_nolink(Sower.TaskSupervisor, fn -> 1061 + Repo.put_org_id(agent.org_id) 1058 1062 1059 - Logger.info( 1060 - msg: "Deployment processing started", 1061 - request_id: request_id, 1062 - agent_id: agent.id 1063 - ) 1063 + Logger.info( 1064 + msg: "Deployment processing started", 1065 + request_id: request_id, 1066 + agent_id: agent.id 1067 + ) 1064 1068 1065 - case do_deployment(request_id, subscriptions, opts) do 1066 - {:ok, deployment} -> 1067 - Logger.info( 1068 - msg: "Deployment broadcast successful", 1069 - request_id: request_id, 1070 - deployment_sid: deployment.sid, 1071 - skipped: deployment.skipped 1072 - ) 1069 + case do_deployment(request_id, subscriptions, opts) do 1070 + {:ok, deployment} -> 1071 + Logger.info( 1072 + msg: "Deployment broadcast successful", 1073 + request_id: request_id, 1074 + deployment_sid: deployment.sid, 1075 + skipped: deployment.skipped 1076 + ) 1073 1077 1074 - SowerWeb.Endpoint.broadcast( 1075 - "agent:#{agent.sid}", 1076 - "deployment", 1077 - Map.from_struct(deployment) 1078 - ) 1078 + SowerWeb.Endpoint.broadcast( 1079 + "agent:#{agent.sid}", 1080 + "deployment", 1081 + Map.from_struct(deployment) 1082 + ) 1079 1083 1080 - {:error, reason} -> 1081 - Logger.error( 1082 - msg: "Deployment processing failed", 1083 - request_id: request_id, 1084 - reason: to_string(reason) 1085 - ) 1084 + {:error, reason} -> 1085 + Logger.error( 1086 + msg: "Deployment processing failed", 1087 + request_id: request_id, 1088 + reason: to_string(reason) 1089 + ) 1086 1090 1087 - SowerWeb.Endpoint.broadcast( 1088 - "agent:#{agent.sid}", 1089 - "deployment:error", 1090 - %{request_id: request_id, reason: to_string(reason)} 1091 - ) 1092 - end 1093 - end) 1091 + SowerWeb.Endpoint.broadcast( 1092 + "agent:#{agent.sid}", 1093 + "deployment:error", 1094 + %{request_id: request_id, reason: to_string(reason)} 1095 + ) 1096 + end 1097 + end) 1094 1098 1095 - {:ok, request_id} 1099 + {:ok, request_id, task} 1096 1100 end 1097 1101 1098 1102 defp do_deployment(request_id, subscriptions, opts) do
+1 -1
apps/sower/lib/sower_web/agent_channel.ex
··· 121 121 req, 122 122 socket.assigns.agent 123 123 ) do 124 - {:ok, request_id} -> 124 + {:ok, request_id, _task} -> 125 125 {:ok, %{request_id: request_id}} 126 126 127 127 {:error, error} ->
+9 -5
apps/sower/test/sower/orchestration_test.exs
··· 922 922 "force" => false 923 923 } 924 924 925 - assert {:ok, request_id} = Orchestration.handle_deployment_request(payload, agent) 925 + assert {:ok, request_id, task} = Orchestration.handle_deployment_request(payload, agent) 926 926 assert is_binary(request_id) 927 + Task.await(task) 927 928 end 928 929 929 930 test "returns error for deployment request with unauthorized subscription", %{ ··· 964 965 965 966 request_id = "dr_test_#{System.unique_integer([:positive])}" 966 967 967 - # process_deployment should return immediately with the request_id 968 - assert {:ok, ^request_id} = 968 + assert {:ok, ^request_id, task} = 969 969 Orchestration.process_deployment(request_id, [subscription], agent) 970 + 971 + Task.await(task) 970 972 end 971 973 974 + @tag :capture_log 972 975 test "process_deployment handles error case with no matching seeds", %{organization: _org} do 973 976 agent = agent_fixture() 974 977 ··· 982 985 983 986 request_id = "dr_test_error_#{System.unique_integer([:positive])}" 984 987 985 - # Should still return {:ok, request_id} since processing is async 986 - assert {:ok, ^request_id} = 988 + assert {:ok, ^request_id, task} = 987 989 Orchestration.process_deployment(request_id, [subscription], agent) 990 + 991 + Task.await(task) 988 992 end 989 993 end 990 994
-1
apps/sower/test/sower/storage_test.exs
··· 1 1 defmodule Sower.StorageTest do 2 2 use Sower.DataCase 3 3 4 - alias Sower.Orchestration 5 4 alias Sower.Storage 6 5 alias SowerClient.Storage.DeploymentLogUploadRequest 7 6 alias SowerClient.Storage.PresignedUploadReply
+2
apps/sower/test/sower_web/controllers/auth_controller_test.exs
··· 27 27 assert Phoenix.Flash.get(conn.assigns.flash || %{}, :auth_error) == nil 28 28 end 29 29 30 + @tag :capture_log 30 31 test "callback/2 failed user creation sets login-specific flash", %{conn: conn} do 31 32 auth = %Ueberauth.Auth{ 32 33 uid: Ecto.UUID.generate(), ··· 40 41 assert Phoenix.Flash.get(conn.assigns.flash, :error) == nil 41 42 end 42 43 44 + @tag :capture_log 43 45 test "callback/2 ueberauth failure sets login-specific flash", %{conn: conn} do 44 46 conn = 45 47 conn
+13
apps/sower/test/sower_web/live/deployment_live_show_test.exs
··· 143 143 144 144 assert_receive {:DOWN, ^monitor_ref, :process, _pid, _reason} 145 145 146 + # Registry cleanup is async; wait for unregistration 147 + :ok = 148 + Enum.reduce_while(1..50, :error, fn _, _ -> 149 + if Enum.any?(Registry.lookup(Sower.PubSub, topic), fn {pid, _} -> 150 + pid == show_live.pid 151 + end) do 152 + Process.sleep(10) 153 + {:cont, :error} 154 + else 155 + {:halt, :ok} 156 + end 157 + end) 158 + 146 159 refute Enum.any?(Registry.lookup(Sower.PubSub, topic), fn {pid, _} -> 147 160 pid == show_live.pid 148 161 end)
+1
apps/sower_agent/test/sower_agent/client_deployment_test.exs
··· 117 117 assert Map.has_key?(socket.active_deployments, "deploy_789") 118 118 end 119 119 120 + @tag :capture_log 120 121 test "handles deployment:error event" do 121 122 socket = MockSocket.new() 122 123