Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Deploy Script #11

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,21 @@ IPC framework intended for use in First Robotics Competition using [ZeroMQ](http
## How to Build:
1. Clone the repository
2. Install [Bazel](https://bazel.build/)
3. If using Mac, follow [these](https://github.com/bazelbuild/sandboxfs/blob/master/INSTALL.md) instructions to install Sandboxfs
3. Follow [these](https://github.com/bazelbuild/sandboxfs/blob/master/INSTALL.md) instructions to install Sandboxfs
4. run `bazel build //... --config=roborio` to build the project
5. ???
6. Profit

## How to Deploy:
Make sure you have python3, scp, ssh, and sync available from your command line, otherwise the deploy script will not work.

If your RoboRIO is not configured for our architecture, run `bazel run //tools/deploy:setup_roborio -- roborio-5419-frc.local` with whatever the host name/ip of your robot is. This will configure the rio to run our start script and not the default start script. In the future it will possibly configure other settings such as directories and symlinks.

Run the deploy rule as follows: `bazel run //src:main_deploy --config=roborio -c opt`. The script will ssh into the roborio, install needed packages (rysnc, ssl, python, etc) and then deploy the robot binaries.

## Planned Features:
- Full cross-compiler support (only roborio for now)
- Automated deploy script using Bazel + Python
- Full cross-compiler support (This is mostly working as of now)
- Automated deploy script using Bazel + Python (Written, but needs to be tested)
- Full IPC communication with both C++ and Python APIs (More languages later?)
- Fully unit-testable structure
- Robust logging system
Expand Down
12 changes: 11 additions & 1 deletion src/BUILD
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# https://docs.bazel.build/versions/master/be/c-cpp.html#cc_binary
load("//tools/deploy:deploy.bzl", "robot_deploy")

cc_binary(
name = "main",
srcs = [
Expand All @@ -12,3 +13,12 @@ cc_binary(
"@wpilibc-cpp",
],
)

# waiting for https://github.com/bazelbuild/bazel/issues/3780 to be resolved
robot_deploy(
name = "main_deploy",
srcs = [],
deploy_location = "roborio-5419-frc.local",
start_srcs = [":main"],
# restricted_to=["//platforms:roborio"]
)
Empty file added tools/BUILD
Empty file.
22 changes: 22 additions & 0 deletions tools/deploy/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
filegroup(
name = "rio_robot_command",
srcs = ["robotCommand"],
)

py_binary(
name = "deployer",
srcs = [
"deployer.py",
],
visibility = ["//visibility:public"],
)

sh_binary(
name = "setup_roborio",
srcs = ["setup_roborio.sh"],
data = [
":rio_robot_command",
# '@arm_frc_linux_gnueabi_repo//:compiler_pieces',
],
visibility = ["//visibility:public"],
)
104 changes: 104 additions & 0 deletions tools/deploy/deploy.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
load(
"@bazel_tools//tools/build_defs/repo:utils.bzl",
"update_attrs",
"workspace_and_buildfile",
)

_deploy_dir_attr = {
"srcs": attr.label_list(
mandatory = True,
allow_files = True,
),
"dir": attr.string(
mandatory = True,
),
}

def _deploy_dir_impl(ctx):
return struct(
downloader_dir = ctx.attr.dir,
downloader_srcs = ctx.files.srcs,
)

deploy_dir = repository_rule(
attrs = _deploy_dir_attr,
implementation = _deploy_dir_impl,
)

_robot_deploy_attr = {
"_downloader": attr.label(
executable = True,
cfg = "host",
default = Label("//tools/deploy:deployer"),
),
"start_srcs": attr.label_list(
mandatory = True,
allow_files = True,
),
"srcs": attr.label_list(
mandatory = True,
allow_files = True,
),
"dirs": attr.label_list(
mandatory = False,
providers = [
"downloader_dir",
"downloader_srcs",
],
),
"deploy_location": attr.string(
default = "roborio-5419-frc.local",
),
}

def _robot_deploy_impl(ctx):
all_files = ctx.files.srcs + ctx.files.start_srcs + [ctx.outputs._startlist]
ctx.actions.write(
output = ctx.outputs.executable,
is_executable = True,
# im not sure what the code actually does here, well figure it out
content = "\n".join([
"#!/bin/bash",
"set -e",
'cd "${BASH_SOURCE[0]}.runfiles/%s"' % ctx.workspace_name,
] + ['%s %s --dirs %s -- %s "$@"' % (
ctx.executable._downloader.short_path,
" ".join([src.short_path for src in d.downloader_srcs]),
d.downloader_dir,
ctx.attr.deploy_location,
) for d in ctx.attr.dirs] + [
'exec %s %s -- %s "$@"' % (
ctx.executable._downloader.short_path,
" ".join([src.short_path for src in all_files]),
ctx.attr.deploy_location,
),
]),
)

ctx.actions.write(
output = ctx.outputs._startlist,
content = "\n".join([f.basename for f in ctx.files.start_srcs]) + "\n",
)
to_download = [ctx.outputs._startlist]
to_download += all_files
for d in ctx.attr.dirs:
to_download += d.downloader_srcs

return struct(
runfiles = ctx.runfiles(
files = to_download + ctx.files._downloader,
transitive_files = ctx.attr._downloader.default_runfiles.files,
collect_data = True,
collect_default = True,
),
files = depset([ctx.outputs.executable]),
)

robot_deploy = rule(
implementation = _robot_deploy_impl,
attrs = _robot_deploy_attr,
outputs = {
"_startlist": "%{name}.start_list.dir/start_list.txt",
},
executable = True,
)
94 changes: 94 additions & 0 deletions tools/deploy/deployer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# This file is run by shell scripts generated by the aos_downloader Skylark
# macro. Everything before the first -- is a hard-coded list of files to
# download.

from __future__ import print_function

import sys
import subprocess
import re
import os


def install(ssh_target, pkg):
"""Installs a package from NI on the ssh target."""
print("Installing", pkg)
PKG_URL = "http://download.ni.com/ni-linux-rt/feeds/2015/arm/ipk/cortexa9-vfpv3/" + pkg
subprocess.check_call(["wget", PKG_URL, "-O", pkg])
try:
subprocess.check_call([
"scp", "-S", "ssh", pkg,
ssh_target + ":/tmp/" + pkg
])
subprocess.check_call([
"ssh", ssh_target, "opkg", "install",
"/tmp/" + pkg
])
subprocess.check_call(
["ssh", ssh_target, "rm", "/tmp/" + pkg])
finally:
subprocess.check_call(["rm", pkg])


def main(argv):
args = argv[argv.index("--") + 1:]

relative_dir = ""
recursive = False

if "--dirs" in argv:
dirs_index = argv.index("--dirs")
srcs = argv[1:dirs_index]
relative_dir = argv[dirs_index + 1]
recursive = True
else:
srcs = argv[1:argv.index("--")]

ROBORIO_TARGET_DIR = "/home/admin/robot_code"
ROBORIO_USER = "admin"

target_dir = ROBORIO_TARGET_DIR
user = ROBORIO_USER
destination = args[-1]

result = re.match("(?:([^:@]+)@)?([^:@]+)(?::([^:@]+))?", destination)
if not result:
print(
"Not sure how to parse destination \"%s\"!" % destination,
file=sys.stderr)
return 1
if result.group(1):
user = result.group(1)
hostname = result.group(2)
if result.group(3):
target_dir = result.group(3)

ssh_target = "%s@%s" % (user, hostname)

rsync_cmd = ([
"rsync", "-e", "ssh", "-c",
"-v", "-z", "--copy-links"
] + srcs + ["%s:%s/%s" % (ssh_target, target_dir, relative_dir)])
try:
subprocess.check_call(rsync_cmd)
except subprocess.CalledProcessError as e:
if e.returncode == 127:
print("Unconfigured roboRIO, installing rsync.")
install(ssh_target, "libattr1_2.4.47-r0.36_cortexa9-vfpv3.ipk")
install(ssh_target, "libacl1_2.2.52-r0.36_cortexa9-vfpv3.ipk")
install(ssh_target, "rsync_3.1.0-r0.7_cortexa9-vfpv3.ipk")
subprocess.check_call(rsync_cmd)
else:
raise e

if not recursive:
subprocess.check_call(
("ssh", ssh_target, "&&".join([
"chmod u+s %s/starter_exe" % target_dir,
"echo \'Done moving new executables into place\'",
"bash -c \'sync && sync && sync\'",
])))


if __name__ == "__main__":
main(sys.argv)
1 change: 1 addition & 0 deletions tools/deploy/robotCommand
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/home/admin/robot_code/starter.sh
23 changes: 23 additions & 0 deletions tools/deploy/setup_roborio.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/bash

set -Eeuo pipefail

if [ $# != 1 ];
then
echo "# setup_robot.sh is used to configure a newly flashed roboRIO"
echo ""
echo "Usage: setup_roborio.sh 10.54.19.2"
echo ""
echo "# or if that does not work, try"
echo ""
echo "Usage: setup_roborio.sh roboRIO-5419-frc.local"
exit 1
fi

readonly ROBOT_HOSTNAME="$1"

# This fails if the code isn't running.
ssh "admin@${ROBOT_HOSTNAME}" 'PATH="${PATH}":/usr/local/natinst/bin/ /usr/local/frc/bin/frcKillRobot.sh -r -t' || true

echo "Deploying robotCommand startup script"
scp tools/deploy/robotCommand "admin@${ROBOT_HOSTNAME}:/home/lvuser/"