normalize update mechanisms.

pull/306/head
connor rigby 2017-05-08 10:34:59 -07:00
parent e62da92b84
commit 6bd143babc
6 changed files with 124 additions and 54 deletions

View File

@ -41,20 +41,7 @@ defmodule Farmbot.Configurator.Router do
# Arduino-FW or FBOS Upload form.
get "/firmware/upload" do
html = ~s"""
<html>
<body>
<p>
Upload a FarmbotOS Firmware file (.fw) or a Arduino Firmware file (.hex)
</p>
<form action="/api/upload_firmware" method="post" enctype="multipart/form-data" accept="*">
<input type="file" name="firmware" id="fileupload">
<input type="submit" value="submit">
</form>
</body>
</html>
"""
conn |> send_resp(200, html)
conn |> send_resp(200, make_html("firmware_upload"))
end
# REST API
@ -271,7 +258,8 @@ defmodule Farmbot.Configurator.Router do
if target != "host" do
defp handle_os(file, conn) do
Logger.info "Firmware update"
Logger.info "Firmware update from network."
Farmbot.System.Updates.setup_post_update()
case Nerves.Firmware.upgrade_and_finalize(file) do
{:error, reason} -> conn |> send_resp(400, inspect(reason))
:ok ->

View File

@ -101,13 +101,22 @@ defmodule Farmbot.System.Updates do
Logger.info "Found file: #{inspect file}", type: :success
e -> Logger.error "Could not find update file: #{inspect e}"
end
setup_post_update()
mod(@target).install(path)
Farmbot.System.reboot()
end
@doc """
Will cause `post_install/0` to be called next reboot.
"""
def setup_post_update() do
FS.transaction fn ->
Logger.info "Seting up post update!", type: :busy
path = "#{FS.path()}/.post_update"
:ok = File.write(path, "DONT CAT ME\r\n")
end
mod(@target).install(path)
Farmbot.System.reboot()
end, true
end
@spec post_install :: no_return

View File

@ -1,39 +1,103 @@
if Code.ensure_loaded?(Mix.Tasks.Firmware.Push) do
defmodule Mix.Tasks.Farmbot.Upload do
@moduledoc false
use Mix.Task
require IEx
defmodule Mix.Tasks.Farmbot.Upload do
@moduledoc false
use Mix.Task
alias Mix.Tasks.Firmware.Push
@shortdoc "Uploads a file to a url"
def run([ipaddr | rest]) do
otp_app = Mix.Project.config[:app]
target = Mix.Project.config[:target]
fw_file = if Enum.find(rest, fn(flag) ->
flag == "--signed"
end) do
path = Path.join(["images", "#{Mix.env()}", "#{target}", "#{otp_app}-signed.fw"])
Mix.shell.info "Using signed image: #{path}"
path
else
path = Path.join(["images", "#{Mix.env()}", "#{target}", "#{otp_app}.fw"])
Mix.shell.info "Using unsigned image: #{path}"
path
end
Push.run([ipaddr, "--firmware", "#{fw_file}", "--reboot", "true"])
@shortdoc "Uploads a file to a url"
def run(opts) do
otp_app = Mix.Project.config[:app]
target = Mix.Project.config[:target]
{keywords, [ip_address], _} =
opts |> OptionParser.parse(switches: [signed: :boolean])
signed_bool = Keyword.get(keywords, :signed, false)
file_name = Path.join(["images", "#{Mix.env()}", "#{target}", find_file_name(otp_app, signed_bool)])
unless File.exists?(file_name) do
Mix.raise("Could not find firmware: #{file_name}")
end
Mix.shell.info "Uploading firmware: #{file_name}"
start_httpc()
http_opts = [relaxed: true, autoredirect: true]
opts = []
{:ok, file} = :file.read_file('#{file_name}')
file = :binary.bin_to_list(file)
url = 'http://#{ip_address}/api/upload_firmware'
boundary = '------------a450glvjfEoqerAc1p431paQlfDac152cadADfd'
content_type = :lists.concat(['multipart/form-data; boundary=', boundary])
body = format_multipart_formdata(boundary, [], [{:firmware, 'firmware.fw', file}])
headers = [{'Content-Length', :erlang.integer_to_list(:erlang.length(body))}]
Mix.shell.info "Starting FW upload."
:httpc.request(:post,
{url, headers, content_type, body},
http_opts, opts)
|> response
end
else
defp start_httpc() do
Application.ensure_started(:inets)
Application.ensure_started(:ssl)
:inets.start(:httpc, profile: :farmbot_firmware)
defmodule Mix.Tasks.Farmbot.Upload do
@moduledoc false
use Mix.Task
@shortdoc "Uploads a file to a url"
def run(_) do
Mix.raise """
Something in your environment is borked!
"""
end
opts = [
max_sessions: 8,
max_keep_alive_length: 4,
max_pipeline_length: 4,
keep_alive_timeout: 120_000,
pipeline_timeout: 60_000
]
:httpc.set_options(opts, :farmbot_firmware)
end
def response({:ok, {{_, 200, _}, _, _}}) do
Mix.shell.info "Done"
end
def response({:ok, {{_, status_code, _}, _, error}}) do
Mix.shell.info "\nThere was an error applying the firmware: #{inspect status_code} #{inspect error}"
end
def response({:error, error}) do
Mix.shell.info "\nThere was an error applying the firmware: #{inspect error}"
end
defp find_file_name(otp_app, true), do: "#{otp_app}-signed.fw"
defp find_file_name(otp_app, false), do: "#{otp_app}.fw"
# i stole this from: https://gist.github.com/ArthurClemens/dbd70f9b7a4342810d923670a9db0f39
defp format_multipart_formdata(boundary, fields, files) do
field_parts = :lists.map(fn({field_name, field_content}) ->
[:lists.concat(['--', boundary]),
:lists.concat(['Content-Disposition: form-data; name=\"',
:erlang.atom_to_list(field_name), '\"']),
'',
field_content]
end, fields)
field_parts_2 = :lists.append(field_parts)
file_parts = :lists.map(fn({field_name, file_name, file_content}) ->
[
:lists.concat(['--', boundary]),
:lists.concat(['Content-Disposition: format-data; name=\"',
:erlang.atom_to_list(field_name),
'\"; filename=\"', file_name, '\"']),
:lists.concat(['Content-Type: ', 'application/octet-stream']),
'',
file_content
]
end, files)
file_parts_2 = :lists.append(file_parts)
ending_parts = [:lists.concat(['--', boundary, '--']), '']
parts = :lists.append([field_parts_2, file_parts_2, ending_parts])
:string.join(parts, '\r\n')
end
end

View File

@ -99,7 +99,7 @@ defmodule Farmbot.Mixfile do
defp target_applications("host"), do: []
defp target_applications(_system), do: [
:nerves_interim_wifi,
:nerves_firmware_http,
# :nerves_firmware_http,
:nerves_firmware,
:nerves_ssdp_server
]
@ -221,7 +221,7 @@ defmodule Farmbot.Mixfile do
# {:nerves_interim_wifi, path: "../nerves_interim_wifi"},
{:nerves_interim_wifi, github: "nerves-project/nerves_interim_wifi"},
{:nerves_firmware_http, "~> 0.3.1"},
# {:nerves_firmware_http, "~> 0.3.1"},
# {:nerves_firmware, "~> 0.3"},
# {:nerves_firmware, path: "../nerves_firmware", override: true},

View File

@ -1,7 +1,5 @@
*
!index.html
!.gitignore
!easter_eggs.json
!farmbot_logo.png
!firmware_shell.html
!log.html
!*.html

View File

@ -0,0 +1,11 @@
<html>
<body>
<p>
Upload a FarmbotOS Firmware file (.fw) or a Arduino Firmware file (.hex)
</p>
<form action="/api/upload_firmware" method="post" enctype="multipart/form-data" accept="*">
<input type="file" name="firmware" id="fileupload">
<input type="submit" value="submit">
</form>
</body>
</html>