fix ssh, remove Downloader
parent
d569618679
commit
f29798eba1
|
@ -0,0 +1 @@
|
|||
../tmp/authorized_keys
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEAvkZEivnVE57KRR4viuL/Zai1ZdMI4xZW2SdAW+5oHEZxKsXV
|
||||
1+tJ1UWGw4Pq/IRIAPkI45nlEjfZeTN/enryMO6UOv75mkZBo0GQBYs2FjGZ7BWh
|
||||
bf/8WFkeuE3hOp1Y+DTsX4CMm75kyVCmCYba0ZskbhawwCWg7d2hsnVoH3Fm5Wsu
|
||||
s8Lg1fHXQmGxS9vEZ2sTehPhKNM1O4lyYkZE0v/Whf7AbwE/BKkg5+lcDke7GScX
|
||||
HpeqEv5Hk5HVL4d8O+7mkFPmIOBuHFJEvGwAwu0nP0jsYuD+velQPd8UFqp+x4+/
|
||||
TLe+sxEUsGyULj5Rvj1Zc75LWTAqdpei8rL+ZwIDAQABAoIBAQCL6p/8kjpdcybK
|
||||
LKhJOSMVXjoF5mlik8rJZFOvRmDIagRNr6zQWfSxH685lof1qFBKZtZih4grHWaN
|
||||
+ZCEQGRYFxhpSZXCHGen5U5CxVvAdjj5oe5TNSoazW4JmTtGHu6Nll063QyIejki
|
||||
0GtIFiJSgAMJ8SWIJiNwxslgsXUfqL7no1Ax1cmLCCjWDyFiTcUtj1niEsqF9/fH
|
||||
+BMauA17D3SDvZz2cI8ZBPM23sEBTNhbcYXqEPY40Y6+1XLupb85rgAcnMK6bvA6
|
||||
RIu7in1nnrzKeLbobCvV8N6ceooVJFqOlhnClOJnm6tXxRQoLFr4NNadSOZTbgae
|
||||
Lk2QdMwBAoGBAOxXN+zm/z+4/9UvEiiQk6+DU4dEC5VLtSlL3t19eTxNxRxmzGu1
|
||||
q8bjsLqeg1tFTR+QyRT6qSSYcQCKYmQYn5XyjqqDWgykqx+3TLSeftpyXHteJ4ly
|
||||
ZT0l2eBls2kn3zKUwWsWgRgTHJAWqj5wv84tHeKEaeP4Vw8M1jn0FQgPAoGBAM4a
|
||||
FmcVqB21f9drEQZCVyuxqrd3EesuBI+BmcdzMMWdNrwPSqKkLwXYyhkii8iE6SHy
|
||||
ufbGUUg7RoXYCXC6TU7U3WC2IR63cJMwubeNqNo8BGivjM+OC3UWt8jYt4gsFSiS
|
||||
FH03qmKmFbwVQJJa5jBrTUhDIr+e9IGuJiZdzAwpAoGBAOGZ9BGZA2hxkQyP7TZ5
|
||||
o/BzXOWPJ7tw1FLCUNB++J5+4jxVSKc8eOfW6xmJPHyAb7lE/R2glJsRRTUt4/QG
|
||||
qTd6nYLY9bmB6rPQxlDXxuGBLxS4cOc8IrW3Dv6sNhGX9PAUnXk6tyoi/C3y5LnL
|
||||
6NvHp3JV0brCkWAVcd1KSFUzAoGAajdZmlC37m+ubed4w6AG6rbKg3iy3GA/63Fg
|
||||
PkDoPr+yCKNc1IsGzN2X8fIy1elheKRuIHa+Rxp9UtcurlDlLJ5ZZNEYKothUgoH
|
||||
fEheX/IUV5s5V3IeB37ownKly2Dkmp8GPi4k+mgbxW2ydHqyLPvvvTEURGr4GakV
|
||||
cipe0VkCgYEAz8MvEWp8F7ZSb+JL1ov7cpoqyTJVd9n2x0A58SAjTPo6od0FYzwZ
|
||||
AVhCtkwiU7VV8xOudwrhqeGax0GsbIczYivoHMEm2ZCFHZo5aYrdShFoLgqJ+9C6
|
||||
AuFBssHRz+nN1ErHNzeekW/WrW1cCrfvx6NLPXCoDuiLE5N4RIgUJDI=
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -0,0 +1 @@
|
|||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+RkSK+dUTnspFHi+K4v9lqLVl0wjjFlbZJ0Bb7mgcRnEqxdXX60nVRYbDg+r8hEgA+QjjmeUSN9l5M396evIw7pQ6/vmaRkGjQZAFizYWMZnsFaFt//xYWR64TeE6nVj4NOxfgIybvmTJUKYJhtrRmyRuFrDAJaDt3aGydWgfcWblay6zwuDV8ddCYbFL28RnaxN6E+Eo0zU7iXJiRkTS/9aF/sBvAT8EqSDn6VwOR7sZJxcel6oS/keTkdUvh3w77uaQU+Yg4G4cUkS8bADC7Sc/SOxi4P696VA93xQWqn7Hj79Mt76zERSwbJQuPlG+PVlzvktZMCp2l6Lysv5n connor@connor-mini-pc
|
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
|
@ -0,0 +1 @@
|
|||
../tmp/authorized_keys
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEAvkZEivnVE57KRR4viuL/Zai1ZdMI4xZW2SdAW+5oHEZxKsXV
|
||||
1+tJ1UWGw4Pq/IRIAPkI45nlEjfZeTN/enryMO6UOv75mkZBo0GQBYs2FjGZ7BWh
|
||||
bf/8WFkeuE3hOp1Y+DTsX4CMm75kyVCmCYba0ZskbhawwCWg7d2hsnVoH3Fm5Wsu
|
||||
s8Lg1fHXQmGxS9vEZ2sTehPhKNM1O4lyYkZE0v/Whf7AbwE/BKkg5+lcDke7GScX
|
||||
HpeqEv5Hk5HVL4d8O+7mkFPmIOBuHFJEvGwAwu0nP0jsYuD+velQPd8UFqp+x4+/
|
||||
TLe+sxEUsGyULj5Rvj1Zc75LWTAqdpei8rL+ZwIDAQABAoIBAQCL6p/8kjpdcybK
|
||||
LKhJOSMVXjoF5mlik8rJZFOvRmDIagRNr6zQWfSxH685lof1qFBKZtZih4grHWaN
|
||||
+ZCEQGRYFxhpSZXCHGen5U5CxVvAdjj5oe5TNSoazW4JmTtGHu6Nll063QyIejki
|
||||
0GtIFiJSgAMJ8SWIJiNwxslgsXUfqL7no1Ax1cmLCCjWDyFiTcUtj1niEsqF9/fH
|
||||
+BMauA17D3SDvZz2cI8ZBPM23sEBTNhbcYXqEPY40Y6+1XLupb85rgAcnMK6bvA6
|
||||
RIu7in1nnrzKeLbobCvV8N6ceooVJFqOlhnClOJnm6tXxRQoLFr4NNadSOZTbgae
|
||||
Lk2QdMwBAoGBAOxXN+zm/z+4/9UvEiiQk6+DU4dEC5VLtSlL3t19eTxNxRxmzGu1
|
||||
q8bjsLqeg1tFTR+QyRT6qSSYcQCKYmQYn5XyjqqDWgykqx+3TLSeftpyXHteJ4ly
|
||||
ZT0l2eBls2kn3zKUwWsWgRgTHJAWqj5wv84tHeKEaeP4Vw8M1jn0FQgPAoGBAM4a
|
||||
FmcVqB21f9drEQZCVyuxqrd3EesuBI+BmcdzMMWdNrwPSqKkLwXYyhkii8iE6SHy
|
||||
ufbGUUg7RoXYCXC6TU7U3WC2IR63cJMwubeNqNo8BGivjM+OC3UWt8jYt4gsFSiS
|
||||
FH03qmKmFbwVQJJa5jBrTUhDIr+e9IGuJiZdzAwpAoGBAOGZ9BGZA2hxkQyP7TZ5
|
||||
o/BzXOWPJ7tw1FLCUNB++J5+4jxVSKc8eOfW6xmJPHyAb7lE/R2glJsRRTUt4/QG
|
||||
qTd6nYLY9bmB6rPQxlDXxuGBLxS4cOc8IrW3Dv6sNhGX9PAUnXk6tyoi/C3y5LnL
|
||||
6NvHp3JV0brCkWAVcd1KSFUzAoGAajdZmlC37m+ubed4w6AG6rbKg3iy3GA/63Fg
|
||||
PkDoPr+yCKNc1IsGzN2X8fIy1elheKRuIHa+Rxp9UtcurlDlLJ5ZZNEYKothUgoH
|
||||
fEheX/IUV5s5V3IeB37ownKly2Dkmp8GPi4k+mgbxW2ydHqyLPvvvTEURGr4GakV
|
||||
cipe0VkCgYEAz8MvEWp8F7ZSb+JL1ov7cpoqyTJVd9n2x0A58SAjTPo6od0FYzwZ
|
||||
AVhCtkwiU7VV8xOudwrhqeGax0GsbIczYivoHMEm2ZCFHZo5aYrdShFoLgqJ+9C6
|
||||
AuFBssHRz+nN1ErHNzeekW/WrW1cCrfvx6NLPXCoDuiLE5N4RIgUJDI=
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -0,0 +1 @@
|
|||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+RkSK+dUTnspFHi+K4v9lqLVl0wjjFlbZJ0Bb7mgcRnEqxdXX60nVRYbDg+r8hEgA+QjjmeUSN9l5M396evIw7pQ6/vmaRkGjQZAFizYWMZnsFaFt//xYWR64TeE6nVj4NOxfgIybvmTJUKYJhtrRmyRuFrDAJaDt3aGydWgfcWblay6zwuDV8ddCYbFL28RnaxN6E+Eo0zU7iXJiRkTS/9aF/sBvAT8EqSDn6VwOR7sZJxcel6oS/keTkdUvh3w77uaQU+Yg4G4cUkS8bADC7Sc/SOxi4P696VA93xQWqn7Hj79Mt76zERSwbJQuPlG+PVlzvktZMCp2l6Lysv5n connor@connor-mini-pc
|
|
@ -0,0 +1 @@
|
|||
../tmp/authorized_keys
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEAvkZEivnVE57KRR4viuL/Zai1ZdMI4xZW2SdAW+5oHEZxKsXV
|
||||
1+tJ1UWGw4Pq/IRIAPkI45nlEjfZeTN/enryMO6UOv75mkZBo0GQBYs2FjGZ7BWh
|
||||
bf/8WFkeuE3hOp1Y+DTsX4CMm75kyVCmCYba0ZskbhawwCWg7d2hsnVoH3Fm5Wsu
|
||||
s8Lg1fHXQmGxS9vEZ2sTehPhKNM1O4lyYkZE0v/Whf7AbwE/BKkg5+lcDke7GScX
|
||||
HpeqEv5Hk5HVL4d8O+7mkFPmIOBuHFJEvGwAwu0nP0jsYuD+velQPd8UFqp+x4+/
|
||||
TLe+sxEUsGyULj5Rvj1Zc75LWTAqdpei8rL+ZwIDAQABAoIBAQCL6p/8kjpdcybK
|
||||
LKhJOSMVXjoF5mlik8rJZFOvRmDIagRNr6zQWfSxH685lof1qFBKZtZih4grHWaN
|
||||
+ZCEQGRYFxhpSZXCHGen5U5CxVvAdjj5oe5TNSoazW4JmTtGHu6Nll063QyIejki
|
||||
0GtIFiJSgAMJ8SWIJiNwxslgsXUfqL7no1Ax1cmLCCjWDyFiTcUtj1niEsqF9/fH
|
||||
+BMauA17D3SDvZz2cI8ZBPM23sEBTNhbcYXqEPY40Y6+1XLupb85rgAcnMK6bvA6
|
||||
RIu7in1nnrzKeLbobCvV8N6ceooVJFqOlhnClOJnm6tXxRQoLFr4NNadSOZTbgae
|
||||
Lk2QdMwBAoGBAOxXN+zm/z+4/9UvEiiQk6+DU4dEC5VLtSlL3t19eTxNxRxmzGu1
|
||||
q8bjsLqeg1tFTR+QyRT6qSSYcQCKYmQYn5XyjqqDWgykqx+3TLSeftpyXHteJ4ly
|
||||
ZT0l2eBls2kn3zKUwWsWgRgTHJAWqj5wv84tHeKEaeP4Vw8M1jn0FQgPAoGBAM4a
|
||||
FmcVqB21f9drEQZCVyuxqrd3EesuBI+BmcdzMMWdNrwPSqKkLwXYyhkii8iE6SHy
|
||||
ufbGUUg7RoXYCXC6TU7U3WC2IR63cJMwubeNqNo8BGivjM+OC3UWt8jYt4gsFSiS
|
||||
FH03qmKmFbwVQJJa5jBrTUhDIr+e9IGuJiZdzAwpAoGBAOGZ9BGZA2hxkQyP7TZ5
|
||||
o/BzXOWPJ7tw1FLCUNB++J5+4jxVSKc8eOfW6xmJPHyAb7lE/R2glJsRRTUt4/QG
|
||||
qTd6nYLY9bmB6rPQxlDXxuGBLxS4cOc8IrW3Dv6sNhGX9PAUnXk6tyoi/C3y5LnL
|
||||
6NvHp3JV0brCkWAVcd1KSFUzAoGAajdZmlC37m+ubed4w6AG6rbKg3iy3GA/63Fg
|
||||
PkDoPr+yCKNc1IsGzN2X8fIy1elheKRuIHa+Rxp9UtcurlDlLJ5ZZNEYKothUgoH
|
||||
fEheX/IUV5s5V3IeB37ownKly2Dkmp8GPi4k+mgbxW2ydHqyLPvvvTEURGr4GakV
|
||||
cipe0VkCgYEAz8MvEWp8F7ZSb+JL1ov7cpoqyTJVd9n2x0A58SAjTPo6od0FYzwZ
|
||||
AVhCtkwiU7VV8xOudwrhqeGax0GsbIczYivoHMEm2ZCFHZo5aYrdShFoLgqJ+9C6
|
||||
AuFBssHRz+nN1ErHNzeekW/WrW1cCrfvx6NLPXCoDuiLE5N4RIgUJDI=
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -0,0 +1 @@
|
|||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+RkSK+dUTnspFHi+K4v9lqLVl0wjjFlbZJ0Bb7mgcRnEqxdXX60nVRYbDg+r8hEgA+QjjmeUSN9l5M396evIw7pQ6/vmaRkGjQZAFizYWMZnsFaFt//xYWR64TeE6nVj4NOxfgIybvmTJUKYJhtrRmyRuFrDAJaDt3aGydWgfcWblay6zwuDV8ddCYbFL28RnaxN6E+Eo0zU7iXJiRkTS/9aF/sBvAT8EqSDn6VwOR7sZJxcel6oS/keTkdUvh3w77uaQU+Yg4G4cUkS8bADC7Sc/SOxi4P696VA93xQWqn7Hj79Mt76zERSwbJQuPlG+PVlzvktZMCp2l6Lysv5n connor@connor-mini-pc
|
|
@ -1,166 +0,0 @@
|
|||
defmodule Downloader do
|
||||
@moduledoc """
|
||||
Blatently ripped off
|
||||
from: https://github.com/nerves-project/nerves/blob/master/lib/nerves/utils/http_client.ex
|
||||
"""
|
||||
use GenServer
|
||||
require Logger
|
||||
@progress_steps 50
|
||||
@redirect_status_codes [301, 302, 303, 307, 308]
|
||||
|
||||
def start_link do
|
||||
start_httpc()
|
||||
GenServer.start_link(__MODULE__, [], name: __MODULE__)
|
||||
end
|
||||
|
||||
def stop(client \\ __MODULE__) do
|
||||
GenServer.stop(client)
|
||||
end
|
||||
|
||||
def get(client \\ __MODULE__, url) do
|
||||
GenServer.call(client, {:get, url}, :infinity)
|
||||
end
|
||||
|
||||
def run(url, path) do
|
||||
{:ok, file} = get(__MODULE__, url)
|
||||
:ok = File.write!(path, file)
|
||||
if File.exists?(path) do
|
||||
path
|
||||
else
|
||||
throw :NO_FILE
|
||||
end
|
||||
end
|
||||
|
||||
def init([]) do
|
||||
{:ok, %{
|
||||
url: nil,
|
||||
content_length: 0,
|
||||
buffer: "",
|
||||
buffer_size: 0,
|
||||
filename: "",
|
||||
caller: nil,
|
||||
number_of_redirects: 0,
|
||||
}}
|
||||
end
|
||||
|
||||
def handle_call({:get, _url}, _, %{number_of_redirects: n}=s) when n > 5 do
|
||||
GenServer.reply(s.caller, {:error, :too_many_redirects})
|
||||
{:noreply, %{s | url: nil, number_of_redirects: 0, caller: nil}}
|
||||
end
|
||||
|
||||
def handle_call({:get, url}, from, s) do
|
||||
headers = [
|
||||
{'Content-Type', 'application/octet-stream'}
|
||||
]
|
||||
|
||||
http_opts = [timeout: :infinity, autoredirect: false]
|
||||
opts = [stream: :self, receiver: self(), sync: false]
|
||||
:httpc.request(:get,
|
||||
{String.to_char_list(url), headers}, http_opts, opts, :nerves)
|
||||
{:noreply, %{s | url: url, caller: from}}
|
||||
end
|
||||
|
||||
def handle_info({:http, {_, :stream_start, headers}}, s) do
|
||||
#TODO This is sometimes nil
|
||||
# Something about chunked transfer-encoding
|
||||
content_length = maybe_content_length(headers)
|
||||
filename = try do
|
||||
{_, filename} =
|
||||
headers
|
||||
|> Enum.find(fn({key, _}) -> key == 'content-disposition' end)
|
||||
filename
|
||||
|> to_string
|
||||
|> String.split(";")
|
||||
|> List.last
|
||||
|> String.strip
|
||||
|> String.trim("filename=")
|
||||
rescue
|
||||
_ ->
|
||||
"unknown-filename"
|
||||
end
|
||||
|
||||
|
||||
{:noreply, %{s | content_length: content_length, filename: filename}}
|
||||
end
|
||||
|
||||
def handle_info({:http, {_, :stream, data}}, s) do
|
||||
size = byte_size(data) + s.buffer_size
|
||||
buffer = s.buffer <> data
|
||||
put_progress(size, s.content_length)
|
||||
{:noreply, %{s | buffer_size: size, buffer: buffer}}
|
||||
end
|
||||
|
||||
def handle_info({:http, {_, :stream_end, _headers}}, s) do
|
||||
IO.write(:stderr, "\n")
|
||||
GenServer.reply(s.caller, {:ok, s.buffer})
|
||||
{:noreply, %{s | filename: "", content_length: 0, buffer: "", buffer_size: 0, url: nil}}
|
||||
end
|
||||
|
||||
def handle_info({:http, {_ref, {{_, status_code, _}, headers, _body}}}, s) when status_code in @redirect_status_codes do
|
||||
case Enum.find(headers, fn({key,_}) -> key == 'location' end) do
|
||||
{'location', next_url} ->
|
||||
handle_call({:get, List.to_string(next_url)}, s.caller, %{s | buffer: "", buffer_size: 0, number_of_redirects: s.number_of_redirects + 1})
|
||||
_ ->
|
||||
GenServer.reply(s.caller, {:error, status_code})
|
||||
end
|
||||
end
|
||||
|
||||
def handle_info({:http, {error, _headers}}, s) do
|
||||
GenServer.reply(s.caller, {:error, error})
|
||||
{:noreply, s}
|
||||
end
|
||||
|
||||
def terminate(reason, state) do
|
||||
GenServer.reply(state.caller, {:error, reason})
|
||||
end
|
||||
|
||||
def put_progress(size, nil) do
|
||||
case rem(size, 2) do
|
||||
0 ->
|
||||
IO.write(:stderr, "\r|-=-=-=-=-=-=-=-=-=-=-=-=-| ---%")
|
||||
_ ->
|
||||
IO.write(:stderr, "\r|=-=-=-=-=-=-=-=-=-=-=-=-=| ---%")
|
||||
end
|
||||
end
|
||||
|
||||
def put_progress(size, max) do
|
||||
fraction = (size / max)
|
||||
completed = trunc(fraction * @progress_steps)
|
||||
percent = trunc(fraction * 100)
|
||||
unfilled = @progress_steps - completed
|
||||
if rem(size, 10) == 0, do: Logger.info("Download: #{percent}%")
|
||||
IO.write(:stderr, "\r|#{String.duplicate("=", completed)}#{String.duplicate(" ", unfilled)}| #{percent}% (#{bytes_to_mb(size)} / #{bytes_to_mb(max)}) MB")
|
||||
end
|
||||
|
||||
defp maybe_content_length(headers) do
|
||||
try do
|
||||
{_, content_length} =
|
||||
headers
|
||||
|> Enum.find(fn({key, _}) -> key == 'content-length' end)
|
||||
|
||||
{content_length, _} =
|
||||
content_length
|
||||
|> to_string()
|
||||
|> Integer.parse()
|
||||
content_length
|
||||
rescue
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
|
||||
defp start_httpc do
|
||||
:inets.start(:httpc, profile: :nerves)
|
||||
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, :nerves)
|
||||
end
|
||||
|
||||
defp bytes_to_mb(bytes) do
|
||||
trunc(bytes / 1024 / 1024)
|
||||
end
|
||||
end
|
|
@ -4,7 +4,7 @@ defmodule Farmbot.CeleryScript.Command.CheckUpdates do
|
|||
"""
|
||||
|
||||
require Logger
|
||||
alias Farmbot.CeleryScript.Command
|
||||
alias Farmbot.CeleryScript.{Command, Error}
|
||||
@behaviour Command
|
||||
|
||||
@doc ~s"""
|
||||
|
@ -17,12 +17,13 @@ defmodule Farmbot.CeleryScript.Command.CheckUpdates do
|
|||
def run(%{package: package}, [], context) do
|
||||
case package do
|
||||
"arduino_firmware" ->
|
||||
raise "arduino firmware is now bundled into the OS."
|
||||
raise Error, context: context,
|
||||
message: "arduino firmware is now bundled into the OS."
|
||||
|
||||
"farmbot_os" ->
|
||||
Farmbot.System.Updates.check_and_download_updates()
|
||||
Farmbot.System.Updates.check_and_download_updates(context)
|
||||
|
||||
u -> raise("unknown package: #{u}")
|
||||
u -> raise(Error, message: "unknown package: #{u}", context: context)
|
||||
end
|
||||
context
|
||||
end
|
||||
|
|
|
@ -17,13 +17,18 @@ defmodule Farmbot.CeleryScript.Command.ExecuteScript do
|
|||
@spec run(%{label: binary},
|
||||
[Command.Pair.t], Context.t) :: Context.t | no_return
|
||||
def run(%{label: uuid}, env_vars, context) when is_uuid(uuid) do
|
||||
Command.set_user_env(%{}, env_vars, context)
|
||||
new_context = Command.set_user_env(%{}, env_vars, context)
|
||||
case Manager.lookup(context, uuid) do
|
||||
{:ok, %Farmware{} = fw} -> Runtime.execute(context, fw)
|
||||
{:ok, %Farmware{} = fw} -> Runtime.execute(new_context, fw)
|
||||
{:error, e} ->
|
||||
raise Error,
|
||||
message: "Could not locate farmware: #{e}",
|
||||
context: context
|
||||
context: new_context
|
||||
end
|
||||
end
|
||||
|
||||
def run(%{label: not_uuid}, _, context) do
|
||||
raise Error, context: context,
|
||||
message: "Expected a uuid but got: #{inspect not_uuid}"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,15 +3,9 @@ defmodule Farmbot.Database.Syncable.Regimen do
|
|||
A Regimen from the Farmbot API.
|
||||
"""
|
||||
|
||||
alias Farmbot.{Context, Database}
|
||||
alias Farmbot.Database
|
||||
alias Database.Syncable
|
||||
use Syncable, model: [
|
||||
:regimen_items
|
||||
], endpoint: {"/regimens", "/regimens"}
|
||||
|
||||
defimpl Farmbot.FarmEvent.Executer, for: __MODULE__ do
|
||||
def execute_event(regimen, %Context{} = ctx, now) do
|
||||
{:ok, _pid} = Farmbot.Regimen.Supervisor.add_child(ctx, regimen, now)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,7 +3,7 @@ defmodule Farmbot.Database.Syncable.Sequence do
|
|||
A Sequence from the Farmbot API.
|
||||
"""
|
||||
|
||||
alias Farmbot.{Context, Database}
|
||||
alias Farmbot.{Database}
|
||||
alias Database.Syncable
|
||||
use Syncable, model: [
|
||||
:version,
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
defimpl Farmbot.FarmEvent.Executer, for: Farmbot.Database.Syncable.Regimen do
|
||||
def execute_event(regimen, %Farmbot.Context{} = ctx, now) do
|
||||
{:ok, _pid} = Farmbot.Regimen.Supervisor.add_child(ctx, regimen, now)
|
||||
end
|
||||
end
|
|
@ -5,7 +5,6 @@ defmodule Farmbot.FarmEvent.Runner do
|
|||
require Logger
|
||||
alias Farmbot.{Context, DebugLog, Database, CeleryScript}
|
||||
import Farmbot.FarmEvent.Executer
|
||||
alias CeleryScript.Ast
|
||||
use DebugLog
|
||||
use GenServer
|
||||
alias Database.Syncable.{
|
||||
|
|
|
@ -9,8 +9,9 @@ defmodule Farmbot.FarmEvent.Supervisor do
|
|||
children = [
|
||||
worker(Farmbot.Regimen.Supervisor,
|
||||
[context, [name: Farmbot.Regimen.Supervisor ]], [restart: :permanent]),
|
||||
|
||||
worker(Farmbot.FarmEvent.Runner,
|
||||
[context, [name: Farmbot.FarmEvent.Runner ]], [restart: :permanent])
|
||||
[context, [name: Farmbot.FarmEvent.Runner ]], [restart: :permanent])
|
||||
]
|
||||
opts = [strategy: :one_for_one]
|
||||
supervise(children, opts)
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
defmodule Farmbot.System.Network.SSH do
|
||||
@moduledoc """
|
||||
Module to manage SSH via an Erlang port.
|
||||
"""
|
||||
use GenServer
|
||||
require Logger
|
||||
alias Farmbot.System.FS
|
||||
|
||||
@banner "/tmp/banner"
|
||||
@cmd "dropbear -R -F -a -B -E -b #{@banner}"
|
||||
@var_run_dir "/var/run/dropbear"
|
||||
|
||||
def init(_) do
|
||||
Logger.info ">> Is starting SSH service."
|
||||
Process.flag(:trap_exit, true)
|
||||
make_banner()
|
||||
|
||||
# this is where dropbear puts keys and stuff.
|
||||
if !File.exists? @var_run_dir do
|
||||
Logger.info ">> needs to create a place for ssh keys."
|
||||
File.mkdir_p @var_run_dir
|
||||
end
|
||||
|
||||
if File.exists? "#{FS.path()}/dropbear_ecdsa_host_key" do
|
||||
Logger.info ">> loading old ssh keys"
|
||||
File.cp "#{FS.path()}/dropbear_ecdsa_host_key", @var_run_dir
|
||||
end
|
||||
|
||||
port = open_port()
|
||||
{:ok, port}
|
||||
end
|
||||
|
||||
def open_port do
|
||||
Port.open({:spawn, @cmd},
|
||||
[:stream,
|
||||
:binary,
|
||||
:exit_status,
|
||||
:hide,
|
||||
:use_stdio,
|
||||
:stderr_to_stdout])
|
||||
end
|
||||
|
||||
def start_link, do: GenServer.start_link(__MODULE__, [], name: __MODULE__)
|
||||
|
||||
def stop(reason) do
|
||||
GenServer.stop(__MODULE__, reason)
|
||||
end
|
||||
|
||||
def handle_info({:EXIT, port, reason}, state)
|
||||
when state == port do
|
||||
Logger.error ">>`s ssh client died: #{inspect reason}"
|
||||
new_state = open_port()
|
||||
{:noreply, new_state}
|
||||
end
|
||||
|
||||
def handle_info({_, {:data, data}}, port) when is_bitstring(data) do
|
||||
Logger.info ">> got ssh data: #{String.trim(data)}"
|
||||
save_contents()
|
||||
{:noreply, port}
|
||||
end
|
||||
|
||||
def handle_info(_i, port) do
|
||||
{:noreply, port}
|
||||
end
|
||||
|
||||
def terminate(_reason, _state) do
|
||||
save_contents()
|
||||
end
|
||||
|
||||
def save_contents do
|
||||
Logger.info ">> Saving ssh keys"
|
||||
case File.read "#{@var_run_dir}/dropbear_ecdsa_host_key" do
|
||||
{:ok, c} ->
|
||||
FS.transaction fn() ->
|
||||
File.write "#{FS.path()}/dropbear_ecdsa_host_key", c
|
||||
end
|
||||
_ -> :ok
|
||||
end
|
||||
:ok
|
||||
end
|
||||
|
||||
def make_banner do
|
||||
contents =
|
||||
"""
|
||||
__________________________________________________________________
|
||||
| WELCOME TO FARMBOT SHELL. I AM TRULY SORRY YOU HAVE TO BE HERE |
|
||||
|_______________________________|________________________________|
|
||||
| THERE IS NO $PATH | THERE IS NO BASH |
|
||||
| THERE IS NO SU | THERE IS NO APT-GET |
|
||||
| THERE IS NO MAKE | THERE IS NO WGET |
|
||||
|_______________________________|________________________________|
|
||||
"""
|
||||
File.write(@banner, contents)
|
||||
end
|
||||
end
|
|
@ -4,7 +4,7 @@ defmodule Farmbot.System.Network do
|
|||
"""
|
||||
require Logger
|
||||
alias Farmbot.System.FS.ConfigStorage, as: CS
|
||||
alias Farmbot.System.Network.{Ntp, SSH}
|
||||
alias Farmbot.System.Network.Ntp
|
||||
alias Farmbot.{Context, Auth, HTTP, DebugLog}
|
||||
use DebugLog
|
||||
use GenServer
|
||||
|
@ -115,17 +115,26 @@ defmodule Farmbot.System.Network do
|
|||
end
|
||||
|
||||
defp maybe_start_ssh do
|
||||
{:ok, ssh} = get_config("ssh")
|
||||
try do
|
||||
if ssh do
|
||||
Logger.info ">> starting SSH server."
|
||||
spawn SSH, :start_link, []
|
||||
end
|
||||
{:ok, public_key} = get_config("ssh")
|
||||
ssh_dir = '/ssh'
|
||||
ssh_dir_exists? = File.exists?(ssh_dir)
|
||||
shell = {Elixir.IEx, :start, []}
|
||||
# shell = {Elixir.Nerves.Runtime.Shell, :start, []}
|
||||
if is_binary(public_key) and ssh_dir_exists? do
|
||||
File.write "#{ssh_dir}/authorized_keys", public_key
|
||||
debug_log "Starting SSH."
|
||||
:ok = :ssh.start()
|
||||
opts =
|
||||
[
|
||||
{:shell, shell},
|
||||
{:system_dir, ssh_dir},
|
||||
{:user_dir, ssh_dir},
|
||||
]
|
||||
{:ok, _sshd} = :ssh.daemon(22, opts)
|
||||
:ok
|
||||
else
|
||||
debug_log "Not starting SSH"
|
||||
:ok
|
||||
rescue
|
||||
error ->
|
||||
Logger.warn(">> Failed to start ssh: #{inspect error}")
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -184,29 +193,34 @@ defmodule Farmbot.System.Network do
|
|||
"""
|
||||
def on_connect(context, pre_fun \\ nil, post_fun \\ nil)
|
||||
def on_connect(%Context{} = context, pre_fun, post_fun) do
|
||||
# Start the Downloader http client.
|
||||
Supervisor.start_child(Farmbot.System.Supervisor,
|
||||
Supervisor.Spec.worker(Downloader, [], [restart: :permanent]))
|
||||
try do
|
||||
# If we were supplied a pre connect callback, do that.
|
||||
if pre_fun, do: pre_fun.()
|
||||
|
||||
# If we were supplied a pre connect callback, do that.
|
||||
if pre_fun, do: pre_fun.()
|
||||
:ok = connection_test(context)
|
||||
|
||||
:ok = connection_test(context)
|
||||
:ok = maybe_set_time()
|
||||
:ok = maybe_start_ssh()
|
||||
:ok = maybe_get_fpf(context)
|
||||
|
||||
:ok = maybe_set_time()
|
||||
:ok = maybe_start_ssh()
|
||||
:ok = maybe_get_fpf(context)
|
||||
Logger.info ">> is trying to log in."
|
||||
{:ok, token} = Auth.try_log_in!(context.auth)
|
||||
|
||||
Logger.info ">> is trying to log in."
|
||||
{:ok, token} = Auth.try_log_in!(context.auth)
|
||||
:ok = maybe_setup_rollbar(token)
|
||||
|
||||
:ok = maybe_setup_rollbar(token)
|
||||
if post_fun do
|
||||
post_fun.(token)
|
||||
end
|
||||
|
||||
if post_fun do
|
||||
post_fun.(token)
|
||||
{:ok, token}
|
||||
rescue
|
||||
exception ->
|
||||
Logger.error "Farmbot failed to log in."
|
||||
debug_log("#{inspect exception}")
|
||||
Farmbot.System.factory_reset("#{inspect exception}")
|
||||
end
|
||||
|
||||
{:ok, token}
|
||||
|
||||
end
|
||||
|
||||
if Mix.env == :prod do
|
||||
|
@ -290,10 +304,6 @@ defmodule Farmbot.System.Network do
|
|||
end
|
||||
|
||||
def terminate(reason, state) do
|
||||
ssh_pid = Process.whereis(SSH)
|
||||
if ssh_pid do
|
||||
SSH.stop(reason)
|
||||
end
|
||||
target_pid = Process.whereis(mod(state.target))
|
||||
if target_pid do
|
||||
GenServer.stop(target_pid, reason)
|
||||
|
@ -301,7 +311,7 @@ defmodule Farmbot.System.Network do
|
|||
end
|
||||
|
||||
# Behavior
|
||||
@type return_type :: :ok | {:error, term}
|
||||
@type return_type :: :ok | {:error, term}
|
||||
@callback scan(Context.t, binary) :: [binary] | {:error, term}
|
||||
@callback enumerate(Context.t) :: [binary] | {:error, term}
|
||||
@callback start_interface(Context.t, binary, map) :: return_type
|
||||
|
|
|
@ -30,7 +30,9 @@ defmodule Farmbot.System.NervesCommon.FileSystem do
|
|||
:ok = format_state_part()
|
||||
else
|
||||
# If not, we are fine. continue
|
||||
_ -> :ok
|
||||
_ ->
|
||||
File.touch "/tmp/authorized_keys"
|
||||
:ok
|
||||
end
|
||||
|
||||
:ok = tzdata_hack()
|
||||
|
|
|
@ -26,7 +26,7 @@ defmodule Farmbot.System.Updates do
|
|||
def do_update_check do
|
||||
context = Farmbot.Context.new()
|
||||
if Farmbot.BotState.get_config(context, :os_auto_update) do
|
||||
check_and_download_updates()
|
||||
check_and_download_updates(context)
|
||||
else
|
||||
Logger.info ">> Will not do update check!"
|
||||
end
|
||||
|
@ -35,13 +35,14 @@ defmodule Farmbot.System.Updates do
|
|||
@doc """
|
||||
Checks for updates, and if there is an update, downloads, and applies it.
|
||||
"""
|
||||
@spec check_and_download_updates :: :ok | {:error, term} | :no_updates
|
||||
def check_and_download_updates do
|
||||
case check_updates() do
|
||||
@spec check_and_download_updates(Context.t)
|
||||
:: :ok | {:error, term} | :no_updates
|
||||
def check_and_download_updates(%Context{} = ctx) do
|
||||
case check_updates(ctx) do
|
||||
{:update, url} ->
|
||||
Logger.info ">> has found a new Operating System update! #{url}",
|
||||
type: :busy
|
||||
install_updates(url)
|
||||
install_updates(ctx, url)
|
||||
:no_updates ->
|
||||
Logger.info ">> is already on the latest Operating System version!",
|
||||
type: :success
|
||||
|
@ -55,9 +56,9 @@ defmodule Farmbot.System.Updates do
|
|||
@doc """
|
||||
Checks for updates
|
||||
"""
|
||||
@spec check_updates :: {:update, binary} | :no_updates | {:error, term}
|
||||
def check_updates do
|
||||
context = Context.new()
|
||||
@spec check_updates(Context.t)
|
||||
:: {:update, binary} | :no_updates | {:error, term}
|
||||
def check_updates(%Context{} = context) do
|
||||
current = Farmbot.BotState.get_os_version(context)
|
||||
if String.contains?(current, "rc") do
|
||||
msg = "Release Candidate Releases don't currently support updates!"
|
||||
|
@ -68,7 +69,8 @@ defmodule Farmbot.System.Updates do
|
|||
end
|
||||
end
|
||||
|
||||
@spec check_updates :: {:update, binary} | :no_updates | {:error, term}
|
||||
@spec check_updates(Context.t)
|
||||
:: {:update, binary} | :no_updates | {:error, term}
|
||||
defp do_http_req(%Context{} = ctx) do
|
||||
case HTTP.get(ctx, releases_url(ctx)) do
|
||||
{:ok, %HTTP.Response{body: body, status_code: 200}} ->
|
||||
|
@ -88,15 +90,11 @@ defmodule Farmbot.System.Updates do
|
|||
@doc """
|
||||
Installs an update from a url
|
||||
"""
|
||||
@spec install_updates(String.t) :: no_return
|
||||
def install_updates(url) do
|
||||
@spec install_updates(Context.t, String.t) :: no_return
|
||||
def install_updates(%Context{} = context, url) do
|
||||
# Ignore the compiler warning here.
|
||||
# "I'll fix it later i promise" -- Connor Rigby
|
||||
case Process.whereis(Downloader) do
|
||||
pid when is_pid(pid) -> :ok
|
||||
_ -> Downloader.start_link()
|
||||
end
|
||||
path = Downloader.run(url, @path)
|
||||
path = HTTP.download_file!(context, url, @path)
|
||||
case File.stat(path) do
|
||||
{:ok, file} ->
|
||||
Logger.info "Found file: #{inspect file}", type: :success
|
||||
|
|
|
@ -103,17 +103,20 @@ defmodule Farmbot.Transport do
|
|||
Emit a message over all transports
|
||||
"""
|
||||
@spec emit(Context.t, term) :: :ok
|
||||
def emit(%Context{} = ctx, message), do: GenStage.cast(ctx.transport, {:emit, message})
|
||||
def emit(%Context{} = ctx, message),
|
||||
do: GenStage.cast(ctx.transport, {:emit, message})
|
||||
|
||||
@doc """
|
||||
Log a log message over all transports
|
||||
"""
|
||||
@spec log(Context.t, term) :: :ok
|
||||
def log(%Context{} = ctx, message), do: GenStage.cast(ctx.transport, {:log, message})
|
||||
def log(%Context{} = ctx, message),
|
||||
do: GenStage.cast(ctx.transport, {:log, message})
|
||||
|
||||
@doc """
|
||||
Force a state push
|
||||
"""
|
||||
@spec force_state_push(Context.t) :: State.t
|
||||
def force_state_push(%Context{} = ctx), do: GenServer.call(ctx.transport, :force_state_push)
|
||||
def force_state_push(%Context{} = ctx),
|
||||
do: GenServer.call(ctx.transport, :force_state_push)
|
||||
end
|
||||
|
|
3
mix.exs
3
mix.exs
|
@ -75,7 +75,8 @@ defmodule Farmbot.Mixfile do
|
|||
:gen_mqtt,
|
||||
:ex_json_schema,
|
||||
:fs,
|
||||
# :rollbax
|
||||
:ex_rollbar,
|
||||
:ssh
|
||||
] ++ included_apps(Mix.env)]
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
defmodule DisableSSHAgain do
|
||||
def run(json) do
|
||||
network = json["network"]
|
||||
net_config =
|
||||
if network do
|
||||
# if we have a network config, make ssh key false
|
||||
Map.put(network, "ssh", false)
|
||||
else
|
||||
# if no network, just make it false.
|
||||
false
|
||||
end
|
||||
|
||||
%{json | "network" => net_config}
|
||||
end
|
||||
end
|
|
@ -1,12 +1,12 @@
|
|||
defmodule Farmbot.CeleryScript.Command.CheckUpdatesTest do
|
||||
use ExUnit.Case, async: true
|
||||
alias Farmbot.CeleryScript.{Ast, Command}
|
||||
alias Farmbot.CeleryScript.{Ast, Command, Error}
|
||||
import Mock
|
||||
|
||||
test "doesnt check for arduino updates anymore" do
|
||||
ctx = Farmbot.Context.new()
|
||||
ast = %Ast{kind: "check_updates", args: %{package: "arduino_firmware"}, body: []}
|
||||
assert_raise RuntimeError, "arduino firmware is now bundled into the OS.", fn() ->
|
||||
assert_raise Error, "arduino firmware is now bundled into the OS.", fn() ->
|
||||
Command.do_command(ast, ctx)
|
||||
end
|
||||
end
|
||||
|
@ -14,18 +14,18 @@ defmodule Farmbot.CeleryScript.Command.CheckUpdatesTest do
|
|||
test "doesnt know what to do with other strings" do
|
||||
ctx = Farmbot.Context.new()
|
||||
ast = %Ast{kind: "check_updates", args: %{package: "explorer.exe"}, body: []}
|
||||
assert_raise RuntimeError, "unknown package: #{ast.args.package}", fn() ->
|
||||
assert_raise Error, "unknown package: #{ast.args.package}", fn() ->
|
||||
Command.do_command(ast, ctx)
|
||||
end
|
||||
end
|
||||
|
||||
test "does update check for fbos" do
|
||||
# the real update thing will be checked elsewhere, this is just for the ast node.
|
||||
with_mock Farmbot.System.Updates, [check_and_download_updates: fn() -> :ok end] do
|
||||
with_mock Farmbot.System.Updates, [check_and_download_updates: fn(_) -> :ok end] do
|
||||
ctx = Farmbot.Context.new()
|
||||
ast = %Ast{kind: "check_updates", args: %{package: "farmbot_os"}, body: []}
|
||||
Command.do_command(ast, ctx)
|
||||
assert called Farmbot.System.Updates.check_and_download_updates
|
||||
assert called Farmbot.System.Updates.check_and_download_updates(ctx)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,7 +7,7 @@ interface Props {
|
|||
mobx: MainState;
|
||||
}
|
||||
export function AdvancedSettings({ mobx }: Props) {
|
||||
let ssh = false;
|
||||
let ssh: string | false = false;
|
||||
let ntp = false;
|
||||
let hasNetwork = mobx.configuration.network ? true : false;
|
||||
let customFW = false;
|
||||
|
@ -53,11 +53,10 @@ export function AdvancedSettings({ mobx }: Props) {
|
|||
{/* SSH */}
|
||||
<fieldset>
|
||||
<label> Enable SSH </label>
|
||||
<input type="checkbox" defaultChecked={ssh}
|
||||
onChange={(event) => {
|
||||
let blah = event.currentTarget.checked;
|
||||
mobx.toggleSSH(blah);
|
||||
}} />
|
||||
<input onChange={(event) => {
|
||||
let blah = event.currentTarget.value;
|
||||
mobx.enableSSH(blah);
|
||||
}} />
|
||||
</fieldset>
|
||||
|
||||
{/* NTP */}
|
||||
|
@ -75,4 +74,4 @@ export function AdvancedSettings({ mobx }: Props) {
|
|||
|
||||
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ export interface BotConfigFile {
|
|||
/** Should this bot set time after boot. */
|
||||
ntp: boolean;
|
||||
/** ssh */
|
||||
ssh: boolean;
|
||||
ssh: string | false;
|
||||
} | false;
|
||||
/** Just holds the server. All other authorization should use a jwt */
|
||||
authorization: {
|
||||
|
|
|
@ -185,9 +185,9 @@ export class MainState {
|
|||
}
|
||||
|
||||
@action
|
||||
toggleSSH(b: boolean) {
|
||||
enableSSH(public_key: string) {
|
||||
if (this.configuration.network) {
|
||||
this.configuration.network.ssh = b;
|
||||
this.configuration.network.ssh = public_key;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue