Update EnigmaWorker to not die causing supervisor restarts.

* Tests for enigma cleanup
pull/763/head
Rick Carlino 2019-03-20 16:04:26 -05:00 committed by Connor Rigby
parent 4c5a093e2c
commit 890ddfbd86
No known key found for this signature in database
GPG Key ID: 29A88B24B70456E0
9 changed files with 100 additions and 22 deletions

View File

@ -12,7 +12,8 @@ defmodule FarmbotCore.Asset.Supervisor do
FbosConfig,
PinBinding,
Peripheral,
PersistentRegimen
PersistentRegimen,
Private.Enigma
}
def start_link(args) do
@ -30,6 +31,7 @@ defmodule FarmbotCore.Asset.Supervisor do
{AssetSupervisor, module: Peripheral},
{AssetSupervisor, module: FarmwareInstallation},
{AssetSupervisor, module: FarmwareEnv},
{AssetSupervisor, [module: Enigma, strategy: :transient]},
AssetMonitor,
EnigmaHandler,
]

View File

@ -16,7 +16,8 @@ defmodule FarmbotCore.AssetMonitor do
FarmwareEnv,
Peripheral,
PersistentRegimen,
PinBinding
PinBinding,
Private.Enigma
}
alias FarmbotCore.{AssetSupervisor, AssetWorker}
@ -116,6 +117,7 @@ defmodule FarmbotCore.AssetMonitor do
do: [
Device,
FbosConfig,
Enigma,
FarmEvent,
Peripheral,
PersistentRegimen,

View File

@ -12,24 +12,36 @@ defimpl FarmbotCore.AssetWorker, for: FarmbotCore.Asset.Private.Enigma do
def preload(%Enigma{}), do: []
def start_link(%Enigma{} = enigma, _args) do
GenServer.start_link(__MODULE__, %Enigma{} = enigma)
GenServer.start_link(__MODULE__, enigma)
end
def init(%Enigma{} = enigma) do
{:ok, %Enigma{} = enigma, 0}
{:ok, %{enigma: enigma, dead: false}, 0}
end
def terminate(_, enigma) do
BotState.clear_enigma(enigma)
FarmbotCore.EnigmaHandler.handle_down(enigma)
def terminate(_reason, state) do
finish(state)
end
def handle_info(:timeout, %Enigma{} = enigma) do
def handle_info(:timeout, %{dead: true} = state) do
{:noreply, state, :hibernate}
end
def handle_info(:timeout, %{enigma: enigma, dead: false} = state) do
BotState.add_enigma(enigma)
# Handle enigma and block stuff.
case FarmbotCore.EnigmaHandler.handle_up(enigma) do
{:error, _} -> {:noreply, enigma, @error_retry_time_ms}
:ok -> {:stop, :normal, enigma}
{:error, _} ->
{:noreply, state, @error_retry_time_ms}
:ok ->
{:noreply, finish(%{state | dead: true})}
end
end
def finish(state) do
BotState.clear_enigma(state.enigma)
FarmbotCore.EnigmaHandler.handle_down(state.enigma)
state
end
end

View File

@ -1,8 +1,14 @@
defmodule FarmbotCore.EnigmaHandler do
use GenServer
@callback enigma_up(FarmbotCore.Assets.Private.Enigma.t) :: any
@callback enigma_down(FarmbotCore.Assets.Private.Enigma.t) :: any
@type enigma :: FarmbotCore.Assets.Private.Enigma.t()
@doc """
The `up` callback will cause the EnigmaWorker to terminate if the
callback returns `:ok`
"""
@type enigma_up_callback :: (enigma -> :ok | {:error, term})
@type enigma_down_callback :: (enigma -> :ok | {:error, term})
def start_link(args, opts \\ [name: __MODULE__]) do
GenServer.start_link(__MODULE__, args, opts)

View File

@ -7,7 +7,7 @@ defmodule FarmbotCore.Config.Repo.Migrations.AddEnigmasTable do
add(:problem_tag, :string)
add(:priority, :integer)
add(:monitor, :boolean)
add(:monitor, :boolean, default: true)
timestamps(inserted_at: :created_at, type: :utc_datetime)
end
end

View File

@ -2,7 +2,7 @@ defmodule FarmbotCore.Private.EnigmaWorkerTest do
use ExUnit.Case, async: true
alias FarmbotCore.{BotState, EnigmaHandler, Asset.Private.Enigma, AssetWorker}
test "enigmas existance is persisted to the bot's state" do
test "enigmas existence is persisted to the bot's state" do
Process.flag(:trap_exit, true)
uuid = Ecto.UUID.generate()
@ -36,6 +36,46 @@ defmodule FarmbotCore.Private.EnigmaWorkerTest do
refute enigmas[uuid]
end
test "post-teardown cleanup" do
uuid = Ecto.UUID.generate()
enigma = %Enigma{
local_id: uuid,
priority: 100,
problem_tag: "yolo.woosh",
created_at: DateTime.utc_now()
}
bot_state = BotState.subscribe()
# Ensures this enigma hasn't been registered yet somehow
refute bot_state.enigmas[uuid]
test_pid = self()
up_fun = fn _ ->
send(test_pid, :up)
:ok
end
EnigmaHandler.register_up("yolo.woosh", up_fun)
down_fun = fn _ ->
send(test_pid, :down)
:ok
end
EnigmaHandler.register_down("yolo.woosh", down_fun)
# Start the enigma manager process
{:ok, pid} = AssetWorker.start_link(enigma)
assert_receive {BotState, %{changes: %{enigmas: enigmas}}}
assert enigmas[uuid]
assert_receive :up
assert_receive :down
assert_receive {BotState, %{changes: %{enigmas: enigmas}}}
refute enigmas[uuid]
end
test "enigmas lifecycle events" do
uuid = Ecto.UUID.generate()

View File

@ -5,12 +5,25 @@ defmodule FarmbotOS.Init.EnigmaFirmwareMissing do
require FarmbotCore.Logger
use Supervisor
@doc false
def start_link(args) do
Supervisor.start_link(__MODULE__, args, name: __MODULE__)
end
@doc false
def init([]) do
setup()
# This is wrong. TODO: Use tasks #shipit
:ignore
end
def setup() do
EnigmaHandler.register_up("firmware.missing", &enigma_up/1)
EnigmaHandler.register_down("firmware.missing", &enigma_down/1)
needs_flash? =
FarmbotCore.Config.get_config_value(:string, "settings", "firmware_needs_flash")
needs_flash? = FarmbotCore.Config.get_config_value(:bool, "settings", "firmware_needs_flash")
firmware_hardware = FarmbotCore.Asset.fbos_config(:firmware_hardware)
situation = {needs_flash?, firmware_hardware}
@ -38,15 +51,17 @@ defmodule FarmbotOS.Init.EnigmaFirmwareMissing do
end
end
# Returning :ok here will cause the enigma to be cleared.
# We don't need this callback currently, so return error.
def enigma_up(_) do
:ok
{:error, :noop}
end
def enigma_down(_) do
swap_transport(FarmbotCore.Asset.fbos_config(:firmware_path))
end
def swap_transport(tty) do
defp swap_transport(tty) do
# Swap transport on FW module.
# Close tranpsort if it is open currently.
_ = FarmbotFirmware.close_transport()

View File

@ -11,7 +11,8 @@ defmodule FarmbotOS.Init.Supervisor do
children =
(config[:init_children] || []) ++
[
FarmbotOS.Init.FSCheckup
FarmbotOS.Init.FSCheckup,
FarmbotOS.Init.EnigmaFirmwareMissing
]
Supervisor.init(children, strategy: :one_for_all)

View File

@ -95,9 +95,9 @@ defmodule Farmbot.TestSupport.AssetFixtures do
def enigma() do
Private.create_or_update_enigma!(%{
problem_tag: "firmware.missing",
priority: 100,
monitor: false
problem_tag: "firmware.missing",
priority: 100,
monitor: false
})
end
end