diff --git a/.github/renovate-config.js b/.github/renovate-config.js index 203de8bf3a9..a098d213965 100644 --- a/.github/renovate-config.js +++ b/.github/renovate-config.js @@ -179,7 +179,10 @@ module.exports = { customVersioning( // 20250122_091948 {year}{month}{day}_{build} "^(?\\d{4})(?\\d{2})(?\\d{2})_(?\\d+)$", - ["ghcr.io/nextcloud-releases/aio-imaginary"] + [ + "ghcr.io/nextcloud-releases/aio-imaginary", + "ghcr.io/nextcloud-releases/aio-talk", + ] ), customVersioning( // 2024.10.22-7ca5933 diff --git a/.github/scripts/ci.py b/.github/scripts/ci.py index 3159339f15c..9d45f26921d 100755 --- a/.github/scripts/ci.py +++ b/.github/scripts/ci.py @@ -62,6 +62,11 @@ def parse_args(): type=bool, help="Wait for user input before stopping the app", ) + parser.add_argument( + "--run-host", + required=False, + help="Run the app on the specified docker host", + ) parsed = parser.parse_args() return { @@ -72,6 +77,7 @@ def parse_args(): "render_only_debug": parsed.render_only_debug, "project": secrets.token_hex(16), "wait": parsed.wait, + "run_host": parsed.run_host, } @@ -84,6 +90,8 @@ def print_info(): print_stderr(f" - render-only: [{args['render_only']}]") print_stderr(f" - render-only-debug: [{args['render_only_debug']}]") print_stderr(f" - wait: [{args['wait']}]") + if args["run_host"] is not None: + print_stderr(f" - run-host: [{args['run_host']}]") def command_exists(command): @@ -100,12 +108,15 @@ def check_required_commands(): def get_base_cmd(): rendered_compose = "templates/rendered/docker-compose.yaml" - return " ".join( + cmd = " ".join( [ f"docker compose -p {args['project']} -f", f"ix-dev/{args['train']}/{args['app']}/{rendered_compose}", ] ) + if args["run_host"] is not None: + cmd = f"DOCKER_HOST={args['run_host']} {cmd}" + return cmd def pull_app_catalog_container(): diff --git a/cspell.config.yaml b/cspell.config.yaml index f28fc141fd6..8600e18010d 100644 --- a/cspell.config.yaml +++ b/cspell.config.yaml @@ -362,6 +362,7 @@ words: - storjlabs - strato - subquestions + - supervisord - syncthing - sysadminsmedia - sysctls diff --git a/ix-dev/test/nextcloud/app.yaml b/ix-dev/test/nextcloud/app.yaml index 8a2c6401b2c..ccbff21cc2f 100644 --- a/ix-dev/test/nextcloud/app.yaml +++ b/ix-dev/test/nextcloud/app.yaml @@ -70,4 +70,4 @@ sources: - https://hub.docker.com/r/ixsystems/nextcloud-notify-push title: Nextcloud train: test -version: 1.0.30 +version: 1.0.31 diff --git a/ix-dev/test/nextcloud/ix_values.yaml b/ix-dev/test/nextcloud/ix_values.yaml index 81da3a16539..1d2363dd7a8 100644 --- a/ix-dev/test/nextcloud/ix_values.yaml +++ b/ix-dev/test/nextcloud/ix_values.yaml @@ -1,22 +1,28 @@ images: + # image: + # repository: ixsystems/nextcloud-fpm + # tag: 31.0.6-fpm-7e93480c image: - repository: ixsystems/nextcloud-fpm - tag: 31.0.6-fpm-7e93480c - notify_push_image: - repository: ixsystems/nextcloud-notify-push - tag: 1.1.0-25d76a32 + repository: ghcr.io/stavros-k/nextcloud-fpm + tag: 31.0.6-fpm nginx_image: repository: nginxinc/nginx-unprivileged tag: 1.29.0 - postgres_17_image: - repository: postgres - tag: "17.5" - redis_image: - repository: bitnami/redis - tag: 8.0.2 + notify_push_image: + repository: ixsystems/nextcloud-notify-push + tag: 1.1.0-25d76a32 + talk_image: + repository: ghcr.io/nextcloud-releases/aio-talk + tag: "20250701_092737" imaginary_image: repository: ghcr.io/nextcloud-releases/aio-imaginary tag: "20250619_082329" + redis_image: + repository: bitnami/redis + tag: 8.0.2 + postgres_17_image: + repository: postgres + tag: "17.5" postgres_upgrade_image: repository: ixsystems/postgres-upgrade tag: 1.0.2 @@ -30,11 +36,15 @@ consts: nginx_container_name: nginx imaginary_container_name: imaginary notify_push_container_name: notify-push + talk_container_name: talk + db_user: nextcloud db_name: nextcloud + internal_nextcloud_port: 9000 internal_imaginary_port: 9000 internal_notify_push_port: 7867 + internal_talk_signaling_port: 8081 html_path: /var/www/html data_path: /nc-data diff --git a/ix-dev/test/nextcloud/questions.yaml b/ix-dev/test/nextcloud/questions.yaml index fbbb4bad6b0..374b0c86fd0 100644 --- a/ix-dev/test/nextcloud/questions.yaml +++ b/ix-dev/test/nextcloud/questions.yaml @@ -458,6 +458,17 @@ questions: type: string show_if: [["enabled", "=", true]] required: true + - variable: internal_url + label: Internal URL + description: | + The internal URL to use for Collabora.
+ This URL needs to be accessible from the Nextcloud container.
+ But not required to be accessible from the outside (ie clients).
+ schema: + type: string + show_if: [["enabled", "=", true]] + default: http://host.docker.internal:9980 + required: true - variable: allowlist label: Allowlist description: | diff --git a/ix-dev/test/nextcloud/templates/docker-compose.yaml b/ix-dev/test/nextcloud/templates/docker-compose.yaml index bcea5bd5f4f..610a9751c6c 100644 --- a/ix-dev/test/nextcloud/templates/docker-compose.yaml +++ b/ix-dev/test/nextcloud/templates/docker-compose.yaml @@ -122,6 +122,7 @@ {% do nc_container.environment.add_env("IX_COLLABORA", values.nextcloud.collabora.enabled) %} {% if values.nextcloud.collabora.enabled %} {% do nc_container.environment.add_env("IX_COLLABORA_URL", values.nextcloud.collabora.url) %} + {% do nc_container.environment.add_env("IX_COLLABORA_INTERNAL_URL", values.nextcloud.collabora.internal_url) %} {% do nc_container.environment.add_env("IX_COLLABORA_ALLOWLIST", values.nextcloud.collabora.allowlist | join(" ")) %} {% endif %} @@ -189,6 +190,8 @@ {% if values.nextcloud.previews.enabled and values.nextcloud.previews.imaginary.enabled %} {% set imaginary_container = tpl.add_container(values.consts.imaginary_container_name, "imaginary_image") %} {% do imaginary_container.set_user(values.run_as.user, values.run_as.group) %} + {% do imaginary_container.set_init(true) %} + {% do imaginary_container.set_read_only(true) %} {% do imaginary_container.healthcheck.set_test("wget", {"port": values.consts.internal_imaginary_port, "path": "/health"}) %} {% do imaginary_container.set_entrypoint([ "imaginary", @@ -206,6 +209,7 @@ {% if values.nextcloud.notify_push.enabled %} {% set notify_push_container = tpl.add_container(values.consts.notify_push_container_name, "notify_push_image") %} {% do notify_push_container.set_user(values.run_as.user, values.run_as.group) %} + {% do notify_push_container.set_read_only(true) %} {% do notify_push_container.healthcheck.set_test("curl", { "port": values.consts.internal_notify_push_port, "path": "/push/test/cookie", "headers": [("Host", "docker.internal.healthcheck")] @@ -220,6 +224,44 @@ {% do nc_container.environment.add_env("IX_NOTIFY_PUSH_ENDPOINT", "%s://%s/push" | format(ext_protocol, ext_host_port.x)) %} {% endif %} +{# -- Talk -- #} +{% do nc_container.environment.add_env("IX_TALK", values.nextcloud.talk.enabled) %} +{% if values.nextcloud.talk.enabled %} + {% if values.nextcloud.urls.protocol != "https" %} + {% do tpl.funcs.fail("Nextcloud URL Protocol must be set to HTTPS for talk to work") %} + {% endif %} + {% if not values.nextcloud.urls.is_cert_valid %} + {% do tpl.funcs.fail("Nextcloud URL Certificate must be valid for talk to work") %} + {% endif %} + + {% set talk_container = tpl.add_container(values.consts.talk_container_name, "talk_image") %} + {% do talk_container.set_user(values.run_as.user, values.run_as.group) %} + {% do talk_container.healthcheck.set_custom_test(["CMD", "/healthcheck.sh"]) %} + {% do talk_container.set_init(true) %} + {% do talk_container.set_read_only(true) %} + {% do talk_container.environment.add_env("NC_DOMAIN", ext_host_port.x) %} + {% do talk_container.environment.add_env("TALK_HOST", values.consts.talk_container_name) %} + {% do talk_container.environment.add_env("TALK_PORT", values.network.talk_port.port_number) %} + {% do talk_container.environment.add_env("TURN_SECRET", values.nextcloud.talk.turn_secret) %} + {% do talk_container.environment.add_env("INTERNAL_SECRET", values.nextcloud.talk.internal_secret) %} + {% do talk_container.environment.add_env("SIGNALING_SECRET", values.nextcloud.talk.signaling_secret) %} + + {% do talk_container.add_port(values.network.talk_port) %} + {% do talk_container.add_port(values.network.talk_port, {"protocol": "udp"}) %} + {% do talk_container.add_storage("/var/log/supervisord", {"type": "tmpfs"}) %} + {% do talk_container.add_storage("/var/run/supervisord", {"type": "tmpfs"}) %} + {% do talk_container.add_storage("/opt/eturnal/run", {"type": "tmpfs"}) %} + {% do talk_container.add_storage("/conf", {"type": "tmpfs"}) %} + {% do talk_container.add_storage("/tmp", {"type": "tmpfs"}) %} + + {% do nc_container.environment.add_env("IX_TALK_SIGNALING_SECRET", values.nextcloud.talk.signaling_secret) %} + {% do nc_container.environment.add_env("IX_TALK_TURN_SECRET", values.nextcloud.talk.turn_secret) %} + {% do nc_container.environment.add_env("IX_TALK_SIGNALING_SERVER", "%s/standalone-signaling/" | format(ext_host_port.x)) %} + {% do nc_container.environment.add_env("IX_TALK_SIGNALING_SERVER_VERIFY", values.nextcloud.urls.is_cert_valid) %} + {% do nc_container.environment.add_env("IX_TALK_STUN_SERVERS", "%s:%d"|format(values.nextcloud.urls.host, values.network.talk_port.port_number)) %} + {% do nc_container.environment.add_env("IX_TALK_TURN_SERVER", "%s:%d"|format(values.nextcloud.urls.host, values.network.talk_port.port_number)) %} +{% endif %} + {% if perm_container.has_actions() %} {% do perm_container.activate() %} {% do nc_container.depends.add_dependency(values.consts.perms_container_name, "service_completed_successfully") %} diff --git a/ix-dev/test/nextcloud/templates/macros/nginx.conf b/ix-dev/test/nextcloud/templates/macros/nginx.conf index 3216df23dc1..5bc2c1da8d5 100644 --- a/ix-dev/test/nextcloud/templates/macros/nginx.conf +++ b/ix-dev/test/nextcloud/templates/macros/nginx.conf @@ -151,6 +151,26 @@ http { } {%- endif %} + {%- if values.nextcloud.talk.enabled %} + location /standalone-signaling/ { + proxy_pass {{ "http://%s:%d/" | format(values.consts.talk_container_name, values.consts.internal_talk_signaling_port) }}; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + location /standalone-signaling/spreed { + proxy_pass {{ "http://%s:%d/spreed" | format(values.consts.talk_container_name, values.consts.internal_talk_signaling_port) }}; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + {%- endif %} + # Make a regex exception for `/.well-known` so that clients can still # access it despite the existence of the regex rule # `location ~ /(\.|autotest|...)` which would otherwise handle requests diff --git a/ix-dev/test/nextcloud/templates/test_values/basic-values.yaml b/ix-dev/test/nextcloud/templates/test_values/basic-values.yaml index a549cf92624..a455208f66b 100644 --- a/ix-dev/test/nextcloud/templates/test_values/basic-values.yaml +++ b/ix-dev/test/nextcloud/templates/test_values/basic-values.yaml @@ -23,7 +23,8 @@ nextcloud: php_memory_limit_mb: 512 urls: protocol: http - host: 10.20.30.6 + host: localhost + is_cert_valid: false external_port: null trusted_domains: - cloud.example.com @@ -77,6 +78,7 @@ nextcloud: collabora: enabled: false url: http://collabora.example.com + internal_url: http://collabora:9980 allowlist: - 0.0.0.0/0 @@ -94,6 +96,12 @@ nextcloud: file_max_size: -1 infected_action: only_log + talk: + enabled: false + turn_secret: random_string_of_characters + signaling_secret: random_string_of_characters + internal_secret: random_string_of_characters + additional_envs: [] run_as: @@ -104,12 +112,15 @@ network: web_port: bind_mode: published port_number: 8080 - certificate_id: + talk_port: + bind_mode: "" + port_number: 8082 + certificate_id: null ix_volumes: - postgres_data: /mnt/data/test/postgres_data - nextcloud-data: /mnt/data/test/nextcloud-data - nextcloud-html: /mnt/data/test/nextcloud-html + postgres_data: /opt/tests/mnt/nextcloud/postgres_data + nextcloud-data: /opt/tests/mnt/nextcloud/nextcloud-data + nextcloud-html: /opt/tests/mnt/nextcloud/nextcloud-html storage: data: diff --git a/ix-dev/test/nextcloud/templates/test_values/build-values.yaml b/ix-dev/test/nextcloud/templates/test_values/build-values.yaml index 413b663a62a..66fc38394e7 100644 --- a/ix-dev/test/nextcloud/templates/test_values/build-values.yaml +++ b/ix-dev/test/nextcloud/templates/test_values/build-values.yaml @@ -95,6 +95,12 @@ nextcloud: file_max_size: -1 infected_action: only_log + talk: + enabled: false + turn_secret: random_string_of_characters + signaling_secret: random_string_of_characters + internal_secret: random_string_of_characters + additional_envs: [] run_as: @@ -105,6 +111,9 @@ network: web_port: bind_mode: published port_number: 8080 + talk_port: + bind_mode: "" + port_number: 8082 certificate_id: ix_volumes: diff --git a/ix-dev/test/nextcloud/templates/test_values/https-values.yaml b/ix-dev/test/nextcloud/templates/test_values/https-values.yaml index 1326bf3aab6..3d5badbd0cd 100644 --- a/ix-dev/test/nextcloud/templates/test_values/https-values.yaml +++ b/ix-dev/test/nextcloud/templates/test_values/https-values.yaml @@ -25,6 +25,7 @@ nextcloud: urls: protocol: http host: localhost + is_cert_valid: true external_port: null trusted_domains: - cloud.example.com @@ -95,6 +96,12 @@ nextcloud: file_max_size: -1 infected_action: only_log + talk: + enabled: true + turn_secret: random_string_of_characters + signaling_secret: random_string_of_characters + internal_secret: random_string_of_characters + additional_envs: [] run_as: @@ -105,6 +112,9 @@ network: web_port: bind_mode: published port_number: 8080 + talk_port: + bind_mode: published + port_number: 8082 certificate_id: "2" ix_volumes: