Permalink
Browse files

Nerves Hub (#647)

  • Loading branch information...
ConnorRigby committed Nov 22, 2018
1 parent c2f6495 commit 561b823b041ff65bd87b81726530619fc9cc4923
Showing with 8,882 additions and 877 deletions.
  1. +257 −164 .circleci/config.yml
  2. +1 −1 .gitignore
  3. +8 −2 config/config.exs
  4. +8 −1 config/host/dev.exs
  5. +2 −1 config/host/test.exs
  6. +9 −0 config/nerves_hub.exs
  7. +13 −2 config/target/dev.exs
  8. +6 −3 config/target/prod.exs
  9. +161 −0 docs/RELEASE.md
  10. +1 −1 lib/farmbot.ex
  11. +12 −1 lib/farmbot/bot_state/bot_state.ex
  12. +26 −2 lib/farmbot/bot_state/transport/amqp/amqp.ex
  13. +2 −0 lib/farmbot/celery_script/ast/node/change_ownership.ex
  14. +8 −9 lib/farmbot/celery_script/ast/node/check_updates.ex
  15. +5 −0 lib/farmbot/project.ex
  16. +2 −1 lib/farmbot/system/init/fs_checkup.ex
  17. +29 −0 lib/farmbot/system/init/supervisor.ex
  18. +142 −0 lib/farmbot/system/nerves_hub/nerves_hub.ex
  19. +3 −2 lib/farmbot/system/registry.ex
  20. +4 −19 lib/farmbot/system/supervisor.ex
  21. +2 −1 lib/farmbot/system/system.ex
  22. +0 −18 lib/farmbot/system/updates/update_handler.ex
  23. +0 −59 lib/farmbot/system/updates/update_timer.ex
  24. +0 −326 lib/farmbot/system/updates/updates.ex
  25. +21 −5 mix.exs
  26. +8 −3 mix.lock.host
  27. +8 −0 mix.lock.rpi0
  28. +10 −2 mix.lock.rpi3
  29. +0 −7 platform/host/nerves_firmware.ex
  30. +27 −0 platform/host/nerves_hub_handler.ex
  31. +0 −26 platform/host/update_handler.ex
  32. +119 −0 platform/target/nerves_hub_client.ex
  33. +0 −1 platform/target/uevent.ex
  34. +0 −66 platform/target/update_handler.ex
  35. +2,665 −0 priv/arduino_firmware.hex
  36. +2,658 −0 priv/farmduino.hex
  37. +2,661 −0 priv/farmduino_k14.hex
  38. +1 −0 priv/prod.pub
  39. +1 −0 priv/staging.pub
  40. +2 −0 rel/vm.args
  41. +0 −1 scripts/update_deps_for_all_platforms.sh
  42. +0 −19 test/farmbot/system/updates/update_timer_test.exs
  43. +0 −108 test/farmbot/system/updates/updates_test.exs
  44. +0 −26 test/support/test_update_handler.ex

Large diffs are not rendered by default.

Oops, something went wrong.
@@ -57,4 +57,4 @@ RELEASE_NOTES
.elixir_ls
nerves-hub
*.pem
*.pem
@@ -69,7 +69,11 @@ config :farmbot,
global_overlay_dir = "rootfs_overlay"
config :nerves, :firmware, rootfs_overlay: [global_overlay_dir]
config :nerves, :firmware,
rootfs_overlay: [global_overlay_dir],
provisioning: :nerves_hub
import_config("nerves_hub.exs")
case target do
"host" ->
@@ -82,5 +86,7 @@ case target do
do: import_config("target/#{target}.exs")
if File.exists?(custom_rootfs_overlay_dir),
do: config :nerves, :firmware, rootfs_overlay: [global_overlay_dir, custom_rootfs_overlay_dir]
do: config :nerves, :firmware,
rootfs_overlay: [global_overlay_dir, custom_rootfs_overlay_dir],
provisioning: :nerves_hub
end
@@ -45,8 +45,15 @@ config :farmbot, default_server: "https://staging.farm.bot"
config :farmbot, :behaviour, [
authorization: Farmbot.Bootstrap.Authorization,
system_tasks: Farmbot.Host.SystemTasks,
update_handler: Farmbot.Host.UpdateHandler,
nerves_hub_handler: Farmbot.Host.NervesHubHandler,
# firmware_handler: Farmbot.Firmware.UartHandler
]
config :nerves_runtime,
enable_syslog: false,
target: "host",
kernel: [
autoload_modules: false
]
config :farmbot, :uart_handler, tty: "/dev/ttyACM0"
@@ -27,7 +27,8 @@ config :farmbot, :farmware, first_part_farmware_manifest_url: nil
config :farmbot, :behaviour,
authorization: Farmbot.Bootstrap.Authorization,
system_tasks: Farmbot.Test.SystemTasks,
update_handler: FarmbotTestSupport.TestUpdateHandler
update_handler: FarmbotTestSupport.TestUpdateHandler,
nerves_hub_handler: Farmbot.Host.NervesHubHandler
config :farmbot, Farmbot.Repo, [
adapter: Sqlite.Ecto2,
@@ -0,0 +1,9 @@
use Mix.Config
config :nerves_hub,
client: Farmbot.System.NervesHubClient,
public_keys: [File.read!("priv/staging.pub"), File.read!("priv/prod.pub")]
config :nerves_hub, NervesHub.Socket, [
reconnect_interval: 5_000,
]
@@ -33,6 +33,9 @@ config :farmbot, :init, [
# Allows for first boot configuration.
Farmbot.Target.Bootstrap.Configurator,
# Handles OTA updates from NervesHub
Farmbot.System.NervesHubClient,
# Start up Network
Farmbot.Target.Network,
@@ -76,16 +79,24 @@ config :farmbot, :behaviour,
authorization: Farmbot.Bootstrap.Authorization,
system_tasks: Farmbot.Target.SystemTasks,
firmware_handler: Farmbot.Firmware.StubHandler,
update_handler: Farmbot.Target.UpdateHandler,
pin_binding_handler: Farmbot.Target.PinBinding.AleHandler,
leds_handler: Farmbot.Target.Leds.AleHandler
leds_handler: Farmbot.Target.Leds.AleHandler,
nerves_hub_handler: Farmbot.System.NervesHubClient
local_file = Path.join(System.user_home!(), ".ssh/id_rsa.pub")
local_key = if File.exists?(local_file), do: [File.read!(local_file)], else: []
config :nerves_network, regulatory_domain: "US"
config :nerves_firmware_ssh, authorized_keys: local_key
config :nerves_init_gadget,
ifname: "usb0",
address_method: :dhcpd,
mdns_domain: "farmbot.local",
node_name: nil,
node_host: :mdns_domain
config :shoehorn,
init: [:nerves_runtime, :nerves_init_gadget, :nerves_firmware_ssh],
handler: Farmbot.ShoehornHandler,
app: :farmbot
@@ -17,7 +17,7 @@ config :farmbot, Farmbot.System.ConfigStorage,
database: "/root/config-#{Mix.env()}.sqlite3"
config :farmbot, ecto_repos: [Farmbot.Repo, Farmbot.System.ConfigStorage]
config :logger, LoggerBackendSqlite, [
database: "/root/debug_logs.sqlite3",
max_logs: 10000
@@ -33,6 +33,9 @@ config :farmbot, :init, [
# Allows for first boot configuration.
Farmbot.Target.Bootstrap.Configurator,
# Handles OTA updates from NervesHub
Farmbot.System.NervesHubClient,
# Start up Network
Farmbot.Target.Network,
@@ -75,9 +78,9 @@ config :farmbot, :behaviour,
authorization: Farmbot.Bootstrap.Authorization,
system_tasks: Farmbot.Target.SystemTasks,
firmware_handler: Farmbot.Firmware.StubHandler,
update_handler: Farmbot.Target.UpdateHandler,
pin_binding_handler: Farmbot.Target.PinBinding.AleHandler,
leds_handler: Farmbot.Target.Leds.AleHandler
leds_handler: Farmbot.Target.Leds.AleHandler,
nerves_hub_handler: Farmbot.System.NervesHubClient
config :nerves_network, regulatory_domain: "US"
config :shoehorn,
@@ -0,0 +1,161 @@
## Provisioning the Release System
Publishing a FarmBotOS release requires coordination of a few different systems.
* FarmBot Web App
* FarmBot OS
* NervesHub
* CircleCI
* GitHub branches and releases
## Legacy System
The legacy system is somewhat simpiler. It goes as follows:
### Pull request into `master` branch.
```
git checkout master
git merge staging
git push origin master
```
Obviously this will not actually work because of testing and things, but that
is what happens behind the scenes on GitHub.
### CircleCI builds release
Once merged into master CircleCI will create a `draft` release on GitHub. This
must be QA'd and confirmed manually before publishing. Once published, FarmBot
will check the `OS_AUTO_UPDATE_URL` in the JWT.
### Beta updates
Users may opt into beta updates by settings `os_beta_updates: true` on their
device's `FbosConfig` endpoint.
The system works the same as production, except that the release is drafted based
on the `beta` branch. The other change is that CircleCI publishes a _real_ release
overwriting a previous release of this version if it exists. the release is tagged
as `pre_release: true` in GitHub releases. this prevents the production system
from downloading `beta` updates.
## NervesHub System
The NervesHub system is simpiler to use, but more complex to setup.
### User registration
Create a admin user. This should be the same `ADMIN_EMAIL` used in
the WebApp configuration.
```
mix nerves_hub.user register
Email address: admin@farmbot.io
Name: farmbot
NervesHub password: *super secret*
Local password: *super duper secret*
```
```
mix nerves_hub.product create
name: farmbot
Local password: *super duper secret*
```
### Signing keys
Now a choice will need to be made.
If fwup signing keys existed beforehand (they did for FarmBot Inc) do:
```
mix nerves_hub.key import <PATH/TO/PUBLIC/KEY> <PATH/TO/PRIVATE/KEY>
Local password: *super duper secret*
```
If new keys are required (probably named "prod") do:
```
mix nerves_hub.key create <NAME>
Local password: *super duper secret*
```
### Exporting certs and keys
The API and CI need copies of these keys and certs.
```
mix nerves_hub.user cert export
Local password: *super duper secret*
User certs exported to: <PATH/TO/EXPORTED_CERTS.tar.gz>
tar -xf <PATH/TO/EXPORTED_CERTS.tar.gz> -C nerves-hub/
```
```
mix nerves_hub.key export prod
Local password: *super duper secret*
Fwup keys exported to: <PATH/TO/EXPORTED_KEYS.tar.gz>
tar -xf <PATH/TO/EXPORTED_KEYS.tar.gz> -C nerves-hub/
```
You will also need the CA cert bundle for the WebApp:
(this may only work for BASH)
```bash
{ curl -s https://raw.githubusercontent.com/nerves-hub/nerves_hub_cli/master/priv/ca_certs/root-ca.pem | head -20 \
&& curl -s https://raw.githubusercontent.com/nerves-hub/nerves_hub_cli/master/priv/ca_certs/intermediate-server-ca.pem | head -20 \
&& curl -s https://raw.githubusercontent.com/nerves-hub/nerves_hub_cli/master/priv/ca_certs/intermediate-user-ca.pem | head -20;
} > nerves-hub/nerves-hub-ca-certs.pem
```
Now the FarmBot API needs the values of in it's environment:
* `NERVES_HUB_KEY` -> `cat nerves-hub/key.pem`
* `NERVES_HUB_CERT` -> `cat nerves-hub/cert.pem`
* `NERVES_HUB_CA` -> `cat nerves-hub/nerves-hub-ca-certs.pem`
CircleCI will need:
* `NERVES_HUB_KEY` -> `cat nerves-hub/key.pem`
* `NERVES_HUB_CERT` -> `cat nerves-hub/cert.pem`
* `NERVES_HUB_FW_PRIVATE_KEY` -> `cat nerves-hub/<KEY NAME>.priv`
* `NERVES_HUB_FW_PUBLIC_KEY` -> `cat nerves-hub/<KEY NAME>.pub`
### Provisioning and Tags
Tags/Deployments follow this structure:
```json
[
"application:<MIX_ENV>",
"channel:<CHANNEL>"
]
```
NOTE: the tags **NOT** json objects, they are simple strings
split by a `:` character. This is done _only_ for readability.
where `MIX_ENV` will be one of:
* `dev`
* `prod`
and `CHANNEL` will be one of:
* `beta`
* `stable`
There should be at least one deployment matching the following
tags:
* `["application:dev", "channel:stable"]`
* a development FBOS release on the `stable` channel
* `["application:prod", "channel:stable"]`
* a production FBOS release on the `stable` channel
* `["application:dev", "channel:beta"]`
* a development FBOS release on the `beta` channel
* `["application:prod", "channel:beta"]`
* a production FBOS release on the `beta` channel
* `["application:dev", "channel:stable"]`
* a development FBOS release on the `stable` channel
* `["application:prod", "channel:stable"]`
* a production FBOS release on the `stable` channel
* `["application:dev", "channel:beta"]`
* a development FBOS release on the `beta` channel
* `["application:prod", "channel:beta"]`
* a production FBOS release on the `beta` channel
### First time setup
```
heroku config:set NERVES_HUB_CERT="$NERVES_HUB_CERT" --app=$HEROKU_APPNAME
heroku config:set NERVES_HUB_KEY="$NERVES_HUB_KEY" --app=$HEROKU_APPNAME
heroku config:set NERVES_HUB_CA="$NERVES_HUB_CA" --app=$HEROKU_APPNAME
heroku config:set NERVES_HUB_ORG="$NERVES_HUB_ORG" --app=$HEROKU_APPNAME
```
@@ -3,7 +3,6 @@ defmodule Farmbot do
Supervises the individual modules that make up the Farmbot Application.
This is the entry point of the application.
"""
require Farmbot.Logger
require Logger
use Application
@@ -20,6 +19,7 @@ defmodule Farmbot do
def init([]) do
children = [
{Farmbot.System.Registry, []},
{Farmbot.Logger.Supervisor, []},
{Farmbot.System.Supervisor, []},
{Farmbot.Bootstrap.Supervisor, []}
@@ -52,6 +52,10 @@ defmodule Farmbot.BotState do
end
end
def set_update_available(bool) when is_boolean(bool) do
GenStage.call(__MODULE__, {:set_update_available, bool})
end
def report_disk_usage(percent) when is_number(percent) do
GenStage.call(__MODULE__, {:report_disk_usage, percent})
end
@@ -201,6 +205,12 @@ defmodule Farmbot.BotState do
{:noreply, [], state}
end
def handle_call({:set_update_available, bool}, _from, state) do
new_info_settings = %{state.informational_settings | update_available: bool}
state = %{state | informational_settings: new_info_settings}
{:reply, :ok, [state], state}
end
def handle_call({:report_disk_usage, percent}, _from, state) do
new_info_settings = %{state.informational_settings | disk_usage: percent}
state = %{state | informational_settings: new_info_settings}
@@ -391,6 +401,7 @@ defmodule Farmbot.BotState do
defstruct [
informational_settings: %{
update_available: false,
controller_version: @version,
firmware_version: "disconnected",
firmware_commit: @arduino_commit,
@@ -405,7 +416,7 @@ defmodule Farmbot.BotState do
locked: false,
cache_bust: 0,
soc_temp: 0, # degrees celcius
wifi_level: nil, # decibels
wifi_level: nil, # decibels
uptime: 0, # seconds
memory_usage: 0, # megabytes
disk_usage: 0, # percent
Oops, something went wrong.

0 comments on commit 561b823

Please sign in to comment.