Deployment and lifecycle management for Nix
0
fork

Configure Feed

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

fix: retry garden connection when server is unavailable at startup

When the garden process starts and cannot obtain a token (e.g. server
not yet ready), it now starts with an unconnected socket and schedules
reconnection with backoff, instead of returning :ignore which left
the garden permanently stranded.

Token resolution functions now return {:ok, token}/{:error, reason}
tuples instead of token/nil, so failures propagate clearly through
build_connect_config and do_connect.

sow-158

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+42 -30
+42 -30
apps/garden/lib/garden/socket.ex
··· 113 113 {:ok, Map.put(socket, :active_deployments, %{})} 114 114 115 115 {:error, reason} -> 116 - Logger.error( 117 - "Could not start #{__MODULE__} because of " <> 118 - "validation failure: #{inspect(reason)}" 116 + Logger.warning( 117 + msg: "Initial connection failed, will retry", 118 + error: inspect(reason) 119 119 ) 120 120 121 - :ignore 121 + socket = 122 + new_socket() 123 + |> Map.put(:active_deployments, %{}) 124 + |> assign(:reconnect_counter, 0) 125 + 126 + {:ok, schedule_reconnect(socket)} 122 127 end 123 128 end 124 129 ··· 143 148 end 144 149 145 150 defp do_connect(socket \\ nil) do 146 - config = build_connect_config() 147 - 148 - case if(socket, do: connect(socket, config), else: connect(config)) do 149 - {:ok, socket} -> 150 - {:ok, socket} 151 - 152 - {:error, reason} -> 153 - {:error, reason} 151 + with {:ok, config} <- build_connect_config(), 152 + {:ok, socket} <- 153 + if(socket, do: connect(socket, config), else: connect(config)) do 154 + {:ok, socket} 155 + else 156 + {:error, reason} -> {:error, reason} 154 157 end 155 158 end 156 159 157 160 defp build_connect_config do 158 161 config = Application.get_all_env(__MODULE__) 159 - token = resolve_connect_token() 160 162 uri = Keyword.get(config, :uri) 161 163 162 - Logger.info(msg: "Connecting to server socket", endpoint: uri) 164 + case resolve_connect_token() do 165 + {:ok, token} -> 166 + Logger.info(msg: "Connecting to server socket", endpoint: uri) 163 167 164 - auth_header = {"x-auth-token", token} 165 - uri = URI.to_string(uri) 168 + auth_header = {"x-auth-token", token} 169 + uri = URI.to_string(uri) 166 170 167 - config 168 - |> Keyword.put(:uri, uri) 169 - |> Keyword.update(:headers, [auth_header], fn headers -> 170 - [auth_header | headers] 171 - end) 171 + connect_config = 172 + config 173 + |> Keyword.put(:uri, uri) 174 + |> Keyword.update(:headers, [auth_header], fn headers -> 175 + [auth_header | headers] 176 + end) 177 + 178 + {:ok, connect_config} 179 + 180 + {:error, reason} -> 181 + Logger.warning(msg: "Could not resolve connect token", reason: inspect(reason)) 182 + {:error, reason} 183 + end 172 184 end 173 185 174 186 defp resolve_connect_token do ··· 188 200 try_reauthenticate(storage) 189 201 else 190 202 Logger.debug(msg: "Using stored Boruta access token") 191 - "boruta:#{token}" 203 + {:ok, "boruta:#{token}"} 192 204 end 193 205 194 206 %{ ··· 229 241 230 242 storage |> Map.put(:oauth_credentials, updated_creds) |> Storage.write() 231 243 Logger.debug(msg: "Reauthenticated via JWT assertion") 232 - "boruta:#{token_response.access_token}" 244 + {:ok, "boruta:#{token_response.access_token}"} 233 245 234 - {:error, _} -> 235 - nil 246 + {:error, reason} -> 247 + {:error, {:reauthentication_failed, reason}} 236 248 end 237 249 end 238 250 239 - defp try_reauthenticate(_), do: nil 251 + defp try_reauthenticate(_), do: {:error, :no_credentials} 240 252 241 253 defp try_http_registration(storage) do 242 254 config = Garden.Config.get() ··· 266 278 }) 267 279 268 280 storage |> Map.put(:oauth_credentials, updated_creds) |> Storage.write() 269 - "boruta:#{token_response.access_token}" 281 + {:ok, "boruta:#{token_response.access_token}"} 270 282 271 - {:error, _} -> 283 + {:error, reason} -> 272 284 Storage.write(storage) 273 - nil 285 + {:error, {:token_request_failed, reason}} 274 286 end 275 287 276 288 {:error, reason} -> 277 289 Logger.warning(msg: "HTTP registration failed", reason: inspect(reason)) 278 - nil 290 + {:error, {:registration_failed, reason}} 279 291 end 280 292 end 281 293