diff --git a/config/config.exs b/config/config.exs index 8debe2c63..cec318fdd 100644 --- a/config/config.exs +++ b/config/config.exs @@ -67,11 +67,6 @@ config :nerves_hub, NervesHubWeb.Endpoint, render_errors: [view: NervesHubWeb.ErrorView, accepts: ~w(html json)], pubsub_server: NervesHub.PubSub -config :opentelemetry, - span_processor: :batch, - traces_exporter: :otlp, - resource: %{service: %{name: "nerves_hub"}} - config :swoosh, :api_client, Swoosh.ApiClient.Finch # Environment specific config diff --git a/config/test.exs b/config/test.exs index a230c1527..f7b516df4 100644 --- a/config/test.exs +++ b/config/test.exs @@ -83,12 +83,4 @@ config :nerves_hub, NervesHubWeb.Endpoint, config :nerves_hub, NervesHubWeb.DeviceSocketSharedSecretAuth, enabled: true -# OTel -config :opentelemetry, tracer: :otel_tracer_noop, traces_exporter: :none - -config :opentelemetry, :processors, - otel_batch_processor: %{ - exporter: {:otel_exporter_tab, []} - } - config :sentry, environment_name: :test diff --git a/lib/nerves_hub/deployments.ex b/lib/nerves_hub/deployments.ex index 1fb4b2748..219a73c48 100644 --- a/lib/nerves_hub/deployments.ex +++ b/lib/nerves_hub/deployments.ex @@ -2,7 +2,6 @@ defmodule NervesHub.Deployments do import Ecto.Query require Logger - require OpenTelemetry.Tracer, as: Tracer alias NervesHub.AuditLogs alias NervesHub.Deployments.Deployment @@ -305,41 +304,33 @@ defmodule NervesHub.Deployments do Do nothing if a deployment is already set """ def set_deployment(%{deployment_id: nil} = device) do - Tracer.with_span "Deployments.set_deployment" do - Tracer.set_attribute("nerves_hub.deployment.status", "setting") - - case alternate_deployments(device, [true]) do - [] -> - Logger.debug("No matching deployments for #{device.identifier}") - - %{device | deployment: nil} - - [deployment] -> - device - |> Ecto.Changeset.change() - |> Ecto.Changeset.put_change(:deployment_id, deployment.id) - |> Repo.update!() - |> Repo.preload([:deployment]) - - [deployment | _] -> - Logger.debug( - "More than one deployment matches for #{device.identifier}, setting to the first" - ) - - device - |> Ecto.Changeset.change() - |> Ecto.Changeset.put_change(:deployment_id, deployment.id) - |> Repo.update!() - |> Repo.preload([:deployment]) - end + case alternate_deployments(device, [true]) do + [] -> + Logger.debug("No matching deployments for #{device.identifier}") + + %{device | deployment: nil} + + [deployment] -> + device + |> Ecto.Changeset.change() + |> Ecto.Changeset.put_change(:deployment_id, deployment.id) + |> Repo.update!() + |> Repo.preload([:deployment]) + + [deployment | _] -> + Logger.debug( + "More than one deployment matches for #{device.identifier}, setting to the first" + ) + + device + |> Ecto.Changeset.change() + |> Ecto.Changeset.put_change(:deployment_id, deployment.id) + |> Repo.update!() + |> Repo.preload([:deployment]) end end def set_deployment(device) do - Tracer.with_span "Deployments.set_deployment" do - Tracer.set_attribute("nerves_hub.deployment.status", "existed") - - Repo.preload(device, [:deployment]) - end + Repo.preload(device, [:deployment]) end end diff --git a/lib/nerves_hub/deployments/orchestrator.ex b/lib/nerves_hub/deployments/orchestrator.ex index ba7881509..1261aafa4 100644 --- a/lib/nerves_hub/deployments/orchestrator.ex +++ b/lib/nerves_hub/deployments/orchestrator.ex @@ -11,7 +11,6 @@ defmodule NervesHub.Deployments.Orchestrator do use GenServer require Logger - require OpenTelemetry.Tracer, as: Tracer alias NervesHub.Devices alias NervesHub.Devices.Device @@ -50,68 +49,57 @@ defmodule NervesHub.Deployments.Orchestrator do was successful, and the process is repeated. """ def trigger_update(deployment) do - Tracer.with_span "Deploments.Orchestrator.trigger_update" do - :telemetry.execute([:nerves_hub, :deployment, :trigger_update], %{count: 1}) - - match_conditions = [ - {:and, {:==, {:map_get, :deployment_id, :"$1"}, deployment.id}, - {:==, {:map_get, :updating, :"$1"}, false}, - {:==, {:map_get, :updates_enabled, :"$1"}, true}, - {:"/=", {:map_get, :firmware_uuid, :"$1"}, deployment.firmware.uuid}} - ] - - match_return = %{ - device_id: {:element, 1, :"$_"}, - pid: {:element, 1, {:element, 2, :"$_"}}, - firmware_uuid: {:map_get, :firmware_uuid, {:element, 2, {:element, 2, :"$_"}}} - } - - devices = - Registry.select(NervesHub.Devices, [ - {{:_, :_, :"$1"}, match_conditions, [match_return]} - ]) - - # Get a rough count of devices to update - count = deployment.concurrent_updates - Devices.count_inflight_updates_for(deployment) - # Just in case inflight goes higher than concurrent, limit it to 0 - count = max(count, 0) - - Tracer.set_attributes(%{ - "nerves_hub.deployments.devices_to_update" => count, - "nerves_hub.deployments.devices_online" => Enum.count(devices) - }) - - # use a reduce to bounce out early? - # limit the number of devices to 5 minutes / 500ms? - - devices - |> Enum.take(count) - |> Enum.each(fn %{device_id: device_id, pid: pid} -> - Tracer.with_span "Deployments.Orchestrator.each_device" do - Tracer.set_attribute("nerves_hub.devices.id", device_id) - - :telemetry.execute([:nerves_hub, :deployment, :trigger_update, :device], %{count: 1}) - - device = %Device{id: device_id} - - # Check again because other nodes are processing at the same time - if Devices.count_inflight_updates_for(deployment) < deployment.concurrent_updates do - case Devices.told_to_update(device, deployment) do - {:ok, inflight_update} -> - send(pid, {"deployments/update", inflight_update}) - - :error -> - Logger.error( - "An inflight update could not be created or found for the device #{device.identifier} (#{device.id})" - ) - end - end - - # Slow the update a bit to allow for concurrent nodes - Process.sleep(500) + :telemetry.execute([:nerves_hub, :deployment, :trigger_update], %{count: 1}) + + match_conditions = [ + {:and, {:==, {:map_get, :deployment_id, :"$1"}, deployment.id}, + {:==, {:map_get, :updating, :"$1"}, false}, + {:==, {:map_get, :updates_enabled, :"$1"}, true}, + {:"/=", {:map_get, :firmware_uuid, :"$1"}, deployment.firmware.uuid}} + ] + + match_return = %{ + device_id: {:element, 1, :"$_"}, + pid: {:element, 1, {:element, 2, :"$_"}}, + firmware_uuid: {:map_get, :firmware_uuid, {:element, 2, {:element, 2, :"$_"}}} + } + + devices = + Registry.select(NervesHub.Devices, [ + {{:_, :_, :"$1"}, match_conditions, [match_return]} + ]) + + # Get a rough count of devices to update + count = deployment.concurrent_updates - Devices.count_inflight_updates_for(deployment) + # Just in case inflight goes higher than concurrent, limit it to 0 + count = max(count, 0) + + # use a reduce to bounce out early? + # limit the number of devices to 5 minutes / 500ms? + + devices + |> Enum.take(count) + |> Enum.each(fn %{device_id: device_id, pid: pid} -> + :telemetry.execute([:nerves_hub, :deployment, :trigger_update, :device], %{count: 1}) + + device = %Device{id: device_id} + + # Check again because other nodes are processing at the same time + if Devices.count_inflight_updates_for(deployment) < deployment.concurrent_updates do + case Devices.told_to_update(device, deployment) do + {:ok, inflight_update} -> + send(pid, {"deployments/update", inflight_update}) + + :error -> + Logger.error( + "An inflight update could not be created or found for the device #{device.identifier} (#{device.id})" + ) end - end) - end + end + + # Slow the update a bit to allow for concurrent nodes + Process.sleep(500) + end) end def init(deployment) do diff --git a/lib/nerves_hub/devices.ex b/lib/nerves_hub/devices.ex index 4119cd77a..cc10a6947 100644 --- a/lib/nerves_hub/devices.ex +++ b/lib/nerves_hub/devices.ex @@ -25,8 +25,6 @@ defmodule NervesHub.Devices do alias NervesHub.Repo alias NervesHub.TaskSupervisor, as: Tasks - require OpenTelemetry.Tracer, as: Tracer - @min_fwup_delta_updatable_version ">=1.6.0" def get_device!(device_id) do @@ -623,33 +621,21 @@ defmodule NervesHub.Devices do This may clear the deployment from the device if the version or tags are different. """ def verify_deployment(%{deployment_id: nil} = device) do - Tracer.with_span "Devices.verify_deployment" do - Tracer.set_attribute("nerves_hub.device.deployment_id", nil) - - device - end + device end def verify_deployment(device) do - Tracer.with_span "Devices.verify_deployment" do - Tracer.set_attribute("nerves_hub.device.deployment_id", device.deployment_id) - - device = Repo.preload(device, [:deployment]) + device = Repo.preload(device, [:deployment]) - case matches_deployment?(device, device.deployment) do - true -> - Tracer.set_attribute("nerves_hub.device.match_deployment", true) - - device - - false -> - Tracer.set_attribute("nerves_hub.device.match_deployment", false) + case matches_deployment?(device, device.deployment) do + true -> + device - device - |> Ecto.Changeset.change() - |> Ecto.Changeset.put_change(:deployment_id, nil) - |> Repo.update!() - end + false -> + device + |> Ecto.Changeset.change() + |> Ecto.Changeset.put_change(:deployment_id, nil) + |> Repo.update!() end end diff --git a/lib/nerves_hub/devices/device_link.ex b/lib/nerves_hub/devices/device_link.ex index 61ff4bf2f..8927aa55f 100644 --- a/lib/nerves_hub/devices/device_link.ex +++ b/lib/nerves_hub/devices/device_link.ex @@ -19,7 +19,6 @@ defmodule NervesHub.Devices.DeviceLink do alias Phoenix.Socket.Broadcast require Logger - require OpenTelemetry.Tracer, as: Tracer defmodule State do defstruct [ @@ -117,8 +116,7 @@ defmodule NervesHub.Devices.DeviceLink do nil end - ctx = OpenTelemetry.Ctx.get_current() - GenServer.call(link, {:connect, push_cb, params, monitor, ctx}) + GenServer.call(link, {:connect, push_cb, params, monitor, :ctx}) end @doc """ @@ -174,137 +172,130 @@ defmodule NervesHub.Devices.DeviceLink do {:reply, :ok, do_disconnect(state)} end - def handle_call({:connect, push_cb, params, monitor, ctx}, _from, %{device: device} = state) do - OpenTelemetry.Ctx.attach(ctx) - + def handle_call({:connect, push_cb, params, monitor, _ctx}, _from, %{device: device} = state) do # Cancel any pending reconnect timer before we get too busy doing work _ = if state.reconnect_timer, do: Process.cancel_timer(state.reconnect_timer) - Tracer.with_span "DeviceLink.connect" do - with {:ok, device} <- update_metadata(device, params), - {:ok, device} <- Devices.device_connected(device) do - Tracer.set_attribute("nerves_hub.device.id", device.id) - Tracer.set_attribute("nerves_hub.device.identifier", device.identifier) - - state = %{state | device_api_version: Map.get(params, "device_api_version", "1.0.0")} + with {:ok, device} <- update_metadata(device, params), + {:ok, device} <- Devices.device_connected(device) do + state = %{state | device_api_version: Map.get(params, "device_api_version", "1.0.0")} - description = "device #{device.identifier} connected to the server" + description = "device #{device.identifier} connected to the server" - AuditLogs.audit_with_ref!( - device, - device, - description, - state.reference_id - ) + AuditLogs.audit_with_ref!( + device, + device, + description, + state.reference_id + ) - device = - device - |> Devices.verify_deployment() - |> Deployments.set_deployment() - |> Repo.preload(deployment: [:archive, :firmware]) + device = + device + |> Devices.verify_deployment() + |> Deployments.set_deployment() + |> Repo.preload(deployment: [:archive, :firmware]) - # clear out any inflight updates, there shouldn't be one at this point - # we might make a new one right below it, so clear it beforehand - Devices.clear_inflight_update(device) + # clear out any inflight updates, there shouldn't be one at this point + # we might make a new one right below it, so clear it beforehand + Devices.clear_inflight_update(device) - # Let the orchestrator handle this going forward ? - update_payload = Devices.resolve_update(device) + # Let the orchestrator handle this going forward ? + update_payload = Devices.resolve_update(device) - push_update? = - update_payload.update_available and not is_nil(update_payload.firmware_url) and - update_payload.firmware_meta[:uuid] != params["currently_downloading_uuid"] + push_update? = + update_payload.update_available and not is_nil(update_payload.firmware_url) and + update_payload.firmware_meta[:uuid] != params["currently_downloading_uuid"] - if push_update? do - # Push the update to the device - push_cb.("update", update_payload) + if push_update? do + # Push the update to the device + push_cb.("update", update_payload) - deployment = device.deployment + deployment = device.deployment - description = - "device #{device.identifier} received update for firmware #{deployment.firmware.version}(#{deployment.firmware.uuid}) via deployment #{deployment.name} on connect" + description = + "device #{device.identifier} received update for firmware #{deployment.firmware.version}(#{deployment.firmware.uuid}) via deployment #{deployment.name} on connect" - AuditLogs.audit_with_ref!( - deployment, - device, - description, - state.reference_id - ) + AuditLogs.audit_with_ref!( + deployment, + device, + description, + state.reference_id + ) - # if there's an update, track it - Devices.told_to_update(device, deployment) - end + # if there's an update, track it + Devices.told_to_update(device, deployment) + end - ## After join - :telemetry.execute([:nerves_hub, :devices, :connect], %{count: 1}) + ## After join + :telemetry.execute([:nerves_hub, :devices, :connect], %{count: 1}) - # local node tracking - Registry.update_value(NervesHub.Devices, device.id, fn value -> - update = %{ - deployment_id: device.deployment_id, - firmware_uuid: device.firmware_metadata.uuid, - updates_enabled: device.updates_enabled && !Devices.device_in_penalty_box?(device), - updating: push_update? - } - - Map.merge(value, update) - end) + # local node tracking + Registry.update_value(NervesHub.Devices, device.id, fn value -> + update = %{ + deployment_id: device.deployment_id, + firmware_uuid: device.firmware_metadata.uuid, + updates_enabled: device.updates_enabled && !Devices.device_in_penalty_box?(device), + updating: push_update? + } + + Map.merge(value, update) + end) - # Cluster tracking - reply = - try do - Tracker.online(device) - {:ok, self()} - rescue - ex in NervesHub.Tracker.Exception -> - :telemetry.execute([:nerves_hub, :tracker, :exception], %{count: 1}) - {:error, ex} - end - - if Version.match?(state.device_api_version, ">= 2.0.0") do - if device.deployment && device.deployment.archive do - archive = device.deployment.archive - - push_cb.("archive", %{ - size: archive.size, - uuid: archive.uuid, - version: archive.version, - description: archive.description, - platform: archive.platform, - architecture: archive.architecture, - uploaded_at: archive.inserted_at, - url: Archives.url(archive) - }) - end + # Cluster tracking + reply = + try do + Tracker.online(device) + {:ok, self()} + rescue + ex in NervesHub.Tracker.Exception -> + :telemetry.execute([:nerves_hub, :tracker, :exception], %{count: 1}) + {:error, ex} end - state = - case monitor do - {transport_pid, ref_id} -> - ref = Process.monitor(transport_pid) - %{state | reference_id: ref_id, transport_pid: transport_pid, transport_ref: ref} + if Version.match?(state.device_api_version, ">= 2.0.0") do + if device.deployment && device.deployment.archive do + archive = device.deployment.archive + + push_cb.("archive", %{ + size: archive.size, + uuid: archive.uuid, + version: archive.version, + description: archive.description, + platform: archive.platform, + architecture: archive.architecture, + uploaded_at: archive.inserted_at, + url: Archives.url(archive) + }) + end + end - _ -> - state - end + state = + case monitor do + {transport_pid, ref_id} -> + ref = Process.monitor(transport_pid) + %{state | reference_id: ref_id, transport_pid: transport_pid, transport_ref: ref} - state = - %{ + _ -> state - | device: device, - push_cb: push_cb, - reconnect_timer: nil, - update_started?: push_update? - } - |> maybe_start_penalty_timer() - - {:reply, reply, state} - else - {:error, err} -> - {:reply, {:error, err}, state} + end - err -> - {:reply, {:error, err}, state} - end + state = + %{ + state + | device: device, + push_cb: push_cb, + reconnect_timer: nil, + update_started?: push_update? + } + |> maybe_start_penalty_timer() + + {:reply, reply, state} + else + {:error, err} -> + {:reply, {:error, err}, state} + + err -> + {:reply, {:error, err}, state} end end @@ -345,16 +336,12 @@ defmodule NervesHub.Devices.DeviceLink do end def handle_call({:receive, "status_update", %{"status" => _status}}, _from, state) do - trace("DeviceLink.status_update", state.device, fn -> - # TODO store in tracker or the database? - {:reply, :ok, state} - end) + # TODO store in tracker or the database? + {:reply, :ok, state} end def handle_call({:receive, "rebooting", _}, _from, state) do - trace("DeviceLink.rebooting", state.device, fn -> - {:reply, :ok, state} - end) + {:reply, :ok, state} end def handle_call( @@ -362,10 +349,8 @@ defmodule NervesHub.Devices.DeviceLink do _from, %{device: device} = state ) do - trace("DeviceLink.connection_types", device, fn -> - {:ok, device} = Devices.update_device(device, %{"connection_types" => types}) - {:reply, :ok, %{state | device: device}} - end) + {:ok, device} = Devices.update_device(device, %{"connection_types" => types}) + {:reply, :ok, %{state | device: device}} end def handle_call({:receive, _event, _payload}, _from, state) do @@ -386,63 +371,57 @@ defmodule NervesHub.Devices.DeviceLink do %Broadcast{event: "deployments/changed", topic: "deployment:none", payload: payload}, %{device: device} = state ) do - trace("DeviceLink.deployment_changed", device, fn -> - if device_matches_deployment_payload?(device, payload) do - {:noreply, assign_deployment(state, payload)} - else - {:noreply, state} - end - end) + if device_matches_deployment_payload?(device, payload) do + {:noreply, assign_deployment(state, payload)} + else + {:noreply, state} + end end def handle_info( %Broadcast{event: "deployments/changed", payload: payload}, %{device: device} = state ) do - trace("DeviceLink.deployment_changed", device, fn -> - if device_matches_deployment_payload?(device, payload) do - :telemetry.execute([:nerves_hub, :devices, :deployment, :changed], %{count: 1}) - {:noreply, assign_deployment(state, payload)} - else - # jitter over a minute but spaced out to attempt to not - # slam the database when all devices check - jitter = :rand.uniform(30) * 2 * 1000 - Process.send_after(self(), :resolve_changed_deployment, jitter) - {:noreply, state} - end - end) + if device_matches_deployment_payload?(device, payload) do + :telemetry.execute([:nerves_hub, :devices, :deployment, :changed], %{count: 1}) + {:noreply, assign_deployment(state, payload)} + else + # jitter over a minute but spaced out to attempt to not + # slam the database when all devices check + jitter = :rand.uniform(30) * 2 * 1000 + Process.send_after(self(), :resolve_changed_deployment, jitter) + {:noreply, state} + end end def handle_info(:resolve_changed_deployment, %{device: device} = state) do - trace("DeviceLink.resolve_changed_deployment", device, fn -> - :telemetry.execute([:nerves_hub, :devices, :deployment, :changed], %{count: 1}) - - device = - device - |> Repo.reload() - |> Deployments.set_deployment() - |> Repo.preload([deployment: [:firmware]], force: true) + :telemetry.execute([:nerves_hub, :devices, :deployment, :changed], %{count: 1}) - description = - if device.deployment_id do - "device #{device.identifier} reloaded deployment and is attached to deployment #{device.deployment.name}" - else - "device #{device.identifier} reloaded deployment and is no longer attached to a deployment" - end + device = + device + |> Repo.reload() + |> Deployments.set_deployment() + |> Repo.preload([deployment: [:firmware]], force: true) - AuditLogs.audit_with_ref!( - device, - device, - description, - state.reference_id - ) + description = + if device.deployment_id do + "device #{device.identifier} reloaded deployment and is attached to deployment #{device.deployment.name}" + else + "device #{device.identifier} reloaded deployment and is no longer attached to a deployment" + end - Registry.update_value(NervesHub.Devices, device.id, fn value -> - Map.put(value, :deployment_id, device.deployment_id) - end) + AuditLogs.audit_with_ref!( + device, + device, + description, + state.reference_id + ) - {:noreply, update_device(state, device)} + Registry.update_value(NervesHub.Devices, device.id, fn value -> + Map.put(value, :deployment_id, device.deployment_id) end) + + {:noreply, update_device(state, device)} end # manually pushed @@ -450,12 +429,9 @@ defmodule NervesHub.Devices.DeviceLink do %Broadcast{event: "deployments/update", payload: %{deployment_id: nil} = payload}, state ) do - trace("DeviceLink.deployments_update", state.device, fn -> - :telemetry.execute([:nerves_hub, :devices, :update, :manual], %{count: 1}) - Tracer.set_attribute("nerves_hub.deployment.manual", true) - state.push_cb.("update", payload) - {:noreply, state} - end) + :telemetry.execute([:nerves_hub, :devices, :update, :manual], %{count: 1}) + state.push_cb.("update", payload) + {:noreply, state} end def handle_info(%Broadcast{event: "deployments/update"}, state) do @@ -463,67 +439,61 @@ defmodule NervesHub.Devices.DeviceLink do end def handle_info({"deployments/update", inflight_update}, %{device: device} = state) do - trace("DeviceLink.deployments_update", device, fn -> - :telemetry.execute([:nerves_hub, :devices, :update, :automatic], %{count: 1}) + :telemetry.execute([:nerves_hub, :devices, :update, :automatic], %{count: 1}) - device = Repo.preload(device, [deployment: [:firmware]], force: true) + device = Repo.preload(device, [deployment: [:firmware]], force: true) - payload = Devices.resolve_update(device) + payload = Devices.resolve_update(device) - case payload.update_available do - true -> - deployment = device.deployment - firmware = deployment.firmware + case payload.update_available do + true -> + deployment = device.deployment + firmware = deployment.firmware - description = - "deployment #{deployment.name} update triggered device #{device.identifier} to update firmware #{firmware.uuid}" + description = + "deployment #{deployment.name} update triggered device #{device.identifier} to update firmware #{firmware.uuid}" - # If we get here, the device is connected and high probability it receives - # the update message so we can Audit and later assert on this audit event - # as a loosely valid attempt to update - AuditLogs.audit_with_ref!( - deployment, - device, - description, - state.reference_id - ) + # If we get here, the device is connected and high probability it receives + # the update message so we can Audit and later assert on this audit event + # as a loosely valid attempt to update + AuditLogs.audit_with_ref!( + deployment, + device, + description, + state.reference_id + ) - Devices.update_started!(inflight_update) - state.push_cb.("update", payload) + Devices.update_started!(inflight_update) + state.push_cb.("update", payload) - {:noreply, state} + {:noreply, state} - false -> - {:noreply, state} - end - end) + false -> + {:noreply, state} + end end def handle_info(%Broadcast{event: "moved"}, state) do - trace("DeviceLink.deployment_moved", state.device, fn -> - # The old deployment is no longer valid, so let's look one up again - handle_info(:resolve_changed_deployment, state) - end) + # The old deployment is no longer valid, so let's look one up again + handle_info(:resolve_changed_deployment, state) end # Update local state and tell the various servers of the new information def handle_info(%Broadcast{event: "devices/updated"}, %{device: device} = state) do - trace("DeviceLink.devices_updated", device, fn -> - device = Repo.reload(device) + device = Repo.reload(device) - Registry.update_value(NervesHub.Devices, device.id, fn value -> - Map.merge(value, %{ - updates_enabled: device.updates_enabled && !Devices.device_in_penalty_box?(device) - }) - end) + Registry.update_value(NervesHub.Devices, device.id, fn value -> + Map.merge(value, %{ + updates_enabled: device.updates_enabled && !Devices.device_in_penalty_box?(device) + }) + end) - state = - state - |> update_device(device) - |> maybe_start_penalty_timer() + state = + state + |> update_device(device) + |> maybe_start_penalty_timer() - {:noreply, state} - end) + {:noreply, state} end def handle_info(%Broadcast{event: event, payload: payload}, state) do @@ -533,29 +503,25 @@ defmodule NervesHub.Devices.DeviceLink do end def handle_info(:penalty_box_check, %{device: device} = state) do - trace("DeviceLink.penalty_box_check", device, fn -> - updates_enabled = device.updates_enabled && !Devices.device_in_penalty_box?(device) - - Tracer.set_attribute("nerves_hub.device.updates_enabled", updates_enabled) + updates_enabled = device.updates_enabled && !Devices.device_in_penalty_box?(device) - :telemetry.execute([:nerves_hub, :devices, :penalty_box, :check], %{ - updates_enabled: updates_enabled - }) + :telemetry.execute([:nerves_hub, :devices, :penalty_box, :check], %{ + updates_enabled: updates_enabled + }) - Registry.update_value(NervesHub.Devices, device.id, fn value -> - Map.merge(value, %{updates_enabled: updates_enabled}) - end) + Registry.update_value(NervesHub.Devices, device.id, fn value -> + Map.merge(value, %{updates_enabled: updates_enabled}) + end) - # Just in case time is weird or it got placed back in between checks - state = - if !updates_enabled do - maybe_start_penalty_timer(state) - else - state - end + # Just in case time is weird or it got placed back in between checks + state = + if !updates_enabled do + maybe_start_penalty_timer(state) + else + state + end - {:noreply, state} - end) + {:noreply, state} end def handle_info(:timeout_reconnect, state) do @@ -595,25 +561,23 @@ defmodule NervesHub.Devices.DeviceLink do end defp assign_deployment(%{device: device} = state, payload) do - trace("DeviceLink.assign_deployment", device, fn -> - device = - device - |> Ecto.Changeset.change() - |> Ecto.Changeset.put_change(:deployment_id, payload.id) - |> Repo.update!() - |> Repo.preload([deployment: [:firmware]], force: true) - - description = - "device #{device.identifier} reloaded deployment and is attached to deployment #{device.deployment.name}" + device = + device + |> Ecto.Changeset.change() + |> Ecto.Changeset.put_change(:deployment_id, payload.id) + |> Repo.update!() + |> Repo.preload([deployment: [:firmware]], force: true) - AuditLogs.audit_with_ref!(device, device, description, state.reference_id) + description = + "device #{device.identifier} reloaded deployment and is attached to deployment #{device.deployment.name}" - Registry.update_value(NervesHub.Devices, device.id, fn value -> - Map.put(value, :deployment_id, device.deployment_id) - end) + AuditLogs.audit_with_ref!(device, device, description, state.reference_id) - update_device(state, device) + Registry.update_value(NervesHub.Devices, device.id, fn value -> + Map.put(value, :deployment_id, device.deployment_id) end) + + update_device(state, device) end def update_device(state, device) do @@ -646,17 +610,6 @@ defmodule NervesHub.Devices.DeviceLink do %{state | penalty_timer: ref} end - defp trace(name, device, fun) do - Tracer.with_span name do - Tracer.set_attributes(%{ - "nerves_hub.device.id" => device.id, - "nerves_hub.device.identifier" => device.identifier - }) - - fun.() - end - end - defp do_disconnect(state) do _ = if state.transport_ref do diff --git a/lib/nerves_hub/ssl.ex b/lib/nerves_hub/ssl.ex index 54cb6ca37..19053d2f1 100644 --- a/lib/nerves_hub/ssl.ex +++ b/lib/nerves_hub/ssl.ex @@ -3,8 +3,6 @@ defmodule NervesHub.SSL do alias NervesHub.Certificate alias NervesHub.RateLimit - require OpenTelemetry.Tracer, as: Tracer - @type pkix_path_validation_reason :: :cert_expired | :invalid_issuer @@ -44,38 +42,30 @@ defmodule NervesHub.SSL do end def verify_fun(otp_cert, {:bad_cert, err}, state) when err in [:unknown_ca, :cert_expired] do - Tracer.with_span "SSL.bad_cert" do - aki = Certificate.get_aki(otp_cert) - ski = Certificate.get_ski(otp_cert) - - cond do - aki == ski -> - Tracer.set_attribute("nerves_hub.ssl.bad_cert", "aki=ski") - - # If the signer CA is also the root, then AKI == SKI. We can skip - # checking as it will be validated later on if the device needs - # registration - {:valid, state} - - is_binary(ski) and match?({:ok, _db_ca}, Devices.get_ca_certificate_by_ski(ski)) -> - Tracer.set_attribute("nerves_hub.ssl.bad_cert", "intermediate cert") - - # Signer CA sent with the device certificate, but is an intermediary - # so the chain is incomplete labeling it as unknown_ca. - # - # Since we have this CA registered, validate so we can move on to the device - # cert next and expiration will be checked later if registration of a new - # device cert needs to happen. - {:valid, state} - - true -> - Tracer.set_attribute("nerves_hub.ssl.bad_cert", "no signer ca") - - # The signer CA was not included in the request, so this is most - # likely a device cert that needs verification. If it isn't, then - # this is some other unknown CA that will fail - do_verify(otp_cert, state) - end + aki = Certificate.get_aki(otp_cert) + ski = Certificate.get_ski(otp_cert) + + cond do + aki == ski -> + # If the signer CA is also the root, then AKI == SKI. We can skip + # checking as it will be validated later on if the device needs + # registration + {:valid, state} + + is_binary(ski) and match?({:ok, _db_ca}, Devices.get_ca_certificate_by_ski(ski)) -> + # Signer CA sent with the device certificate, but is an intermediary + # so the chain is incomplete labeling it as unknown_ca. + # + # Since we have this CA registered, validate so we can move on to the device + # cert next and expiration will be checked later if registration of a new + # device cert needs to happen. + {:valid, state} + + true -> + # The signer CA was not included in the request, so this is most + # likely a device cert that needs verification. If it isn't, then + # this is some other unknown CA that will fail + do_verify(otp_cert, state) end end diff --git a/lib/nerves_hub_web/channels/device_channel.ex b/lib/nerves_hub_web/channels/device_channel.ex index e89f7483a..7e1ce944e 100644 --- a/lib/nerves_hub_web/channels/device_channel.ex +++ b/lib/nerves_hub_web/channels/device_channel.ex @@ -10,20 +10,16 @@ defmodule NervesHubWeb.DeviceChannel do alias NervesHub.Devices.DeviceLink - require OpenTelemetry.Tracer, as: Tracer - def join("device", params, %{assigns: %{device: device}} = socket) do - Tracer.with_span "DeviceChannel.join" do - socket_pid = self() + socket_pid = self() - push_cb = fn event, payload -> - send(socket_pid, {:push, event, payload}) - end + push_cb = fn event, payload -> + send(socket_pid, {:push, event, payload}) + end - with {:ok, link} <- - DeviceLink.connect(device, push_cb, params, monitor: socket.assigns.reference_id) do - {:ok, assign(socket, :device_link_pid, link)} - end + with {:ok, link} <- + DeviceLink.connect(device, push_cb, params, monitor: socket.assigns.reference_id) do + {:ok, assign(socket, :device_link_pid, link)} end end diff --git a/mix.exs b/mix.exs index 395553d1e..6fededae9 100644 --- a/mix.exs +++ b/mix.exs @@ -19,9 +19,7 @@ defmodule NervesHub.MixProject do include_executables_for: [:unix], reboot_system_after_config: true, applications: [ - nerves_hub: :permanent, - opentelemetry_exporter: :permanent, - opentelemetry: :temporary + nerves_hub: :permanent ] ] ] @@ -40,8 +38,7 @@ defmodule NervesHub.MixProject do :jason, :logger, :runtime_tools, - :timex, - :tls_certificate_check + :timex ] ] end @@ -81,9 +78,6 @@ defmodule NervesHub.MixProject do {:mox, "~> 1.0", only: [:test, :dev]}, {:nimble_csv, "~> 1.1"}, {:oban, "~> 2.11"}, - {:opentelemetry, "~> 1.3"}, - {:opentelemetry_api, "~> 1.2"}, - {:opentelemetry_exporter, "~> 1.4"}, {:phoenix, "~> 1.7.0"}, {:phoenix_active_link, "~> 0.3.1"}, {:phoenix_ecto, "~> 4.0"}, diff --git a/mix.lock b/mix.lock index e3b8dd933..9f34898ef 100644 --- a/mix.lock +++ b/mix.lock @@ -1,25 +1,21 @@ %{ - "acceptor_pool": {:hex, :acceptor_pool, "1.0.0", "43c20d2acae35f0c2bcd64f9d2bde267e459f0f3fd23dab26485bf518c281b21", [:rebar3], [], "hexpm", "0cbcd83fdc8b9ad2eee2067ef8b91a14858a5883cb7cd800e6fcd5803e158788"}, "bandit": {:hex, :bandit, "1.1.1", "7158770ed1584c12964902ebce91c649b2c37a56e8ff36cc4f394baeca0876dc", [:mix], [{:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "d521272da28186412d4f8b480e17009f0bafb844dc7ac7e7a30d79eaf1f141fd"}, "base62": {:hex, :base62, "1.2.2", "85c6627eb609317b70f555294045895ffaaeb1758666ab9ef9ca38865b11e629", [:mix], [{:custom_base, "~> 0.2.1", [hex: :custom_base, repo: "hexpm", optional: false]}], "hexpm", "d41336bda8eaa5be197f1e4592400513ee60518e5b9f4dcf38f4b4dae6f377bb"}, "bcrypt_elixir": {:hex, :bcrypt_elixir, "3.1.0", "0b110a9a6c619b19a7f73fa3004aa11d6e719a67e672d1633dc36b6b2290a0f7", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "2ad2acb5a8bc049e8d5aa267802631912bb80d5f4110a178ae7999e69dca1bf7"}, - "castore": {:hex, :castore, "1.0.4", "ff4d0fb2e6411c0479b1d965a814ea6d00e51eb2f58697446e9c41a97d940b28", [:mix], [], "hexpm", "9418c1b8144e11656f0be99943db4caf04612e3eaecefb5dae9a2a87565584f8"}, + "castore": {:hex, :castore, "1.0.5", "9eeebb394cc9a0f3ae56b813459f990abb0a3dedee1be6b27fdb50301930502f", [:mix], [], "hexpm", "8d7c597c3e4a64c395980882d4bca3cebb8d74197c590dc272cfd3b6a6310578"}, "certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"}, - "chatterbox": {:hex, :ts_chatterbox, "0.13.0", "6f059d97bcaa758b8ea6fffe2b3b81362bd06b639d3ea2bb088335511d691ebf", [:rebar3], [{:hpack, "~> 0.2.3", [hex: :hpack_erl, repo: "hexpm", optional: false]}], "hexpm", "b93d19104d86af0b3f2566c4cba2a57d2e06d103728246ba1ac6c3c0ff010aa7"}, "circular_buffer": {:hex, :circular_buffer, "0.4.1", "477f370fd8cfe1787b0a1bade6208bbd274b34f1610e41f1180ba756a7679839", [:mix], [], "hexpm", "633ef2e059dde0d7b89bbab13b1da9d04c6685e80e68fbdf41282d4fae746b72"}, "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"}, "comeonin": {:hex, :comeonin, "5.4.0", "246a56ca3f41d404380fc6465650ddaa532c7f98be4bda1b4656b3a37cc13abe", [:mix], [], "hexpm", "796393a9e50d01999d56b7b8420ab0481a7538d0caf80919da493b4a6e51faf1"}, "crontab": {:hex, :crontab, "1.1.13", "3bad04f050b9f7f1c237809e42223999c150656a6b2afbbfef597d56df2144c5", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "d67441bec989640e3afb94e123f45a2bc42d76e02988c9613885dc3d01cf7085"}, - "ctx": {:hex, :ctx, "0.6.0", "8ff88b70e6400c4df90142e7f130625b82086077a45364a78d208ed3ed53c7fe", [:rebar3], [], "hexpm", "a14ed2d1b67723dbebbe423b28d7615eb0bdcba6ff28f2d1f1b0a7e1d4aa5fc2"}, "custom_base": {:hex, :custom_base, "0.2.1", "4a832a42ea0552299d81652aa0b1f775d462175293e99dfbe4d7dbaab785a706", [:mix], [], "hexpm", "8df019facc5ec9603e94f7270f1ac73ddf339f56ade76a721eaa57c1493ba463"}, "db_connection": {:hex, :db_connection, "2.6.0", "77d835c472b5b67fc4f29556dee74bf511bbafecdcaf98c27d27fa5918152086", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c2f992d15725e721ec7fbc1189d4ecdb8afef76648c746a8e1cad35e3b8a35f3"}, "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, "decorator": {:hex, :decorator, "1.4.0", "a57ac32c823ea7e4e67f5af56412d12b33274661bb7640ec7fc882f8d23ac419", [:mix], [], "hexpm", "0a07cedd9083da875c7418dea95b78361197cf2bf3211d743f6f7ce39656597f"}, "dns_cluster": {:git, "https://github.com/davydog187/dns_cluster.git", "2535ab2a30d7d37767f40a7b6424b270e3fc9df7", [branch: "support-multiple-queries"]}, - "earmark": {:hex, :earmark, "1.4.26", "f0e3c3d5c278a6d448ad8c27ab0ecdec9c57a7710553138c56af220a6330a4fd", [:mix], [{:earmark_parser, "~> 1.4.26", [hex: :earmark_parser, repo: "hexpm", optional: false]}], "hexpm", "e1231882b56bece0692af33f0959f06c9cd580c2dc2ecb1dc9f16f2750fa78c5"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.26", "f4291134583f373c7d8755566122908eb9662df4c4b63caa66a0eabe06569b0a", [:mix], [], "hexpm", "48d460899f8a0c52c5470676611c01f64f3337bad0b26ddab43648428d94aabc"}, + "earmark": {:hex, :earmark, "1.4.46", "8c7287bd3137e99d26ae4643e5b7ef2129a260e3dcf41f251750cb4563c8fb81", [:mix], [], "hexpm", "798d86db3d79964e759ddc0c077d5eb254968ed426399fbf5a62de2b5ff8910a"}, "ecto": {:hex, :ecto, "3.11.1", "4b4972b717e7ca83d30121b12998f5fcdc62ba0ed4f20fd390f16f3270d85c3e", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ebd3d3772cd0dfcd8d772659e41ed527c28b2a8bde4b00fe03e0463da0f1983b"}, - "ecto_sql": {:hex, :ecto_sql, "3.11.0", "c787b24b224942b69c9ff7ab9107f258ecdc68326be04815c6cce2941b6fad1c", [:mix], [{:db_connection, "~> 2.5 or ~> 2.4.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.11.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "77aa3677169f55c2714dda7352d563002d180eb33c0dc29cd36d39c0a1a971f5"}, + "ecto_sql": {:hex, :ecto_sql, "3.11.1", "e9abf28ae27ef3916b43545f9578b4750956ccea444853606472089e7d169470", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.11.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ce14063ab3514424276e7e360108ad6c2308f6d88164a076aac8a387e1fea634"}, "elixir_make": {:hex, :elixir_make, "0.7.7", "7128c60c2476019ed978210c245badf08b03dbec4f24d05790ef791da11aa17c", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "5bc19fff950fad52bbe5f211b12db9ec82c6b34a9647da0c2224b8b8464c7e6c"}, "ex_aws": {:hex, :ex_aws, "2.5.0", "1785e69350b16514c1049330537c7da10039b1a53e1d253bbd703b135174aec3", [:mix], [{:configparser_ex, "~> 4.0", [hex: :configparser_ex, repo: "hexpm", optional: true]}, {:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:jsx, "~> 2.8 or ~> 3.0", [hex: :jsx, repo: "hexpm", optional: true]}, {:mime, "~> 1.2 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:sweet_xml, "~> 0.7", [hex: :sweet_xml, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "971b86e5495fc0ae1c318e35e23f389e74cf322f2c02d34037c6fc6d405006f1"}, "ex_aws_s3": {:hex, :ex_aws_s3, "2.5.2", "cee302b8e9ee198cc0d89f1de2a7d6a8921e1a556574476cf5590d2156590fe3", [:mix], [{:ex_aws, "~> 2.0", [hex: :ex_aws, repo: "hexpm", optional: false]}, {:sweet_xml, ">= 0.0.0", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm", "cc5bd945a22a99eece4721d734ae2452d3717e81c357a781c8574663254df4a1"}, @@ -29,11 +25,8 @@ "floki": {:hex, :floki, "0.35.2", "87f8c75ed8654b9635b311774308b2760b47e9a579dabf2e4d5f1e1d42c39e0b", [:mix], [], "hexpm", "6b05289a8e9eac475f644f09c2e4ba7e19201fd002b89c28c1293e7bd16773d9"}, "gen_smtp": {:hex, :gen_smtp, "1.2.0", "9cfc75c72a8821588b9b9fe947ae5ab2aed95a052b81237e0928633a13276fd3", [:rebar3], [{:ranch, ">= 1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "5ee0375680bca8f20c4d85f58c2894441443a743355430ff33a783fe03296779"}, "gettext": {:hex, :gettext, "0.24.0", "6f4d90ac5f3111673cbefc4ebee96fe5f37a114861ab8c7b7d5b30a1108ce6d8", [:mix], [{:expo, "~> 0.5.1", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "bdf75cdfcbe9e4622dd18e034b227d77dd17f0f133853a1c73b97b3d6c770e8b"}, - "gproc": {:hex, :gproc, "0.8.0", "cea02c578589c61e5341fce149ea36ccef236cc2ecac8691fba408e7ea77ec2f", [:rebar3], [], "hexpm", "580adafa56463b75263ef5a5df4c86af321f68694e7786cb057fd805d1e2a7de"}, - "grpcbox": {:hex, :grpcbox, "0.16.0", "b83f37c62d6eeca347b77f9b1ec7e9f62231690cdfeb3a31be07cd4002ba9c82", [:rebar3], [{:acceptor_pool, "~> 1.0.0", [hex: :acceptor_pool, repo: "hexpm", optional: false]}, {:chatterbox, "~> 0.13.0", [hex: :ts_chatterbox, repo: "hexpm", optional: false]}, {:ctx, "~> 0.6.0", [hex: :ctx, repo: "hexpm", optional: false]}, {:gproc, "~> 0.8.0", [hex: :gproc, repo: "hexpm", optional: false]}], "hexpm", "294df743ae20a7e030889f00644001370a4f7ce0121f3bbdaf13cf3169c62913"}, "hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~>2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"}, "hlclock": {:hex, :hlclock, "1.0.0", "7a72fc7a20a9382499216227edf97a8b118e21fc3fcad0e81b8d10c616ce1431", [:mix], [], "hexpm", "d3f994336a7fcbc68bf08b14b2101b61e57bef82c032a6e05c1cdc753612c941"}, - "hpack": {:hex, :hpack_erl, "0.2.3", "17670f83ff984ae6cd74b1c456edde906d27ff013740ee4d9efaa4f1bf999633", [:rebar3], [], "hexpm", "06f580167c4b8b8a6429040df36cc93bba6d571faeaec1b28816523379cbb23a"}, "hpax": {:hex, :hpax, "0.1.2", "09a75600d9d8bbd064cdd741f21fc06fc1f4cf3d0fcc335e5aa19be1a7235c84", [:mix], [], "hexpm", "2c87843d5a23f5f16748ebe77969880e29809580efdaccd615cd3bed628a8c13"}, "html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, @@ -47,13 +40,9 @@ "mix_test_watch": {:hex, :mix_test_watch, "1.1.1", "eee6fc570d77ad6851c7bc08de420a47fd1e449ef5ccfa6a77ef68b72e7e51ad", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "f82262b54dee533467021723892e15c3267349849f1f737526523ecba4e6baae"}, "mox": {:hex, :mox, "1.1.0", "0f5e399649ce9ab7602f72e718305c0f9cdc351190f72844599545e4996af73c", [:mix], [], "hexpm", "d44474c50be02d5b72131070281a5d3895c0e7a95c780e90bc0cfe712f633a13"}, "nimble_csv": {:hex, :nimble_csv, "1.2.0", "4e26385d260c61eba9d4412c71cea34421f296d5353f914afe3f2e71cce97722", [:mix], [], "hexpm", "d0628117fcc2148178b034044c55359b26966c6eaa8e2ce15777be3bbc91b12a"}, - "nimble_options": {:hex, :nimble_options, "1.0.2", "92098a74df0072ff37d0c12ace58574d26880e522c22801437151a159392270e", [:mix], [], "hexpm", "fd12a8db2021036ce12a309f26f564ec367373265b53e25403f0ee697380f1b8"}, + "nimble_options": {:hex, :nimble_options, "1.1.0", "3b31a57ede9cb1502071fade751ab0c7b8dbe75a9a4c2b5bbb0943a690b63172", [:mix], [], "hexpm", "8bbbb3941af3ca9acc7835f5655ea062111c9c27bcac53e004460dfd19008a99"}, "nimble_pool": {:hex, :nimble_pool, "1.0.0", "5eb82705d138f4dd4423f69ceb19ac667b3b492ae570c9f5c900bb3d2f50a847", [:mix], [], "hexpm", "80be3b882d2d351882256087078e1b1952a28bf98d0a287be87e4a24a710b67a"}, - "oban": {:hex, :oban, "2.16.3", "33ebe7da637cce4da5438c1636bc25448a8628994a0c064ac6078bbe6dc97bd6", [:mix], [{:ecto_sql, "~> 3.6", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4d8a7fb62f63cf2f2080c78954425f5fd8916ef57196b7f79b5bc657abb2ac5f"}, - "opentelemetry": {:hex, :opentelemetry, "1.3.1", "f0a342a74379e3540a634e7047967733da4bc8b873ec9026e224b2bd7369b1fc", [:rebar3], [{:opentelemetry_api, "~> 1.2.2", [hex: :opentelemetry_api, repo: "hexpm", optional: false]}, {:opentelemetry_semantic_conventions, "~> 0.2", [hex: :opentelemetry_semantic_conventions, repo: "hexpm", optional: false]}], "hexpm", "de476b2ac4faad3e3fe3d6e18b35dec9cb338c3b9910c2ce9317836dacad3483"}, - "opentelemetry_api": {:hex, :opentelemetry_api, "1.2.2", "693f47b0d8c76da2095fe858204cfd6350c27fe85d00e4b763deecc9588cf27a", [:mix, :rebar3], [{:opentelemetry_semantic_conventions, "~> 0.2", [hex: :opentelemetry_semantic_conventions, repo: "hexpm", optional: false]}], "hexpm", "dc77b9a00f137a858e60a852f14007bb66eda1ffbeb6c05d5fe6c9e678b05e9d"}, - "opentelemetry_exporter": {:hex, :opentelemetry_exporter, "1.6.0", "f4fbf69aa9f1541b253813221b82b48a9863bc1570d8ecc517bc510c0d1d3d8c", [:rebar3], [{:grpcbox, ">= 0.0.0", [hex: :grpcbox, repo: "hexpm", optional: false]}, {:opentelemetry, "~> 1.3", [hex: :opentelemetry, repo: "hexpm", optional: false]}, {:opentelemetry_api, "~> 1.2", [hex: :opentelemetry_api, repo: "hexpm", optional: false]}, {:tls_certificate_check, "~> 1.18", [hex: :tls_certificate_check, repo: "hexpm", optional: false]}], "hexpm", "1802d1dca297e46f21e5832ecf843c451121e875f73f04db87355a6cb2ba1710"}, - "opentelemetry_semantic_conventions": {:hex, :opentelemetry_semantic_conventions, "0.2.0", "b67fe459c2938fcab341cb0951c44860c62347c005ace1b50f8402576f241435", [:mix, :rebar3], [], "hexpm", "d61fa1f5639ee8668d74b527e6806e0503efc55a42db7b5f39939d84c07d6895"}, + "oban": {:hex, :oban, "2.17.1", "42d6221a1c17b63d81c19e3bad9ea82b59e39c47c1f9b7670ee33628569a449b", [:mix], [{:ecto_sql, "~> 3.6", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c02686ada7979b00e259c0efbafeae2749f8209747b3460001fe695c5bdbeee6"}, "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, "phoenix": {:hex, :phoenix, "1.7.10", "02189140a61b2ce85bb633a9b6fd02dff705a5f1596869547aeb2b2b95edd729", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "cf784932e010fd736d656d7fead6a584a4498efefe5b8227e9f383bf15bb79d0"}, "phoenix_active_link": {:hex, :phoenix_active_link, "0.3.2", "946d013b0839341d85ac4259fadf944d9767186e424a78ef37d310beac92d840", [:mix], [{:phoenix_html, "~> 2.10 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "81fcd4333541d6f1586e1d5fd0c34efed9bc4129573862bea340e234b6f22aeb"}, @@ -74,7 +63,7 @@ "scrivener": {:hex, :scrivener, "2.7.2", "1d913c965ec352650a7f864ad7fd8d80462f76a32f33d57d1e48bc5e9d40aba2", [:mix], [], "hexpm", "7866a0ec4d40274efbee1db8bead13a995ea4926ecd8203345af8f90d2b620d9"}, "scrivener_ecto": {:hex, :scrivener_ecto, "2.7.0", "cf64b8cb8a96cd131cdbcecf64e7fd395e21aaa1cb0236c42a7c2e34b0dca580", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:scrivener, "~> 2.4", [hex: :scrivener, repo: "hexpm", optional: false]}], "hexpm", "e809f171687806b0031129034352f5ae44849720c48dd839200adeaf0ac3e260"}, "scrivener_html": {:git, "https://github.com/nerves-hub/scrivener_html", "e7d2ffcf241cebc8490da44b5d09ac297b57a91b", [branch: "phx-1.5"]}, - "sentry": {:hex, :sentry, "10.0.3", "2b94e40faaaca9117aad15fef2c2c432b183051457dcca449d7301faea9ad0ac", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:plug, "~> 1.6", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, "~> 2.3", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm", "dde930e66adf690227616d31992f0f694e91a3a556add234d45fed19741580fb"}, + "sentry": {:hex, :sentry, "10.1.0", "5d73c23deb5d95f3027fbb09801bd8e787065be61f0065418aed3961becbbe9f", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:plug, "~> 1.6", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, "~> 2.3", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm", "f4319e7491133046912b4cf7cbe6f5226b309275d1a6d05386cce2ac7f97b2d2"}, "slipstream": {:hex, :slipstream, "1.1.0", "e3581e9bc73036e4283b33447475499d18c813c7662aa6b86e131633a7e912f3", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mint_web_socket, "~> 0.2 or ~> 1.0", [hex: :mint_web_socket, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.1 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "66eb1ac7c43573511b5bad90c24c128bb4e69f588bff65d0c409adf4c7eb02e6"}, "socket_drano": {:hex, :socket_drano, "0.5.0", "448c5949412e9dd6e6d0464d9182f15ea1f2cce8fef2b4cd252c087154aa3449", [:mix], [{:phoenix, ">= 1.4.7", [hex: :phoenix, repo: "hexpm", optional: true]}, {:ranch, ">= 1.7.0", [hex: :ranch, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "b236313d43e1226d97aa83d90d21aa1d786a3384bc8669a97dc2e0911d35d357"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, @@ -86,7 +75,6 @@ "telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"}, "thousand_island": {:hex, :thousand_island, "1.2.0", "4f548ae771ab5f96bc7e199f9824c0c2ce6d365f8c93f5f64dbbb33988e484bf", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "521671fea179672addb6af46455fc2a77be1edda4c0ed351633e0ef37a4b3584"}, "timex": {:hex, :timex, "3.7.11", "bb95cb4eb1d06e27346325de506bcc6c30f9c6dea40d1ebe390b262fad1862d1", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.20", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "8b9024f7efbabaf9bd7aa04f65cf8dcd7c9818ca5737677c7b76acbc6a94d1aa"}, - "tls_certificate_check": {:hex, :tls_certificate_check, "1.19.0", "c76c4c5d79ee79a2b11c84f910c825d6f024a78427c854f515748e9bd025e987", [:rebar3], [{:ssl_verify_fun, "~> 1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "4083b4a298add534c96125337cb01161c358bb32dd870d5a893aae685fd91d70"}, "tzdata": {:hex, :tzdata, "1.1.1", "20c8043476dfda8504952d00adac41c6eda23912278add38edc140ae0c5bcc46", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "a69cec8352eafcd2e198dea28a34113b60fdc6cb57eb5ad65c10292a6ba89787"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},