working link, update, and testing docker container and scripts

This commit is contained in:
Tyler Sturos 2025-04-01 21:10:31 -08:00
parent 947f3f565f
commit fc8682668b
16 changed files with 322 additions and 262 deletions

View File

@ -142,3 +142,8 @@ tasks:
cmds:
- docker push ${QUILIBRIUM_IMAGE_NAME:-quilibrium}:{{.VERSION}}
- docker push ${QUILIBRIUM_IMAGE_NAME:-quilibrium}:latest
test:qclient:
desc: Test the Quilibrium docker image.
cmds:
- client/test/run_tests.sh -d 'ubuntu' -v '24.04'

View File

@ -0,0 +1,51 @@
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
"source.quilibrium.com/quilibrium/monorepo/client/utils"
"source.quilibrium.com/quilibrium/monorepo/node/config"
)
var versionFlag string
var downloadSignaturesCmd = &cobra.Command{
Use: "download-signatures",
Short: "Download signature files for the current binary",
Long: `Download signature files for the current binary. This command will download
the digest file and all signature files needed for verification. If --version is specified,
it will download signatures for that version. Otherwise, it will download signatures for
the latest version.`,
Run: func(cmd *cobra.Command, args []string) {
var version string
if versionFlag != "" {
// Use specified version
version = versionFlag
} else {
// Get the current version
version = config.GetVersionString()
}
// Download signature files
if err := utils.DownloadReleaseSignatures(utils.ReleaseTypeQClient, version); err != nil {
fmt.Fprintf(os.Stderr, "Error downloading signature files: %v\n", err)
os.Exit(1)
}
fmt.Printf("Successfully downloaded signature files for version %s\n", version)
},
}
func init() {
downloadSignaturesCmd.Flags().StringVarP(
&versionFlag,
"version",
"v",
"",
"Version to download signatures for (defaults to latest version)",
)
rootCmd.AddCommand(downloadSignaturesCmd)
}

View File

@ -45,18 +45,6 @@ Example: qclient link --path /usr/local/bin`),
return err
}
// Save the symlink path to ClientConfig
config := utils.ClientConfig{
SymlinkPath: targetPath,
}
// Use the UpdateClientConfig function to save the config
if err := utils.UpdateClientConfig(&config); err != nil {
fmt.Printf("Warning: Failed to save symlink path to config: %v\n", err)
} else {
fmt.Printf("Saved symlink path %s to client configuration\n", targetPath)
}
fmt.Printf("Symlink created at %s\n", targetPath)
return nil
},
@ -71,7 +59,7 @@ func determineSymlinkLocation() (string, string, error) {
}
// Otherwise, find a suitable directory in PATH
return utils.NodeDefaultSymlinkDir, utils.DefaultQClientSymlinkPath, nil
return utils.DefaultSymlinkDir, utils.DefaultQClientSymlinkPath, nil
}
// isDirectoryInPath checks if a directory is in the PATH environment variable

View File

@ -85,11 +85,11 @@ func installNode(version string) {
if err := installByVersion(version); err != nil {
fmt.Fprintf(os.Stderr, "Error installing specific version: %v\n", err)
return
os.Exit(1)
}
// Finish installation
nodeBinaryPath := filepath.Join(installPath, version, "node")
nodeBinaryPath := filepath.Join(installPath, string(utils.ReleaseTypeNode), version)
finishInstallation(nodeBinaryPath, version)
}

View File

@ -15,10 +15,10 @@ var (
// Default symlink path for the node binary
defaultSymlinkPath = "/usr/local/bin/quilibrium-node"
// Default installation directory base paths in order of preference
// Default installation directory base path
installPath = "/opt/quilibrium"
// Default data directory paths in order of preference
// Default data directory paths
dataPath = "/var/lib/quilibrium"
logPath = "/var/log/quilibrium"

View File

@ -7,6 +7,7 @@ import (
"encoding/hex"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
@ -30,6 +31,8 @@ var simulateFail bool
var LightNode bool = false
var DryRun bool = false
var publicRPC bool = false
var standardizedQClientFileName string = "qclient-" + config.GetVersionString() + "-" + osType + "-" + arch
var rootCmd = &cobra.Command{
Use: "qclient",
Short: "Quilibrium client",
@ -52,23 +55,37 @@ It provides commands for installing, updating, and managing Quilibrium nodes.`,
}
checksum := sha3.Sum256(b)
digest, err := os.ReadFile(ex + ".dgst")
// First check var data path for signatures
varDataPath := filepath.Join(utils.ClientDataPath, config.GetVersionString())
digestPath := filepath.Join(varDataPath, standardizedQClientFileName+".dgst")
fmt.Printf("Checking signature for %s\n", digestPath)
// Try to read digest from var data path first
digest, err := os.ReadFile(digestPath)
if err != nil {
fmt.Println("The digest file was not found. Do you want to continue without signature verification? (y/n)")
fmt.Println("You can also use --signature-check=false in your command to skip this prompt")
// Fall back to checking next to executable
digest, err = os.ReadFile(ex + ".dgst")
if err != nil {
fmt.Println("The digest file was not found. Do you want to continue without signature verification? (y/n)")
fmt.Println("You can also use --signature-check=false in your command to skip this prompt")
reader := bufio.NewReader(os.Stdin)
response, _ := reader.ReadString('\n')
response = strings.TrimSpace(strings.ToLower(response))
reader := bufio.NewReader(os.Stdin)
response, _ := reader.ReadString('\n')
response = strings.TrimSpace(strings.ToLower(response))
if response != "y" && response != "yes" {
fmt.Println("Exiting due to missing digest file")
os.Exit(1)
if response != "y" && response != "yes" {
fmt.Println("Exiting due to missing digest file")
os.Exit(1)
}
fmt.Println("Continuing without signature verification")
signatureCheck = false
}
}
fmt.Println("Continuing without signature verification")
signatureCheck = false
} else {
if signatureCheck {
parts := strings.Split(string(digest), " ")
if len(parts) != 2 {
fmt.Println("Invalid digest file format")
@ -89,10 +106,16 @@ It provides commands for installing, updating, and managing Quilibrium nodes.`,
count := 0
for i := 1; i <= len(config.Signatories); i++ {
signatureFile := fmt.Sprintf(ex+".dgst.sig.%d", i)
// Try var data path first for signature files
signatureFile := filepath.Join(varDataPath, fmt.Sprintf("%s.dgst.sig.%d", filepath.Base(ex), i))
sig, err := os.ReadFile(signatureFile)
if err != nil {
continue
// Fall back to checking next to executable
signatureFile = fmt.Sprintf(ex+".dgst.sig.%d", i)
sig, err = os.ReadFile(signatureFile)
if err != nil {
continue
}
}
pubkey, _ := hex.DecodeString(config.Signatories[i-1])
@ -219,8 +242,8 @@ func init() {
// Create config directory if it doesn't exist
rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
// Skip for help command
if cmd.Name() == "help" {
// Skip for help command and download-signatures command
if cmd.Name() == "help" || cmd.Name() == "download-signatures" {
return nil
}
@ -235,20 +258,37 @@ func init() {
return fmt.Errorf("failed to get executable path: %v", err)
}
digestPath := ex + ".dgst"
// First check var data path for signatures
version := config.GetVersionString()
varDataPath := filepath.Join(utils.ClientDataPath, version)
digestPath := filepath.Join(varDataPath, standardizedQClientFileName+".dgst")
fmt.Printf("Checking signature for %s\n", digestPath)
if signatureCheck && !utils.FileExists(digestPath) {
fmt.Println("Signature file not found. Would you like to download it? (y/n)")
reader := bufio.NewReader(os.Stdin)
response, _ := reader.ReadString('\n')
response = strings.TrimSpace(strings.ToLower(response))
// Fall back to checking next to executable
digestPath = ex + ".dgst"
if !utils.FileExists(digestPath) {
fmt.Println("Signature file not found. Would you like to download it? (y/n)")
reader := bufio.NewReader(os.Stdin)
response, _ := reader.ReadString('\n')
response = strings.TrimSpace(strings.ToLower(response))
if response == "y" || response == "yes" {
fmt.Println("Downloading signature file...")
// TODO: Implement signature download logic
fmt.Println("Signature download not implemented yet. Please download manually.")
} else {
fmt.Println("Continuing without signature verification")
signatureCheck = false
if response == "y" || response == "yes" {
fmt.Println("Downloading signature files...")
if version == "" {
fmt.Println("Could not determine version from executable name")
return fmt.Errorf("could not determine version from executable name")
}
// Download signature files
if err := utils.DownloadReleaseSignatures(utils.ReleaseTypeQClient, version); err != nil {
fmt.Printf("Error downloading signature files: %v\n", err)
return fmt.Errorf("failed to download signature files: %v", err)
}
fmt.Println("Successfully downloaded signature files")
} else {
fmt.Println("Continuing without signature verification")
signatureCheck = false
}
}
}
return nil

View File

@ -9,6 +9,7 @@ import (
"github.com/spf13/cobra"
"source.quilibrium.com/quilibrium/monorepo/client/utils"
"source.quilibrium.com/quilibrium/monorepo/node/config"
)
var (
@ -23,6 +24,8 @@ var updateCmd = &cobra.Command{
Long: `Update Quilibrium client to a specified version or the latest version.
If no version is specified, the latest version will be installed.
If the current version is already the latest version, the command will exit with a message.
Examples:
# Update to the latest version
qclient update
@ -65,6 +68,25 @@ func determineVersion(args []string) string {
// updateClient handles the client update process
func updateClient(version string) {
currentVersion := config.GetVersionString()
// If version is "latest", get the latest version
if version == "latest" {
latestVersion, err := utils.GetLatestVersion(utils.ReleaseTypeQClient)
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting latest version: %v\n", err)
return
}
version = latestVersion
fmt.Fprintf(os.Stdout, "Latest version: %s\n", version)
}
if version == currentVersion {
fmt.Fprintf(os.Stdout, "Already on version %s\n", currentVersion)
return
}
// Check if we need sudo privileges
if err := utils.CheckAndRequestSudo(fmt.Sprintf("Updating client at %s requires root privileges", utils.ClientInstallPath)); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
@ -85,17 +107,6 @@ func updateClient(version string) {
return
}
// If version is "latest", get the latest version
if version == "latest" {
latestVersion, err := utils.GetLatestVersion(utils.ReleaseTypeQClient)
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting latest version: %v\n", err)
return
}
version = latestVersion
fmt.Fprintf(os.Stdout, "Latest version: %s\n", version)
}
// Construct the expected filename for the specified version
// Remove 'v' prefix if present for filename construction
versionWithoutV := strings.TrimPrefix(version, "v")
@ -123,22 +134,9 @@ func updateClient(version string) {
// finishInstallation completes the update process
func finishInstallation(version string) {
// Read current config
config, err := utils.ReadClientConfig()
if err != nil {
fmt.Fprintf(os.Stderr, "Error reading config: %v\n", err)
return
}
// Update config with new version
config.Version = version
if err := utils.UpdateClientConfig(config); err != nil {
fmt.Fprintf(os.Stderr, "Error updating config: %v\n", err)
return
}
// Construct executable path
execPath := filepath.Join(utils.ClientInstallPath, version, "qclient")
execPath := filepath.Join(utils.ClientDataPath, version, "qclient-"+version+"-"+osType+"-"+arch)
// Make the binary executable
if err := os.Chmod(execPath, 0755); err != nil {
@ -148,9 +146,6 @@ func finishInstallation(version string) {
// Create symlink to the new version
symlinkPath := utils.DefaultQClientSymlinkPath
if config.SymlinkPath != "" {
symlinkPath = config.SymlinkPath
}
// Check if we need sudo privileges for creating symlink in system directory
if strings.HasPrefix(symlinkPath, "/usr/") || strings.HasPrefix(symlinkPath, "/bin/") || strings.HasPrefix(symlinkPath, "/sbin/") {

View File

@ -8,11 +8,12 @@ import (
"github.com/spf13/cobra"
"source.quilibrium.com/quilibrium/monorepo/client/utils"
"source.quilibrium.com/quilibrium/monorepo/node/config"
)
// Version information - fallback if executable name doesn't contain version
const (
DefaultVersion = "1.0.0"
var (
DefaultVersion = config.GetVersionString()
)
// VersionInfo holds version and hash information

View File

@ -2,8 +2,8 @@
ARG DISTRO=ubuntu
ARG VERSION=24.04
# Use the specified distribution as the base image
FROM --platform=$BUILDPLATFORM ${DISTRO}:${VERSION}
# Base stage with common setup
FROM --platform=$BUILDPLATFORM ${DISTRO}:${VERSION} AS base
ARG TARGETARCH
ARG TARGETOS
@ -22,18 +22,9 @@ RUN apt-get update && apt-get install -y \
RUN useradd -m -s /bin/bash testuser && \
echo "testuser ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
# Set working directory
# Final test stage
FROM base AS qclient-test
WORKDIR /app
# Copy the client binary and test script
COPY build/qclient/${TARGETARCH}_${TARGETOS}/qclient /opt/quilibrium/bin/qclient
COPY test_install.sh /app/
# Set permissions
RUN chmod +x /opt/quilibrium/bin/qclient /app/test_install.sh
# Switch to test user
USER testuser
# Run the test script
CMD ["/app/test_install.sh"]

View File

@ -1,87 +0,0 @@
FROM ubuntu:24.04 AS build-base
ENV PATH="${PATH}:/root/.cargo/bin/"
# Install GMP 6.2 (6.3 which MacOS is using only available on Debian unstable)
RUN apt-get update && apt-get install -y \
build-essential \
curl \
git \
cmake \
libgmp-dev \
libmpfr-dev \
libmpfr6 \
wget \
m4 \
pkg-config \
gcc \
g++ \
make \
autoconf \
automake \
libtool \
&& rm -rf /var/lib/apt/lists/*
# Install Go 1.22.5 for amd64
RUN wget https://go.dev/dl/go1.22.5.linux-amd64.tar.gz && \
rm -rf /usr/local/go && \
tar -C /usr/local -xzf go1.22.5.linux-amd64.tar.gz && \
rm go1.22.5.linux-amd64.tar.gz
ENV PATH=$PATH:/usr/local/go/bin
# Install FLINT library
RUN git clone https://github.com/flintlib/flint.git && \
cd flint && \
git checkout flint-3.0 && \
./bootstrap.sh && \
./configure \
--prefix=/usr/local \
--with-gmp=/usr/local \
--with-mpfr=/usr/local \
--enable-static \
--disable-shared \
CFLAGS="-O3" && \
make && \
make install && \
cd .. && \
rm -rf flint
# Install Rust toolchain
COPY docker/rustup-init.sh /opt/rustup-init.sh
RUN /opt/rustup-init.sh -y --profile minimal
# Install uniffi-bindgen-go
RUN cargo install uniffi-bindgen-go --git https://github.com/NordSecurity/uniffi-bindgen-go --tag v0.2.1+v0.25.0
FROM build-base AS build
ENV GOEXPERIMENT=arenas
ENV QUILIBRIUM_SIGNATURE_CHECK=false
WORKDIR /opt/ceremonyclient
COPY . .
## Generate Rust bindings for VDF
WORKDIR /opt/ceremonyclient/vdf
RUN ./generate.sh
## Generate Rust bindings for BLS48581
WORKDIR /opt/ceremonyclient/bls48581
RUN ./generate.sh
## Generate Rust bindings for VerEnc
WORKDIR /opt/ceremonyclient/verenc
RUN ./generate.sh
# Build and install qclient
WORKDIR /opt/ceremonyclient/client
RUN ./build.sh -o qclient
# Create final stage to copy the binary
FROM scratch AS export-stage
COPY --from=build /opt/ceremonyclient/client/qclient ./client/test/

View File

@ -141,10 +141,12 @@ If you just want to build the qclient in a Docker container without running the
```bash
# in the project root directory
## Will take awhile to build flint on initial build
docker build -f client/test/Dockerfile.qclient -t qclient .
sudo task build_qclient_amd64_linux
sudo task build_qclient_arm64_linux
# for mac, you will need to build on a mac
```
This command builds the Docker image with the qclient binary according to the specifications in `Dockerfile.qclient`. The resulting image will be tagged as `qclient`.
This command builds the Docker image with the qclient binary according to the specifications in `Dockerfile.source`. The resulting image will be tagged as `qclient`.
## Contributing
@ -152,4 +154,3 @@ When adding new distributions or versions:
1. Update the default test configurations in the script
2. Ensure the corresponding Dockerfile supports the new distribution/version
3. Test the changes thoroughly before committing
4. Verify that all dependencies in `Dockerfile.qclient` are available in the target distribution

View File

@ -1,5 +1,8 @@
#!/bin/bash
set -e
CLIENT_DIR="${CLIENT_DIR:-$( cd "$(dirname "$(realpath "$( dirname "${BASH_SOURCE[0]}" )")")" >/dev/null 2>&1 && pwd )}"
echo "CLIENT_DIR: $CLIENT_DIR"
# Help function
show_help() {
@ -11,6 +14,7 @@ show_help() {
echo " -v, --version VERSION Specify the version (e.g., 22.04, 12)"
echo " -t, --tag TAG Specify a custom tag for the test container"
echo " -h, --help Show this help message"
echo " --no-cache Disable all Docker build cache"
echo ""
echo "If no arguments are provided, runs tests on all supported distributions"
exit 0
@ -20,6 +24,7 @@ show_help() {
DISTRO=""
VERSION=""
TAG=""
NO_CACHE=""
while [[ $# -gt 0 ]]; do
case $1 in
-d|--distro)
@ -34,6 +39,10 @@ while [[ $# -gt 0 ]]; do
TAG="$2"
shift 2
;;
--no-cache)
NO_CACHE="--no-cache"
shift
;;
-h|--help)
show_help
;;
@ -44,25 +53,43 @@ while [[ $# -gt 0 ]]; do
esac
done
# Build the client binary using Dockerfile.qclient
echo "Building client binary using Dockerfile.qclient..."
docker build -t quil-qclient-builder -f Dockerfile.qclient ..
docker create --name quil-qclient-temp quil-qclient-builder
docker cp quil-qclient-temp:/usr/local/bin/qclient ./qclient
docker rm quil-qclient-temp
# Function to run tests for a specific distribution
run_distro_test() {
local distro=$1
local version=$2
local tag=$3
echo "Testing on $distro $version..."
# Build the base stage first (this can be cached)
docker build \
$NO_CACHE \
--build-arg DISTRO=$distro \
--build-arg VERSION=$version \
-t quil-test-$tag-base \
--target base \
-f client/test/Dockerfile .
# Build the final test stage
docker build \
--build-arg DISTRO=$distro \
--build-arg VERSION=$version \
-t quil-test-$tag \
-f Dockerfile .
docker run --rm quil-test-$tag
--target qclient-test \
-f client/test/Dockerfile .
# Ensure test files are executable
chmod +x "$CLIENT_DIR/test/test_install.sh"
chmod +x "$CLIENT_DIR/test/test_utils.sh"
chmod +x "$CLIENT_DIR/build/amd64_linux/qclient"
# Set ownership to match testuser (uid:gid 1000:1000)
chown 1000:1000 "$CLIENT_DIR/build/amd64_linux/qclient"
# Run the container with mounted test directory and binary
docker run --rm \
-v "$CLIENT_DIR/test:/app" \
-v "$CLIENT_DIR/build/amd64_linux/qclient:/opt/quilibrium/bin/qclient" \
quil-test-$tag
}
# If custom distro/version/tag is provided, run single test

109
client/test/test_install.sh Normal file → Executable file
View File

@ -1,115 +1,90 @@
#!/bin/bash
set -e
# Source the test utilities
source "$(dirname "$0")/test_utils.sh"
# Get distribution information
DISTRO=$(lsb_release -si 2>/dev/null || echo "Unknown")
VERSION=$(lsb_release -sr 2>/dev/null || echo "Unknown")
echo "Starting Quilibrium node installation test on $DISTRO $VERSION..."
# Test: Link the qclient binary to ensure it's in the PATH
echo "Linking qclient binary for testing..."
if [ -f "/opt/quilibrium/bin/qclient" ]; then
echo "qclient binary already exists at /opt/quilibrium/bin/qclient"
else
echo "qclient binary not found at /opt/quilibrium/bin/qclient"
exit 1
fi
# Test: Ensure qclient is in the PATH
echo "Testing qclient in PATH..."
run_test_with_format "sudo /opt/quilibrium/bin/qclient link --signature-check=false"
run_test_with_format "which qclient"
run_test_with_format "qclient version --signature-check=false"
# Test 0: Install latest version
# Check if download-signatures command exists in qclient help
run_test_with_format "qclient help | grep -q 'download-signatures'"
# Test downloading signatures
run_test_with_format "sudo qclient download-signatures"
# Test 1: Install latest version
echo "Test 1: Installing latest version..."
qclient node install
run_test_with_format "sudo qclient node install"
get_latest_version() {
# Fetch the latest version from the releases API
local latest_version=$(curl -s https://releases.quilibrium.com/release | head -n 1 | cut -d'-' -f2)
echo "$latest_version"
}
LATEST_VERSION=$(get_latest_version)
# Verify installation
echo "Verifying installation..."
if [ ! -f "/opt/quilibrium/$LATEST_VERSION/node-$LATEST_VERSION-linux-amd64" ]; then
echo "Error: Latest version binary not found"
exit 1
fi
run_test_with_format "test -f /opt/quilibrium/$LATEST_VERSION/node-$LATEST_VERSION-linux-amd64"
# Verify latest version matches
echo "Verifying latest version matches..."
get_latest_version
run_test_with_format "get_latest_version"
# Test 2: Install specific version
echo "Test 2: Installing specific version..."
qclient node install "2.0.6.2"
run_test_with_format "qclient node install '2.0.6.2' --signature-check=false"
# Verify specific version installation
echo "Verifying specific version installation..."
if [ ! -f "/opt/quilibrium/2.0.6.2/node-2.0.6.2-linux-amd64" ]; then
echo "Error: Specific version binary not found"
exit 1
fi
run_test_with_format "test -f /opt/quilibrium/2.0.6.2/node-2.0.6.2-linux-amd64"
# Test 3: Verify service file creation
echo "Test 3: Verifying service file creation..."
if [ ! -f "/etc/systemd/system/quilibrium-node.service" ]; then
echo "Error: Service file not found"
exit 1
fi
run_test_with_format "test -f /etc/systemd/system/quilibrium-node.service"
# Verify service file content
echo "Verifying service file content..."
if ! grep -q "EnvironmentFile=/etc/default/quilibrium-node" /etc/systemd/system/quilibrium-node.service; then
echo "Error: Service file missing EnvironmentFile directive"
exit 1
fi
run_test_with_format "grep -q 'EnvironmentFile=/etc/default/quilibrium-node' /etc/systemd/system/quilibrium-node.service"
# Test 4: Verify environment file
echo "Test 4: Verifying environment file..."
if [ ! -f "/etc/default/quilibrium-node" ]; then
echo "Error: Environment file not found"
exit 1
fi
run_test_with_format "test -f /etc/default/quilibrium-node"
# Verify environment file permissions
echo "Verifying environment file permissions..."
if [ "$(stat -c %a /etc/default/quilibrium-node)" != "640" ]; then
echo "Error: Environment file has incorrect permissions"
exit 1
fi
run_test_with_format "test '$(stat -c %a /etc/default/quilibrium-node)' = '640'"
# Test 5: Verify data directory
echo "Test 5: Verifying data directory..."
if [ ! -d "/var/lib/quilibrium" ]; then
echo "Error: Data directory not found"
exit 1
fi
run_test_with_format "test -d /var/lib/quilibrium"
# Verify data directory permissions
echo "Verifying data directory permissions..."
if [ "$(stat -c %a /var/lib/quilibrium)" != "755" ]; then
echo "Error: Data directory has incorrect permissions"
exit 1
fi
run_test_with_format "test '$(stat -c %a /var/lib/quilibrium)' = '755'"
# Test 6: Verify config file
echo "Test 6: Verifying config file..."
if [ ! -f "/var/lib/quilibrium/config/node.yaml" ]; then
echo "Error: Config file not found"
exit 1
fi
run_test_with_format "test -f /var/lib/quilibrium/config/node.yaml"
# Verify config file permissions
echo "Verifying config file permissions..."
if [ "$(stat -c %a /var/lib/quilibrium/config/node.yaml)" != "644" ]; then
echo "Error: Config file has incorrect permissions"
exit 1
fi
run_test_with_format "test '$(stat -c %a /var/lib/quilibrium/config/node.yaml)' = '644'"
# Test 7: Verify binary symlink
echo "Test 7: Verifying binary symlink..."
if [ ! -L "/usr/local/bin/quilibrium-node" ]; then
echo "Error: Binary symlink not found"
exit 1
fi
run_test_with_format "test -L /usr/local/bin/quilibrium-node"
# Test 8: Verify binary execution
echo "Test 8: Verifying binary execution..."
if ! quilibrium-node --version > /dev/null 2>&1; then
echo "Error: Binary execution failed"
exit 1
fi
run_test_with_format "quilibrium-node --version"
echo "All tests passed successfully on $DISTRO $VERSION!"

49
client/test/test_utils.sh Executable file
View File

@ -0,0 +1,49 @@
#!/bin/bash
# ANSI color codes
GREEN='\033[0;32m'
BLUE='\033[0;34m'
RED='\033[0;31m'
NC='\033[0m' # No Color
# Function to run a test command and format its output
run_test_with_format() {
local test_cmd="$1"
local indent=" " # 4 spaces for indentation
echo -e "${BLUE}Running test: $test_cmd${NC}"
echo "----------------------------------------"
# Run the command and capture stdout and stderr separately
local stdout
local stderr
stdout=$(eval "$test_cmd" 2> >(tee /dev/stderr))
exit_code=$?
stderr=$(cat)
# Format and print the stdout with indentation
if [ -n "$stdout" ]; then
echo "$stdout" | while IFS= read -r line; do
echo -e "${GREEN}$indent$line${NC}"
done
fi
# Check for stderr output and exit code
if [ -n "$stderr" ] || [ $exit_code -ne 0 ]; then
echo -e "${RED}${indent}Test failed:${NC}"
if [ -n "$stderr" ]; then
echo "$stderr" | while IFS= read -r line; do
echo -e "${RED}${indent}$line${NC}"
done
fi
if [ $exit_code -ne 0 ]; then
echo -e "${RED}${indent}Exit code: $exit_code${NC}"
fi
echo "----------------------------------------"
return 1
fi
echo -e "${GREEN}${indent}Test completed successfully${NC}"
echo "----------------------------------------"
return 0
}

View File

@ -53,7 +53,11 @@ func GetLatestVersion(releaseType ReleaseType) (string, error) {
// DownloadReleaseFile downloads a release file from the Quilibrium releases server
func DownloadReleaseFile(releaseType ReleaseType, fileName string, version string, showError bool) error {
url := fmt.Sprintf("%s/%s", BaseReleaseURL, fileName)
destPath := filepath.Join(DataPath, string(releaseType), version, fileName)
destDir := filepath.Join(DataPath, string(releaseType), version)
os.MkdirAll(destDir, 0755)
destPath := filepath.Join(destDir, fileName)
fmt.Printf("Downloading %s to %s\n", url, destPath)
resp, err := http.Get(url)
if err != nil {
@ -87,6 +91,7 @@ func DownloadReleaseFile(releaseType ReleaseType, fileName string, version strin
func DownloadReleaseSignatures(releaseType ReleaseType, version string) error {
var files []string
baseName := fmt.Sprintf("%s-%s-%s-%s", releaseType, version, osType, arch)
fmt.Printf("Downloading signatures for %s\n", baseName)
// Add digest file URL
files = append(files, baseName+".dgst")
@ -94,9 +99,28 @@ func DownloadReleaseSignatures(releaseType ReleaseType, version string) error {
// Add signature file URLs
signerNums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}
for _, num := range signerNums {
// Check if the remote signature file exists
sigFile := fmt.Sprintf("%s.dgst.sig.%d", baseName, num)
remoteURL := fmt.Sprintf("%s/%s", BaseReleaseURL, sigFile)
// Make a HEAD request to check if the file exists
resp, err := http.Head(remoteURL)
if err != nil || resp.StatusCode != http.StatusOK {
// Skip this file if it doesn't exist on the server
fmt.Printf("Signature file %s not found on server, skipping\n", sigFile)
continue
}
if resp != nil && resp.Body != nil {
resp.Body.Close()
}
files = append(files, fmt.Sprintf("%s.dgst.sig.%d", baseName, num))
}
if len(files) == 0 {
fmt.Printf("No signature files found for %s\n", baseName)
return nil
}
for _, file := range files {
err := DownloadReleaseFile(releaseType, file, version, false)
if err != nil {

View File

@ -17,14 +17,14 @@ import (
// DefaultNodeUser is the default user name for node operations
var DefaultNodeUser = "quilibrium"
var ClientConfigDir = filepath.Join("/etc/quilibrium/", "config")
var ClientConfigPath = filepath.Join(ClientConfigDir, "client.yaml")
var ClientInstallPath = filepath.Join("/opt/quilibrium/", "client")
var ClientConfigPath = filepath.Join(ClientConfigDir, string(ReleaseTypeQClient)+".yaml")
var ClientInstallPath = filepath.Join("/opt/quilibrium/", string(ReleaseTypeQClient))
var DataPath = filepath.Join("/var/quilibrium/", "data")
var ClientDataPath = filepath.Join(DataPath, "client")
var NodeDataPath = filepath.Join(DataPath, "node")
var NodeDefaultSymlinkDir = "/usr/local/bin"
var DefaultNodeSymlinkPath = filepath.Join(NodeDefaultSymlinkDir, "quilibrium-node")
var DefaultQClientSymlinkPath = filepath.Join(NodeDefaultSymlinkDir, "qclient")
var ClientDataPath = filepath.Join(DataPath, string(ReleaseTypeQClient))
var NodeDataPath = filepath.Join(DataPath, string(ReleaseTypeNode))
var DefaultSymlinkDir = "/usr/local/bin"
var DefaultNodeSymlinkPath = filepath.Join(DefaultSymlinkDir, string(ReleaseTypeNode))
var DefaultQClientSymlinkPath = filepath.Join(DefaultSymlinkDir, string(ReleaseTypeQClient))
var osType = runtime.GOOS
var arch = runtime.GOARCH