mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-06-04 16:50:53 +00:00
Replace the gosuper component with a node module that handles communication with systemd, and stop using an init system in the supervisor container
Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
This commit is contained in:
parent
11e8899455
commit
348ff66cee
69
Dockerfile
69
Dockerfile
@ -1,63 +1,4 @@
|
|||||||
ARG ARCH=amd64
|
ARG ARCH=amd64
|
||||||
# Build golang supervisor
|
|
||||||
FROM debian:jessie-20170723 as gosuper
|
|
||||||
|
|
||||||
RUN apt-get update \
|
|
||||||
&& apt-get install -y \
|
|
||||||
build-essential \
|
|
||||||
curl \
|
|
||||||
rsync \
|
|
||||||
&& rm -rf /var/lib/apt/lists/
|
|
||||||
|
|
||||||
ENV GOLANG_VERSION 1.8.3
|
|
||||||
ENV GOLANG_DOWNLOAD_URL https://golang.org/dl/go$GOLANG_VERSION.linux-amd64.tar.gz
|
|
||||||
ENV GOLANG_DOWNLOAD_SHA256 1862f4c3d3907e59b04a757cfda0ea7aa9ef39274af99a784f5be843c80c6772
|
|
||||||
|
|
||||||
COPY gosuper/go-${GOLANG_VERSION}-patches /go-${GOLANG_VERSION}-patches
|
|
||||||
|
|
||||||
RUN mkdir /usr/src/go \
|
|
||||||
&& cd /usr/src/go \
|
|
||||||
&& curl -L -o go.tar.gz $GOLANG_DOWNLOAD_URL \
|
|
||||||
&& echo "${GOLANG_DOWNLOAD_SHA256} go.tar.gz" | sha256sum -c - \
|
|
||||||
&& tar xzf go.tar.gz -C /usr/local \
|
|
||||||
&& cd /usr/src \
|
|
||||||
&& rm -rf go \
|
|
||||||
&& export GOROOT_BOOTSTRAP=/usr/local/go-bootstrap \
|
|
||||||
&& cp -r /usr/local/go /usr/local/go-bootstrap \
|
|
||||||
&& cd /usr/local/go/src \
|
|
||||||
&& patch -p2 -i /go-${GOLANG_VERSION}-patches/0001-dont-fail-when-no-mmx.patch \
|
|
||||||
&& patch -p2 -i /go-${GOLANG_VERSION}-patches/0002-implement-atomic-quadword-ops-with-FILD-FISTP.patch \
|
|
||||||
&& ./make.bash \
|
|
||||||
&& rm -rf /usr/local/go-bootstrap
|
|
||||||
|
|
||||||
ENV UPX_VERSION 3.94
|
|
||||||
# UPX doesn't provide fingerprints so I checked this one manually
|
|
||||||
ENV UPX_SHA256 e1fc0d55c88865ef758c7e4fabbc439e4b5693b9328d219e0b9b3604186abe20
|
|
||||||
|
|
||||||
RUN mkdir /usr/src/upx \
|
|
||||||
&& cd /usr/src/upx \
|
|
||||||
&& curl -L -o upx.tar.xz https://github.com/upx/upx/releases/download/v$UPX_VERSION/upx-$UPX_VERSION-amd64_linux.tar.xz \
|
|
||||||
&& echo "${UPX_SHA256} upx.tar.xz" | sha256sum -c - \
|
|
||||||
&& tar xf upx.tar.xz --strip-components=1 \
|
|
||||||
&& cp ./upx /usr/bin/ \
|
|
||||||
&& cd /usr/src \
|
|
||||||
&& rm -rf upx
|
|
||||||
|
|
||||||
ENV GOPATH /go
|
|
||||||
ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH
|
|
||||||
|
|
||||||
COPY ./gosuper /go/src/resin-supervisor/gosuper
|
|
||||||
|
|
||||||
WORKDIR /go/src/resin-supervisor/gosuper
|
|
||||||
|
|
||||||
ENV GOOS linux
|
|
||||||
ENV GO386=387
|
|
||||||
|
|
||||||
ARG ARCH
|
|
||||||
RUN bash ./build.sh
|
|
||||||
RUN rsync -a --delete /go/bin/gosuper /build/
|
|
||||||
|
|
||||||
##############################################################################
|
|
||||||
|
|
||||||
# The node version here should match the version of the runtime image which is
|
# The node version here should match the version of the runtime image which is
|
||||||
# specified in the base-image subdirectory in the project
|
# specified in the base-image subdirectory in the project
|
||||||
@ -146,12 +87,7 @@ RUN find . -path '*/coverage/*' -o -path '*/test/*' -o -path '*/.nyc_output/*' \
|
|||||||
&& find . -type f -path '*/node_modules/knex/build*' -delete \
|
&& find . -type f -path '*/node_modules/knex/build*' -delete \
|
||||||
&& rm -rf node_modules/sqlite3/node.dtps
|
&& rm -rf node_modules/sqlite3/node.dtps
|
||||||
|
|
||||||
# Create /var/run/resin for the gosuper to place its socket in
|
COPY entry.sh package.json rootfs-overlay/usr/src/app/
|
||||||
RUN mkdir -p rootfs-overlay/var/run/resin
|
|
||||||
|
|
||||||
COPY entry.sh run.sh package.json rootfs-overlay/usr/src/app/
|
|
||||||
|
|
||||||
COPY inittab rootfs-overlay/etc/inittab
|
|
||||||
|
|
||||||
RUN rsync -a --delete node_modules rootfs-overlay /build
|
RUN rsync -a --delete node_modules rootfs-overlay /build
|
||||||
|
|
||||||
@ -171,7 +107,6 @@ WORKDIR /usr/src/app
|
|||||||
|
|
||||||
COPY --from=node-build /usr/src/app/dist ./dist
|
COPY --from=node-build /usr/src/app/dist ./dist
|
||||||
COPY --from=node-deps /build/node_modules ./node_modules
|
COPY --from=node-deps /build/node_modules ./node_modules
|
||||||
COPY --from=gosuper /build/gosuper ./gosuper
|
|
||||||
COPY --from=node-deps /build/rootfs-overlay/ /
|
COPY --from=node-deps /build/rootfs-overlay/ /
|
||||||
|
|
||||||
VOLUME /data
|
VOLUME /data
|
||||||
@ -186,3 +121,5 @@ ENV CONFIG_MOUNT_POINT=/boot/config.json \
|
|||||||
|
|
||||||
HEALTHCHECK --interval=5m --start-period=1m --timeout=30s --retries=3 \
|
HEALTHCHECK --interval=5m --start-period=1m --timeout=30s --retries=3 \
|
||||||
CMD wget -qO- http://127.0.0.1:${LISTEN_PORT:-48484}/v1/healthy || exit 1
|
CMD wget -qO- http://127.0.0.1:${LISTEN_PORT:-48484}/v1/healthy || exit 1
|
||||||
|
|
||||||
|
CMD [ "./entry.sh" ]
|
||||||
|
34
Makefile
34
Makefile
@ -6,7 +6,6 @@
|
|||||||
# Build targets (require Docker 17.05 or greater):
|
# Build targets (require Docker 17.05 or greater):
|
||||||
# * supervisor (default) - builds a resin-supervisor image
|
# * supervisor (default) - builds a resin-supervisor image
|
||||||
# * deploy - pushes a resin-supervisor image to the registry, retrying up to 3 times
|
# * deploy - pushes a resin-supervisor image to the registry, retrying up to 3 times
|
||||||
# * gosuper - builds the "gosuper" component (a golang image with the Go supervisor component at /go/bin/gosuper and /build/gosuper)
|
|
||||||
# * nodedeps, nodebuild - builds the node component, with the node_modules and src at /usr/src/app and /build (also includes a rootfs-overlay there)
|
# * nodedeps, nodebuild - builds the node component, with the node_modules and src at /usr/src/app and /build (also includes a rootfs-overlay there)
|
||||||
# * supervisor-dind: build the development docker-in-docker supervisor that run-supervisor uses (requires a SUPERVISOR_IMAGE to be available locally)
|
# * supervisor-dind: build the development docker-in-docker supervisor that run-supervisor uses (requires a SUPERVISOR_IMAGE to be available locally)
|
||||||
#
|
#
|
||||||
@ -19,8 +18,6 @@
|
|||||||
#
|
#
|
||||||
# Test/development targets:
|
# Test/development targets:
|
||||||
# * run-supervisor, stop-supervisor - build and start or stop a docker-in-docker resin-supervisor (requires aufs, ability to run privileged containers, and a SUPERVISOR_IMAGE to be available locally)
|
# * run-supervisor, stop-supervisor - build and start or stop a docker-in-docker resin-supervisor (requires aufs, ability to run privileged containers, and a SUPERVISOR_IMAGE to be available locally)
|
||||||
# * format-gosuper, test-gosuper - build a gosuper image and run formatting or unit tests
|
|
||||||
# * test-integration - run an integration test (see gosuper/supertest). Requires a docker-in-docker supervisor to be running
|
|
||||||
#
|
#
|
||||||
# Variables for test/dev targets:
|
# Variables for test/dev targets:
|
||||||
# * IMAGE: image to build and run (either for run-supervisor or test-gosuper/integration)
|
# * IMAGE: image to build and run (either for run-supervisor or test-gosuper/integration)
|
||||||
@ -193,33 +190,4 @@ nodedeps:
|
|||||||
nodebuild:
|
nodebuild:
|
||||||
$(MAKE) -f $(THIS_FILE) TARGET_COMPONENT=node-build IMAGE=$(IMAGE) ARCH=$(ARCH) supervisor-image
|
$(MAKE) -f $(THIS_FILE) TARGET_COMPONENT=node-build IMAGE=$(IMAGE) ARCH=$(ARCH) supervisor-image
|
||||||
|
|
||||||
gosuper:
|
.PHONY: supervisor deploy nodedeps nodebuild supervisor-dind run-supervisor
|
||||||
$(MAKE) -f $(THIS_FILE) TARGET_COMPONENT=gosuper IMAGE=$(IMAGE) ARCH=$(ARCH) supervisor-image
|
|
||||||
|
|
||||||
test-gosuper: gosuper
|
|
||||||
docker run \
|
|
||||||
--rm \
|
|
||||||
-v /var/run/dbus:/mnt/root/run/dbus \
|
|
||||||
-e DBUS_SYSTEM_BUS_ADDRESS="/mnt/root/run/dbus/system_bus_socket" \
|
|
||||||
$(IMAGE) bash -c \
|
|
||||||
'./test_formatting.sh && go test -v ./gosuper'
|
|
||||||
|
|
||||||
format-gosuper: gosuper
|
|
||||||
docker run \
|
|
||||||
--rm \
|
|
||||||
-v $(shell pwd)/gosuper:/go/src/resin-supervisor/gosuper \
|
|
||||||
$(IMAGE) \
|
|
||||||
go fmt ./...
|
|
||||||
|
|
||||||
test-integration: gosuper
|
|
||||||
docker run \
|
|
||||||
--rm \
|
|
||||||
--net=host \
|
|
||||||
-e SUPERVISOR_IP="$(shell docker inspect --format '{{ .NetworkSettings.IPAddress }}' resin_supervisor_1)" \
|
|
||||||
--volumes-from resin_supervisor_1 \
|
|
||||||
-v /var/run/dbus:/mnt/root/run/dbus \
|
|
||||||
-e DBUS_SYSTEM_BUS_ADDRESS="/mnt/root/run/dbus/system_bus_socket" \
|
|
||||||
$(IMAGE) \
|
|
||||||
go test -v ./supertest
|
|
||||||
|
|
||||||
.PHONY: supervisor deploy nodedeps nodebuild gosuper supervisor-dind run-supervisor
|
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
#
|
#
|
||||||
# It pulls intermediate images for caching, if available:
|
# It pulls intermediate images for caching, if available:
|
||||||
# resin/$ARCH-supervisor-node:$TAG
|
# resin/$ARCH-supervisor-node:$TAG
|
||||||
# resin/$ARCH-supervisor-go:$TAG
|
|
||||||
#
|
#
|
||||||
# In all of these cases it will use "master" if $TAG is not found.
|
# In all of these cases it will use "master" if $TAG is not found.
|
||||||
#
|
#
|
||||||
@ -49,15 +48,12 @@ TARGET_IMAGE=resin/$ARCH-supervisor:$TAG
|
|||||||
# Intermediate images and cache
|
# Intermediate images and cache
|
||||||
NODE_IMAGE=resin/$ARCH-supervisor-node:$TAG
|
NODE_IMAGE=resin/$ARCH-supervisor-node:$TAG
|
||||||
NODE_BUILD_IMAGE=resin/$ARCH-supervisor-node:$TAG-build
|
NODE_BUILD_IMAGE=resin/$ARCH-supervisor-node:$TAG-build
|
||||||
GO_IMAGE=resin/$ARCH-supervisor-go:$TAG
|
|
||||||
|
|
||||||
TARGET_CACHE=$TARGET_IMAGE
|
TARGET_CACHE=$TARGET_IMAGE
|
||||||
GO_CACHE=$GO_IMAGE
|
|
||||||
NODE_CACHE=$NODE_IMAGE
|
NODE_CACHE=$NODE_IMAGE
|
||||||
NODE_BUILD_CACHE=$NODE_BUILD_IMAGE
|
NODE_BUILD_CACHE=$NODE_BUILD_IMAGE
|
||||||
|
|
||||||
TARGET_CACHE_MASTER=resin/$ARCH-supervisor:master
|
TARGET_CACHE_MASTER=resin/$ARCH-supervisor:master
|
||||||
GO_CACHE_MASTER=resin/$ARCH-supervisor-go:master
|
|
||||||
NODE_CACHE_MASTER=resin/$ARCH-supervisor-node:master
|
NODE_CACHE_MASTER=resin/$ARCH-supervisor-node:master
|
||||||
NODE_BUILD_CACHE_MASTER=resin/$ARCH-supervisor-node:master-build
|
NODE_BUILD_CACHE_MASTER=resin/$ARCH-supervisor-node:master-build
|
||||||
|
|
||||||
@ -73,29 +69,17 @@ function tryPullForCache() {
|
|||||||
# Only if the pull succeeds we add a --cache-from option
|
# Only if the pull succeeds we add a --cache-from option
|
||||||
tryPullForCache $TARGET_CACHE
|
tryPullForCache $TARGET_CACHE
|
||||||
tryPullForCache $TARGET_CACHE_MASTER
|
tryPullForCache $TARGET_CACHE_MASTER
|
||||||
tryPullForCache $GO_CACHE
|
|
||||||
tryPullForCache $GO_CACHE_MASTER
|
|
||||||
tryPullForCache $NODE_CACHE
|
tryPullForCache $NODE_CACHE
|
||||||
tryPullForCache $NODE_CACHE_MASTER
|
tryPullForCache $NODE_CACHE_MASTER
|
||||||
tryPullForCache $NODE_BUILD_CACHE
|
tryPullForCache $NODE_BUILD_CACHE
|
||||||
tryPullForCache $NODE_BUILD_CACHE_MASTER
|
tryPullForCache $NODE_BUILD_CACHE_MASTER
|
||||||
|
|
||||||
if [ "$ENABLE_TESTS" = "true" ]; then
|
|
||||||
make ARCH=$ARCH IMAGE=$GO_IMAGE test-gosuper
|
|
||||||
fi
|
|
||||||
|
|
||||||
export DOCKER_BUILD_OPTIONS=${CACHE_FROM}
|
export DOCKER_BUILD_OPTIONS=${CACHE_FROM}
|
||||||
export ARCH
|
export ARCH
|
||||||
export PUBNUB_PUBLISH_KEY
|
export PUBNUB_PUBLISH_KEY
|
||||||
export PUBNUB_SUBSCRIBE_KEY
|
export PUBNUB_SUBSCRIBE_KEY
|
||||||
export MIXPANEL_TOKEN
|
export MIXPANEL_TOKEN
|
||||||
|
|
||||||
make IMAGE=$GO_IMAGE gosuper
|
|
||||||
if [ "$PUSH_IMAGES" = "true" ]; then
|
|
||||||
make IMAGE=$GO_IMAGE deploy || true
|
|
||||||
fi
|
|
||||||
export DOCKER_BUILD_OPTIONS="${DOCKER_BUILD_OPTIONS} --cache-from ${GO_IMAGE}"
|
|
||||||
|
|
||||||
make IMAGE=$NODE_BUILD_IMAGE nodebuild
|
make IMAGE=$NODE_BUILD_IMAGE nodebuild
|
||||||
if [ "$PUSH_IMAGES" = "true" ]; then
|
if [ "$PUSH_IMAGES" = "true" ]; then
|
||||||
make IMAGE=$NODE_BUILD_IMAGE deploy || true
|
make IMAGE=$NODE_BUILD_IMAGE deploy || true
|
||||||
@ -123,12 +107,10 @@ fi
|
|||||||
if [ "$CLEANUP" = "true" ]; then
|
if [ "$CLEANUP" = "true" ]; then
|
||||||
tryRemove $TARGET_IMAGE
|
tryRemove $TARGET_IMAGE
|
||||||
|
|
||||||
tryRemove $GO_IMAGE
|
|
||||||
tryRemove $NODE_IMAGE
|
tryRemove $NODE_IMAGE
|
||||||
tryRemove $NODE_BUILD_IMAGE
|
tryRemove $NODE_BUILD_IMAGE
|
||||||
|
|
||||||
tryRemove $TARGET_CACHE
|
tryRemove $TARGET_CACHE
|
||||||
tryRemove $GO_CACHE
|
|
||||||
tryRemove $NODE_BUILD_CACHE
|
tryRemove $NODE_BUILD_CACHE
|
||||||
tryRemove $NODE_CACHE
|
tryRemove $NODE_CACHE
|
||||||
fi
|
fi
|
||||||
|
@ -18,7 +18,7 @@ Alternatively, the Resin API (api.resin.io) has a proxy endpoint at `POST /super
|
|||||||
|
|
||||||
The API is versioned (currently at v1), except for `/ping`.
|
The API is versioned (currently at v1), except for `/ping`.
|
||||||
|
|
||||||
You might notice that the formats of some responses differ. This is because they were implemented later, and in Go instead of node.js.
|
You might notice that the formats of some responses differ. This is because they were implemented later, and in Go instead of node.js - even if the Go pieces were later removed, so we kept the response format for backwards compatibility.
|
||||||
|
|
||||||
Here's the full list of endpoints implemented so far. In all examples, replace everything between `< >` for the corresponding values.
|
Here's the full list of endpoints implemented so far. In all examples, replace everything between `< >` for the corresponding values.
|
||||||
|
|
||||||
@ -119,7 +119,6 @@ When successful, responds with 202 accepted and a JSON object:
|
|||||||
"Error": ""
|
"Error": ""
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
(This is implemented in Go)
|
|
||||||
|
|
||||||
#### Request body
|
#### Request body
|
||||||
Can contain a `force` property, which if set to `true` will cause the update lock to be overridden.
|
Can contain a `force` property, which if set to `true` will cause the update lock to be overridden.
|
||||||
@ -158,7 +157,6 @@ When successful, responds with 202 accepted and a JSON object:
|
|||||||
"Error": ""
|
"Error": ""
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
(This is implemented in Go)
|
|
||||||
|
|
||||||
#### Request body
|
#### Request body
|
||||||
Can contain a `force` property, which if set to `true` will cause the update lock to be overridden.
|
Can contain a `force` property, which if set to `true` will cause the update lock to be overridden.
|
||||||
@ -197,8 +195,6 @@ When successful, responds with 200 and a JSON object:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
(This is implemented in Go)
|
|
||||||
|
|
||||||
#### Request body
|
#### Request body
|
||||||
Has to be a JSON object with an `appId` property, corresponding to the ID of the application the device is running.
|
Has to be a JSON object with an `appId` property, corresponding to the ID of the application the device is running.
|
||||||
|
|
||||||
|
15
entry.sh
15
entry.sh
@ -2,16 +2,9 @@
|
|||||||
|
|
||||||
set -o errexit
|
set -o errexit
|
||||||
|
|
||||||
[ -d /dev/net ] ||
|
|
||||||
mkdir -p /dev/net
|
|
||||||
[ -c /dev/net/tun ] ||
|
|
||||||
mknod /dev/net/tun c 10 200
|
|
||||||
[ -d /mnt/root/tmp/resin-supervisor ] ||
|
[ -d /mnt/root/tmp/resin-supervisor ] ||
|
||||||
mkdir -p /mnt/root/tmp/resin-supervisor
|
mkdir -p /mnt/root/tmp/resin-supervisor
|
||||||
|
|
||||||
mkdir -p /var/run/resin
|
|
||||||
mount -t tmpfs -o size=1m tmpfs /var/run/resin
|
|
||||||
|
|
||||||
# If DOCKER_ROOT isn't set then default it
|
# If DOCKER_ROOT isn't set then default it
|
||||||
if [ -z "${DOCKER_ROOT}" ]; then
|
if [ -z "${DOCKER_ROOT}" ]; then
|
||||||
DOCKER_ROOT=/mnt/root/var/lib/rce
|
DOCKER_ROOT=/mnt/root/var/lib/rce
|
||||||
@ -23,3 +16,11 @@ DOCKER_LIB_PATH=${DOCKER_ROOT#/mnt/root}
|
|||||||
if [ ! -d "${DOCKER_LIB_PATH}" ]; then
|
if [ ! -d "${DOCKER_LIB_PATH}" ]; then
|
||||||
ln -s "${DOCKER_ROOT}" "${DOCKER_LIB_PATH}"
|
ln -s "${DOCKER_ROOT}" "${DOCKER_LIB_PATH}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ -z "$DOCKER_SOCKET" ]; then
|
||||||
|
export DOCKER_SOCKET=/run/docker.sock
|
||||||
|
fi
|
||||||
|
|
||||||
|
export DBUS_SYSTEM_BUS_ADDRESS="unix:path=/mnt/root/run/dbus/system_bus_socket"
|
||||||
|
|
||||||
|
exec node /usr/src/app/dist/app.js
|
||||||
|
7
inittab
7
inittab
@ -1,7 +0,0 @@
|
|||||||
# busybox inittab
|
|
||||||
# format: tty:ignored:action:command
|
|
||||||
|
|
||||||
stdout::sysinit:/usr/src/app/entry.sh
|
|
||||||
|
|
||||||
stdout::respawn:/usr/src/app/run.sh node /usr/src/app/dist/app.js
|
|
||||||
stdout::respawn:/usr/src/app/run.sh /usr/src/app/gosuper
|
|
@ -29,6 +29,7 @@
|
|||||||
"coffee-loader": "^0.7.3",
|
"coffee-loader": "^0.7.3",
|
||||||
"coffee-script": "~1.11.0",
|
"coffee-script": "~1.11.0",
|
||||||
"copy-webpack-plugin": "^4.2.3",
|
"copy-webpack-plugin": "^4.2.3",
|
||||||
|
"dbus-native": "^0.2.5",
|
||||||
"docker-delta": "^2.0.4",
|
"docker-delta": "^2.0.4",
|
||||||
"docker-progress": "^2.7.2",
|
"docker-progress": "^2.7.2",
|
||||||
"docker-toolbelt": "^3.2.1",
|
"docker-toolbelt": "^3.2.1",
|
||||||
|
22
run.sh
22
run.sh
@ -1,22 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
if [ -z "$GOSUPER_SOCKET" ]; then
|
|
||||||
export GOSUPER_SOCKET=/var/run/resin/gosuper.sock
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$DOCKER_SOCKET" ]; then
|
|
||||||
export DOCKER_SOCKET=/run/docker.sock
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$HOST_PROC" ]; then
|
|
||||||
export HOST_PROC=/mnt/root/proc
|
|
||||||
fi
|
|
||||||
|
|
||||||
export DBUS_SYSTEM_BUS_ADDRESS="/mnt/root/run/dbus/system_bus_socket"
|
|
||||||
|
|
||||||
# If DOCKER_ROOT isn't set then default it
|
|
||||||
if [ -z "${DOCKER_ROOT}" ]; then
|
|
||||||
DOCKER_ROOT=/mnt/root/var/lib/rce
|
|
||||||
fi
|
|
||||||
|
|
||||||
exec $@
|
|
@ -4,9 +4,10 @@ childProcess = Promise.promisifyAll(require('child_process'))
|
|||||||
fs = Promise.promisifyAll(require('fs'))
|
fs = Promise.promisifyAll(require('fs'))
|
||||||
|
|
||||||
constants = require './lib/constants'
|
constants = require './lib/constants'
|
||||||
gosuper = require './lib/gosuper'
|
systemd = require './lib/systemd'
|
||||||
fsUtils = require './lib/fs-utils'
|
fsUtils = require './lib/fs-utils'
|
||||||
{ checkTruthy, checkInt } = require './lib/validation'
|
{ checkTruthy, checkInt } = require './lib/validation'
|
||||||
|
{ UnitNotLoadedError } = require './lib/errors'
|
||||||
|
|
||||||
hostConfigConfigVarPrefix = 'RESIN_HOST_'
|
hostConfigConfigVarPrefix = 'RESIN_HOST_'
|
||||||
bootConfigEnvVarPrefix = hostConfigConfigVarPrefix + 'CONFIG_'
|
bootConfigEnvVarPrefix = hostConfigConfigVarPrefix + 'CONFIG_'
|
||||||
@ -33,10 +34,12 @@ forbiddenConfigKeys = [
|
|||||||
]
|
]
|
||||||
arrayConfigKeys = [ 'dtparam', 'dtoverlay', 'device_tree_param', 'device_tree_overlay' ]
|
arrayConfigKeys = [ 'dtparam', 'dtoverlay', 'device_tree_param', 'device_tree_overlay' ]
|
||||||
|
|
||||||
|
logToDisplayServiceName = 'resin-info@tty1'
|
||||||
|
vpnServiceName = 'openvpn-resin'
|
||||||
|
|
||||||
module.exports = class DeviceConfig
|
module.exports = class DeviceConfig
|
||||||
constructor: ({ @db, @config, @logger }) ->
|
constructor: ({ @db, @config, @logger }) ->
|
||||||
@rebootRequired = false
|
@rebootRequired = false
|
||||||
@gosuperHealthy = true
|
|
||||||
@configKeys = {
|
@configKeys = {
|
||||||
appUpdatePollInterval: { envVarName: 'RESIN_SUPERVISOR_POLL_INTERVAL', varType: 'int', defaultValue: '60000' }
|
appUpdatePollInterval: { envVarName: 'RESIN_SUPERVISOR_POLL_INTERVAL', varType: 'int', defaultValue: '60000' }
|
||||||
localMode: { envVarName: 'RESIN_SUPERVISOR_LOCAL_MODE', varType: 'bool', defaultValue: 'false' }
|
localMode: { envVarName: 'RESIN_SUPERVISOR_LOCAL_MODE', varType: 'bool', defaultValue: 'false' }
|
||||||
@ -253,27 +256,33 @@ module.exports = class DeviceConfig
|
|||||||
return @bootConfigToEnv(conf)
|
return @bootConfigToEnv(conf)
|
||||||
|
|
||||||
getLogToDisplay: ->
|
getLogToDisplay: ->
|
||||||
gosuper.get('/v1/log-to-display', { json: true })
|
systemd.serviceActiveState(logToDisplayServiceName)
|
||||||
.spread (res, body) ->
|
.then (activeState) ->
|
||||||
if res.statusCode == 404
|
return activeState not in [ 'inactive', 'deactivating' ]
|
||||||
return
|
.catchReturn(UnitNotLoadedError, null)
|
||||||
if res.statusCode != 200
|
|
||||||
throw new Error("Error getting log to display status: #{res.statusCode} #{body.Error}")
|
|
||||||
return Boolean(body.Data)
|
|
||||||
|
|
||||||
setLogToDisplay: (val) =>
|
setLogToDisplay: (val) =>
|
||||||
Promise.try =>
|
Promise.try =>
|
||||||
enable = checkTruthy(val)
|
enable = checkTruthy(val)
|
||||||
if !enable?
|
if !enable?
|
||||||
throw new Error("Invalid value in call to setLogToDisplay: #{val}")
|
throw new Error("Invalid value in call to setLogToDisplay: #{val}")
|
||||||
gosuper.post('/v1/log-to-display', { json: true, body: Enable: enable })
|
@getLogToDisplay()
|
||||||
.spread (response, body) =>
|
.then (currentState) ->
|
||||||
if response.statusCode != 200
|
if !currentState? or currentState == enable
|
||||||
throw new Error("#{response.statusCode} #{body.Error}")
|
return false
|
||||||
|
if enable
|
||||||
|
systemd.startService(logToDisplayServiceName)
|
||||||
|
.then ->
|
||||||
|
systemd.enableService(logToDisplayServiceName)
|
||||||
|
.return(true)
|
||||||
else
|
else
|
||||||
if body.Data == true
|
systemd.stopService(logToDisplayServiceName)
|
||||||
@rebootRequired = true
|
.then ->
|
||||||
return body.Data
|
systemd.disableService(logToDisplayServiceName)
|
||||||
|
.return(true)
|
||||||
|
.tap (changed) =>
|
||||||
|
if changed
|
||||||
|
@rebootRequired = true
|
||||||
|
|
||||||
setBootConfig: (deviceType, target) =>
|
setBootConfig: (deviceType, target) =>
|
||||||
Promise.try =>
|
Promise.try =>
|
||||||
@ -302,18 +311,14 @@ module.exports = class DeviceConfig
|
|||||||
@logger.logSystemMessage("Error setting boot config: #{err}", { error: err }, 'Apply boot config error')
|
@logger.logSystemMessage("Error setting boot config: #{err}", { error: err }, 'Apply boot config error')
|
||||||
|
|
||||||
getVPNEnabled: ->
|
getVPNEnabled: ->
|
||||||
gosuper.get('/v1/vpncontrol', { json: true })
|
systemd.serviceActiveState(vpnServiceName)
|
||||||
.spread (res, body) ->
|
.then (activeState) ->
|
||||||
if res.statusCode != 200
|
return activeState not in [ 'inactive', 'deactivating' ]
|
||||||
throw new Error("Error getting vpn status: #{res.statusCode} #{body.Error}")
|
.catchReturn(UnitNotLoadedError, null)
|
||||||
@gosuperHealthy = true
|
|
||||||
return Boolean(body.Data)
|
|
||||||
.tapCatch (err) =>
|
|
||||||
@gosuperHealthy = false
|
|
||||||
|
|
||||||
setVPNEnabled: (val) ->
|
setVPNEnabled: (val) ->
|
||||||
enable = checkTruthy(val) ? true
|
enable = checkTruthy(val) ? true
|
||||||
gosuper.post('/v1/vpncontrol', { json: true, body: Enable: enable })
|
if enable
|
||||||
.spread (response, body) ->
|
systemd.startService(vpnServiceName)
|
||||||
if response.statusCode != 202
|
else
|
||||||
throw new Error("#{response.statusCode} #{body?.Error}")
|
systemd.stopService(vpnServiceName)
|
||||||
|
@ -10,7 +10,7 @@ network = require './network'
|
|||||||
|
|
||||||
constants = require './lib/constants'
|
constants = require './lib/constants'
|
||||||
validation = require './lib/validation'
|
validation = require './lib/validation'
|
||||||
device = require './lib/device'
|
systemd = require './lib/systemd'
|
||||||
updateLock = require './lib/update-lock'
|
updateLock = require './lib/update-lock'
|
||||||
{ singleToMulticontainerApp } = require './lib/migration'
|
{ singleToMulticontainerApp } = require './lib/migration'
|
||||||
|
|
||||||
@ -139,7 +139,7 @@ module.exports = class DeviceState extends EventEmitter
|
|||||||
cycleTimeMs = cycleTime[0] * 1000 + cycleTime[1] / 1e6
|
cycleTimeMs = cycleTime[0] * 1000 + cycleTime[1] / 1e6
|
||||||
cycleTimeWithinInterval = cycleTimeMs - @applications.timeSpentFetching < 2 * conf.appUpdatePollInterval
|
cycleTimeWithinInterval = cycleTimeMs - @applications.timeSpentFetching < 2 * conf.appUpdatePollInterval
|
||||||
applyTargetHealthy = conf.offlineMode or !@applyInProgress or @applications.fetchesInProgress > 0 or cycleTimeWithinInterval
|
applyTargetHealthy = conf.offlineMode or !@applyInProgress or @applications.fetchesInProgress > 0 or cycleTimeWithinInterval
|
||||||
return applyTargetHealthy and @deviceConfig.gosuperHealthy
|
return applyTargetHealthy
|
||||||
|
|
||||||
normaliseLegacy: =>
|
normaliseLegacy: =>
|
||||||
# When legacy apps are present, we kill their containers and migrate their /data to a named volume
|
# When legacy apps are present, we kill their containers and migrate their /data to a named volume
|
||||||
@ -345,7 +345,7 @@ module.exports = class DeviceState extends EventEmitter
|
|||||||
@applications.stopAll({ force, skipLock })
|
@applications.stopAll({ force, skipLock })
|
||||||
.then =>
|
.then =>
|
||||||
@logger.logSystemMessage('Rebooting', {}, 'Reboot')
|
@logger.logSystemMessage('Rebooting', {}, 'Reboot')
|
||||||
device.reboot()
|
systemd.reboot()
|
||||||
.tap =>
|
.tap =>
|
||||||
@shuttingDown = true
|
@shuttingDown = true
|
||||||
@emitAsync('shutdown')
|
@emitAsync('shutdown')
|
||||||
@ -354,7 +354,7 @@ module.exports = class DeviceState extends EventEmitter
|
|||||||
@applications.stopAll({ force, skipLock })
|
@applications.stopAll({ force, skipLock })
|
||||||
.then =>
|
.then =>
|
||||||
@logger.logSystemMessage('Shutting down', {}, 'Shutdown')
|
@logger.logSystemMessage('Shutting down', {}, 'Shutdown')
|
||||||
device.shutdown()
|
systemd.shutdown()
|
||||||
.tap =>
|
.tap =>
|
||||||
@shuttingDown = true
|
@shuttingDown = true
|
||||||
@emitAsync('shutdown')
|
@emitAsync('shutdown')
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
Promise = require 'bluebird'
|
Promise = require 'bluebird'
|
||||||
_ = require 'lodash'
|
_ = require 'lodash'
|
||||||
gosuper = require './lib/gosuper'
|
systemd = require './lib/systemd'
|
||||||
path = require 'path'
|
path = require 'path'
|
||||||
constants = require './lib/constants'
|
constants = require './lib/constants'
|
||||||
fs = Promise.promisifyAll(require('fs'))
|
fs = Promise.promisifyAll(require('fs'))
|
||||||
@ -28,18 +28,10 @@ redsocksFooter = '}\n'
|
|||||||
|
|
||||||
proxyFields = [ 'type', 'ip', 'port', 'login', 'password' ]
|
proxyFields = [ 'type', 'ip', 'port', 'login', 'password' ]
|
||||||
|
|
||||||
proxyBasePath = path.join('/mnt/root', constants.bootMountPoint, 'system-proxy')
|
proxyBasePath = path.join(constants.rootMountPoint, constants.bootMountPoint, 'system-proxy')
|
||||||
redsocksConfPath = path.join(proxyBasePath, 'redsocks.conf')
|
redsocksConfPath = path.join(proxyBasePath, 'redsocks.conf')
|
||||||
noProxyPath = path.join(proxyBasePath, 'no_proxy')
|
noProxyPath = path.join(proxyBasePath, 'no_proxy')
|
||||||
|
|
||||||
restartSystemdService = (serviceName) ->
|
|
||||||
gosuper.post('/v1/restart-service', { json: true, body: Name: serviceName })
|
|
||||||
.spread (response, body) ->
|
|
||||||
if response.statusCode != 200
|
|
||||||
err = new Error("Error restarting service #{serviceName}: #{response.statusCode} #{body}")
|
|
||||||
err.statusCode = response.statusCode
|
|
||||||
throw err
|
|
||||||
|
|
||||||
readProxy = ->
|
readProxy = ->
|
||||||
fs.readFileAsync(redsocksConfPath)
|
fs.readFileAsync(redsocksConfPath)
|
||||||
.then (redsocksConf) ->
|
.then (redsocksConf) ->
|
||||||
@ -97,11 +89,11 @@ setProxy = (conf) ->
|
|||||||
redsocksConf += redsocksFooter
|
redsocksConf += redsocksFooter
|
||||||
writeFileAtomic(redsocksConfPath, redsocksConf)
|
writeFileAtomic(redsocksConfPath, redsocksConf)
|
||||||
.then ->
|
.then ->
|
||||||
restartSystemdService('resin-proxy-config')
|
systemd.restartService('resin-proxy-config')
|
||||||
.then ->
|
.then ->
|
||||||
restartSystemdService('redsocks')
|
systemd.restartService('redsocks')
|
||||||
|
|
||||||
hostnamePath = '/mnt/root/etc/hostname'
|
hostnamePath = path.join(constants.rootMountPoint, '/etc/hostname')
|
||||||
readHostname = ->
|
readHostname = ->
|
||||||
fs.readFileAsync(hostnamePath)
|
fs.readFileAsync(hostnamePath)
|
||||||
.then (hostnameData) ->
|
.then (hostnameData) ->
|
||||||
@ -110,7 +102,7 @@ readHostname = ->
|
|||||||
setHostname = (val, configModel) ->
|
setHostname = (val, configModel) ->
|
||||||
configModel.set(hostname: val)
|
configModel.set(hostname: val)
|
||||||
.then ->
|
.then ->
|
||||||
restartSystemdService('resin-hostname')
|
systemd.restartService('resin-hostname')
|
||||||
|
|
||||||
|
|
||||||
exports.get = ->
|
exports.get = ->
|
||||||
|
@ -8,7 +8,6 @@ supervisorNetworkInterface = 'supervisor0'
|
|||||||
module.exports =
|
module.exports =
|
||||||
rootMountPoint: rootMountPoint
|
rootMountPoint: rootMountPoint
|
||||||
databasePath: checkString(process.env.DATABASE_PATH) ? '/data/database.sqlite'
|
databasePath: checkString(process.env.DATABASE_PATH) ? '/data/database.sqlite'
|
||||||
gosuperAddress: "http://unix:#{process.env.GOSUPER_SOCKET}:"
|
|
||||||
dockerSocket: process.env.DOCKER_SOCKET ? '/var/run/docker.sock'
|
dockerSocket: process.env.DOCKER_SOCKET ? '/var/run/docker.sock'
|
||||||
supervisorImage: checkString(process.env.SUPERVISOR_IMAGE) ? 'resin/rpi-supervisor'
|
supervisorImage: checkString(process.env.SUPERVISOR_IMAGE) ? 'resin/rpi-supervisor'
|
||||||
ledFile: checkString(process.env.LED_FILE) ? '/sys/class/leds/led0/brightness'
|
ledFile: checkString(process.env.LED_FILE) ? '/sys/class/leds/led0/brightness'
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
gosuper = require './gosuper'
|
|
||||||
|
|
||||||
gosuperAction = (action) ->
|
|
||||||
gosuper.post("/v1/#{action}", { json: true })
|
|
||||||
.spread (res, body) ->
|
|
||||||
if res.statusCode != 202
|
|
||||||
throw new Error(body.Error)
|
|
||||||
return body
|
|
||||||
|
|
||||||
exports.reboot = ->
|
|
||||||
gosuperAction('reboot')
|
|
||||||
|
|
||||||
exports.shutdown = ->
|
|
||||||
gosuperAction('shutdown')
|
|
@ -3,3 +3,4 @@
|
|||||||
exports.NotFoundError = (err) -> checkInt(err.statusCode) is 404
|
exports.NotFoundError = (err) -> checkInt(err.statusCode) is 404
|
||||||
exports.ENOENT = (err) -> err.code is 'ENOENT'
|
exports.ENOENT = (err) -> err.code is 'ENOENT'
|
||||||
exports.EEXIST = (err) -> err.code is 'EEXIST'
|
exports.EEXIST = (err) -> err.code is 'EEXIST'
|
||||||
|
exports.UnitNotLoadedError = (err) -> err[0]?.endsWith?('not loaded.')
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
Promise = require 'bluebird'
|
|
||||||
_ = require 'lodash'
|
|
||||||
{ request } = require './request'
|
|
||||||
constants = require './constants'
|
|
||||||
|
|
||||||
emptyHostRequest = request.defaults({ headers: Host: '' })
|
|
||||||
gosuperRequest = (method, endpoint, options = {}, callback) ->
|
|
||||||
if _.isFunction(options)
|
|
||||||
callback = options
|
|
||||||
options = {}
|
|
||||||
options.method = method
|
|
||||||
options.url = constants.gosuperAddress + endpoint
|
|
||||||
emptyHostRequest(options, callback)
|
|
||||||
|
|
||||||
gosuperPost = _.partial(gosuperRequest, 'POST')
|
|
||||||
gosuperGet = _.partial(gosuperRequest, 'GET')
|
|
||||||
|
|
||||||
module.exports =
|
|
||||||
post: Promise.promisify(gosuperPost, multiArgs: true)
|
|
||||||
get: Promise.promisify(gosuperGet, multiArgs: true)
|
|
55
src/lib/systemd.coffee
Normal file
55
src/lib/systemd.coffee
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
dbus = require 'dbus-native'
|
||||||
|
Promise = require 'bluebird'
|
||||||
|
|
||||||
|
bus = Promise.promisifyAll(dbus.systemBus())
|
||||||
|
|
||||||
|
systemdManagerMethodCall = (method, signature = '', body = []) ->
|
||||||
|
bus.invokeAsync({
|
||||||
|
path: '/org/freedesktop/systemd1'
|
||||||
|
destination: 'org.freedesktop.systemd1'
|
||||||
|
interface: 'org.freedesktop.systemd1.Manager'
|
||||||
|
member: method
|
||||||
|
signature
|
||||||
|
body
|
||||||
|
})
|
||||||
|
|
||||||
|
exports.restartService = (serviceName) ->
|
||||||
|
systemdManagerMethodCall('RestartUnit', 'ss', [ "#{serviceName}.service", 'fail' ])
|
||||||
|
|
||||||
|
exports.startService = (serviceName) ->
|
||||||
|
systemdManagerMethodCall('StartUnit', 'ss', [ "#{serviceName}.service", 'fail' ])
|
||||||
|
|
||||||
|
exports.stopService = (serviceName) ->
|
||||||
|
systemdManagerMethodCall('StopUnit', 'ss', [ "#{serviceName}.service", 'fail' ])
|
||||||
|
|
||||||
|
exports.enableService = (serviceName) ->
|
||||||
|
systemdManagerMethodCall('EnableUnitFiles', 'asbb', [ [ "#{serviceName}.service" ], false, false ])
|
||||||
|
|
||||||
|
exports.disableService = (serviceName) ->
|
||||||
|
systemdManagerMethodCall('DisableUnitFiles', 'asb', [ [ "#{serviceName}.service" ], false ])
|
||||||
|
|
||||||
|
exports.reboot = Promise.method ->
|
||||||
|
setTimeout( ->
|
||||||
|
systemdManagerMethodCall('Reboot')
|
||||||
|
, 1000)
|
||||||
|
|
||||||
|
exports.shutdown = Promise.method ->
|
||||||
|
setTimeout( ->
|
||||||
|
systemdManagerMethodCall('PowerOff')
|
||||||
|
, 1000)
|
||||||
|
|
||||||
|
getUnitProperty = (unitName, property) ->
|
||||||
|
systemdManagerMethodCall('GetUnit', 's', [ unitName ])
|
||||||
|
.then (unitPath) ->
|
||||||
|
bus.invokeAsync({
|
||||||
|
path: unitPath
|
||||||
|
destination: 'org.freedesktop.systemd1'
|
||||||
|
interface: 'org.freedesktop.DBus.Properties'
|
||||||
|
member: 'Get'
|
||||||
|
signature: 'ss'
|
||||||
|
body: [ 'org.freedesktop.systemd1.Unit', property ]
|
||||||
|
})
|
||||||
|
.get(1).get(0)
|
||||||
|
|
||||||
|
exports.serviceActiveState = (serviceName) ->
|
||||||
|
getUnitProperty("#{serviceName}.service", 'ActiveState')
|
Loading…
x
Reference in New Issue
Block a user