diff --git a/localinstall/.gitignore b/localinstall/.gitignore new file mode 100644 index 00000000..56112eac --- /dev/null +++ b/localinstall/.gitignore @@ -0,0 +1,7 @@ +kernelci +config/out/* +.sudo_as_admin_successful +.cache +.local +.docker +.bash_history \ No newline at end of file diff --git a/localinstall/2-install_api.sh b/localinstall/2-install_api.sh deleted file mode 100755 index cb641c54..00000000 --- a/localinstall/2-install_api.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/bin/sh -. ./main.cfg - -# is docker-compose exists? if not use docker compose -if [ -z "$(which docker-compose)" ]; then - echo "docker-compose is not installed, using docker compose" - DOCKER_COMPOSE="docker compose" -else - DOCKER_COMPOSE="docker-compose" -fi - -# i am groot? -if [ $(id -u) -ne 0 ]; then - SUDO=sudo -else - SUDO= -fi - -cp .env-api kernelci/kernelci-api/.env -cp api-configs.yaml kernelci/kernelci-core/config/core/ -cp kernelci-cli.toml kernelci/kernelci-core/kernelci.toml - -cd kernelci/kernelci-api -mkdir -p docker/redis/data -${SUDO} chmod -R 0777 docker/storage/data -${SUDO} chmod -R 0777 docker/redis/data -# enable ssh and storage nginx -sed -i 's/^# / /' docker-compose.yaml -if [ -f ../../ssh.key ]; then - echo "ssh.key already exists" -else - # generate non-interactively ssh key to ssh.key - ssh-keygen -t rsa -b 4096 -N "" -f ../../ssh.key -fi -# get public key and add to docker/ssh/user-data/authorized_keys -cat ../../ssh.key.pub > docker/ssh/user-data/authorized_keys - -# down, just in case old containers are running -${DOCKER_COMPOSE} down -${DOCKER_COMPOSE} up -d -echo "Waiting for API to be up" -sleep 1 -# loop until the API is up, try 5 times -i=0 -while [ $i -lt 5 ]; do - ANSWER=$(curl http://localhost:8001/latest/) - # must be {"message":"KernelCI API"} - if [ "$ANSWER" != "{\"message\":\"KernelCI API\"}" ]; then - echo "API is not up" - i=$((i+1)) - sleep 5 - else - echo "API is up" - break - fi -done - -# check for expect -if [ -z "$(which expect)" ]; then - echo "expect is not installed, please install it" - exit 1 -fi - -# INFO, if you have issues with stale/old data, check for -# docker volume kernelci-api_mongodata and delete it -../../helpers/scripts_setup_admin_user.exp "${YOUR_EMAIL}" "${ADMIN_PASSWORD}" - -cd ../kernelci-core -echo "Issuing token for admin user" -../../helpers/kci_user_token_admin.exp "${ADMIN_PASSWORD}" > ../../admin-token.txt -ADMIN_TOKEN=$(cat ../../admin-token.txt | tr -d '\r\n') - -echo "[kci.secrets] -api.\"docker-host\".token = \"$ADMIN_TOKEN\" -" >> kernelci.toml - diff --git a/localinstall/Containerfile b/localinstall/Containerfile new file mode 100644 index 00000000..2fb255c9 --- /dev/null +++ b/localinstall/Containerfile @@ -0,0 +1,48 @@ +FROM kernelci/kernelci:latest + +ARG USER_ID=1000 +ARG GROUP_ID=1000 + +USER root + +# Install dependencies for Docker installation +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + sudo \ + ca-certificates \ + curl \ + gnupg \ + lsb-release + +# Add Docker's official GPG key +RUN mkdir -p /etc/apt/keyrings && \ + curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg && \ + chmod a+r /etc/apt/keyrings/docker.gpg + +# Set up Docker repository (assuming Debian-based image) +RUN echo \ + "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \ + $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null + +# Install Docker Engine, CLI, and Compose plugin +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + docker-ce \ + docker-ce-cli \ + containerd.io \ + docker-compose-plugin \ + expect + +# Make sure a user exists with the same USER_ID/GROUP_ID as the host user +# to allow access to the host docker socket +RUN groupadd -g ${GROUP_ID} kernelci || true && \ + useradd -u ${USER_ID} -g ${GROUP_ID} -m -s /bin/bash kernelci || true + +# Add the user to the sudoers +RUN usermod -aG sudo kernelci && \ + echo "kernelci ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers + +USER kernelci +WORKDIR /home/kernelci + +ENTRYPOINT ["/bin/bash", "./scripts/run.sh"] diff --git a/localinstall/README.md b/localinstall/README.md index 57cc8876..f99864d8 100644 --- a/localinstall/README.md +++ b/localinstall/README.md @@ -1,16 +1,24 @@ -# kci-easy - +# kci-deploy Get your own KernelCI instance up and running in no time. -## Getting started - -### Prerequisites +## Prerequisites +- Docker -- git -- Docker (with `compose` plugin, set up for a regular user) -- Python environment with [KernelCI core dependencies](https://github.com/kernelci/kernelci-core/blob/main/requirements.txt) installed -- expect +## Configure +Configure and setup credentials in config files located in `config` folder. -### Running +## Run +You can deploy your KernelCI deployment by simply executing: +```bash +./kci-deploy.sh deploy +``` -Change `ADMIN_PASSWORD` in the `main.cfg`, then run shell scripts from the root directory in their order. +You can stop your local deployment by executing: +```bash +./kci-deploy.sh stop +``` +and +```bash +./kci-deploy.sh start +``` +to start it again. diff --git a/localinstall/.env-api b/localinstall/config/.env-api similarity index 100% rename from localinstall/.env-api rename to localinstall/config/.env-api diff --git a/localinstall/api-configs.yaml b/localinstall/config/api-configs.yaml similarity index 100% rename from localinstall/api-configs.yaml rename to localinstall/config/api-configs.yaml diff --git a/localinstall/kernelci-cli.toml b/localinstall/config/kernelci-cli.toml similarity index 100% rename from localinstall/kernelci-cli.toml rename to localinstall/config/kernelci-cli.toml diff --git a/localinstall/main.cfg b/localinstall/config/main.cfg similarity index 100% rename from localinstall/main.cfg rename to localinstall/config/main.cfg diff --git a/localinstall/kci-deploy.py b/localinstall/kci-deploy.py deleted file mode 100644 index 0e3f62d1..00000000 --- a/localinstall/kci-deploy.py +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env python3 -# - -import os - -os.system("./1-rebuild_all.sh") -os.system("./2-install_api.sh") -os.system("./3-install_pipeline.sh") -os.system("./4-start-cycle.sh") diff --git a/localinstall/kci-deploy.sh b/localinstall/kci-deploy.sh new file mode 100755 index 00000000..aeb740dd --- /dev/null +++ b/localinstall/kci-deploy.sh @@ -0,0 +1,78 @@ +#!/bin/bash + +set -e + +IMAGE_NAME="local/kernelci-deployer:latest" +BUILD_IMAGE=false +ACTION="" +CONTAINER_ARGS=() + +function print_help() { + echo "Usage: $0 [--build] (deploy|start|stop) [args...]" + echo + echo "Options:" + echo " --build Force rebuild of the Deployer image (optional)" + echo " deploy Configure and start the kernelci deployment" + echo " start Start the already configured kernelci deployment (default if no action specified)" + echo " stop Stop the kernelci deployment" + echo " -h, --help Show this help message" + echo + echo "Arguments after 'deploy', 'start' or 'stop' are passed to the container entrypoint" + exit 0 +} + +# Parse args +while [[ $# -gt 0 ]]; do + case "$1" in + --build) + BUILD_IMAGE=true + shift + ;; + deploy|start|stop) + if [[ -n "$ACTION" ]]; then + echo "Error: Cannot use more than one command among 'deploy', 'start' or 'stop'" + exit 1 + fi + ACTION=$1 + shift + CONTAINER_ARGS=("$@") + break + ;; + -h|--help) + print_help + ;; + *) + echo "Unknown option: $1" + print_help + ;; + esac +done + +# Default +if [[ -z "$ACTION" ]]; then + ACTION="start" +fi + +USER_ID=$(id -u) +GROUP_ID=$(id -g) + +if [[ "$BUILD_IMAGE" = true || -z $(docker images -q "$IMAGE_NAME") ]]; then + echo "Building $IMAGE_NAME" + docker build \ + --build-arg USER_ID=$USER_ID \ + --build-arg GROUP_ID=$GROUP_ID \ + -f Containerfile \ + -t "$IMAGE_NAME" \ + . +fi + +echo "Running $IMAGE_NAME with action '$ACTION' and args: ${CONTAINER_ARGS[*]}" +docker run --rm \ + --name kernelci-deployer \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v "$(pwd)":"$(pwd)" \ + --workdir "$(pwd)" \ + --group-add "$(stat -c '%g' /var/run/docker.sock)" \ + --network host \ + "$IMAGE_NAME" \ + "$ACTION" "${CONTAINER_ARGS[@]}" diff --git a/localinstall/1-rebuild_all.sh b/localinstall/scripts/1-rebuild_all.sh similarity index 79% rename from localinstall/1-rebuild_all.sh rename to localinstall/scripts/1-rebuild_all.sh index 7ad89826..9978e96d 100755 --- a/localinstall/1-rebuild_all.sh +++ b/localinstall/scripts/1-rebuild_all.sh @@ -1,5 +1,8 @@ #!/bin/bash -. ./main.cfg + +. ./config/main.cfg + +set -e # i am groot? if [ $(id -u) -ne 0 ]; then @@ -8,13 +11,6 @@ else SUDO= fi -function failonerror { - if [ $? -ne 0 ]; then - echo "Failed" - exit 1 - fi -} - # if directry kernelci doesn't exist, then we dont have repos cloned if [ ! -d kernelci ]; then echo Create kernelci directory, clone repos and checkout branches @@ -86,32 +82,14 @@ core_url=$(git remote get-url origin) build_args="--build-arg pipeline_rev=$pipeline_rev --build-arg core_rev=$core_rev --build-arg api_rev=$api_rev --build-arg pipeline_url=$pipeline_url --build-arg core_url=$core_url --build-arg api_url=$api_url" px_arg='--prefix=local/staging-' args="build --verbose $px_arg $build_args" -echo Build docker images: kernelci args=$args -./kci docker $args kernelci -failonerror -echo Build docker images: k8s+kernelci -./kci docker $args k8s kernelci -failonerror echo Build docker images: api -./kci docker $args kernelci api --version="$api_rev" -failonerror +./kci docker $args kernelci api echo Build docker images: pipeline -./kci docker $args kernelci pipeline --version="$pipeline_rev" -failonerror -echo Tag docker image of api to latest -docker tag local/staging-kernelci:api-$api_rev local/staging-kernelci:api -failonerror -echo Tag docker image of pipeline to latest -docker tag local/staging-kernelci:pipeline-$pipeline_rev local/staging-kernelci:pipeline -failonerror +./kci docker $args kernelci pipeline echo Build docker images: clang-17+kselftest+kernelci for x86 ./kci docker $args clang-17 kselftest kernelci --arch x86 -failonerror echo Build docker images: gcc-12+kselftest+kernelci for x86 ./kci docker $args gcc-12 kselftest kernelci --arch x86 -failonerror echo Build docker images: gcc-12+kselftest+kernelci for arm64 ./kci docker $args gcc-12 kselftest kernelci --arch arm64 -failonerror - diff --git a/localinstall/scripts/2-prepare_api.sh b/localinstall/scripts/2-prepare_api.sh new file mode 100755 index 00000000..ee9b57af --- /dev/null +++ b/localinstall/scripts/2-prepare_api.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +. ./config/main.cfg + +set -e + +# i am groot? +if [ $(id -u) -ne 0 ]; then + SUDO=sudo +else + SUDO= +fi + +cp config/.env-api kernelci/kernelci-api/.env +cp config/api-configs.yaml kernelci/kernelci-core/config/core/ +cp config/kernelci-cli.toml kernelci/kernelci-core/kernelci.toml + +sed -i "s/#SECRET_KEY=/SECRET_KEY=${API_SECRET_KEY}/" kernelci/kernelci-api/.env + +cd kernelci/kernelci-api +mkdir -p docker/redis/data +${SUDO} chmod -R 0777 docker/storage/data +${SUDO} chmod -R 0777 docker/redis/data +# enable ssh and storage nginx +mkdir -p ../../config/out +sed -i 's/^# / /' docker-compose.yaml +if [ -f ../../config/out/ssh.key ]; then + echo "ssh.key already exists" +else + # generate non-interactively ssh key to ssh.key + ssh-keygen -t rsa -b 4096 -N "" -f ../../config/out/ssh.key +fi +# get public key and add to docker/ssh/user-data/authorized_keys +cat ../../config/out/ssh.key.pub > docker/ssh/user-data/authorized_keys diff --git a/localinstall/scripts/3-start_api.sh b/localinstall/scripts/3-start_api.sh new file mode 100755 index 00000000..2762e66c --- /dev/null +++ b/localinstall/scripts/3-start_api.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +. ./config/main.cfg + +set -e + +# i am groot? +if [ $(id -u) -ne 0 ]; then + SUDO=sudo +else + SUDO= +fi + +cd kernelci/kernelci-api + +# down, just in case old containers are running +docker compose down +docker compose up -d +echo "Waiting for API to be up" +sleep 5 +# loop until the API is up, try 5 times +i=0 +while [ $i -lt 5 ]; do + ANSWER=$(curl http://localhost:8001/latest/) + # must be {"message":"KernelCI API"} + if [ "$ANSWER" != "{\"message\":\"KernelCI API\"}" ]; then + echo "API is not up" + i=$((i+1)) + sleep 5 + else + echo "API is up" + break + fi +done + diff --git a/localinstall/scripts/4-set_api_admin.sh b/localinstall/scripts/4-set_api_admin.sh new file mode 100755 index 00000000..db86d119 --- /dev/null +++ b/localinstall/scripts/4-set_api_admin.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +. ./config/main.cfg + +set -e + +# i am groot? +if [ $(id -u) -ne 0 ]; then + SUDO=sudo +else + SUDO= +fi + +cd kernelci/kernelci-api + +# check for expect +if [ -z "$(which expect)" ]; then + echo "expect is not installed, please install it" + exit 1 +fi + +# INFO, if you have issues with stale/old data, check for +# docker volume kernelci-api_mongodata and delete it +expect ../../helpers/scripts_setup_admin_user.exp "${YOUR_EMAIL}" "${ADMIN_PASSWORD}" + +cd ../kernelci-core +echo "Issuing token for admin user" +expect ../../helpers/kci_user_token_admin.exp "${ADMIN_PASSWORD}" > ../../config/out/admin-token.txt +ADMIN_TOKEN=$(cat ../../config/out/admin-token.txt | tr -d '\r\n') + +echo "[kci.secrets] +api.\"docker-host\".token = \"$ADMIN_TOKEN\" +" >> kernelci.toml \ No newline at end of file diff --git a/localinstall/3-install_pipeline.sh b/localinstall/scripts/5-prepare_pipeline.sh similarity index 66% rename from localinstall/3-install_pipeline.sh rename to localinstall/scripts/5-prepare_pipeline.sh index f54a2e54..d7b3974d 100755 --- a/localinstall/3-install_pipeline.sh +++ b/localinstall/scripts/5-prepare_pipeline.sh @@ -1,5 +1,8 @@ #!/bin/bash -. ./main.cfg + +. ./config/main.cfg + +set -e ## This is hacky way of inserting things that probably will outlive trivial patch after changes # find line number with storage: @@ -17,9 +20,6 @@ tail -n +$((line+1)) kernelci/kernelci-pipeline/config/pipeline.yaml >> tmp.yaml mv tmp.yaml kernelci/kernelci-pipeline/config/pipeline.yaml } -# replace in pipeline.yaml http://172.17.0.1:8001 to http://localhost:8001 -sed -i 's/http:\/\/172.17.0.1:8001/http:\/\/host.docker.internal:8001/g' kernelci/kernelci-pipeline/config/pipeline.yaml - # TODO: Check if this is already done #append_storage @@ -38,25 +38,16 @@ PIPELINE_PWD=$(pwd) # This is BAD hack, but it works for now sed -i 's/lab_type: kubernetes/lab_type: docker/g' config/pipeline.yaml -# replace data/output by $PIPELINE_PWD/data/output -# might be two variants (default and staging) -# - '/data/kernelci-deploy-checkout/kernelci-pipeline/data/ssh/:/home/kernelci/data/ssh' -# - '/data/kernelci-deploy-checkout/kernelci-pipeline/data/output/:/home/kernelci/data/output' -# AND -# - 'data/ssh/:/home/kernelci/data/ssh' -# - 'data/output/:/home/kernelci/data/output' -sed -i "s|- 'data/output/|- '$PIPELINE_PWD/data/output/|g" config/pipeline.yaml -sed -i "s|- 'data/ssh/|- '$PIPELINE_PWD/data/ssh/|g" config/pipeline.yaml -# OR -sed -i "s|- '/data/kernelci-deploy-checkout/kernelci-pipeline/data/ssh/|- '$PIPELINE_PWD/data/ssh/|g" config/pipeline.yaml -sed -i "s|- '/data/kernelci-deploy-checkout/kernelci-pipeline/data/output/|- '$PIPELINE_PWD/data/output/|g" config/pipeline.yaml +# replace named data/output and data/ssh volumes with absolute path since it is needed +sed -i '/volumes:/,/data\/output/{ + s|['\''"].*data\/ssh\/|'\'''"$PIPELINE_PWD"'\/data\/ssh\/|g; + s|['\''"].*data\/output\/|'\'''"$PIPELINE_PWD"'\/data\/output\/|g; +}' config/pipeline.yaml -# set 777 to data/output and data/ssh (TODO: or set proper uid, kernelci is 1000?) -chmod -R 777 data chmod 777 data/ssh -cp ../../ssh.key data/ssh/id_rsa_tarball -chown 1000:1000 data/ssh/id_rsa_tarball +cp ../../config/out/ssh.key data/ssh/id_rsa_tarball chmod 600 data/ssh/id_rsa_tarball +chown -R $(id -u):$(id -g) data/output cd ../.. #replace kernelci/staging- by local/staging- @@ -69,8 +60,7 @@ sed -i 's/kernelci\/staging-/local\/staging-/g' kernelci/kernelci-pipeline/confi # check if kernelci/kernelci-pipeline/config/kernelci.toml # has [trigger] and then force = 1 # this will force builds on each restart -grep -q "force = 1" kernelci/kernelci-pipeline/config/kernelci.toml -if [ $? -ne 0 ]; then +if ! grep -q "force = 1" kernelci/kernelci-pipeline/config/kernelci.toml; then sed -i '/\[trigger\]/a force = 1' kernelci/kernelci-pipeline/config/kernelci.toml fi @@ -88,9 +78,19 @@ EOF #KCI_STORAGE_CREDENTIALS=L0CALT0KEN #KCI_API_TOKEN= #API_TOKEN= -API_TOKEN=$(cat admin-token.txt) +API_TOKEN=$(cat config/out/admin-token.txt) echo "KCI_STORAGE_CREDENTIALS=/home/kernelci/data/ssh/id_rsa_tarball" > .env echo "KCI_API_TOKEN=${API_TOKEN}" >> .env echo "API_TOKEN=${API_TOKEN}" >> .env cp .env kernelci/kernelci-pipeline/.docker-env mv .env kernelci/kernelci-pipeline/.env + +# Add JWT section with the secret key to kernelci.toml for pipeline callback +sed -i 's/#\[jwt\]$/[jwt]/' kernelci/kernelci-pipeline/config/kernelci.toml +sed -i 's/#secret = "SomeSecretString"/secret = "'"${PIPELINE_SECRET_KEY}"'"/' kernelci/kernelci-pipeline/config/kernelci.toml +# Generate kci-dev token +pip install pyjwt +TOKEN=$(kernelci/kernelci-pipeline/tools/jwt_generator.py --toml kernelci/kernelci-pipeline/config/kernelci.toml \ +--email ${YOUR_EMAIL} --permissions checkout,testretry,patchset | grep "JWT token:" | cut -d' ' -f3) +echo $TOKEN > config/out/kci-dev-token.txt +echo "kci-dev token saved to config/out/kci-dev-token.txt" diff --git a/localinstall/4-start-cycle.sh b/localinstall/scripts/6-start_pipeline.sh similarity index 96% rename from localinstall/4-start-cycle.sh rename to localinstall/scripts/6-start_pipeline.sh index caca6d46..ad527016 100755 --- a/localinstall/4-start-cycle.sh +++ b/localinstall/scripts/6-start_pipeline.sh @@ -1,5 +1,4 @@ #!/bin/bash -. ./main.cfg # is docker-compose exists? if not use docker compose if [ -z "$(which docker-compose)" ]; then @@ -9,6 +8,10 @@ else DOCKER_COMPOSE="docker-compose" fi +. ./config/main.cfg + +set -e + cd kernelci/kernelci-pipeline ${DOCKER_COMPOSE} down ${DOCKER_COMPOSE} up -d diff --git a/localinstall/scripts/run.sh b/localinstall/scripts/run.sh new file mode 100755 index 00000000..7b1cffc0 --- /dev/null +++ b/localinstall/scripts/run.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +set -e + +ACTION=$1 + +function print_help() { + echo "Usage: $0 (deploy|start|stop)" + echo + echo " deploy Configure and start deployment" + echo " start Start deployment" + echo " stop Stop deployment" + exit 1 +} + +function check_deploy() { + if [ ! -f kernelci/.done ]; then + echo "Error: Deployment not completed. Please run 'deploy' first." + exit 1 + fi +} + +if [[ -z "$ACTION" ]]; then + echo "Error: Missing required action (deploy, start or stop)" + print_help +fi + +case "$ACTION" in + deploy) + # Check if kernelci directory exists + if [ -f kernelci/.done ]; then + echo "Stopping previous deployment..." + $0 stop + fi + sudo rm -rf kernelci + echo "Starting deployment sequence, this may take a while..." + ./scripts/1-rebuild_all.sh + ./scripts/2-prepare_api.sh + ./scripts/3-start_api.sh + ./scripts/4-set_api_admin.sh + ./scripts/5-prepare_pipeline.sh + ./scripts/6-start_pipeline.sh + touch kernelci/.done + ;; + start) + check_deploy + echo "Starting deployment" + ./scripts/3-start_api.sh + ./scripts/6-start_pipeline.sh + ;; + stop) + check_deploy + echo "Stopping deployment" + cd kernelci/kernelci-api + docker compose down + cd ../kernelci-pipeline + docker compose down + ;; + *) + echo "Error: Invalid action '$ACTION'" + print_help + ;; +esac