8000 add balena-os-in-container worker by rcooke-warwick · Pull Request #519 · balena-os/leviathan · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

add balena-os-in-container worker #519

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

Open
wants to merge 1 commit 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
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ ifndef DRY
@${COMPOSE} up $(SERVICES)
endif

container: Dockerfile
@ln -sf ./compose/balena-os-in-container.yml ./docker-compose.yml
ifndef DRY
@${COMPOSE} build $(SERVICES)
@${COMPOSE} up $(SERVICES)
endif

balena:
@ln -sf ./compose/balena.yml ./docker-compose.yml
ifndef DRY
Expand Down
29 changes: 29 additions & 0 deletions compose/balena-os-in-container.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
version: '2'
volumes:
core-storage:
reports-storage:
services:
core:
privileged: true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason the core service is being run privileged? This isn't required for the QEMU worker.

build: ./core
network_mode: 'host'
volumes:
- 'core-storage:/data'
- 'reports-storage:/reports'
labels:
share: 'core-storage'
worker:
privileged: true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto on the worker service

build:
context: ./worker
args:
- SKIP_INSTALL_BINARY=true
- INSTALL_DOCKER=true
pid: 'host'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it necessary to disable pid namespacing?

environment:
- WORKER_TYPE=container
network_mode: 'host'
ipc: 'host'
volumes:
- 'core-storage:/data'
- 'reports-storage:/reports'
5 changes: 5 additions & 0 deletions worker/Dockerfile.template
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ RUN install_packages libdbus-1-dev libvirt-dev python

ARG SKIP_INSTALL_BINARY


COPY package*.json ./
RUN npm ci

Expand All @@ -30,6 +31,10 @@ RUN install_packages \
gstreamer1.0-tools gstreamer1.0-plugins-base gstreamer1.0-plugins-bad gstreamer1.0-plugins-good \
qemu-system-x86 libvirt-daemon-system bridge-utils iproute2 dnsmasq iptables ebtables

ARG INSTALL_DOCKER

RUN if [ $INSTALL_DOCKER ]; then install_packages docker.io docker-compose git; fi;

# Disable all cgroup controls as we do not use them
RUN sed -i 's/^#cgroup_controllers =.*/cgroup_controllers = []/g' /etc/libvirt/qemu.conf

Expand Down
4 changes: 3 additions & 1 deletion worker/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ import {
resolveLocalTarget,
} from './helpers';
import { TestBotWorker } from './workers/testbot';
import Container from './workers/container';

const workersDict: Dictionary<typeof TestBotWorker> = {
const workersDict: Dictionary<typeof TestBotWorker | typeof Container> = {
testbot_hat: TestBotWorker,
container: Container
};

async function setup(): Promise<express.Application> {
Expand Down
136 changes: 136 additions & 0 deletions worker/lib/workers/container.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import * as Bluebird from 'bluebird';
import * as retry from 'bluebird-retry';
import { exec, ChildProcess, spawn } from 'child_process';
import * as sdk from 'etcher-sdk';
import { EventEmitter } from 'events';
import * as libvirt from 'libvirt';
import { assignIn } from 'lodash';
import { fs } from 'mz';
import { dirname, join } from 'path';
import * as Stream from 'stream';
import * as xml from 'xml-js';
import { manageHandlers } from '../helpers';
import ScreenCapture from '../helpers/graphics';
import { promisify } from 'util';
import { ensureFile } from 'fs-extra';
const execP = promisify(exec)

Bluebird.config({
cancellation: true,
});

class Container extends EventEmitter implements Leviathan.Worker {
private image: string;
private config: string
private osProc?: ChildProcess;
private dockerProc?: ChildProcess;

private signalHandler: (signal: NodeJS.Signals) => Promise<void>;
private internalState: Leviathan.WorkerState = { network: {} };

constructor(options: Leviathan.Options) {
super();
this.image = `/data/os.img`;
this.config = `/data/config.json`;
this.signalHandler = this.teardown.bind(this);
}

public get state() {
return this.internalState;
}

private static generateId(): string {
return Math.random()
.toString(36)
.substring(2, 10);
}

public async setup(): Promise<void> {
const dockerJson = `{"storage-driver": "overlay2","graph": "/data","dns": ["8.8.8.8"],"iptables": false}`
await ensureFile(`/etc/docker/daemon.json`)
await fs.writeFileSync(`/etc/docker/daemon.json`, dockerJson)

try{
await execP(`rm -rf /var/run/docker.pid`)
} catch {
console.log(`No previous docker pid`)
}
this.dockerProc = exec('dockerd &')

// pulling from my own branch at the moment - maybe make a PR on that repo to allow for custom builds
try {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor tab inconsistency here

await execP(`git clone -b ryan/build-from-saved-img https://github.com/balena-os/balenaos-in-container.git ${__dirname}/container`)
} catch(e){
console.log(e)
}

manageHandlers(this.signalHandler, {
register: true,
});
}

public async teardown(signal?: NodeJS.Signals): Promise<void> {
if (signal != null) {
if (signal === 'SIGTERM' || signal === 'SIGINT') {
if (this.osProc != null) {
Comment on lines +73 to +75
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be squashed to a single conditional

this.osProc.kill();
this.osProc = undefined;
}
}

process.kill(process.pid, signal);
}

if (this.osProc != null) {
this.osProc.kill();
this.osProc = undefined;
}

try{
await execP(`${__dirname}/container docker-compose down -v`);
}catch{
console.log(`no balenaos containers running - no removal required`)
}

manageHandlers(this.signalHandler, {
register: false,
});
}

public async flash(stream: Stream.Readable): Promise<void> {
// we need to get the config.json...
// For now, just look at a volume - but we want a better way of getting this here imo
await fs.copyFileSync(this.config, `${__dirname}/container/config.json`);

// do a docker load - need to get sha from it and inject into the dockerfile via build arg
const { stdout, stderr } = await execP(`docker load < ${this.image}`);
let sha = stdout.replace('Loaded image ID: ', '')
let shaReplace = sha.replace(/(\r\n|\n|\r)/gm, "") // could be doing this cleaner
// no-cache arguement here because sometimes it was building an old image
let build = exec(`cd ${__dirname}/container && docker-compose build --build-arg SHA=${shaReplace} --no-cache`);
}

public async powerOn(): Promise<void> {
this.osProc = exec(`cd ${__dirname}/container && docker-compose up --force-recreate`);
}

// maybe off a powerdown option - but it won't really be useful afaik
public async powerOff(): Promise<void> {
return
}

// no networm config required - so just return
public async network(configuration: {
wired?: { nat: boolean };
}): Promise<void> {
return
}

public async captureScreen(
action: 'start' | 'stop',
): Promise<void | Stream.Readable> {
throw new Error(`balena-os in container cannot perform screen capture!`)
}
}

export default Container;
0