commit 3608dd8a3127751f11bceb7cea7ba76e82fa7a76 Author: Héctor Molinero Fernández Date: Wed Feb 20 23:10:38 2019 +0100 First commit diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..178135c --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +/dist/ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..178135c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/dist/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..0dd9f0f --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,38 @@ +image: docker:stable + +services: + - docker:dind + +stages: + - build:images + - push:images + +variables: + DOCKER_HOST: tcp://docker:2375 + DOCKER_DRIVER: overlay2 + +build:image: + stage: build:images + before_script: + - docker info + - apk add --no-cache coreutils git make + script: + - make build-image save-image + artifacts: + expire_in: 1 hour + paths: + - dist/ + +push:image: + stage: push:images + before_script: + - apk add --no-cache coreutils git make + - docker login -u "${CI_DEPLOY_USER}" -p "${CI_DEPLOY_PASSWORD}" "${CI_REGISTRY}" >/dev/null 2>&1 + script: + - make load-image push-image + only: + - /^v([0-9.]+)(-.+)?$/ + except: + - branches + dependencies: + - build:image diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3a98a1c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,48 @@ +FROM ubuntu:18.04 + +# Environment +ENV QEMU_CPU=2 +ENV QEMU_RAM=1024M +ENV QEMU_DISK_SIZE=16G +ENV QEMU_DISK_FORMAT=qcow2 +ENV QEMU_KEYBOARD=en-us +ENV QEMU_KVM=false + +# Install system packages +RUN export DEBIAN_FRONTEND=noninteractive \ + && apt-get update \ + && apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ + net-tools \ + novnc \ + qemu-kvm \ + qemu-system-x86 \ + qemu-utils \ + runit \ + unzip \ + && rm -rf /var/lib/apt/lists/* + +# Create data directories +RUN mkdir -p /var/lib/qemu/images/ /var/lib/qemu/iso/ + +# Download ReactOS ISO +ARG REACTOS_ISO_URL=https://github.com/reactos/reactos/releases/download/0.4.10-release/ReactOS-0.4.10-iso.zip +ARG REACTOS_ISO_CHECKSUM=6e7b80a8d89adf1ed73a4733227d1ecf368bb237fafc322b9fb319a737dcd061 +RUN mkdir /tmp/reactos/ \ + && curl -Lo /tmp/reactos/reactos.zip "${REACTOS_ISO_URL}" \ + && echo "${REACTOS_ISO_CHECKSUM} /tmp/reactos/reactos.zip" | sha256sum -c \ + && unzip /tmp/reactos/reactos.zip -d /tmp/reactos/ \ + && mv /tmp/reactos/*.iso /var/lib/qemu/iso/reactos.iso \ + && rm -rf /tmp/reactos/ + +# Copy services +COPY --chown=root:root scripts/service/ /etc/service/ + +# Copy scripts +COPY --chown=root:root scripts/bin/ /usr/local/bin/ + +# Expose noVNC port +EXPOSE 6080/tcp + +CMD ["/usr/local/bin/docker-foreground-cmd"] diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..7597f5c --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,22 @@ +The MIT License (MIT) +===================== + +Copyright © 2019 Héctor Molinero Fernández + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..dddd8c9 --- /dev/null +++ b/Makefile @@ -0,0 +1,112 @@ +#!/usr/bin/make -f + +SHELL := /bin/sh +.SHELLFLAGS = -eu -c + +DOCKER := $(shell command -v docker 2>/dev/null) +GIT := $(shell command -v git 2>/dev/null) + +DISTDIR := ./dist + +IMAGE_NAMESPACE := hectormolinero +IMAGE_NAME := qemu-reactos +IMAGE_VERSION := v0 + +# If git is available and the directory is a repository, use the latest tag as IMAGE_VERSION. +ifeq ([$(notdir $(GIT))][$(wildcard .git/.)],[git][.git/.]) + IMAGE_VERSION := $(shell '$(GIT)' describe --abbrev=0 --tags 2>/dev/null || printf '%s' '$(IMAGE_VERSION)') +endif + +IMAGE_LATEST_TAG := $(IMAGE_NAMESPACE)/$(IMAGE_NAME):latest +IMAGE_VERSION_TAG := $(IMAGE_NAMESPACE)/$(IMAGE_NAME):$(IMAGE_VERSION) + +IMAGE_DOCKERFILE := ./Dockerfile +IMAGE_TARBALL := $(DISTDIR)/$(IMAGE_NAME).tgz + +################################################## +## "all" target +################################################## + +.PHONY: all +all: save-image + +################################################## +## "build-*" targets +################################################## + +.PHONY: build-image +build-image: + '$(DOCKER)' build \ + --tag '$(IMAGE_LATEST_TAG)' \ + --tag '$(IMAGE_VERSION_TAG)' \ + --file '$(IMAGE_DOCKERFILE)' ./ + +################################################## +## "save-*" targets +################################################## + +define save_image + '$(DOCKER)' save '$(1)' | gzip -n > '$(2)' +endef + +.PHONY: save-image +save-image: $(IMAGE_TARBALL) + +$(IMAGE_TARBALL): build-image + mkdir -p '$(DISTDIR)' + $(call save_image,$(IMAGE_VERSION_TAG),$@) + +################################################## +## "load-*" targets +################################################## + +define load_image + '$(DOCKER)' load -i '$(1)' +endef + +define tag_image + '$(DOCKER)' tag '$(1)' '$(2)' +endef + +.PHONY: load-image +load-image: + $(call load_image,$(IMAGE_TARBALL)) + $(call tag_image,$(IMAGE_VERSION_TAG),$(IMAGE_LATEST_TAG)) + +################################################## +## "push-*" targets +################################################## + +define push_image + '$(DOCKER)' push '$(1)' +endef + +.PHONY: push-image +push-image: + $(call push_image,$(IMAGE_VERSION_TAG)) + $(call push_image,$(IMAGE_LATEST_TAG)) + +################################################## +## "version" target +################################################## + +.PHONY: version +version: + @if printf -- '%s' '$(IMAGE_VERSION)' | grep -q '^v[0-9]\{1,\}$$'; then \ + NEW_IMAGE_VERSION=$$(awk -v 'v=$(IMAGE_VERSION)' 'BEGIN {printf "v%.0f", substr(v,2)+1}'); \ + printf -- '%s\n' "$${NEW_IMAGE_VERSION}" > ./VERSION; \ + '$(GIT)' add ./VERSION; '$(GIT)' commit -m "$${NEW_IMAGE_VERSION}"; \ + '$(GIT)' tag -a "$${NEW_IMAGE_VERSION}" -m "$${NEW_IMAGE_VERSION}"; \ + else \ + >&2 printf -- 'Malformed version string: %s\n' '$(IMAGE_VERSION)'; \ + exit 1; \ + fi + +################################################## +## "clean" target +################################################## + +.PHONY: clean +clean: + rm -f '$(IMAGE_TARBALL)' + if [ -d '$(DISTDIR)' ]; then rmdir '$(DISTDIR)'; fi diff --git a/README.md b/README.md new file mode 100644 index 0000000..6acfeeb --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# ReactOS on Docker + +A Docker image for the [ReactOS](https://www.reactos.org) operating system. + +## Start an instance +```sh +docker run --detach \ + --name qemu-reactos \ + --restart on-failure:3 \ + --publish 127.0.0.1:6080:6080/tcp \ + --privileged --env QEMU_KVM=true \ + hectormolinero/qemu-reactos:latest +``` +> The instance will be available through a web browser from: http://localhost:6080/vnc_auto.html + +## Environment variables +#### `QEMU_CPU` +Number of cores the VM is permitted to use (`2` by default). + +#### `QEMU_RAM` +Amount of memory the VM is permitted to use (`1024M` by default). + +#### `QEMU_DISK_SIZE` +VM disk size (`16G` by default). + +#### `QEMU_DISK_FORMAT` +VM disk format (`qcow2` by default). + +#### `QEMU_KEYBOARD` +VM keyboard layout (`en-us` by default). + +#### `QEMU_KVM` +Start QEMU in KVM mode (`false` by default). +> The `--privileged` option is required to use KVM in the container. + +## License +See the [license](LICENSE.md) file. diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..eeea123 --- /dev/null +++ b/run.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +set -eu +export LC_ALL=C + +DOCKER_IMAGE_NAMESPACE=hectormolinero +DOCKER_IMAGE_NAME=qemu-reactos +DOCKER_IMAGE_VERSION=latest +DOCKER_IMAGE=${DOCKER_IMAGE_NAMESPACE}/${DOCKER_IMAGE_NAME}:${DOCKER_IMAGE_VERSION} +DOCKER_CONTAINER=${DOCKER_IMAGE_NAME} + +imageExists() { [ -n "$(docker images -q "$1")" ]; } +containerExists() { docker ps -aqf name="$1" --format '{{.Names}}' | grep -Fxq "$1"; } +containerIsRunning() { docker ps -qf name="$1" --format '{{.Names}}' | grep -Fxq "$1"; } + +if ! imageExists "${DOCKER_IMAGE}"; then + >&2 printf -- '%s\n' "${DOCKER_IMAGE} image doesn't exist!" + exit 1 +fi + +if containerIsRunning "${DOCKER_CONTAINER}"; then + printf -- '%s\n' "Stopping \"${DOCKER_CONTAINER}\" container..." + docker stop "${DOCKER_CONTAINER}" >/dev/null +fi + +if containerExists "${DOCKER_CONTAINER}"; then + printf -- '%s\n' "Removing \"${DOCKER_CONTAINER}\" container..." + docker rm "${DOCKER_CONTAINER}" >/dev/null +fi + +printf -- '%s\n' "Creating \"${DOCKER_CONTAINER}\" container..." +docker run --detach \ + --name "${DOCKER_CONTAINER}" \ + --hostname "${DOCKER_CONTAINER}" \ + --restart on-failure:3 \ + --log-opt max-size=32m \ + --publish '127.0.0.1:5900:5900/tcp' \ + --publish '127.0.0.1:6080:6080/tcp' \ + --publish '127.0.0.1:15900:5900/tcp' \ + --privileged --env QEMU_KVM=true \ + "${DOCKER_IMAGE}" "$@" >/dev/null + +printf -- '%s\n\n' 'Done!' +exec docker logs -f "${DOCKER_CONTAINER}" diff --git a/scripts/bin/docker-foreground-cmd b/scripts/bin/docker-foreground-cmd new file mode 100755 index 0000000..0367bdb --- /dev/null +++ b/scripts/bin/docker-foreground-cmd @@ -0,0 +1,9 @@ +#!/bin/sh + +set -eu + +# Create ReactOS disk +qemu-img create -f "${QEMU_DISK_FORMAT}" /var/lib/qemu/images/reactos.img "${QEMU_DISK_SIZE}" + +# Start all services +exec runsvdir -P /etc/service/ diff --git a/scripts/service/novnc/run b/scripts/service/novnc/run new file mode 100755 index 0000000..444abde --- /dev/null +++ b/scripts/service/novnc/run @@ -0,0 +1,7 @@ +#!/bin/sh + +set -eu + +cd ~/ +exec 2>&1 +exec /usr/share/novnc/utils/launch.sh --listen 6080 --vnc localhost:5900 diff --git a/scripts/service/qemu/run b/scripts/service/qemu/run new file mode 100755 index 0000000..09f8acb --- /dev/null +++ b/scripts/service/qemu/run @@ -0,0 +1,18 @@ +#!/bin/sh + +set -eu + +QEMU=$(command -v qemu-system-x86_64) +QEMU_ARGS="${QEMU_ARGS-} -smp ${QEMU_CPU} -m ${QEMU_RAM}" +QEMU_ARGS="${QEMU_ARGS-} -vga std -display vnc=:0" +QEMU_ARGS="${QEMU_ARGS-} -drive file=/var/lib/qemu/images/reactos.img,index=0,media=disk,format=${QEMU_DISK_FORMAT}" +QEMU_ARGS="${QEMU_ARGS-} -drive file=/var/lib/qemu/iso/reactos.iso,index=2,media=cdrom,format=raw" +QEMU_ARGS="${QEMU_ARGS-} -boot order=cd,menu=on,splash-time=5000" +QEMU_ARGS="${QEMU_ARGS-} -netdev user,id=n0,hostfwd=tcp::13389-:3389,hostfwd=tcp::15900-:5900 -device rtl8139,netdev=n0" +QEMU_ARGS="${QEMU_ARGS-} -usb -device usb-tablet" +QEMU_ARGS="${QEMU_ARGS-} -k ${QEMU_KEYBOARD}" +if [ "${QEMU_KVM}" = true ]; then QEMU_ARGS="${QEMU_ARGS-} -enable-kvm"; fi + +cd ~/ +exec 2>&1 +exec "${QEMU}" ${QEMU_ARGS}