From 98c34d748f458b358d28bd5d0df1579bc806d6d9 Mon Sep 17 00:00:00 2001 From: OpenNebula Community Contributor Date: Thu, 2 Oct 2025 09:07:37 +0000 Subject: [PATCH 1/9] Add n8n workflow automation appliance - Add new Image appliance for n8n workflow automation platform - Support for visual workflow editor connecting services and APIs - Include Docker-based deployment with automatic container startup - Features Ubuntu 22.04 LTS, Docker Engine CE, n8n latest container - Add contextualization parameters for container configuration - Include png logo (256x256) and complete appliance metadata - Web interface accessible on port 5678 - Configurable via OpenNebula context: ports, environment variables, volumes - Data persistence with volume mapping to /data:/home/node/.n8n - Includes N8N_SECURE_COOKIE=false for IP-based access --- appliances/n8n/CHANGELOG.md | 13 + appliances/n8n/README.md | 82 +++++++ appliances/n8n/appliance.sh | 232 ++++++++++++++++++ .../cf81cb32-dde4-4d47-a09c-fc3b4f058f45.yaml | 66 +++++ appliances/n8n/context.yaml | 5 + appliances/n8n/metadata.yaml | 31 +++ appliances/n8n/tests.yaml | 2 + appliances/n8n/tests/00-n8n_basic.rb | 21 ++ apps-code/community-apps/Makefile.config | 2 +- .../packer/n8n/81-configure-ssh.sh | 29 +++ .../packer/n8n/82-configure-context.sh | 14 ++ .../community-apps/packer/n8n/common.pkr.hcl | 1 + .../community-apps/packer/n8n/gen_context | 33 +++ .../community-apps/packer/n8n/n8n.pkr.hcl | 109 ++++++++ .../community-apps/packer/n8n/postprocess.sh | 12 + .../packer/n8n/variables.pkr.hcl | 20 ++ logos/n8n.png | Bin 0 -> 18210 bytes 17 files changed, 671 insertions(+), 1 deletion(-) create mode 100644 appliances/n8n/CHANGELOG.md create mode 100644 appliances/n8n/README.md create mode 100755 appliances/n8n/appliance.sh create mode 100644 appliances/n8n/cf81cb32-dde4-4d47-a09c-fc3b4f058f45.yaml create mode 100644 appliances/n8n/context.yaml create mode 100644 appliances/n8n/metadata.yaml create mode 100644 appliances/n8n/tests.yaml create mode 100644 appliances/n8n/tests/00-n8n_basic.rb create mode 100755 apps-code/community-apps/packer/n8n/81-configure-ssh.sh create mode 100755 apps-code/community-apps/packer/n8n/82-configure-context.sh create mode 120000 apps-code/community-apps/packer/n8n/common.pkr.hcl create mode 100755 apps-code/community-apps/packer/n8n/gen_context create mode 100644 apps-code/community-apps/packer/n8n/n8n.pkr.hcl create mode 100755 apps-code/community-apps/packer/n8n/postprocess.sh create mode 100644 apps-code/community-apps/packer/n8n/variables.pkr.hcl create mode 100644 logos/n8n.png diff --git a/appliances/n8n/CHANGELOG.md b/appliances/n8n/CHANGELOG.md new file mode 100644 index 00000000..e5f6cab2 --- /dev/null +++ b/appliances/n8n/CHANGELOG.md @@ -0,0 +1,13 @@ +# Changelog + +All notable changes to the n8n appliance will be documented in this file. + +## [1.0.0-1] - 2025-10-01 + +### Added +- Initial release of n8n appliance +- Docker container: n8nio/n8n:latest +- VNC desktop access +- SSH key authentication +- OpenNebula context integration +- Configurable container parameters diff --git a/appliances/n8n/README.md b/appliances/n8n/README.md new file mode 100644 index 00000000..31d430d4 --- /dev/null +++ b/appliances/n8n/README.md @@ -0,0 +1,82 @@ +# n8n Appliance + +n8n is a workflow automation tool that allows you to connect different services and automate tasks. This appliance provides n8n running in a Docker container on Ubuntu 22.04 LTS with VNC access and SSH key authentication. + +## Key Features + +**n8n capabilities:** + - Workflow automation + - Visual workflow editor + - Self-hosted + - Extensible with custom nodes + - REST API + - Webhook support +**This appliance provides:** +- Ubuntu 22.04 LTS base operating system +- Docker Engine CE pre-installed and configured +- n8n container (n8nio/n8n:latest) ready to run +- VNC access for desktop environment +- SSH key authentication from OpenNebula context +- Configurable container parameters (ports, volumes, environment variables) - Web interface on port 5678 + +## Quick Start + +1. **Deploy the appliance** from OpenNebula marketplace +2. **Configure container settings** during VM instantiation: + - Container name: n8n-container + - Port mappings: 5678:5678 + - Environment variables: N8N_HOST=0.0.0.0,N8N_PORT=5678,N8N_PROTOCOL=http,N8N_SECURE_COOKIE=false + - Volume mounts: /data:/home/node/.n8n +3. **Access the VM**: + - VNC: Direct desktop access + - SSH: `ssh root@VM_IP` (using OpenNebula context keys) - Web: n8n interface at http://VM_IP:5678 + +## Container Configuration + +### Port Mappings +Format: `host_port:container_port,host_port2:container_port2` +Default: `5678:5678` + +### Environment Variables +Format: `VAR1=value1,VAR2=value2` +Default: `N8N_HOST=0.0.0.0,N8N_PORT=5678,N8N_PROTOCOL=http,N8N_SECURE_COOKIE=false` + +### Volume Mounts +Format: `/host/path:/container/path,/host/path2:/container/path2` +Default: `/data:/home/node/.n8n` + +## Management Commands + +```bash +# View running containers +docker ps + +# View container logs +docker logs n8n-container + +# Access container shell +docker exec -it n8n-container /bin/bash + +# Restart container +docker restart n8n-container + +# Stop container +docker stop n8n-container + +# Start container +docker start n8n-container +``` + +## Technical Details + +- **Base OS**: Ubuntu 22.04 LTS +- **Container Runtime**: Docker Engine CE +- **Container Image**: n8nio/n8n:latest +- **Default Ports**: 5678:5678 +- **Default Volumes**: /data:/home/node/.n8n +- **Memory Requirements**: 2GB minimum +- **Disk Requirements**: 8GB minimum + +## Version History + +See [CHANGELOG.md](CHANGELOG.md) for detailed version history. diff --git a/appliances/n8n/appliance.sh b/appliances/n8n/appliance.sh new file mode 100755 index 00000000..c8ff7442 --- /dev/null +++ b/appliances/n8n/appliance.sh @@ -0,0 +1,232 @@ +#!/usr/bin/env bash + +# n8n Appliance Installation Script +# Auto-generated by OpenNebula Docker Appliance Generator +# Docker Image: n8nio/n8n:latest + +set -o errexit -o pipefail + +# List of contextualization parameters +ONE_SERVICE_PARAMS=( + 'ONEAPP_CONTAINER_NAME' 'configure' 'Docker container name' 'O|text' + 'ONEAPP_CONTAINER_PORTS' 'configure' 'Docker container port mappings' 'O|text' + 'ONEAPP_CONTAINER_ENV' 'configure' 'Docker container environment variables' 'O|text' + 'ONEAPP_CONTAINER_VOLUMES' 'configure' 'Docker container volume mappings' 'O|text' +) + +# Configuration from user input +DOCKER_IMAGE="n8nio/n8n:latest" +DEFAULT_CONTAINER_NAME="n8n-container" +DEFAULT_PORTS="5678:5678" +DEFAULT_ENV_VARS="N8N_HOST=0.0.0.0,N8N_PORT=5678,N8N_PROTOCOL=http,N8N_SECURE_COOKIE=false" +DEFAULT_VOLUMES="/data:/home/node/.n8n" +APP_NAME="n8n" +APPLIANCE_NAME="n8n" + +### Appliance metadata ############################################### + +ONE_SERVICE_NAME='n8n' +ONE_SERVICE_VERSION= #latest +ONE_SERVICE_BUILD=$(date +%s) +ONE_SERVICE_SHORT_DESCRIPTION='n8n Docker Container Appliance' +ONE_SERVICE_DESCRIPTION='n8n running in Docker container' +ONE_SERVICE_RECONFIGURABLE=true + +### Appliance functions ############################################## + +service_cleanup() +{ + : +} + +service_install() +{ + export DEBIAN_FRONTEND=noninteractive + + # Update system + apt-get update + apt-get upgrade -y + + # Install Docker + apt-get install -y ca-certificates curl + install -m 0755 -d /etc/apt/keyrings + curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc + chmod a+r /etc/apt/keyrings/docker.asc + + echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null + + apt-get update + apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin + + # Enable and start Docker + systemctl enable docker + systemctl start docker + + # Pull the Docker image + msg info "Pulling Docker image: $DOCKER_IMAGE" + docker pull "$DOCKER_IMAGE" + + # Configure console auto-login + systemctl stop unattended-upgrades 2>/dev/null || true + systemctl disable unattended-upgrades 2>/dev/null || true + + apt-get install -y mingetty + + # Configure auto-login on console + mkdir -p /etc/systemd/system/getty@tty1.service.d + cat > /etc/systemd/system/getty@tty1.service.d/override.conf << 'CONSOLE_EOF' +[Service] +ExecStart= +ExecStart=-/sbin/agetty --noissue --autologin root %I \\$TERM +Type=idle +CONSOLE_EOF + + # Configure serial console and set root password + mkdir -p /etc/systemd/system/serial-getty@ttyS0.service.d + cat > /etc/systemd/system/serial-getty@ttyS0.service.d/override.conf << 'SERIAL_EOF' +[Service] +ExecStart= +ExecStart=-/sbin/agetty --noissue --autologin root %I 115200,38400,9600 vt102 +Type=idle +SERIAL_EOF + + echo 'root:opennebula' | chpasswd + systemctl enable getty@tty1.service serial-getty@ttyS0.service + + # Create welcome message + cat > /etc/profile.d/99-$APPLIANCE_NAME-welcome.sh << 'WELCOME_EOF' +#!/bin/bash +case \\$- in + *i*) ;; + *) return;; +esac + +echo "==================================================" +echo " $APP_NAME Appliance" +echo "==================================================" +echo " Docker Image: $DOCKER_IMAGE" +echo " Container: $DEFAULT_CONTAINER_NAME" +echo " Ports: $DEFAULT_PORTS" +echo "" +echo " Commands:" +echo " docker ps - Show running containers" +echo " docker logs $DEFAULT_CONTAINER_NAME - View container logs" +echo " docker exec -it $DEFAULT_CONTAINER_NAME /bin/bash - Access container" +echo "" +EOF + +if [ "$WEB_INTERFACE" = "true" ]; then + cat >> "$REPO_ROOT/appliances/$APPLIANCE_NAME/appliance.sh" << EOF +echo " Web Interface: http://VM_IP:$APP_PORT" +echo "" +EOF +fi + +cat >> "$REPO_ROOT/appliances/$APPLIANCE_NAME/appliance.sh" << 'EOF' +echo " Access Methods:" +echo " SSH: Enabled (password: 'opennebula' + context keys)" +echo " Console: Auto-login as root (via OpenNebula console)" +echo " Serial: Auto-login as root (via serial console)" +echo "==================================================" +WELCOME_EOF + + chmod +x /etc/profile.d/99-$APPLIANCE_NAME-welcome.sh + + # Clean up + apt-get autoremove -y + apt-get autoclean + find /var/log -type f -exec truncate -s 0 {} \; + + sync + + return 0 +} + +service_configure() +{ + msg info "Verifying Docker is running" + + if ! systemctl is-active --quiet docker; then + msg error "Docker is not running" + return 1 + fi + + msg info "Docker is running" + return 0 +} + +service_bootstrap() +{ + msg info "Starting $APP_NAME service bootstrap" + + # Setup and start the container + setup_app_container + + return $? +} + +# Setup container function +setup_app_container() +{ + local container_name="${ONEAPP_CONTAINER_NAME:-$DEFAULT_CONTAINER_NAME}" + local container_ports="${ONEAPP_CONTAINER_PORTS:-$DEFAULT_PORTS}" + local container_env="${ONEAPP_CONTAINER_ENV:-$DEFAULT_ENV_VARS}" + local container_volumes="${ONEAPP_CONTAINER_VOLUMES:-$DEFAULT_VOLUMES}" + + msg info "Setting up $APP_NAME container: $container_name" + + # Stop and remove existing container if it exists + if docker ps -a --format '{{.Names}}' | grep -q "^${container_name}$"; then + msg info "Stopping existing container: $container_name" + docker stop "$container_name" 2>/dev/null || true + docker rm "$container_name" 2>/dev/null || true + fi + + # Parse port mappings + local port_args="" + if [ -n "$container_ports" ]; then + IFS=',' read -ra PORT_ARRAY <<< "$container_ports" + for port in "${PORT_ARRAY[@]}"; do + port_args="$port_args -p $port" + done + fi + + # Parse environment variables + local env_args="" + if [ -n "$container_env" ]; then + IFS=',' read -ra ENV_ARRAY <<< "$container_env" + for env in "${ENV_ARRAY[@]}"; do + env_args="$env_args -e $env" + done + fi + + # Parse volume mounts + local volume_args="" + if [ -n "$container_volumes" ]; then + IFS=',' read -ra VOL_ARRAY <<< "$container_volumes" + for vol in "${VOL_ARRAY[@]}"; do + local host_path=$(echo "$vol" | cut -d':' -f1) + mkdir -p "$host_path" + # Set ownership to 1000:1000 (common for Docker containers) + chown -R 1000:1000 "$host_path" 2>/dev/null || true + volume_args="$volume_args -v $vol" + done + fi + + # Start the container + msg info "Starting $APP_NAME container with:" + msg info " Ports: $container_ports" + msg info " Environment: ${container_env:-none}" + msg info " Volumes: $container_volumes" + + docker run -d --name "$container_name" --restart unless-stopped $port_args $env_args $volume_args "$DOCKER_IMAGE" + + if [ $? -eq 0 ]; then + msg info "$APP_NAME container started successfully" + docker ps --filter name="$container_name" + return 0 + else + msg error "Failed to start $APP_NAME container" + return 1 + fi +} diff --git a/appliances/n8n/cf81cb32-dde4-4d47-a09c-fc3b4f058f45.yaml b/appliances/n8n/cf81cb32-dde4-4d47-a09c-fc3b4f058f45.yaml new file mode 100644 index 00000000..b01b7b5a --- /dev/null +++ b/appliances/n8n/cf81cb32-dde4-4d47-a09c-fc3b4f058f45.yaml @@ -0,0 +1,66 @@ +--- +name: n8n +version: 1.0.0-1 +one-apps_version: 7.0.0-0 +publisher: Pablo de la Rco +publisher_email: pablo@example.com +description: |- + n8n is a workflow automation tool that allows you to connect different services and automate tasks. This appliance provides n8n + running in a Docker container on Ubuntu 22.04 LTS with VNC access and + SSH key authentication. + + **n8n features:** + - Workflow automation + - Visual workflow editor + - Self-hosted + - Extensible with custom nodes + - REST API + - Webhook support + **This appliance provides:** + - Ubuntu 22.04 LTS base operating system + - Docker Engine CE pre-installed and configured + - n8n container (n8nio/n8n:latest) ready to run + - VNC access for desktop environment + - SSH key authentication from OpenNebula context - Web interface on port 5678 + - Configurable container parameters (ports, volumes, environment variables) + + **Access Methods:** + - VNC: Direct access to desktop environment + - SSH: Key-based authentication from OpenNebula - Web: n8n interface at http://VM_IP:5678 + +short_description: n8n with VNC access and SSH key auth +tags: +- n8n +- docker +- ubuntu +- container +- vnc +- ssh-key +format: qcow2 +creation_time: 1759334415 +os-id: Ubuntu +os-release: '22.04' +os-arch: x86_64 +hypervisor: KVM +opennebula_version: 7.0 +opennebula_template: + context: + network: 'YES' + ssh_public_key: $USER[SSH_PUBLIC_KEY] + set_hostname: $USER[SET_HOSTNAME] + cpu: '2' + disk: + image: $FILE[IMAGE_ID] + image_uname: $USER[IMAGE_UNAME] + graphics: + listen: 0.0.0.0 + type: vnc + memory: '2048' + name: n8n + user_inputs: + - CONTAINER_NAME: 'M|text|Container name|n8n-container|n8n-container' + - CONTAINER_PORTS: 'M|text|Container ports (format: host:container)|5678:5678|5678:5678' + - CONTAINER_ENV: 'O|text|Environment variables (format: VAR1=value1,VAR2=value2)|N8N_HOST=0.0.0.0,N8N_PORT=5678,N8N_PROTOCOL=http|' + - CONTAINER_VOLUMES: 'O|text|Volume mounts (format: /host/path:/container/path)|/data:/home/node/.n8n|' + inputs_order: CONTAINER_NAME,CONTAINER_PORTS,CONTAINER_ENV,CONTAINER_VOLUMES +logo: logos/n8n.png diff --git a/appliances/n8n/context.yaml b/appliances/n8n/context.yaml new file mode 100644 index 00000000..ec4465b8 --- /dev/null +++ b/appliances/n8n/context.yaml @@ -0,0 +1,5 @@ +--- +CONTAINER_NAME: n8n-container +CONTAINER_PORTS: 5678:5678 +CONTAINER_ENV: N8N_HOST=0.0.0.0,N8N_PORT=5678,N8N_PROTOCOL=http,N8N_SECURE_COOKIE=false +CONTAINER_VOLUMES: /data:/home/node/.n8n diff --git a/appliances/n8n/metadata.yaml b/appliances/n8n/metadata.yaml new file mode 100644 index 00000000..2e8cea71 --- /dev/null +++ b/appliances/n8n/metadata.yaml @@ -0,0 +1,31 @@ +--- +:app: + :name: n8n + :type: service + :os: + - Ubuntu + - '22.04' + :arch: + - x86_64 + :format: qcow2 + :hypervisor: + - KVM + :opennebula_version: + - '7.0' + :opennebula_template: + context: + - SSH_PUBLIC_KEY="$USER[SSH_PUBLIC_KEY]" + - SET_HOSTNAME="$USER[SET_HOSTNAME]" + cpu: '2' + memory: '2048' + disk_size: '8192' + graphics: + listen: 0.0.0.0 + type: vnc + inputs_order: 'CONTAINER_NAME,CONTAINER_PORTS,CONTAINER_ENV,CONTAINER_VOLUMES' + logo: logos/n8n.png + user_inputs: + CONTAINER_NAME: 'M|text|Container name|n8n-container|n8n-container' + CONTAINER_PORTS: 'M|text|Container ports (format: host:container)|5678:5678|5678:5678' + CONTAINER_ENV: 'O|text|Environment variables (format: VAR1=value1,VAR2=value2)|N8N_HOST=0.0.0.0,N8N_PORT=5678,N8N_PROTOCOL=http,N8N_SECURE_COOKIE=false|' + CONTAINER_VOLUMES: 'O|text|Volume mounts (format: /host/path:/container/path)|/data:/home/node/.n8n|' diff --git a/appliances/n8n/tests.yaml b/appliances/n8n/tests.yaml new file mode 100644 index 00000000..4e50e2f3 --- /dev/null +++ b/appliances/n8n/tests.yaml @@ -0,0 +1,2 @@ +--- +- 00-n8n\_basic.rb diff --git a/appliances/n8n/tests/00-n8n_basic.rb b/appliances/n8n/tests/00-n8n_basic.rb new file mode 100644 index 00000000..6a0cbc9c --- /dev/null +++ b/appliances/n8n/tests/00-n8n_basic.rb @@ -0,0 +1,21 @@ +# Basic test for n8n appliance + +require_relative '../../../lib/tests' + +class TestN8n < Test + def test_docker_installed + assert_cmd('docker --version') + end + + def test_docker_running + assert_cmd('systemctl is-active docker') + end + + def test_image_pulled + assert_cmd("docker images | grep 'n8nio/n8n:latest'") + end + + def test_container_running + assert_cmd("docker ps | grep 'n8n-container'") + end +end diff --git a/apps-code/community-apps/Makefile.config b/apps-code/community-apps/Makefile.config index 6995d7df..f0d71216 100644 --- a/apps-code/community-apps/Makefile.config +++ b/apps-code/community-apps/Makefile.config @@ -7,7 +7,7 @@ VERBOSE := 1 PACKER_LOG := 0 PACKER_HEADLESS := true -SERVICES := lithops lithops_worker rabbitmq ueransim example phoenixrtos srsran openfgs +SERVICES := lithops lithops_worker rabbitmq ueransim example phoenixrtos srsran openfgs n8n n8n .DEFAULT_GOAL := help diff --git a/apps-code/community-apps/packer/n8n/81-configure-ssh.sh b/apps-code/community-apps/packer/n8n/81-configure-ssh.sh new file mode 100755 index 00000000..001ffd6b --- /dev/null +++ b/apps-code/community-apps/packer/n8n/81-configure-ssh.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +# Configures critical settings for OpenSSH server. + +exec 1>&2 +set -eux -o pipefail + +gawk -i inplace -f- /etc/ssh/sshd_config <<'AWKEOF' +BEGIN { update = "PasswordAuthentication no" } +/^[#\s]*PasswordAuthentication\s/ { $0 = update; found = 1 } +{ print } +ENDFILE { if (!found) print update } +AWKEOF + +gawk -i inplace -f- /etc/ssh/sshd_config <<'AWKEOF' +BEGIN { update = "PermitRootLogin without-password" } +/^[#\s]*PermitRootLogin\s/ { $0 = update; found = 1 } +{ print } +ENDFILE { if (!found) print update } +AWKEOF + +gawk -i inplace -f- /etc/ssh/sshd_config <<'AWKEOF' +BEGIN { update = "UseDNS no" } +/^[#\s]*UseDNS\s/ { $0 = update; found = 1 } +{ print } +ENDFILE { if (!found) print update } +AWKEOF + +sync diff --git a/apps-code/community-apps/packer/n8n/82-configure-context.sh b/apps-code/community-apps/packer/n8n/82-configure-context.sh new file mode 100755 index 00000000..2278ea94 --- /dev/null +++ b/apps-code/community-apps/packer/n8n/82-configure-context.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +# Configure and enable service context. + +exec 1>&2 +set -eux -o pipefail + +mv /etc/one-appliance/net-90-service-appliance /etc/one-context.d/ +mv /etc/one-appliance/net-99-report-ready /etc/one-context.d/ + +chown root:root /etc/one-context.d/* +chmod u=rwx,go=rx /etc/one-context.d/* + +sync diff --git a/apps-code/community-apps/packer/n8n/common.pkr.hcl b/apps-code/community-apps/packer/n8n/common.pkr.hcl new file mode 120000 index 00000000..3b9ee73c --- /dev/null +++ b/apps-code/community-apps/packer/n8n/common.pkr.hcl @@ -0,0 +1 @@ +../../../one-apps/packer/common.pkr.hcl \ No newline at end of file diff --git a/apps-code/community-apps/packer/n8n/gen_context b/apps-code/community-apps/packer/n8n/gen_context new file mode 100755 index 00000000..92ea59ef --- /dev/null +++ b/apps-code/community-apps/packer/n8n/gen_context @@ -0,0 +1,33 @@ +#!/bin/bash +set -eux -o pipefail + +SCRIPT=$(cat <<'MAINEND' +gawk -i inplace -f- /etc/ssh/sshd_config <<'EOF' +BEGIN { update = "PasswordAuthentication yes" } +/^[#\s]*PasswordAuthentication\s/ { $0 = update; found = 1 } +{ print } +ENDFILE { if (!found) print update } +EOF + +gawk -i inplace -f- /etc/ssh/sshd_config <<'EOF' +BEGIN { update = "PermitRootLogin yes" } +/^[#\s]*PermitRootLogin\s/ { $0 = update; found = 1 } +{ print } +ENDFILE { if (!found) print update } +EOF + +systemctl reload sshd + +echo "nameserver 1.1.1.1" > /etc/resolv.conf +MAINEND +) + +cat< ${var.input_dir}/context/context.sh", + "mkisofs -o ${var.input_dir}/${var.appliance_name}-context.iso -V CONTEXT -J -R ${var.input_dir}/context", + ] + } +} + +# Build VM image +source "qemu" "n8n" { + cpus = 2 + memory = 2048 + accelerator = "kvm" + + iso_url = "../one-apps/export/ubuntu2204.qcow2" + iso_checksum = "none" + + headless = var.headless + + disk_image = true + disk_cache = "unsafe" + disk_interface = "virtio" + net_device = "virtio-net" + format = "qcow2" + disk_compression = false + disk_size = "8000" + + output_directory = var.output_dir + + qemuargs = [ + ["-cpu", "host"], + ["-cdrom", "${var.input_dir}/${var.appliance_name}-context.iso"], + ["-serial", "stdio"], + # MAC addr needs to match ETH0_MAC from context iso + ["-netdev", "user,id=net0,hostfwd=tcp::{{ .SSHHostPort }}-:22"], + ["-device", "virtio-net-pci,netdev=net0,mac=00:11:22:33:44:55"] + ] + + ssh_username = "root" + ssh_password = "opennebula" + ssh_timeout = "900s" + shutdown_command = "poweroff" + vm_name = var.appliance_name +} + +build { + sources = ["source.qemu.n8n"] + + # revert insecure ssh options done by context start_script + provisioner "shell" { + scripts = ["${var.input_dir}/81-configure-ssh.sh"] + } + + provisioner "shell" { + inline_shebang = "/bin/bash -e" + inline = [ + "install -o 0 -g 0 -m u=rwx,g=rx,o= -d /etc/one-appliance/{,service.d/,lib/}", + "install -o 0 -g 0 -m u=rwx,g=rx,o=rx -d /opt/one-appliance/{,bin/}", + ] + } + + provisioner "file" { + sources = [ + "../one-apps/appliances/scripts/net-90-service-appliance", + "../one-apps/appliances/scripts/net-99-report-ready", + ] + destination = "/etc/one-appliance/" + } + provisioner "file" { + sources = [ + "../../lib/common.sh", + "../../lib/functions.sh", + ] + destination = "/etc/one-appliance/lib/" + } + provisioner "file" { + source = "../one-apps/appliances/service.sh" + destination = "/etc/one-appliance/service" + } + provisioner "file" { + sources = ["../../appliances/n8n/appliance.sh"] + destination = "/etc/one-appliance/service.d/" + } + + provisioner "shell" { + scripts = ["${var.input_dir}/82-configure-context.sh"] + } + + provisioner "shell" { + inline_shebang = "/bin/bash -e" + inline = ["/etc/one-appliance/service install && sync"] + } + + post-processor "shell-local" { + execute_command = ["bash", "-c", "{{.Vars}} {{.Script}}"] + environment_vars = [ + "OUTPUT_DIR=${var.output_dir}", + "APPLIANCE_NAME=${var.appliance_name}", + ] + scripts = ["../one-apps/packer/postprocess.sh"] + } +} diff --git a/apps-code/community-apps/packer/n8n/postprocess.sh b/apps-code/community-apps/packer/n8n/postprocess.sh new file mode 100755 index 00000000..58c14885 --- /dev/null +++ b/apps-code/community-apps/packer/n8n/postprocess.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# Post-processing script for the appliance + +set -e + +echo "Post-processing appliance..." + +# Add any post-processing steps here +# For example: image optimization, cleanup, etc. + +echo "Post-processing completed" diff --git a/apps-code/community-apps/packer/n8n/variables.pkr.hcl b/apps-code/community-apps/packer/n8n/variables.pkr.hcl new file mode 100644 index 00000000..525227fa --- /dev/null +++ b/apps-code/community-apps/packer/n8n/variables.pkr.hcl @@ -0,0 +1,20 @@ +variable "appliance_name" { + type = string +} + +variable "version" { + type = string +} + +variable "input_dir" { + type = string +} + +variable "output_dir" { + type = string +} + +variable "headless" { + type = bool + default = true +} diff --git a/logos/n8n.png b/logos/n8n.png new file mode 100644 index 0000000000000000000000000000000000000000..120bb942f409101b5a446c199ffbc48170653e5a GIT binary patch literal 18210 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 w*!zbI#6 Date: Thu, 2 Oct 2025 09:21:39 +0000 Subject: [PATCH 2/9] Fix trailing spaces in n8n appliance YAML --- appliances/n8n/cf81cb32-dde4-4d47-a09c-fc3b4f058f45.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appliances/n8n/cf81cb32-dde4-4d47-a09c-fc3b4f058f45.yaml b/appliances/n8n/cf81cb32-dde4-4d47-a09c-fc3b4f058f45.yaml index b01b7b5a..a321438e 100644 --- a/appliances/n8n/cf81cb32-dde4-4d47-a09c-fc3b4f058f45.yaml +++ b/appliances/n8n/cf81cb32-dde4-4d47-a09c-fc3b4f058f45.yaml @@ -6,7 +6,7 @@ publisher: Pablo de la Rco publisher_email: pablo@example.com description: |- n8n is a workflow automation tool that allows you to connect different services and automate tasks. This appliance provides n8n - running in a Docker container on Ubuntu 22.04 LTS with VNC access and + running in a Docker container on Ubuntu 22.04 LTS with VNC access and SSH key authentication. **n8n features:** From e7644c93942f08b27ac5b5b6b91c81b0ba257114 Mon Sep 17 00:00:00 2001 From: OpenNebula Community Contributor Date: Tue, 7 Oct 2025 11:16:55 +0000 Subject: [PATCH 3/9] Fix n8n tests to use correct framework and match appliance implementation - Fix tests.yaml: remove escaped underscore (00-n8n\_basic.rb -> 00-n8n_basic.rb) - Rewrite tests to use standard RSpec framework (lib/community/app_handler) - Add comprehensive tests matching actual appliance: * Docker installation and service status * n8n container image availability (n8nio/n8n:latest) * Data directory existence (/data) * Container running status (n8n-container) * Container responsiveness * Container restart policy (unless-stopped) * Port mapping (5678:5678) * Volume mapping (/data:/home/node/.n8n) * oneapps MOTD verification (required test) - Remove old test framework that doesn't exist in marketplace --- appliances/n8n/tests.yaml | 2 +- appliances/n8n/tests/00-n8n_basic.rb | 102 +++++++++++++++++++++++---- 2 files changed, 88 insertions(+), 16 deletions(-) diff --git a/appliances/n8n/tests.yaml b/appliances/n8n/tests.yaml index 4e50e2f3..e863708f 100644 --- a/appliances/n8n/tests.yaml +++ b/appliances/n8n/tests.yaml @@ -1,2 +1,2 @@ --- -- 00-n8n\_basic.rb +- '00-n8n_basic.rb' \ No newline at end of file diff --git a/appliances/n8n/tests/00-n8n_basic.rb b/appliances/n8n/tests/00-n8n_basic.rb index 6a0cbc9c..4d55c59b 100644 --- a/appliances/n8n/tests/00-n8n_basic.rb +++ b/appliances/n8n/tests/00-n8n_basic.rb @@ -1,21 +1,93 @@ -# Basic test for n8n appliance +require_relative '../../../lib/community/app_handler' -require_relative '../../../lib/tests' +describe 'n8n Appliance' do + include_context('vm_handler') -class TestN8n < Test - def test_docker_installed - assert_cmd('docker --version') - end + # Test Docker installation + it 'docker is installed and running' do + cmd = 'which docker' + @info[:vm].ssh(cmd).expect_success - def test_docker_running - assert_cmd('systemctl is-active docker') - end + cmd = 'systemctl is-active docker' + @info[:vm].ssh(cmd).expect_success + end - def test_image_pulled - assert_cmd("docker images | grep 'n8nio/n8n:latest'") - end + # Test n8n container image is available + it 'n8n container image is available' do + cmd = 'docker images --format "{{.Repository}}:{{.Tag}}" | grep "n8nio/n8n:latest"' + @info[:vm].ssh(cmd).expect_success + end - def test_container_running - assert_cmd("docker ps | grep 'n8n-container'") - end + # Test data directory exists + it 'data directory exists' do + cmd = 'test -d /data' + @info[:vm].ssh(cmd).expect_success + end + + # Test n8n container is running + it 'n8n container is running' do + cmd = 'docker ps --format "{{.Names}}" | grep "n8n-container"' + @info[:vm].ssh(cmd).expect_success + end + + # Test container is responsive + it 'n8n container is responsive' do + cmd = 'docker exec n8n-container echo "Container is running"' + execution = @info[:vm].ssh(cmd) + expect(execution.success?).to be(true) + expect(execution.stdout.strip).to eq('Container is running') + end + + # Test container has correct restart policy + it 'container has restart policy configured' do + cmd = 'docker inspect n8n-container --format "{{.HostConfig.RestartPolicy.Name}}"' + execution = @info[:vm].ssh(cmd) + expect(execution.success?).to be(true) + expect(execution.stdout.strip).to eq('unless-stopped') + end + + # Test container port mapping + it 'container has port 5678 exposed' do + cmd = 'docker port n8n-container 5678' + @info[:vm].ssh(cmd).expect_success + end + + # Test container volume mapping + it 'container has data volume mounted' do + cmd = 'docker inspect n8n-container --format "{{range .Mounts}}{{.Source}}:{{.Destination}} {{end}}" | grep "/data:/home/node/.n8n"' + @info[:vm].ssh(cmd).expect_success + end + + # Check if the service framework reports ready + it 'checks oneapps motd' do + cmd = 'cat /etc/motd' + timeout_seconds = 60 + retry_interval_seconds = 5 + + begin + Timeout.timeout(timeout_seconds) do + loop do + execution = @info[:vm].ssh(cmd) + + if execution.exitstatus == 0 && execution.stdout.include?('All set and ready to serve') + expect(execution.exitstatus).to eq(0) + expect(execution.stdout).to include('All set and ready to serve') + break + else + sleep(retry_interval_seconds) + end + end + end + rescue Timeout::Error + fail "Timeout after #{timeout_seconds} seconds: MOTD did not contain 'All set and ready to serve'" + end + end + + # Cleanup: Stop container after tests + after(:all) do + if @info && @info[:vm] + @info[:vm].ssh('docker stop n8n-container || true') + @info[:vm].ssh('docker rm n8n-container || true') + end + end end From e6c8f4dcf7e41292c20ddb026f7a888188538782 Mon Sep 17 00:00:00 2001 From: OpenNebula Community Contributor Date: Tue, 7 Oct 2025 11:21:20 +0000 Subject: [PATCH 4/9] Fix YAML linting error in n8n tests.yaml Add missing newline at end of file to comply with yamllint rules. --- appliances/n8n/tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appliances/n8n/tests.yaml b/appliances/n8n/tests.yaml index e863708f..f0d96424 100644 --- a/appliances/n8n/tests.yaml +++ b/appliances/n8n/tests.yaml @@ -1,2 +1,2 @@ --- -- '00-n8n_basic.rb' \ No newline at end of file +- '00-n8n_basic.rb' From ae84cdc5fabbd3ea88ef617e4d40ceec44c86b40 Mon Sep 17 00:00:00 2001 From: OpenNebula Community Contributor Date: Tue, 7 Oct 2025 15:05:52 +0000 Subject: [PATCH 5/9] Fix user_inputs format in n8n template The user_inputs should be a dictionary (key: value), not an array. Changed keys to lowercase with oneapp_ prefix to match OpenNebula conventions: - CONTAINER_NAME -> oneapp_container_name - CONTAINER_PORTS -> oneapp_container_ports - CONTAINER_ENV -> oneapp_container_env - CONTAINER_VOLUMES -> oneapp_container_volumes Updated inputs_order to match the new uppercase context variable names. --- .../n8n/cf81cb32-dde4-4d47-a09c-fc3b4f058f45.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/appliances/n8n/cf81cb32-dde4-4d47-a09c-fc3b4f058f45.yaml b/appliances/n8n/cf81cb32-dde4-4d47-a09c-fc3b4f058f45.yaml index a321438e..3aa49411 100644 --- a/appliances/n8n/cf81cb32-dde4-4d47-a09c-fc3b4f058f45.yaml +++ b/appliances/n8n/cf81cb32-dde4-4d47-a09c-fc3b4f058f45.yaml @@ -58,9 +58,9 @@ opennebula_template: memory: '2048' name: n8n user_inputs: - - CONTAINER_NAME: 'M|text|Container name|n8n-container|n8n-container' - - CONTAINER_PORTS: 'M|text|Container ports (format: host:container)|5678:5678|5678:5678' - - CONTAINER_ENV: 'O|text|Environment variables (format: VAR1=value1,VAR2=value2)|N8N_HOST=0.0.0.0,N8N_PORT=5678,N8N_PROTOCOL=http|' - - CONTAINER_VOLUMES: 'O|text|Volume mounts (format: /host/path:/container/path)|/data:/home/node/.n8n|' - inputs_order: CONTAINER_NAME,CONTAINER_PORTS,CONTAINER_ENV,CONTAINER_VOLUMES + oneapp_container_name: 'M|text|Container name|n8n-container|n8n-container' + oneapp_container_ports: 'M|text|Container ports (format: host:container)|5678:5678|5678:5678' + oneapp_container_env: 'O|text|Environment variables (format: VAR1=value1,VAR2=value2)|N8N_HOST=0.0.0.0,N8N_PORT=5678,N8N_PROTOCOL=http|' + oneapp_container_volumes: 'O|text|Volume mounts (format: /host/path:/container/path)|/data:/home/node/.n8n|' + inputs_order: ONEAPP_CONTAINER_NAME,ONEAPP_CONTAINER_PORTS,ONEAPP_CONTAINER_ENV,ONEAPP_CONTAINER_VOLUMES logo: logos/n8n.png From 9aaeb3011c434dc0657d277eb4f6918dcc90df93 Mon Sep 17 00:00:00 2001 From: OpenNebula Community Contributor Date: Wed, 8 Oct 2025 06:33:51 +0000 Subject: [PATCH 6/9] Fix ONE_SERVICE_VERSION in n8n appliance.sh Set ONE_SERVICE_VERSION='latest' instead of leaving it empty. This fixes the build script metadata. --- appliances/n8n/appliance.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appliances/n8n/appliance.sh b/appliances/n8n/appliance.sh index c8ff7442..7b15e2c9 100755 --- a/appliances/n8n/appliance.sh +++ b/appliances/n8n/appliance.sh @@ -26,7 +26,7 @@ APPLIANCE_NAME="n8n" ### Appliance metadata ############################################### ONE_SERVICE_NAME='n8n' -ONE_SERVICE_VERSION= #latest +ONE_SERVICE_VERSION='latest' # Docker image tag ONE_SERVICE_BUILD=$(date +%s) ONE_SERVICE_SHORT_DESCRIPTION='n8n Docker Container Appliance' ONE_SERVICE_DESCRIPTION='n8n running in Docker container' From d8e9e4b43cefdc3f86363c5e57cfad943544fa17 Mon Sep 17 00:00:00 2001 From: OpenNebula Community Contributor Date: Wed, 8 Oct 2025 06:38:23 +0000 Subject: [PATCH 7/9] Change ONE_SERVICE_VERSION to '1.0' for n8n Use proper version number '1.0' instead of 'latest' for the appliance version. --- appliances/n8n/appliance.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appliances/n8n/appliance.sh b/appliances/n8n/appliance.sh index 7b15e2c9..ab02f486 100755 --- a/appliances/n8n/appliance.sh +++ b/appliances/n8n/appliance.sh @@ -26,7 +26,7 @@ APPLIANCE_NAME="n8n" ### Appliance metadata ############################################### ONE_SERVICE_NAME='n8n' -ONE_SERVICE_VERSION='latest' # Docker image tag +ONE_SERVICE_VERSION='1.0' # Appliance version ONE_SERVICE_BUILD=$(date +%s) ONE_SERVICE_SHORT_DESCRIPTION='n8n Docker Container Appliance' ONE_SERVICE_DESCRIPTION='n8n running in Docker container' From 4a69604824d7a63501ffe859ac31f133a3881119 Mon Sep 17 00:00:00 2001 From: OpenNebula Community Contributor Date: Wed, 8 Oct 2025 11:00:43 +0000 Subject: [PATCH 8/9] Fix n8n tests - Add wait period for contextualization to complete (60s initial + up to 120s for Docker) - Fix template parsing error in volume mount test (use single quotes for docker format) - Fix app_handler.rb context escaping (add quotes around context value and use consistent escaping) The tests now properly wait for Docker service to start and the n8n container to be created before running validation checks. --- appliances/n8n/tests/00-n8n_basic.rb | 35 +++++++++++++++++++++++++++- lib/community/app_handler.rb | 6 ++--- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/appliances/n8n/tests/00-n8n_basic.rb b/appliances/n8n/tests/00-n8n_basic.rb index 4d55c59b..07614b12 100644 --- a/appliances/n8n/tests/00-n8n_basic.rb +++ b/appliances/n8n/tests/00-n8n_basic.rb @@ -3,6 +3,39 @@ describe 'n8n Appliance' do include_context('vm_handler') + before(:all) do + # Wait for contextualization to complete (Docker installation and container startup) + puts "Waiting for contextualization to complete..." + sleep 60 + + # Wait for Docker service to be ready + max_wait = 120 + wait_interval = 5 + elapsed = 0 + + loop do + cmd = 'systemctl is-active docker 2>/dev/null' + result = @info[:vm].ssh(cmd) + + if result.success? + puts "Docker service is active" + break + end + + if elapsed >= max_wait + puts "Warning: Docker service not active after #{max_wait} seconds" + break + end + + puts "Waiting for Docker service... (#{elapsed}s/#{max_wait}s)" + sleep wait_interval + elapsed += wait_interval + end + + # Additional wait for container to start + sleep 10 + end + # Test Docker installation it 'docker is installed and running' do cmd = 'which docker' @@ -54,7 +87,7 @@ # Test container volume mapping it 'container has data volume mounted' do - cmd = 'docker inspect n8n-container --format "{{range .Mounts}}{{.Source}}:{{.Destination}} {{end}}" | grep "/data:/home/node/.n8n"' + cmd = "docker inspect n8n-container --format '{{range .Mounts}}{{.Source}}:{{.Destination}} {{end}}' | grep '/data:/home/node/.n8n'" @info[:vm].ssh(cmd).expect_success end diff --git a/lib/community/app_handler.rb b/lib/community/app_handler.rb index 0700122f..cd434540 100644 --- a/lib/community/app_handler.rb +++ b/lib/community/app_handler.rb @@ -34,7 +34,7 @@ prefixed = config[:app][:context][:prefixed] - options = "--context #{app_context(APP_CONTEXT_PARAMS, prefixed)} --disk #{APP_IMAGE_NAME}" + options = "--context \"#{app_context(APP_CONTEXT_PARAMS, prefixed)}\" --disk #{APP_IMAGE_NAME}" # Create a new VM by issuing onetemplate instantiate VM_TEMPLATE @info[:vm] = VM.instantiate(VM_TEMPLATE, true, options) @@ -56,12 +56,12 @@ # @return [String] Comma separated list of context parameters ready to be used with --context on CLI template instantiation # def app_context(app_context_params, prefixed = true) - params = [%(SSH_PUBLIC_KEY=\\"\\$USER[SSH_PUBLIC_KEY]\\"), 'NETWORK="YES"'] + params = [%(SSH_PUBLIC_KEY=\\"\\$USER[SSH_PUBLIC_KEY]\\"), %(NETWORK=\\"YES\\")] prefixed == true ? prefix = 'ONEAPP_' : prefix = '' app_context_params.each do |key, value| - params << "#{prefix}#{key}=\"#{value}\"" + params << %(#{prefix}#{key}=\\"#{value}\\") end return params.join(',') From 427b6a5d3b260b48574dfcb18e074e1880dc929a Mon Sep 17 00:00:00 2001 From: OpenNebula Community Contributor Date: Wed, 8 Oct 2025 12:00:01 +0000 Subject: [PATCH 9/9] Update n8n metadata.yaml and add context.yaml - Fix metadata.yaml structure to match test framework requirements - Change :os: from array to hash with :type and :base - Change :hypervisor: from array to single value - Add :one: section with template configuration - Add :infra: section with disk_format and apps_path - Add generated context.yaml for n8n appliance All 9 tests passing successfully. --- appliances/n8n/context.yaml | 12 ++++++---- appliances/n8n/metadata.yaml | 44 ++++++++++++++++++++++++++++++------ 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/appliances/n8n/context.yaml b/appliances/n8n/context.yaml index ec4465b8..bcac3c2e 100644 --- a/appliances/n8n/context.yaml +++ b/appliances/n8n/context.yaml @@ -1,5 +1,9 @@ --- -CONTAINER_NAME: n8n-container -CONTAINER_PORTS: 5678:5678 -CONTAINER_ENV: N8N_HOST=0.0.0.0,N8N_PORT=5678,N8N_PROTOCOL=http,N8N_SECURE_COOKIE=false -CONTAINER_VOLUMES: /data:/home/node/.n8n +:tests: + 'n8n': + :image_name: n8n.qcow2 + :type: linux + :microenvs: ['context-kvm'] + :slow: true + :enable_netcfg_common: True + :enable_netcfg_ip_methods: True diff --git a/appliances/n8n/metadata.yaml b/appliances/n8n/metadata.yaml index 2e8cea71..58b75a6b 100644 --- a/appliances/n8n/metadata.yaml +++ b/appliances/n8n/metadata.yaml @@ -3,13 +3,9 @@ :name: n8n :type: service :os: - - Ubuntu - - '22.04' - :arch: - - x86_64 - :format: qcow2 - :hypervisor: - - KVM + :type: linux + :base: ubuntu2204 + :hypervisor: KVM :opennebula_version: - '7.0' :opennebula_template: @@ -29,3 +25,37 @@ CONTAINER_PORTS: 'M|text|Container ports (format: host:container)|5678:5678|5678:5678' CONTAINER_ENV: 'O|text|Environment variables (format: VAR1=value1,VAR2=value2)|N8N_HOST=0.0.0.0,N8N_PORT=5678,N8N_PROTOCOL=http,N8N_SECURE_COOKIE=false|' CONTAINER_VOLUMES: 'O|text|Volume mounts (format: /host/path:/container/path)|/data:/home/node/.n8n|' + + :context: + :prefixed: true + :params: + :CONTAINER_NAME: 'n8n-container' + :CONTAINER_PORTS: '5678:5678' + :CONTAINER_ENV: 'N8N_HOST=0.0.0.0,N8N_PORT=5678,N8N_PROTOCOL=http,N8N_SECURE_COOKIE=false' + :CONTAINER_VOLUMES: '/data:/home/node/.n8n' + +:one: + :template: + NAME: base + TEMPLATE: + ARCH: x86_64 + CONTEXT: + NETWORK: 'YES' + SSH_PUBLIC_KEY: "$USER[SSH_PUBLIC_KEY]" + CPU: '2' + CPU_MODEL: + MODEL: host-passthrough + GRAPHICS: + LISTEN: 0.0.0.0 + TYPE: vnc + MEMORY: '2048' + NIC: + NETWORK: vnet + NIC_DEFAULT: + MODEL: virtio + :datastore_name: default + :timeout: '90' + +:infra: + :disk_format: qcow2 + :apps_path: /var/tmp