feat: add acp bridge server deploy role
This commit is contained in:
parent
557272bf88
commit
9d6e59e802
@ -7,6 +7,7 @@
|
||||
deploy_acp_codex: true
|
||||
deploy_acp_opencode: true
|
||||
deploy_acp_unified: true
|
||||
deploy_acp_bridge_server: true
|
||||
roles:
|
||||
- role: roles/vhosts/acp_codex/
|
||||
when: deploy_acp_codex
|
||||
@ -14,6 +15,9 @@
|
||||
- role: roles/vhosts/acp_opencode/
|
||||
when: deploy_acp_opencode
|
||||
tags: [acp_opencode]
|
||||
- role: roles/vhosts/acp_bridge_server/
|
||||
when: deploy_acp_bridge_server
|
||||
tags: [acp_bridge_server]
|
||||
- role: roles/vhosts/acp_vhosts/
|
||||
when: deploy_acp_unified
|
||||
tags: [acp_vhosts]
|
||||
|
||||
7
deploy_apisix_svc.plus.yaml
Normal file
7
deploy_apisix_svc.plus.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
- name: Deploy managed api.svc.plus service
|
||||
hosts: "{{ apisix_service_hosts | default('apisix') }}"
|
||||
gather_facts: false
|
||||
become: true
|
||||
roles:
|
||||
- roles/vhosts/apisix_service
|
||||
@ -7,6 +7,9 @@ jp-xhttp-contabo.svc.plus ansible_host=46.250.251.132 ansible_user=roo
|
||||
[accounts]
|
||||
acp-server.svc.plus ansible_host=46.250.251.132 ansible_user=root
|
||||
|
||||
[apisix]
|
||||
api.svc.plus ansible_host=46.250.251.132 ansible_user=root
|
||||
|
||||
[postgresql]
|
||||
acp-server.svc.plus ansible_host=46.250.251.132 ansible_user=root
|
||||
|
||||
|
||||
27
roles/vhosts/acp_bridge_server/defaults/main.yml
Normal file
27
roles/vhosts/acp_bridge_server/defaults/main.yml
Normal file
@ -0,0 +1,27 @@
|
||||
---
|
||||
acp_bridge_server_service_name: xworkmate-acp-bridge-server
|
||||
acp_bridge_server_service_user: root
|
||||
acp_bridge_server_service_group: root
|
||||
acp_bridge_server_workdir: /root
|
||||
acp_bridge_server_listen_host: 127.0.0.1
|
||||
acp_bridge_server_listen_port: 8787
|
||||
acp_bridge_server_binary_path: /usr/local/bin/xworkmate-acp-bridge-server
|
||||
acp_bridge_server_local_source_dir: "{{ playbook_dir }}/../xworkmate/go/go_core"
|
||||
acp_bridge_server_local_build_dir: "{{ playbook_dir }}/.artifacts/acp_bridge_server"
|
||||
acp_bridge_server_local_binary_path: "{{ acp_bridge_server_local_build_dir }}/xworkmate-acp-bridge-server"
|
||||
acp_bridge_server_build_goos: linux
|
||||
acp_bridge_server_build_goarch: amd64
|
||||
acp_bridge_server_domain: acp-server.svc.plus
|
||||
acp_bridge_server_public_base_url: https://acp-server.svc.plus
|
||||
acp_bridge_server_caddyfile_path: /etc/caddy/Caddyfile
|
||||
acp_bridge_server_caddy_conf_dir: /etc/caddy/conf.d
|
||||
acp_bridge_server_caddy_fragment_path: /etc/caddy/conf.d/acp-server-bridge.caddy
|
||||
acp_bridge_server_obsolete_caddy_fragment_paths:
|
||||
- /etc/caddy/conf.d/acp-server-bridge-server.caddy
|
||||
acp_bridge_server_allowed_origins:
|
||||
- https://xworkmate.svc.plus
|
||||
- http://localhost:*
|
||||
- http://127.0.0.1:*
|
||||
acp_bridge_server_enable_ufw: true
|
||||
acp_bridge_server_packages:
|
||||
- caddy
|
||||
11
roles/vhosts/acp_bridge_server/handlers/main.yml
Normal file
11
roles/vhosts/acp_bridge_server/handlers/main.yml
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: Restart acp bridge server
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ acp_bridge_server_service_name }}"
|
||||
state: restarted
|
||||
daemon_reload: true
|
||||
|
||||
- name: Reload caddy
|
||||
ansible.builtin.systemd:
|
||||
name: caddy
|
||||
state: reloaded
|
||||
65
roles/vhosts/acp_bridge_server/tasks/main.yml
Normal file
65
roles/vhosts/acp_bridge_server/tasks/main.yml
Normal file
@ -0,0 +1,65 @@
|
||||
---
|
||||
- name: Install ACP bridge server prerequisites
|
||||
ansible.builtin.package:
|
||||
name: "{{ acp_bridge_server_packages }}"
|
||||
state: present
|
||||
|
||||
- name: Ensure local ACP bridge build directory exists
|
||||
ansible.builtin.file:
|
||||
path: "{{ acp_bridge_server_local_build_dir }}"
|
||||
state: directory
|
||||
mode: "0755"
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
|
||||
- name: Build XWorkmate ACP bridge server locally
|
||||
ansible.builtin.command:
|
||||
cmd: go build -o "{{ acp_bridge_server_local_binary_path }}" .
|
||||
chdir: "{{ acp_bridge_server_local_source_dir }}"
|
||||
environment:
|
||||
GOOS: "{{ acp_bridge_server_build_goos }}"
|
||||
GOARCH: "{{ acp_bridge_server_build_goarch }}"
|
||||
CGO_ENABLED: "0"
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
|
||||
- name: Upload XWorkmate ACP bridge server binary
|
||||
ansible.builtin.copy:
|
||||
src: "{{ acp_bridge_server_local_binary_path }}"
|
||||
dest: "{{ acp_bridge_server_binary_path }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
notify: Restart acp bridge server
|
||||
|
||||
- name: Deploy ACP bridge Caddy fragment
|
||||
ansible.builtin.template:
|
||||
src: acp-bridge-server.caddy.j2
|
||||
dest: "{{ acp_bridge_server_caddy_fragment_path }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify: Reload caddy
|
||||
|
||||
- name: Remove deprecated standalone ACP bridge Caddy fragments
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
loop: "{{ acp_bridge_server_obsolete_caddy_fragment_paths }}"
|
||||
notify: Reload caddy
|
||||
|
||||
- name: Deploy ACP bridge systemd service
|
||||
ansible.builtin.template:
|
||||
src: acp-bridge-server.service.j2
|
||||
dest: "/etc/systemd/system/{{ acp_bridge_server_service_name }}.service"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify: Restart acp bridge server
|
||||
|
||||
- name: Ensure ACP bridge server is enabled and running
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ acp_bridge_server_service_name }}"
|
||||
enabled: true
|
||||
state: started
|
||||
daemon_reload: true
|
||||
@ -0,0 +1,4 @@
|
||||
{{ acp_bridge_server_domain }} {
|
||||
encode zstd gzip
|
||||
reverse_proxy {{ acp_bridge_server_listen_host }}:{{ acp_bridge_server_listen_port }}
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
[Unit]
|
||||
Description=XWorkmate ACP Bridge Server
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User={{ acp_bridge_server_service_user }}
|
||||
Group={{ acp_bridge_server_service_group }}
|
||||
WorkingDirectory={{ acp_bridge_server_workdir }}
|
||||
ExecStart={{ acp_bridge_server_binary_path }} serve --config /etc/xworkmate/xworkmate-acp-bridge-server.yaml --listen {{ acp_bridge_server_listen_host }}:{{ acp_bridge_server_listen_port }}
|
||||
Restart=on-failure
|
||||
RestartSec=2
|
||||
NoNewPrivileges=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@ -1,6 +1,6 @@
|
||||
# acp_codex
|
||||
|
||||
Codex ACP deployment role with a public XWorkmate ACP Web endpoint.
|
||||
Codex ACP deployment role behind the unified `acp-server.svc.plus` ingress.
|
||||
|
||||
Installs:
|
||||
|
||||
@ -10,11 +10,11 @@ Installs:
|
||||
Exposes:
|
||||
|
||||
- raw Codex upstream: `codex app-server --listen ws://127.0.0.1:9001`
|
||||
- public ACP Web server: `127.0.0.1:9010` via `acp-bridge`
|
||||
- public HTTPS endpoint: `https://acp-server-codex.svc.plus`
|
||||
- public ACP bridge: `127.0.0.1:9010` via `acp-bridge-codex`
|
||||
- public base URL: `https://acp-server.svc.plus/codex`
|
||||
|
||||
Notes:
|
||||
|
||||
- Caddy terminates TLS and proxies the public domain to the Go ACP server.
|
||||
- The Go ACP server serves `/acp` and `/acp/rpc`.
|
||||
- Caddy terminates TLS on `acp-server.svc.plus` and routes `/codex*` to this bridge.
|
||||
- The Go ACP server serves `/acp` and `/acp/rpc` under the unified `/codex` prefix.
|
||||
- `ACP_ALLOWED_ORIGINS` defaults to `https://xworkmate.svc.plus,http://localhost:*,http://127.0.0.1:*`.
|
||||
|
||||
@ -5,8 +5,10 @@ acp_codex_service_group: root
|
||||
acp_codex_workdir: /root
|
||||
acp_codex_listen_host: 127.0.0.1
|
||||
acp_codex_listen_port: 9001
|
||||
acp_codex_bridge_service_name: acp-bridge
|
||||
acp_codex_bridge_legacy_service_name: xworkmate-codex-acp-bridge
|
||||
acp_codex_bridge_service_name: acp-bridge-codex
|
||||
acp_codex_bridge_legacy_service_names:
|
||||
- acp-bridge
|
||||
- xworkmate-codex-acp-bridge
|
||||
acp_codex_bridge_binary_path: /usr/local/bin/xworkmate-go-core
|
||||
acp_codex_bridge_local_source_dir: "{{ playbook_dir }}/../xworkmate/go/go_core"
|
||||
acp_codex_bridge_local_build_dir: "{{ playbook_dir }}/.artifacts/acp_codex"
|
||||
@ -19,10 +21,11 @@ acp_codex_bridge_allowed_origins:
|
||||
- https://xworkmate.svc.plus
|
||||
- http://localhost:*
|
||||
- http://127.0.0.1:*
|
||||
acp_codex_domain: acp-server-codex.svc.plus
|
||||
acp_codex_public_base_url: https://acp-server.svc.plus/codex
|
||||
acp_codex_caddyfile_path: /etc/caddy/Caddyfile
|
||||
acp_codex_caddy_conf_dir: /etc/caddy/conf.d
|
||||
acp_codex_caddy_fragment_path: /etc/caddy/conf.d/acp-server-codex.caddy
|
||||
acp_codex_obsolete_caddy_fragment_paths:
|
||||
- /etc/caddy/conf.d/acp-server-codex.caddy
|
||||
acp_codex_enable_ufw: true
|
||||
acp_codex_packages:
|
||||
- caddy
|
||||
|
||||
@ -27,27 +27,30 @@
|
||||
mode: "0755"
|
||||
notify: Restart codex acp bridge
|
||||
|
||||
- name: Check legacy Codex ACP bridge systemd unit
|
||||
ansible.builtin.stat:
|
||||
path: "/etc/systemd/system/{{ acp_codex_bridge_legacy_service_name }}.service"
|
||||
register: acp_codex_bridge_legacy_unit
|
||||
|
||||
- name: Stop and disable legacy Codex ACP bridge service
|
||||
- name: Stop and disable legacy Codex ACP bridge services
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ acp_codex_bridge_legacy_service_name }}"
|
||||
name: "{{ item }}"
|
||||
enabled: false
|
||||
state: stopped
|
||||
loop: "{{ acp_codex_bridge_legacy_service_names }}"
|
||||
when:
|
||||
- acp_codex_bridge_legacy_service_name != acp_codex_bridge_service_name
|
||||
- acp_codex_bridge_legacy_unit.stat.exists
|
||||
- item != acp_codex_bridge_service_name
|
||||
failed_when: false
|
||||
|
||||
- name: Remove legacy Codex ACP bridge systemd unit
|
||||
- name: Remove legacy Codex ACP bridge systemd units
|
||||
ansible.builtin.file:
|
||||
path: "/etc/systemd/system/{{ acp_codex_bridge_legacy_service_name }}.service"
|
||||
path: "/etc/systemd/system/{{ item }}.service"
|
||||
state: absent
|
||||
loop: "{{ acp_codex_bridge_legacy_service_names }}"
|
||||
when:
|
||||
- acp_codex_bridge_legacy_service_name != acp_codex_bridge_service_name
|
||||
- acp_codex_bridge_legacy_unit.stat.exists
|
||||
- item != acp_codex_bridge_service_name
|
||||
|
||||
- name: Remove deprecated standalone Codex Caddy fragments
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
loop: "{{ acp_codex_obsolete_caddy_fragment_paths }}"
|
||||
notify: Reload caddy
|
||||
|
||||
- name: Deploy Caddy main file
|
||||
ansible.builtin.template:
|
||||
@ -58,15 +61,6 @@
|
||||
mode: "0644"
|
||||
notify: Reload caddy
|
||||
|
||||
- name: Deploy Codex ACP Caddy site
|
||||
ansible.builtin.template:
|
||||
src: acp-site.caddy.j2
|
||||
dest: "{{ acp_codex_caddy_fragment_path }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify: Reload caddy
|
||||
|
||||
- name: Deploy Codex ACP systemd service
|
||||
ansible.builtin.template:
|
||||
src: codex-app-server.service.j2
|
||||
|
||||
@ -48,7 +48,9 @@
|
||||
- name: Show Codex ACP validation summary
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "Codex domain: {{ acp_codex_domain }}"
|
||||
- "Codex public base URL: {{ acp_codex_public_base_url }}"
|
||||
- "Preferred WebSocket endpoint: {{ acp_codex_public_base_url }}/acp"
|
||||
- "Compatibility HTTP RPC endpoint: {{ acp_codex_public_base_url }}/acp/rpc"
|
||||
- "Upstream listener: {{ acp_codex_listen_host }}:{{ acp_codex_listen_port }}"
|
||||
- "Public ACP listener: {{ acp_codex_bridge_listen_host }}:{{ acp_codex_bridge_listen_port }}"
|
||||
- "Service: {{ acp_codex_status.stdout | default('N/A') }}"
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
{{ acp_codex_domain }} {
|
||||
reverse_proxy {{ acp_codex_bridge_listen_host }}:{{ acp_codex_bridge_listen_port }}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
# acp_opencode
|
||||
|
||||
OpenCode web endpoint plus ACP bridge deployment role.
|
||||
OpenCode service plus ACP bridge behind the unified `acp-server.svc.plus` ingress.
|
||||
|
||||
Installs:
|
||||
|
||||
@ -10,12 +10,12 @@ Exposes:
|
||||
|
||||
- `opencode serve --hostname 127.0.0.1 --port 38992 --print-logs`
|
||||
- `xworkmate-go-core serve --listen 127.0.0.1:3910`
|
||||
- `https://acp-server-opencode.svc.plus`
|
||||
- `https://acp-server-opencode.svc.plus/acp`
|
||||
- `https://acp-server-opencode.svc.plus/acp/rpc`
|
||||
- `https://acp-server.svc.plus/opencode`
|
||||
- `wss://acp-server.svc.plus/opencode/acp`
|
||||
- `https://acp-server.svc.plus/opencode/acp/rpc`
|
||||
|
||||
Notes:
|
||||
|
||||
- `/` stays on the OpenCode HTML UI.
|
||||
- `/acp` and `/acp/rpc` are routed to the XWorkmate ACP bridge with CORS validation.
|
||||
- `/opencode` stays on the OpenCode HTML UI through the unified Caddy prefix.
|
||||
- `/opencode/acp` and `/opencode/acp/rpc` are routed to the XWorkmate ACP bridge with CORS validation.
|
||||
- The bridge advertises only the `opencode` provider by disabling other ACP provider binaries in the service environment.
|
||||
|
||||
@ -21,12 +21,13 @@ acp_opencode_bridge_allowed_origins:
|
||||
- http://127.0.0.1:*
|
||||
acp_opencode_bridge_disabled_binary_path: /nonexistent/acp-disabled
|
||||
acp_opencode_bridge_opencode_binary_path: /usr/bin/opencode
|
||||
acp_opencode_domain: acp-server-opencode.svc.plus
|
||||
acp_opencode_public_base_url: https://acp-server.svc.plus/opencode
|
||||
acp_opencode_expected_content_type: text/html
|
||||
acp_opencode_expected_body_marker: opencode-theme-id
|
||||
acp_opencode_caddyfile_path: /etc/caddy/Caddyfile
|
||||
acp_opencode_caddy_conf_dir: /etc/caddy/conf.d
|
||||
acp_opencode_caddy_fragment_path: /etc/caddy/conf.d/acp-server-opencode.caddy
|
||||
acp_opencode_obsolete_caddy_fragment_paths:
|
||||
- /etc/caddy/conf.d/acp-server-opencode.caddy
|
||||
acp_opencode_enable_ufw: true
|
||||
acp_opencode_packages:
|
||||
- caddy
|
||||
|
||||
@ -48,13 +48,11 @@
|
||||
mode: "0644"
|
||||
notify: Reload caddy
|
||||
|
||||
- name: Deploy OpenCode ACP Caddy site
|
||||
ansible.builtin.template:
|
||||
src: acp-site.caddy.j2
|
||||
dest: "{{ acp_opencode_caddy_fragment_path }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
- name: Remove deprecated standalone OpenCode Caddy fragments
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
loop: "{{ acp_opencode_obsolete_caddy_fragment_paths }}"
|
||||
notify: Reload caddy
|
||||
|
||||
- name: Deploy OpenCode ACP systemd service
|
||||
|
||||
@ -56,7 +56,9 @@
|
||||
- name: Show OpenCode ACP validation summary
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "OpenCode domain: {{ acp_opencode_domain }}"
|
||||
- "OpenCode public base URL: {{ acp_opencode_public_base_url }}"
|
||||
- "Preferred WebSocket endpoint: {{ acp_opencode_public_base_url }}/acp"
|
||||
- "Compatibility HTTP RPC endpoint: {{ acp_opencode_public_base_url }}/acp/rpc"
|
||||
- "OpenCode UI listener: {{ acp_opencode_listen_host }}:{{ acp_opencode_listen_port }}"
|
||||
- "ACP bridge listener: {{ acp_opencode_bridge_listen_host }}:{{ acp_opencode_bridge_listen_port }}"
|
||||
- "Service: {{ acp_opencode_status.stdout | default('N/A') }}"
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
{{ acp_opencode_domain }} {
|
||||
@acp path /acp /acp/*
|
||||
handle @acp {
|
||||
reverse_proxy {{ acp_opencode_bridge_listen_host }}:{{ acp_opencode_bridge_listen_port }}
|
||||
}
|
||||
|
||||
handle {
|
||||
reverse_proxy {{ acp_opencode_listen_host }}:{{ acp_opencode_listen_port }}
|
||||
}
|
||||
}
|
||||
@ -7,6 +7,9 @@ acp_vhosts_codex_upstream_host: 127.0.0.1
|
||||
acp_vhosts_codex_upstream_port: 9010
|
||||
acp_vhosts_opencode_upstream_host: 127.0.0.1
|
||||
acp_vhosts_opencode_upstream_port: 3910
|
||||
acp_vhosts_obsolete_caddy_fragment_paths:
|
||||
- /etc/caddy/conf.d/acp-server-codex.caddy
|
||||
- /etc/caddy/conf.d/acp-server-opencode.caddy
|
||||
acp_vhosts_allowed_origins:
|
||||
- https://xworkmate.svc.plus
|
||||
- http://localhost:*
|
||||
|
||||
@ -60,10 +60,26 @@
|
||||
register: acp_vhosts_opencode_redirect
|
||||
changed_when: false
|
||||
|
||||
- name: Check deprecated standalone ACP Caddy fragments
|
||||
ansible.builtin.stat:
|
||||
path: "{{ item }}"
|
||||
loop: "{{ acp_vhosts_obsolete_caddy_fragment_paths }}"
|
||||
register: acp_vhosts_obsolete_fragments
|
||||
|
||||
- name: Assert deprecated standalone ACP Caddy fragments are absent
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- not item.stat.exists
|
||||
fail_msg: "Deprecated standalone ACP Caddy fragment still exists: {{ item.item }}"
|
||||
success_msg: "Deprecated standalone ACP Caddy fragment removed: {{ item.item }}"
|
||||
loop: "{{ acp_vhosts_obsolete_fragments.results }}"
|
||||
|
||||
- name: Show unified ACP vhost validation summary
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "Unified domain: {{ acp_vhosts_domain }}"
|
||||
- "Codex public base URL: https://{{ acp_vhosts_domain }}/codex"
|
||||
- "OpenCode public base URL: https://{{ acp_vhosts_domain }}/opencode"
|
||||
- "Codex route: /codex -> {{ acp_vhosts_codex_upstream_host }}:{{ acp_vhosts_codex_upstream_port }}"
|
||||
- "OpenCode route: /opencode -> {{ acp_vhosts_opencode_upstream_host }}:{{ acp_vhosts_opencode_upstream_port }}"
|
||||
- "Deployed fragment: {{ acp_vhosts_fragment.stdout | default('N/A') }}"
|
||||
|
||||
29
roles/vhosts/apisix_service/defaults/main.yml
Normal file
29
roles/vhosts/apisix_service/defaults/main.yml
Normal file
@ -0,0 +1,29 @@
|
||||
---
|
||||
apisix_service_example_root: /Users/shenlan/workspaces/cloud-neutral-toolkit/openclaw-deploy-example/deploy/apisix/example/vps
|
||||
apisix_service_root: /opt/svc-ai-gateway
|
||||
apisix_service_conf_dir: "{{ apisix_service_root }}/conf"
|
||||
apisix_service_docs_dir: "{{ apisix_service_root }}/docs"
|
||||
apisix_service_scripts_dir: "{{ apisix_service_root }}/scripts"
|
||||
apisix_service_caddy_conf_dir: /etc/caddy/conf.d
|
||||
apisix_service_caddy_fragment_path: /etc/caddy/conf.d/api.svc.plus.caddy
|
||||
apisix_service_env_file: "{{ apisix_service_root }}/.env"
|
||||
apisix_service_compose_file: "{{ apisix_service_root }}/docker-compose.yml"
|
||||
apisix_service_caddyfile: "{{ apisix_service_root }}/Caddyfile"
|
||||
apisix_service_container_name: svc-ai-gateway
|
||||
apisix_service_http_port: 9080
|
||||
apisix_service_public_host: api.svc.plus
|
||||
apisix_service_validate_env: true
|
||||
apisix_service_source_tree_files:
|
||||
- docker-compose.yml
|
||||
- Caddyfile
|
||||
apisix_service_conf_files:
|
||||
- conf/config.yaml
|
||||
- conf/apisix.yaml
|
||||
apisix_service_doc_files:
|
||||
- docs/api.md
|
||||
- docs/models.md
|
||||
- docs/providers.md
|
||||
apisix_service_script_files:
|
||||
- scripts/validate.sh
|
||||
- scripts/reload.sh
|
||||
- scripts/healthcheck.sh
|
||||
273
roles/vhosts/apisix_service/files/conf/apisix.yaml
Normal file
273
roles/vhosts/apisix_service/files/conf/apisix.yaml
Normal file
@ -0,0 +1,273 @@
|
||||
consumers:
|
||||
- username: ai-gateway-client
|
||||
plugins:
|
||||
key-auth:
|
||||
key: "Bearer ${{AI_GATEWAY_ACCESS_TOKEN:=replace-me}}"
|
||||
|
||||
routes:
|
||||
- id: chat-z-ai-glm5
|
||||
name: chat-z-ai-glm5
|
||||
priority: 100
|
||||
hosts:
|
||||
- "${{API_PUBLIC_HOST:=api.svc.plus}}"
|
||||
uri: /v1/chat/completions
|
||||
methods:
|
||||
- POST
|
||||
vars:
|
||||
- - post_arg.model
|
||||
- ==
|
||||
- z-ai/glm5
|
||||
plugins:
|
||||
key-auth:
|
||||
header: Authorization
|
||||
hide_credentials: true
|
||||
ai-proxy-multi:
|
||||
fallback_strategy:
|
||||
- http_429
|
||||
- http_5xx
|
||||
- rate_limiting
|
||||
instances:
|
||||
- name: ollama-primary
|
||||
provider: openai-compatible
|
||||
weight: 0
|
||||
priority: 30
|
||||
auth:
|
||||
header:
|
||||
Authorization: "Bearer ${{OLLAMA_API_KEY}}"
|
||||
options:
|
||||
model: "${{OLLAMA_CHAT_MODEL:=glm-5:cloud}}"
|
||||
override:
|
||||
endpoint: "${{OLLAMA_CHAT_ENDPOINT}}"
|
||||
- name: nvidia-fallback
|
||||
provider: openai-compatible
|
||||
weight: 0
|
||||
priority: 20
|
||||
auth:
|
||||
header:
|
||||
Authorization: "Bearer ${{NVIDIA_API_KEY}}"
|
||||
options:
|
||||
model: "${{NVIDIA_CHAT_MODEL:=z-ai/glm5}}"
|
||||
override:
|
||||
endpoint: "${{NVIDIA_CHAT_ENDPOINT}}"
|
||||
logging:
|
||||
summaries: true
|
||||
upstream:
|
||||
type: roundrobin
|
||||
nodes:
|
||||
"127.0.0.1:1": 1
|
||||
|
||||
- id: chat-moonshotai-kimi-k2-5
|
||||
name: chat-moonshotai-kimi-k2-5
|
||||
priority: 100
|
||||
hosts:
|
||||
- "${{API_PUBLIC_HOST:=api.svc.plus}}"
|
||||
uri: /v1/chat/completions
|
||||
methods:
|
||||
- POST
|
||||
vars:
|
||||
- - post_arg.model
|
||||
- ==
|
||||
- moonshotai/kimi-k2.5
|
||||
plugins:
|
||||
key-auth:
|
||||
header: Authorization
|
||||
hide_credentials: true
|
||||
ai-proxy:
|
||||
provider: openai-compatible
|
||||
auth:
|
||||
header:
|
||||
Authorization: "Bearer ${{OLLAMA_API_KEY}}"
|
||||
options:
|
||||
model: "${{OLLAMA_KIMI_MODEL:=kimi-k2.5:cloud}}"
|
||||
override:
|
||||
endpoint: "${{OLLAMA_CHAT_ENDPOINT}}"
|
||||
logging:
|
||||
summaries: true
|
||||
upstream:
|
||||
type: roundrobin
|
||||
nodes:
|
||||
"127.0.0.1:1": 1
|
||||
|
||||
- id: chat-minimaxai-minimax-m2-5
|
||||
name: chat-minimaxai-minimax-m2-5
|
||||
priority: 100
|
||||
hosts:
|
||||
- "${{API_PUBLIC_HOST:=api.svc.plus}}"
|
||||
uri: /v1/chat/completions
|
||||
methods:
|
||||
- POST
|
||||
vars:
|
||||
- - post_arg.model
|
||||
- ==
|
||||
- minimaxai/minimax-m2.5
|
||||
plugins:
|
||||
key-auth:
|
||||
header: Authorization
|
||||
hide_credentials: true
|
||||
ai-proxy:
|
||||
provider: openai-compatible
|
||||
auth:
|
||||
header:
|
||||
Authorization: "Bearer ${{OLLAMA_API_KEY}}"
|
||||
options:
|
||||
model: "${{OLLAMA_MINIMAX_MODEL:=minimax-m2.5:cloud}}"
|
||||
override:
|
||||
endpoint: "${{OLLAMA_CHAT_ENDPOINT}}"
|
||||
logging:
|
||||
summaries: true
|
||||
upstream:
|
||||
type: roundrobin
|
||||
nodes:
|
||||
"127.0.0.1:1": 1
|
||||
|
||||
- id: chat-unsupported-model
|
||||
name: chat-unsupported-model
|
||||
priority: 1
|
||||
hosts:
|
||||
- "${{API_PUBLIC_HOST:=api.svc.plus}}"
|
||||
uri: /v1/chat/completions
|
||||
methods:
|
||||
- POST
|
||||
plugins:
|
||||
key-auth:
|
||||
header: Authorization
|
||||
hide_credentials: true
|
||||
serverless-pre-function:
|
||||
phase: access
|
||||
functions:
|
||||
- |
|
||||
return function()
|
||||
local core = require("apisix.core")
|
||||
return core.response.exit(400, {
|
||||
error = {
|
||||
message = "Unsupported chat model. Use z-ai/glm5, moonshotai/kimi-k2.5, or minimaxai/minimax-m2.5.",
|
||||
type = "invalid_request_error",
|
||||
param = "model",
|
||||
code = "model_not_found"
|
||||
}
|
||||
})
|
||||
end
|
||||
upstream:
|
||||
type: roundrobin
|
||||
nodes:
|
||||
"127.0.0.1:1": 1
|
||||
|
||||
- id: embeddings-text-embedding-3-small
|
||||
name: embeddings-text-embedding-3-small
|
||||
priority: 100
|
||||
hosts:
|
||||
- "${{API_PUBLIC_HOST:=api.svc.plus}}"
|
||||
uri: /v1/embeddings
|
||||
methods:
|
||||
- POST
|
||||
vars:
|
||||
- - post_arg.model
|
||||
- ==
|
||||
- text-embedding-3-small
|
||||
plugins:
|
||||
key-auth:
|
||||
header: Authorization
|
||||
hide_credentials: true
|
||||
ai-proxy:
|
||||
provider: openai-compatible
|
||||
auth:
|
||||
header:
|
||||
Authorization: "Bearer ${{EMBEDDINGS_API_KEY}}"
|
||||
options:
|
||||
model: "${{EMBEDDINGS_MODEL:=text-embedding-3-small}}"
|
||||
override:
|
||||
endpoint: "${{EMBEDDINGS_ENDPOINT}}"
|
||||
logging:
|
||||
summaries: true
|
||||
upstream:
|
||||
type: roundrobin
|
||||
nodes:
|
||||
"127.0.0.1:1": 1
|
||||
|
||||
- id: embeddings-unsupported-model
|
||||
name: embeddings-unsupported-model
|
||||
priority: 1
|
||||
hosts:
|
||||
- "${{API_PUBLIC_HOST:=api.svc.plus}}"
|
||||
uri: /v1/embeddings
|
||||
methods:
|
||||
- POST
|
||||
plugins:
|
||||
key-auth:
|
||||
header: Authorization
|
||||
hide_credentials: true
|
||||
serverless-pre-function:
|
||||
phase: access
|
||||
functions:
|
||||
- |
|
||||
return function()
|
||||
local core = require("apisix.core")
|
||||
return core.response.exit(400, {
|
||||
error = {
|
||||
message = "Unsupported embedding model. Use text-embedding-3-small.",
|
||||
type = "invalid_request_error",
|
||||
param = "model",
|
||||
code = "model_not_found"
|
||||
}
|
||||
})
|
||||
end
|
||||
upstream:
|
||||
type: roundrobin
|
||||
nodes:
|
||||
"127.0.0.1:1": 1
|
||||
|
||||
- id: models-catalog
|
||||
name: models-catalog
|
||||
priority: 100
|
||||
hosts:
|
||||
- "${{API_PUBLIC_HOST:=api.svc.plus}}"
|
||||
uri: /v1/models
|
||||
methods:
|
||||
- GET
|
||||
plugins:
|
||||
key-auth:
|
||||
header: Authorization
|
||||
hide_credentials: true
|
||||
serverless-pre-function:
|
||||
phase: access
|
||||
functions:
|
||||
- |
|
||||
return function()
|
||||
local core = require("apisix.core")
|
||||
return core.response.exit(200, {
|
||||
object = "list",
|
||||
data = {
|
||||
{
|
||||
id = "z-ai/glm5",
|
||||
object = "model",
|
||||
owned_by = "svc-ai-gateway",
|
||||
root = "z-ai/glm5"
|
||||
},
|
||||
{
|
||||
id = "moonshotai/kimi-k2.5",
|
||||
object = "model",
|
||||
owned_by = "svc-ai-gateway",
|
||||
root = "moonshotai/kimi-k2.5"
|
||||
},
|
||||
{
|
||||
id = "minimaxai/minimax-m2.5",
|
||||
object = "model",
|
||||
owned_by = "svc-ai-gateway",
|
||||
root = "minimaxai/minimax-m2.5"
|
||||
},
|
||||
{
|
||||
id = "text-embedding-3-small",
|
||||
object = "model",
|
||||
owned_by = "svc-ai-gateway",
|
||||
root = "text-embedding-3-small"
|
||||
}
|
||||
}
|
||||
}, {
|
||||
["Content-Type"] = "application/json"
|
||||
})
|
||||
end
|
||||
upstream:
|
||||
type: roundrobin
|
||||
nodes:
|
||||
"127.0.0.1:1": 1
|
||||
#END
|
||||
16
roles/vhosts/apisix_service/files/conf/config.yaml
Normal file
16
roles/vhosts/apisix_service/files/conf/config.yaml
Normal file
@ -0,0 +1,16 @@
|
||||
apisix:
|
||||
node_listen:
|
||||
- 9080
|
||||
enable_admin: false
|
||||
enable_control: false
|
||||
router:
|
||||
http: radixtree_host_uri
|
||||
|
||||
deployment:
|
||||
role: data_plane
|
||||
role_data_plane:
|
||||
config_provider: yaml
|
||||
|
||||
nginx_config:
|
||||
http:
|
||||
access_log_format: '{"request_id":"$request_id","host":"$host","uri":"$uri","status":"$status","upstream_addr":"$upstream_addr","request_time":"$request_time","upstream_response_time":"$upstream_response_time"}'
|
||||
36
roles/vhosts/apisix_service/files/scripts/healthcheck.sh
Normal file
36
roles/vhosts/apisix_service/files/scripts/healthcheck.sh
Normal file
@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
ENV_FILE="${ROOT_DIR}/.env"
|
||||
COMPOSE_FILE="${ROOT_DIR}/docker-compose.yml"
|
||||
CADDYFILE="/etc/caddy/Caddyfile"
|
||||
|
||||
if [[ -f "$ENV_FILE" ]]; then
|
||||
set -a
|
||||
# shellcheck disable=SC1090
|
||||
source "$ENV_FILE"
|
||||
set +a
|
||||
fi
|
||||
|
||||
: "${API_PUBLIC_HOST:=api.svc.plus}"
|
||||
: "${AI_GATEWAY_ACCESS_TOKEN:?missing AI_GATEWAY_ACCESS_TOKEN in ${ENV_FILE}}"
|
||||
|
||||
for file in "$COMPOSE_FILE" "$CADDYFILE"; do
|
||||
[[ -f "$file" ]] || {
|
||||
printf '[svc-ai-gateway] missing file: %s\n' "$file" >&2
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
|
||||
caddy validate --config "$CADDYFILE" >/dev/null
|
||||
systemctl is-active --quiet caddy
|
||||
docker compose -f "$COMPOSE_FILE" ps --status running apisix >/dev/null
|
||||
|
||||
curl --fail --silent --show-error \
|
||||
--resolve "${API_PUBLIC_HOST}:443:127.0.0.1" \
|
||||
-H "Authorization: Bearer ${AI_GATEWAY_ACCESS_TOKEN}" \
|
||||
"https://${API_PUBLIC_HOST}/v1/models" >/dev/null
|
||||
|
||||
printf '[svc-ai-gateway] healthcheck passed\n'
|
||||
16
roles/vhosts/apisix_service/files/scripts/reload.sh
Normal file
16
roles/vhosts/apisix_service/files/scripts/reload.sh
Normal file
@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
COMPOSE_FILE="${ROOT_DIR}/docker-compose.yml"
|
||||
|
||||
"${SCRIPT_DIR}/validate.sh"
|
||||
|
||||
if docker compose -f "$COMPOSE_FILE" ps --status running apisix >/dev/null 2>&1; then
|
||||
docker compose -f "$COMPOSE_FILE" restart apisix
|
||||
else
|
||||
docker compose -f "$COMPOSE_FILE" up -d apisix
|
||||
fi
|
||||
|
||||
printf '[svc-ai-gateway] reload finished\n'
|
||||
24
roles/vhosts/apisix_service/files/scripts/validate.sh
Normal file
24
roles/vhosts/apisix_service/files/scripts/validate.sh
Normal file
@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
|
||||
CONFIG_FILE="${ROOT_DIR}/conf/config.yaml"
|
||||
RULES_FILE="${ROOT_DIR}/conf/apisix.yaml"
|
||||
COMPOSE_FILE="${ROOT_DIR}/docker-compose.yml"
|
||||
|
||||
for file in "$CONFIG_FILE" "$RULES_FILE" "$COMPOSE_FILE"; do
|
||||
[[ -f "$file" ]] || {
|
||||
printf '[svc-ai-gateway] missing file: %s\n' "$file" >&2
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
|
||||
tail -n 1 "$RULES_FILE" | grep -q '^#END$' || {
|
||||
printf '[svc-ai-gateway] conf/apisix.yaml must end with #END for standalone reloads\n' >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
docker compose -f "$COMPOSE_FILE" config >/dev/null
|
||||
printf '[svc-ai-gateway] validation passed\n'
|
||||
4
roles/vhosts/apisix_service/handlers/main.yml
Normal file
4
roles/vhosts/apisix_service/handlers/main.yml
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
- name: Reload caddy
|
||||
ansible.builtin.command: systemctl reload caddy
|
||||
changed_when: true
|
||||
185
roles/vhosts/apisix_service/tasks/main.yml
Normal file
185
roles/vhosts/apisix_service/tasks/main.yml
Normal file
@ -0,0 +1,185 @@
|
||||
---
|
||||
- name: Check docker CLI is present on the target node
|
||||
ansible.builtin.command: docker --version
|
||||
changed_when: false
|
||||
|
||||
- name: Check docker compose plugin is present on the target node
|
||||
ansible.builtin.command: docker compose version
|
||||
changed_when: false
|
||||
|
||||
- name: Check caddy CLI is present on the target node
|
||||
ansible.builtin.command: caddy version
|
||||
changed_when: false
|
||||
|
||||
- name: Check managed APISIX source tree
|
||||
ansible.builtin.stat:
|
||||
path: "{{ apisix_service_root }}"
|
||||
register: apisix_service_root_stat
|
||||
|
||||
- name: Check managed APISIX conf directory
|
||||
ansible.builtin.stat:
|
||||
path: "{{ apisix_service_conf_dir }}"
|
||||
register: apisix_service_conf_dir_stat
|
||||
|
||||
- name: Check managed APISIX docs directory
|
||||
ansible.builtin.stat:
|
||||
path: "{{ apisix_service_docs_dir }}"
|
||||
register: apisix_service_docs_dir_stat
|
||||
|
||||
- name: Check managed APISIX scripts directory
|
||||
ansible.builtin.stat:
|
||||
path: "{{ apisix_service_scripts_dir }}"
|
||||
register: apisix_service_scripts_dir_stat
|
||||
|
||||
- name: Check managed APISIX Caddy fragment directory
|
||||
ansible.builtin.stat:
|
||||
path: "{{ apisix_service_caddy_conf_dir }}"
|
||||
register: apisix_service_caddy_conf_dir_stat
|
||||
|
||||
- name: Check managed APISIX env file
|
||||
ansible.builtin.stat:
|
||||
path: "{{ apisix_service_env_file }}"
|
||||
register: apisix_service_env_file_stat
|
||||
|
||||
- name: Validate APISIX live configuration prerequisites
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- apisix_service_env_file_stat.stat.exists
|
||||
- apisix_service_public_host | length > 0
|
||||
- apisix_service_http_port | int > 0
|
||||
fail_msg: >-
|
||||
api.svc.plus requires a preseeded .env plus a valid public host and HTTP port.
|
||||
|
||||
- name: Determine whether managed APISIX files can be written in this run
|
||||
ansible.builtin.set_fact:
|
||||
apisix_service_source_tree_writable: "{{ (not ansible_check_mode) or apisix_service_root_stat.stat.exists }}"
|
||||
apisix_service_conf_writable: "{{ (not ansible_check_mode) or apisix_service_conf_dir_stat.stat.exists }}"
|
||||
apisix_service_docs_writable: "{{ (not ansible_check_mode) or apisix_service_docs_dir_stat.stat.exists }}"
|
||||
apisix_service_scripts_writable: "{{ (not ansible_check_mode) or apisix_service_scripts_dir_stat.stat.exists }}"
|
||||
apisix_service_caddy_writable: "{{ (not ansible_check_mode) or apisix_service_caddy_conf_dir_stat.stat.exists }}"
|
||||
|
||||
- name: Ensure managed APISIX directories exist
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
loop:
|
||||
- "{{ apisix_service_root }}"
|
||||
- "{{ apisix_service_conf_dir }}"
|
||||
- "{{ apisix_service_docs_dir }}"
|
||||
- "{{ apisix_service_scripts_dir }}"
|
||||
- "{{ apisix_service_caddy_conf_dir }}"
|
||||
|
||||
- name: Copy managed APISIX source tree files
|
||||
ansible.builtin.copy:
|
||||
src: "{{ apisix_service_example_root }}/{{ item }}"
|
||||
dest: "{{ apisix_service_root }}/{{ item }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
loop: "{{ apisix_service_source_tree_files }}"
|
||||
when: apisix_service_source_tree_writable
|
||||
|
||||
- name: Copy managed APISIX config files
|
||||
ansible.builtin.copy:
|
||||
src: "{{ role_path }}/files/{{ item }}"
|
||||
dest: "{{ apisix_service_root }}/{{ item }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
loop: "{{ apisix_service_conf_files }}"
|
||||
when: apisix_service_conf_writable
|
||||
|
||||
- name: Copy managed APISIX docs
|
||||
ansible.builtin.copy:
|
||||
src: "{{ apisix_service_example_root }}/{{ item }}"
|
||||
dest: "{{ apisix_service_root }}/{{ item }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
loop: "{{ apisix_service_doc_files }}"
|
||||
when: apisix_service_docs_writable
|
||||
|
||||
- name: Copy managed APISIX helper scripts
|
||||
ansible.builtin.copy:
|
||||
src: "{{ role_path }}/files/{{ item }}"
|
||||
dest: "{{ apisix_service_root }}/{{ item }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
loop: "{{ apisix_service_script_files }}"
|
||||
when: apisix_service_scripts_writable
|
||||
|
||||
- name: Deploy managed APISIX Caddy fragment
|
||||
ansible.builtin.copy:
|
||||
src: "{{ apisix_service_example_root }}/Caddyfile"
|
||||
dest: "{{ apisix_service_caddy_fragment_path }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify: Reload caddy
|
||||
when: apisix_service_caddy_writable
|
||||
|
||||
- name: Validate APISIX env contract
|
||||
ansible.builtin.shell: |
|
||||
set -euo pipefail
|
||||
for key in \
|
||||
APISIX_HTTP_PORT \
|
||||
API_PUBLIC_HOST \
|
||||
OLLAMA_API_KEY \
|
||||
OLLAMA_CHAT_ENDPOINT \
|
||||
OLLAMA_CHAT_MODEL \
|
||||
OLLAMA_MINIMAX_MODEL \
|
||||
OLLAMA_KIMI_MODEL \
|
||||
NVIDIA_API_KEY \
|
||||
NVIDIA_CHAT_ENDPOINT \
|
||||
NVIDIA_CHAT_MODEL \
|
||||
KIMI_API_KEY \
|
||||
KIMI_CHAT_ENDPOINT \
|
||||
KIMI_CHAT_MODEL \
|
||||
MINIMAX_API_KEY \
|
||||
MINIMAX_CHAT_ENDPOINT \
|
||||
MINIMAX_CHAT_MODEL \
|
||||
EMBEDDINGS_API_KEY \
|
||||
EMBEDDINGS_ENDPOINT \
|
||||
EMBEDDINGS_MODEL \
|
||||
AI_GATEWAY_ACCESS_TOKEN
|
||||
do
|
||||
grep -q "^${key}=" "{{ apisix_service_env_file }}"
|
||||
done
|
||||
changed_when: false
|
||||
when: apisix_service_validate_env | bool
|
||||
|
||||
- name: Validate Caddy configuration for APISIX
|
||||
ansible.builtin.command: caddy validate --config /etc/caddy/Caddyfile
|
||||
changed_when: false
|
||||
when: not ansible_check_mode
|
||||
|
||||
- name: Pull and start APISIX release
|
||||
ansible.builtin.shell: |
|
||||
set -euo pipefail
|
||||
docker compose -f "{{ apisix_service_compose_file }}" pull
|
||||
docker compose -f "{{ apisix_service_compose_file }}" up -d --remove-orphans
|
||||
args:
|
||||
executable: /bin/bash
|
||||
changed_when: true
|
||||
when: not ansible_check_mode
|
||||
|
||||
- name: Wait for APISIX loopback port
|
||||
ansible.builtin.wait_for:
|
||||
host: 127.0.0.1
|
||||
port: "{{ apisix_service_http_port }}"
|
||||
timeout: 60
|
||||
when: not ansible_check_mode
|
||||
|
||||
- name: Reload Caddy after APISIX update
|
||||
ansible.builtin.command: systemctl reload caddy
|
||||
changed_when: true
|
||||
when: not ansible_check_mode
|
||||
|
||||
- name: Run APISIX health check
|
||||
ansible.builtin.command: "{{ apisix_service_scripts_dir }}/healthcheck.sh"
|
||||
changed_when: false
|
||||
when: not ansible_check_mode
|
||||
@ -50,6 +50,11 @@ cloudflare_dns_records:
|
||||
content: jp-xhttp-contabo.svc.plus
|
||||
ttl: 1
|
||||
proxied: false
|
||||
- type: A
|
||||
name: jp-xhttp-contabo.svc.plus
|
||||
content: 46.250.251.132
|
||||
ttl: 1
|
||||
proxied: false
|
||||
- type: CNAME
|
||||
name: console.svc.plus
|
||||
content: console-8fa9cd3-contabo.svc.plus
|
||||
@ -65,6 +70,16 @@ cloudflare_dns_records:
|
||||
content: vps-accounts.svc.plus
|
||||
ttl: 1
|
||||
proxied: false
|
||||
- type: A
|
||||
name: vps-api.svc.plus
|
||||
content: 46.250.251.132
|
||||
ttl: 1
|
||||
proxied: false
|
||||
- type: CNAME
|
||||
name: api.svc.plus
|
||||
content: vps-api.svc.plus
|
||||
ttl: 1
|
||||
proxied: false
|
||||
- type: CNAME
|
||||
name: preview-accounts.svc.plus
|
||||
content: vps-preview-accounts.svc.plus
|
||||
|
||||
Loading…
Reference in New Issue
Block a user