Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,9 @@ sandbox.ipynb
/ton-http-api/src/core/tlb/tokens-tlb.h

# build
[Bb][Uu][Ii][Ll][Dd]*/
sandbox/
/[Bb][Uu][Ii][Ll][Dd]*/
/sandbox
/artifacts

# vs code
.vscode/
Expand All @@ -226,3 +227,4 @@ sandbox/
# codegen schemas
playground/schemas/
ton-http-api/src/schemas/
ton-http-api/static/openapi.json
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ COPY external/ /app/external/

ARG BUILD_WITH_TON_REPO
ARG BUILD_WITH_TON_REPO
ARG API_VERSION_TAG
RUN if [ -n "${BUILD_WITH_TON_REPO}" ]; then \
echo "Using ton from ${BUILD_WITH_TON_REPO}:${BUILD_WITH_TON_REPO:-master}"; \
rm -rf /app/external/ton/ && \
Expand All @@ -47,7 +48,7 @@ RUN if [ -n "${BUILD_WITH_TON_REPO}" ]; then \

WORKDIR /app/build
RUN touch /app/suppression_mappings.txt \
&& cmake -DCMAKE_BUILD_TYPE=Release -DTON_USE_STATIC_ZLIB=1 -DUSERVER_USE_STATIC_LIBS=1 -DPORTABLE=1 -GNinja .. \
&& cmake -DCMAKE_BUILD_TYPE=Release -DAPI_VERSION_TAG=${API_VERSION_TAG:-2.1} -DTON_USE_STATIC_ZLIB=1 -DUSERVER_USE_STATIC_LIBS=1 -DPORTABLE=1 -GNinja .. \
&& ninja -j$(nproc)
# end builder

Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,19 @@ List of available environment variables:
- `THACPP_STATIC_CONTENT_DIR` - directory for static content served by API (default: `"/static/"`)
- `THACPP_MAX_STACK_ENTRY_DEPTH` - max stack entry depth for runGetMethod (higher values increase memory usage) (default: `256`)

### Ansible
To deploy a service using Ansible, please follow the instructions:
- Adjust file `ansible/inventory.yaml` to match your environment.
- Adjust file `ansible/vars.json` to match your environment.
- *Important*: encode your `global.config.json` into base64 string and put it into `ton_global_config` var. Possible way to do this: `cat global.config.json | base64 -w0`.
- Use `stack_name` to deploy several installations.
- Use `deploy_path` to specify the path to install the service, the deployment path should be different for each installation.
- Run `ansible-playbook -i ansible/inventory.yaml -e @ansible/vars.json deploy-systemd.yaml`

Requirements:
- Ubuntu 22.04 or 24.04, builder and worker nodes should run the same version.
- Ansible core 2.20 or higher.

### Local run

- Install dependencies:
Expand Down
4 changes: 4 additions & 0 deletions ansible.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[defaults]
host_key_checking=false
allow_world_readable_tmpfiles=true
interpreter_python=auto_silent
32 changes: 32 additions & 0 deletions ansible/api.service.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[Unit]
Description={{ stack_name }} API v2++
After=network-online.target
Wants=network-online.target

StartLimitIntervalSec={{ service_config.start_limit_interval_sec }}
StartLimitBurst={{ service_config.start_limit_burst }}

[Service]
Type=simple

User={{ ansible_user }}
Group={{ ansible_user }}
WorkingDirectory={{ deploy_path }}

ExecStart={{ deploy_path }}/ton-http-api-cpp --config {{ deploy_path }}/static_config.yaml --config_vars {{ deploy_path }}/config_vars.yaml 2>&1

Restart={{ service_config.restart_policy }}
RestartSec={{ service_config.restart_sec }}

# Graceful shutdown
KillSignal=SIGTERM
TimeoutStopSec=30

# Hardening, optional but useful
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true

[Install]
WantedBy=multi-user.target
5 changes: 5 additions & 0 deletions ansible/config_vars.yaml.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
tonlib_config_path: {{ deploy_path }}/ton_global_config.json
tonlib_keystore_path: {{ deploy_path }}/keystore
static_content_dir: {{ deploy_path }}/static

{{ config_vars | to_nice_yaml(indent=0, sort_keys=false) }}
7 changes: 7 additions & 0 deletions ansible/healthcheck.service.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[Unit]
Description=Healthcheck for {{ stack_name }}_api_v2pp

[Service]
Type=oneshot

ExecStart=/bin/sh -c 'curl -fsS --max-time 5 "http://localhost:{{ config_vars.server_port }}/api/v2/getMasterchainInfo" >/dev/null || systemctl restart {{ stack_name }}_api_v2pp.service'
10 changes: 10 additions & 0 deletions ansible/healthcheck.timer.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[Unit]
Description=Run {{ stack_name }}_api_v2pp healthcheck periodically

[Timer]
OnBootSec=30
OnUnitActiveSec=2
AccuracySec=5

[Install]
WantedBy=timers.target
22 changes: 22 additions & 0 deletions ansible/inventory.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
all:
vars:
ansible_user: ubuntu
ansible_ssh_private_key_file: ~/.ssh/id_rsa
hosts:
ubuntu-01:
ansible_host: ubuntu-01.local
ubuntu-02:
ansible_host: ubuntu-02.local
builder-01:
ansible_host: tc-node-02.toncenter.local
children:
builder:
hosts:
builder-01:
mainnet__http_api_cpp:
hosts:
ubuntu-01:
ubuntu-02:
testnet__http_api_cpp:
hosts:
ubuntu-01:
28 changes: 28 additions & 0 deletions ansible/vars.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"stack_name": "mainnet",
"deploy_path": "/opt/ton-http-api/mainnet",
"service_config": {
"start_limit_interval_sec": 60,
"restart_sec": 5,
"start_limit_burst": 10,
"restart_policy": "on-failure"
},
"config_vars": {
"server_port": 8877,
"monitor_port": 8878,
"tonlib_boc_endpoints": [],
"tonlib_threads": 2,
"main_worker_threads": 4,
"fs_worker_threads": 1,
"http_worker_threads": 2,
"log_level": "warning",
"log_format": "json",
"system_log_level": "critical",
"system_log_path": "@stderr",
"jsonrpc_log_level": "critical",
"jsonrpc_log_path": "@stderr",
"http_worker_user_agent": "empty",
"max_stack_entry_depth": 1024
},
"ton_global_config": "<replace it with base64 encoded global config json file>"
}
9 changes: 9 additions & 0 deletions config/static_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,15 @@ components_manager:
dir: $static_content_dir
dir#fallback: /app/static/
update-period: 10s

#
# middlewares
#
default-server-middleware-pipeline-builder:
append:
- version-header-middleware
version-header-middleware: {}

#
# api v2 handlers
#
Expand Down
189 changes: 189 additions & 0 deletions deploy-docker.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
- name: Compute image tag
hosts: localhost
connection: local
gather_facts: false
vars:
repo_dir: "{{ playbook_dir }}"
tasks:
- name: Get short git commit
ansible.builtin.command: git rev-parse --short HEAD
args:
chdir: "{{ repo_dir }}"
register: git_commit_short
changed_when: false
no_log: true
- name: Get tags pointing at current commit
ansible.builtin.command: git tag --points-at HEAD --sort=-creatordate
args:
chdir: "{{ repo_dir }}"
register: git_tags
changed_when: false
no_log: true
- name: Get latest git tag in current history
ansible.builtin.command: git describe --tags --abbrev=0
args:
chdir: "{{ repo_dir }}"
register: git_latest_tag
changed_when: false
failed_when: false
- name: Set image_tag
ansible.builtin.set_fact:
image_tag: >-
{{
git_tags.stdout_lines[0]
if git_tags.stdout_lines | length > 0
else (
git_latest_tag.stdout + '-' + git_commit_short.stdout
if git_latest_tag.rc == 0 and git_latest_tag.stdout | length > 0
else git_commit_short.stdout
)
}}
- name: Show image tag
ansible.builtin.debug:
var: image_tag

- name: Build TON HTTP API docker image
hosts: builder
gather_facts: true
vars:
image_tag: "{{ hostvars['localhost']['image_tag'] }}"
tasks:
- name: Ensure docker is installed
become: true
ansible.builtin.package:
name: docker
state: present
- name: Create temp workspace
ansible.builtin.tempfile:
state: directory
prefix: ton_http_api_cpp
suffix: "_{{ image_tag }}"
register: build_dir
- block:
- name: Copy project to builder from . to {{ build_dir.path }} with rsync
ansible.builtin.synchronize:
src: "{{ playbook_dir }}/"
dest: "{{ build_dir.path }}"
rsync_opts:
- "--exclude=.git"
- "--exclude={{ playbook_dir }}/artifacts"
- "--exclude={{ playbook_dir }}/private"
- "--exclude={{ playbook_dir }}/build"
- "--exclude={{ playbook_dir }}/sandbox"
recursive: true
delete: false
- name: Ensure output directory
ansible.builtin.file:
path: "{{ build_dir.path }}/images"
state: directory
mode: '0755'
- name: Build docker image
community.docker.docker_image_build:
name: "{{ stack_name }}_http_api_cpp"
tag: "{{ image_tag }}"
path: "{{ build_dir.path }}"
dockerfile: Dockerfile
target: http-api-cpp
rebuild: always
- name: Export docker image
community.docker.docker_image_export:
names:
- "{{ stack_name }}_http_api_cpp:{{ image_tag }}"
path: "{{ build_dir.path }}/images/{{ stack_name }}_{{ image_tag }}.tar"
- name: Collect artifacts
ansible.builtin.fetch:
src: "{{ build_dir.path }}/images/{{ item }}"
dest: "artifacts/"
flat: true
loop:
- "{{ stack_name }}_{{ image_tag }}.tar"
always:
- name: Cleanup workspace
ansible.builtin.file:
path: "{{ build_dir.path }}"
state: absent

- name: Deploy TON HTTP API docker image
hosts: "{{ stack_name }}__http_api_cpp"
tags: [deploy]
vars:
image_tag: "{{ hostvars['localhost']['image_tag'] }}"
tasks:
- name: Ensure docker is installed
become: true
ansible.builtin.package:
name: docker
state: present
- name: Ensure deploy directory
become: true
ansible.builtin.file:
path: "{{ deploy_path }}"
state: directory
mode: '0755'
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
- name: Copy docker image
ansible.builtin.copy:
src: "artifacts/{{ stack_name }}_{{ image_tag }}.tar"
dest: "{{ deploy_path }}/images/"
- name: Load docker image
community.docker.docker_image_load:
path: "{{ deploy_path }}/images/{{ stack_name }}_{{ image_tag }}.tar"
register: docker_image
- name: Render config file
ansible.builtin.copy:
dest: "{{ deploy_path }}/ton_global_config.json"
content: "{{ ton_global_config | to_nice_json }}"
mode: "0644"
- name: Run docker container
community.docker.docker_container:
name: "{{ stack_name }}_http_api_cpp"
image: "{{ stack_name }}_http_api_cpp:{{ image_tag }}"
env:
"THACPP_TONLIB_CONFIG_PATH": "/ton_global_config.json"
"THACPP_TONLIB_KEYSTORE_PATH": "/tmp"
"THACPP_TONLIB_BOC_ENDPOINTS": "{{ config.get('tonlib_boc_endpoints', []) | to_json }}"
"THACPP_TONLIB_THREADS": "{{ config.get('tonlib_threads', 4) | string }}"
"THACPP_MAIN_WORKER_THREADS": "{{ config.get('main_worker_threads', 4) | string }}"
"THACPP_FS_WORKER_THREADS": "{{ config.get('fs_worker_threads', 1) | string }}"
"THACPP_HTTP_WORKER_THREADS": "{{ config.get('http_worker_threads', 2) | string }}"
"THACPP_LOG_LEVEL": "{{ config.get('log_level', 'warning') | string }}"
"THACPP_LOG_PATH": "{{ config.get('log_path', '@stdout') | string }}"
"THACPP_SYSTEM_LOG_LEVEL": "{{ config.get('system_log_level', 'critical') | string }}"
"THACPP_SYSTEM_LOG_PATH": "{{ config.get('system_log_path', '/logs/system.log') | string }}"
"THACPP_JSONRPC_LOG_LEVEL": "{{ config.get('jsonrpc_log_level', 'info') | string }}"
"THACPP_JSONRPC_LOG_PATH": "{{ config.get('jsonrpc_log_path', '/logs/jsonrpc.log') | string }}"
"THACPP_LOG_FORMAT": "{{ config.get('log_format', 'json') | string }}"
"THACPP_HTTP_WORKER_USER_AGENT": "{{ config.get('http_worker_user_agent', 'ton-http-api-cpp') | string }}"
"THACPP_STATIC_CONTENT_DIR": "{{ config.get('static_content_dir', '/static') | string }}"
"THACPP_MAX_STACK_ENTRY_DEPTH": "{{ config.get('max_stack_entry_depth', 1000) | string }} "
volumes:
- "{{ deploy_path }}/ton_global_config.json:/ton_global_config.json"
- "{{ deploy_path }}/logs:/logs"
healthy_wait_timeout: 20
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:8081/api/v2/healthcheck || exit 1"]
interval: 3s
retries: 3
state: healthy
restart_policy: unless-stopped
detach: true
labels:
autoheal: 1
ulimits:
- "memlock:-1"
- "stack:-1"
- "core:-1"
published_ports:
- "0.0.0.0:{{ api_port }}:8081"
- "0.0.0.0:{{ monitoring_port }}:8082"
- name: Ensure autoheal container
community.docker.docker_container:
name: "autoheal"
image: "willfarrell/autoheal"
env:
AUTOHEAL_CONTAINER_LABEL: autoheal
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
restart_policy: always
detach: true
Loading