diff --git a/Taskfile.yaml b/Taskfile.yaml index 575384e..23c2eed 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -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' diff --git a/client/cmd/download-signatures.go b/client/cmd/download-signatures.go new file mode 100644 index 0000000..9d1e0a5 --- /dev/null +++ b/client/cmd/download-signatures.go @@ -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) +} diff --git a/client/cmd/link.go b/client/cmd/link.go index 03ada27..1183f0c 100644 --- a/client/cmd/link.go +++ b/client/cmd/link.go @@ -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 diff --git a/client/cmd/node/install.go b/client/cmd/node/install.go index a206a08..fa90da5 100644 --- a/client/cmd/node/install.go +++ b/client/cmd/node/install.go @@ -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) } diff --git a/client/cmd/node/node.go b/client/cmd/node/node.go index 7b931d6..cd38148 100644 --- a/client/cmd/node/node.go +++ b/client/cmd/node/node.go @@ -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" diff --git a/client/cmd/root.go b/client/cmd/root.go index e2a3cdc..e6aaf92 100644 --- a/client/cmd/root.go +++ b/client/cmd/root.go @@ -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 diff --git a/client/cmd/update.go b/client/cmd/update.go index 2db2b66..a4e2ec1 100644 --- a/client/cmd/update.go +++ b/client/cmd/update.go @@ -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/") { diff --git a/client/cmd/version.go b/client/cmd/version.go index e55a680..e2fe747 100644 --- a/client/cmd/version.go +++ b/client/cmd/version.go @@ -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 diff --git a/client/test/Dockerfile b/client/test/Dockerfile index 3463ef3..1e7398f 100644 --- a/client/test/Dockerfile +++ b/client/test/Dockerfile @@ -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"] \ No newline at end of file diff --git a/client/test/Dockerfile.qclient b/client/test/Dockerfile.qclient deleted file mode 100644 index 3749984..0000000 --- a/client/test/Dockerfile.qclient +++ /dev/null @@ -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/ - - - diff --git a/client/test/README.md b/client/test/README.md index 2ddd818..0e1a56f 100644 --- a/client/test/README.md +++ b/client/test/README.md @@ -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 \ No newline at end of file diff --git a/client/test/run_tests.sh b/client/test/run_tests.sh index a123f72..d8a4ef5 100755 --- a/client/test/run_tests.sh +++ b/client/test/run_tests.sh @@ -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 diff --git a/client/test/test_install.sh b/client/test/test_install.sh old mode 100644 new mode 100755 index e451677..336e069 --- a/client/test/test_install.sh +++ b/client/test/test_install.sh @@ -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!" \ No newline at end of file diff --git a/client/test/test_utils.sh b/client/test/test_utils.sh new file mode 100755 index 0000000..37f97b4 --- /dev/null +++ b/client/test/test_utils.sh @@ -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 +} \ No newline at end of file diff --git a/client/utils/download.go b/client/utils/download.go index 5c40a11..7f77274 100644 --- a/client/utils/download.go +++ b/client/utils/download.go @@ -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 { diff --git a/client/utils/fileUtils.go b/client/utils/fileUtils.go index ef6d91d..60cf6f6 100644 --- a/client/utils/fileUtils.go +++ b/client/utils/fileUtils.go @@ -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