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
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ name: CI
on:
pull_request:
branches: ["master", "release/**"]
paths-ignore:
- "**/*.md"
- "docs/**"

concurrency:
group: ci-${{ github.event.pull_request.number || github.ref }}
Expand Down
73 changes: 73 additions & 0 deletions .github/workflows/nodectl-e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: nodectl-e2e

on:
# Auto trigger: PR approved targeting release/nodectl/*
pull_request_review:
types: [submitted]

# Manual trigger: label "run-e2e" on any PR
pull_request:
types: [labeled]

# Manual dispatch
workflow_dispatch:

concurrency:
group: nodectl-e2e-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

env:
CARGO_TERM_COLOR: always

jobs:
e2e:
runs-on: ubuntu-latest
timeout-minutes: 30
if: >
(
github.event_name == 'pull_request_review' &&
github.event.review.state == 'approved' &&
startsWith(github.event.pull_request.base.ref, 'release/nodectl/')
) ||
(
github.event_name == 'pull_request' &&
github.event.label.name == 'run-e2e'
) ||
github.event_name == 'workflow_dispatch'
defaults:
run:
working-directory: src
steps:
- uses: actions/checkout@v5

- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y pkg-config clang libssl-dev libzstd-dev \
libgoogle-perftools-dev build-essential xxd jq python3 python3-yaml

- name: Install bun
uses: oven-sh/setup-bun@v2

- uses: Swatinem/rust-cache@v2
with:
workspaces: src -> target
shared-key: nodectl-e2e

- name: Install bun deps
run: bun install
working-directory: src/node/tests/test_load_net

- name: Run nodectl e2e test
env:
MASTER_WALLET_KEY: ${{ secrets.MASTER_WALLET_KEY }}
run: bash node/tests/test_run_net_py/test_nodectl_ci.sh

- name: Upload logs on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: nodectl-e2e-logs
path: |
src/node/tests/test_run_net_py/tmp/nodectl-service.log
src/node/tests/test_run_net_py/tmp/singlehost-bootstrap.log
40 changes: 40 additions & 0 deletions src/node-control/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,43 @@ CONFIG_IDS := 15 16 17 32 34 36

$(CONFIG_IDS):
cargo run -p nodectl -- config-param --config="$(CONFIG)" $@

# ── Singlehost integration test ───────────────────────────────────────────────
# Boots a local 6-node TON network and runs nodectl against it end-to-end.
#
# Variables (all optional):
# NOBUILD=1 skip cargo build (use pre-built binary)
# NODE_CNT=6 number of validator nodes
# PARTICIPANTS_WAIT=600 seconds to wait for election participants
#
# Examples:
# make singlehost
# make singlehost NOBUILD=1
# make singlehost NODE_CNT=4 PARTICIPANTS_WAIT=900

SINGLEHOST_DIR ?= ../node/tests/test_run_net_py
NOBUILD ?= 0
NODE_CNT ?= 6
PARTICIPANTS_WAIT ?= 600

# File target: rebuilt only when requirements.txt changes (or venv is missing).
# Calling `make singlehost-venv` installs deps; subsequent calls are instant.
$(SINGLEHOST_DIR)/.venv/pyvenv.cfg: $(SINGLEHOST_DIR)/requirements.txt
python3 -m venv $(SINGLEHOST_DIR)/.venv
$(SINGLEHOST_DIR)/.venv/bin/pip install -q -r $(SINGLEHOST_DIR)/requirements.txt

singlehost-venv: $(SINGLEHOST_DIR)/.venv/pyvenv.cfg

test-e2e: $(SINGLEHOST_DIR)/.venv/pyvenv.cfg
cd $(SINGLEHOST_DIR) && \
NOBUILD=$(NOBUILD) \
NODE_CNT=$(NODE_CNT) \
PARTICIPANTS_WAIT_SECONDS=$(PARTICIPANTS_WAIT) \
.venv/bin/python3 run_singlehost_nodectl.py

stop-e2e:
cd $(SINGLEHOST_DIR) && \
.venv/bin/python3 test_run_net.py --stop
pkill -9 nodectl

.PHONY: test-e2e stop-e2e singlehost-venv
32 changes: 22 additions & 10 deletions src/node-control/commands/src/commands/nodectl/auth_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use common::{
};
use secrets_vault::{types::secret_id::SecretId, vault_builder::SecretVaultBuilder};
use service::auth::user_store::{store_password_blob, user_secret_id, validate_username};
use std::path::Path;
use std::{io::Read, path::Path};

#[derive(clap::Args, Clone)]
#[command(about = "Manage authentication users")]
Expand Down Expand Up @@ -103,6 +103,11 @@ pub struct AddUserCmd {
help = "User role [possible values: operator, nominator]"
)]
role: Role,
#[arg(
long = "password-stdin",
help = "Read password from stdin instead of interactive prompt (no confirmation)"
)]
password_stdin: bool,
}

#[derive(clap::Args, Clone)]
Expand Down Expand Up @@ -151,8 +156,22 @@ impl AddUserCmd {
}
let min_len = auth.min_password_length;

let password =
rpassword::prompt_password("Enter password: ").context("failed to read password")?;
let password = if self.password_stdin {
let mut input = String::new();
std::io::stdin()
.read_to_string(&mut input)
.context("failed to read password from stdin")?;
input.trim_end_matches(['\n', '\r']).to_owned()
} else {
let pw = rpassword::prompt_password("Enter password: ")
.context("failed to read password")?;
let confirm = rpassword::prompt_password("Confirm password: ")
.context("failed to read password confirmation")?;
if pw != confirm {
anyhow::bail!("passwords do not match");
}
pw
};

if password.is_empty() {
anyhow::bail!("password cannot be empty");
Expand All @@ -161,13 +180,6 @@ impl AddUserCmd {
anyhow::bail!("password must be at least {min_len} characters");
}

let confirm = rpassword::prompt_password("Confirm password: ")
.context("failed to read password confirmation")?;

if password != confirm {
anyhow::bail!("passwords do not match");
}

let hash = hash_password(&password).context("failed to hash password")?;
let vault = SecretVaultBuilder::from_env().await.context("failed to open vault")?;
let secret_id = user_secret_id(&self.username);
Expand Down
1 change: 1 addition & 0 deletions src/node/tests/test_run_net_py/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pyyaml>=6.0,<7
Loading