Download/Upload settings from API.

* `config_update` for the os is depricated.
* Add new bootstrap task to download or upload settings.
* Factory reset will `delete` settings from the API. (Or at least try
too)
* Add `DELETE` and `PUT` methods to HTTP adapter.
* Download settings on `sync`
pull/433/head
connor rigby 2018-01-22 10:12:43 -08:00
parent ca35bae127
commit 168d90c6f9
7 changed files with 116 additions and 58 deletions

View File

@ -0,0 +1,78 @@
defmodule Farmbot.Bootstrap.SettingsSync do
use Task, restart: :transient
use Farmbot.Logger
import Farmbot.System.ConfigStorage, only: [get_config_value: 3, update_config_value: 4, get_config_as_map: 0]
def start_link() do
Task.start_link(__MODULE__, :run, [])
end
def run() do
with {:ok, %{body: body, status_code: 200}} <- Farmbot.HTTP.get("/api/fbos_config"),
{:ok, data} <- Poison.decode(body)
do
do_sync_settings(data)
else
{:ok, status_code: code} ->
Logger.error 1, "HTTP error syncing settings: #{code}"
:ok
err ->
Logger.error 1, "Error syncing settings: #{inspect err}"
:ok
end
rescue
err ->
Logger.error 1, "Error syncing settings: #{Exception.message(err)}"
end
def apply_map(old_map, new_map) do
Enum.all?(new_map, fn({key, new_value}) ->
if old_map[key] != new_value do
case new_value do
val when is_boolean(val) -> update_config_value(:bool, "settings", key, new_value)
val when is_binary(val) -> update_config_value(:string, "settings", key, new_value)
val when is_number(val) -> update_config_value(:float, "settings", key, new_value)
end
end
end)
end
def do_sync_settings(api_data) do
Logger.info 3, "API is the source of truth; Downloading data."
old_config = get_config_as_map()["settings"]
apply_map(old_config, api_data)
:ok
end
def do_sync_settings(_unimportant_data) do
Logger.info 3, "FBOS is the source of truth; Uploading data."
auto_sync = get_config_value(:bool, "settings", "auto_sync")
beta_opt_in = get_config_value(:bool, "settings", "beta_opt_in")
disable_factory_reset = get_config_value(:bool, "settings", "disable_factory_reset")
firmware_output_log = get_config_value(:bool, "settings", "firmware_output_log")
sequence_body_log = get_config_value(:bool, "settings", "sequence_body_log")
sequence_complete_log = get_config_value(:bool, "settings", "sequence_complete_log")
sequence_init_log = get_config_value(:bool, "settings", "sequence_init_log")
arduino_debug_messages = get_config_value(:bool, "settings", "arduino_debug_messages")
os_auto_update = get_config_value(:bool, "settings", "os_auto_update")
firmware_hardware = get_config_value(:string, "settings", "firmware_hardware")
network_not_found_timer = get_config_value(:float, "settings", "network_not_found_timer")
payload = %{
# api_migrated: true,
auto_sync: auto_sync,
beta_opt_in: beta_opt_in,
disable_factory_reset: disable_factory_reset,
firmware_output_log: firmware_output_log,
sequence_body_log: sequence_body_log,
sequence_complete_log: sequence_complete_log,
sequence_init_log: sequence_init_log,
arduino_debug_messages: arduino_debug_messages,
os_auto_update: os_auto_update,
firmware_hardware: firmware_hardware,
network_not_found_timer: network_not_found_timer,
} |> Poison.encode!()
Farmbot.HTTP.delete!("/api/fbos_config")
Farmbot.HTTP.put!("/api/fbos_config", payload)
:ok
end
end

View File

@ -125,11 +125,12 @@ defmodule Farmbot.Bootstrap.Supervisor do
children = [
worker(Farmbot.Bootstrap.AuthTask, []),
supervisor(Farmbot.HTTP.Supervisor, []),
worker(Farmbot.Bootstrap.SettingsSync, [], [restart: :transient]),
supervisor(Farmbot.Firmware.Supervisor, []),
supervisor(Farmbot.BotState.Supervisor, []),
supervisor(Farmbot.BotState.Transport.Supervisor, []),
supervisor(Farmbot.FarmEvent.Supervisor, []),
supervisor(Farmbot.HTTP.Supervisor, []),
supervisor(Farmbot.Repo.Supervisor, []),
supervisor(Farmbot.Farmware.Supervisor, []),
supervisor(Farmbot.Regimen.Supervisor, []),

View File

@ -178,6 +178,8 @@ defmodule Farmbot.BotState.Transport.AMQP do
["bot", ^device, "sync", resource, _]
when resource in ["Log", "User", "Image", "WebcamFeed"] ->
{:noreply, [], state}
["bot", ^device, "sync", "FbosConfig", id] ->
handle_fbos_config(id, payload, state)
["bot", ^device, "sync", resource, id] ->
handle_sync_cmd(resource, id, payload, state)
["bot", ^device, "logs"] -> {:noreply, [], state}
@ -231,6 +233,17 @@ defmodule Farmbot.BotState.Transport.AMQP do
{:noreply, [], state}
end
def handle_fbos_config(_id, payload, state) do
case Poison.decode(payload) do
# TODO(Connor) What do I do with deletes?
{:ok, %{"body" => nil}} -> {:noreply, [], state}
{:ok, %{"body" => config}} ->
config = Map.drop(config, ["created_at", "updated_at", "id"])
Farmbot.Bootstrap.SettingsSync.apply_map(state.state_cache.configuration, config)
{:noreply, [], state}
end
end
defp push_bot_log(chan, bot, log) do
json = Poison.encode!(log)
:ok = AMQP.Basic.publish chan, @exchange, "bot.#{bot}.logs", json

View File

@ -4,33 +4,17 @@ defmodule Farmbot.CeleryScript.AST.Node.ConfigUpdate do
use Farmbot.Logger
allow_args [:package]
def execute(%{package: :farmbot_os}, body, env) do
Logger.warn 2, "`config_update` is a depricated RPC."
def execute(%{package: :farmbot_os}, _body, env) do
Logger.warn 2, "`config_update` for FBOS is depricated."
env = mutate_env(env)
do_reduce_os(body, env)
{:ok, env}
end
def execute(%{package: :arduino_firmware}, body, env) do
Logger.warn 2, "`config_update` is a depricated RPC."
env = mutate_env(env)
do_reduce_fw(body, env)
end
defp do_reduce_os([%{args: %{label: key, value: value}} | rest], env) do
case lookup_os_config(key, value) do
{:ok, {type, group, value}} ->
Farmbot.System.ConfigStorage.update_config_value(type, group, key, value)
Logger.success 3, "Updating: #{inspect key}: #{value}"
do_reduce_os(rest, env)
{:error, reason} ->
{:error, reason, env}
end
end
defp do_reduce_os([], env) do
{:ok, env}
end
defp do_reduce_fw([%{args: %{label: key, value: value}} | rest], env) do
case Farmbot.Firmware.update_param(:"#{key}", value) do
:ok -> do_reduce_fw(rest, env)
@ -39,41 +23,4 @@ defmodule Farmbot.CeleryScript.AST.Node.ConfigUpdate do
end
defp do_reduce_fw([], env), do: {:ok, env}
defp lookup_os_config("os_auto_update", val), do: {:ok, {:bool, "settings", format_bool_for_os(val)}}
defp lookup_os_config("auto_sync", val), do: {:ok, {:bool, "settings", format_bool_for_os(val)}}
defp lookup_os_config("timezone", val), do: {:ok, {:string, "settings", val}}
defp lookup_os_config("disable_factory_reset", val), do: {:ok, {:bool, "settings", format_bool_for_os(val)}}
defp lookup_os_config("sequence_init_log", val), do: {:ok, {:bool, "settings", format_bool_for_os(val)}}
defp lookup_os_config("sequence_body_log", val), do: {:ok, {:bool, "settings", format_bool_for_os(val)}}
defp lookup_os_config("sequence_complete_log", val), do: {:ok, {:bool, "settings", format_bool_for_os(val)}}
defp lookup_os_config("arduino_debug_messages", val), do: {:ok, {:bool, "settings", format_bool_for_os(val)}}
defp lookup_os_config("firmware_input_log", val), do: {:ok, {:bool, "settings", format_bool_for_os(val)}}
defp lookup_os_config("firmware_output_log", val), do: {:ok, {:bool, "settings", format_bool_for_os(val)}}
defp lookup_os_config("beta_opt_in", val), do: {:ok, {:bool, "settings", format_bool_for_os(val)}}
defp lookup_os_config("api_migrated", val), do: {:ok, {:bool, "settings", format_bool_for_os(val)}}
defp lookup_os_config("network_not_found_timer", val) when val > 0, do: {:ok, {:float, "settings", to_float(val)}}
defp lookup_os_config("network_not_found_timer", _val), do: {:error, "network_not_found_timer must be greater than zero"}
defp lookup_os_config("firmware_hardware", "farmduino"), do: {:ok, {:string, "settings", "farmduino"}}
defp lookup_os_config("firmware_hardware", "arduino"), do: {:ok, {:string, "settings", "arduino"}}
defp lookup_os_config("firmware_hardware", unknown), do: {:error, "unknown hardware: #{unknown}"}
defp lookup_os_config(unknown_config, _), do: {:error, "unknown config: #{unknown_config}"}
defp format_bool_for_os(1), do: true
defp format_bool_for_os(0), do: false
defp format_bool_for_os(true), do: true
defp format_bool_for_os(false), do: false
defp to_float(int) when is_integer(int) do
int / 1
end
defp to_float(float) when is_float(float) do
float
end
end

View File

@ -9,6 +9,7 @@ defmodule Farmbot.CeleryScript.AST.Node.FactoryReset do
Farmbot.BotState.set_sync_status(:maintenance)
Farmbot.BotState.force_state_push()
Farmbot.System.ConfigStorage.update_config_value(:bool, "settings", "disable_factory_reset", false)
Farmbot.HTTP.delete("/api/fbos_config")
Logger.warn 1, "Farmbot OS going down for factory reset!"
Farmbot.System.factory_reset "CeleryScript request."
{:ok, env}

View File

@ -88,6 +88,24 @@ defmodule Farmbot.HTTP do
request(:put, url, body, headers, opts)
end
def put!(url, body, headers \\ [], opts \\ [])
def put!(url, body, headers, opts) do
request!(:put, url, body, headers, opts)
end
def delete(url, headers \\ [], opts \\ [])
def delete(url, headers, opts) do
request!(:delete, url, "", headers, opts)
end
def delete!(url, headers \\ [], opts \\ [])
def delete!(url, headers, opts) do
request!(:delete, url, "", headers, opts)
end
@doc "Download a file to the filesystem."
def download_file(url,
path,

View File

@ -370,7 +370,7 @@ defmodule Farmbot.Repo do
case do_sync_all_resources(repo_a) do
:ok ->
do_sync_all_resources(repo_b)
Farmbot.Bootstrap.SettingsSync.run()
err ->
err
end