diff --git a/influxdb3-core.rb b/influxdb3-core.rb new file mode 100644 index 00000000000..48ddbc7b3a0 --- /dev/null +++ b/influxdb3-core.rb @@ -0,0 +1,371 @@ +class Influxdb3Core < Formula + desc "Fast, open source, edge data collector optimized for real-time analytics" + homepage "https://www.influxdata.com/products/influxdb/" + version "3.3.0" + license any_of: ["Apache-2.0", "MIT"] + + + on_macos do + if Hardware::CPU.arm? + url "https://dl.influxdata.com/influxdb/releases/influxdb3-core-#{version}_darwin_arm64.tar.gz" + sha256 "355fd93598619dba5fda91aed03b8d8282e0729a6765378740996f59bfb407df" + else + odie "Intel Mac support is not yet available for InfluxDB 3 Core." + end + end + + def install + # install influxdb3 binary and python/ in the libexec dir + libexec.install "influxdb3" + if File.directory?("python") + (libexec/"python").install Dir["python/*"] + else + odie "Python runtime not found in bundle. InfluxDB 3 Core requires bundled Python." + end + + # Create a wrapper to call influxdb3 from the unversioned libexec dir. Use + # the unversioned #{HOMEBREW_PREFIX}/opt/influxdb3-core/libexec/influxdb3 to + # help with upgrades. #{HOMEBREW_PREFIX}/opt/influxdb3-core is a symlink + # into ../Cellar/influxdb3-core/ created automatically by brew. + (bin/"influxdb3").write <<~EOS + #!/bin/bash + + # '#{HOMEBREW_PREFIX}/opt/influxdb3-core/libexec/python/bin/python -m venv' + # unfortunately resolves symlinks for the 'executable' within pyvenv.cfg. + # Find the venv and then modify pyvenv.cfg before server startup. + if [ "$1" = "serve" ]; then + pyvenv_cfg= + + # Priority 1: VIRTUAL_ENV + if [ -n "$VIRTUAL_ENV" ]; then + pyvenv_cfg="$VIRTUAL_ENV/pyvenv.cfg" + else + # Priority 2: --virtual-env-location + for ((i=1; i<=$#; i++)); do + arg="${!i}" + next_idx=$((i+1)) + next_arg="${!next_idx}" + + if [[ "$arg" == "--virtual-env-location" ]] && [[ -n "$next_arg" ]]; then + # --virtual-env-location /path/to/foo + pyvenv_cfg="$next_arg/pyvenv.cfg" + break + elif [[ "$arg" == --virtual-env-location=* ]]; then + # --virtual-env-location=/path/to/foo + pyvenv_cfg="${arg#*=}/pyvenv.cfg" + break + fi + done + + # Priority 3: INFLUXDB3_PLUGIN_DIR + if [ -z "$pyvenv_cfg" ] && [ -n "$INFLUXDB3_PLUGIN_DIR" ]; then + pyvenv_cfg="$INFLUXDB3_PLUGIN_DIR/.venv/pyvenv.cfg" + fi + + # Priority 4: --plugin-dir + if [ -z "$pyvenv_cfg" ]; then + for ((i=1; i<=$#; i++)); do + arg="${!i}" + next_idx=$((i+1)) + next_arg="${!next_idx}" + + if [[ "$arg" == "--plugin-dir" ]] && [[ -n "$next_arg" ]]; then + # --plugin-dir /path/to/bar + pyvenv_cfg="$next_arg/.venv/pyvenv.cfg" + break + elif [[ "$arg" == --plugin-dir=* ]]; then + # --plugin-dir=/path/to/bar + pyvenv_cfg="${arg#*=}/.venv/pyvenv.cfg" + break + fi + done + fi + fi + + # Update the pyvenv.cfg file + if [ -e "$pyvenv_cfg" ]; then + sed -i '' 's|/Cellar/influxdb3-core/[^/]*/|/opt/influxdb3-core/|g' "$pyvenv_cfg" + fi + fi + + exec "#{HOMEBREW_PREFIX}/opt/influxdb3-core/libexec/influxdb3" "$@" + EOS + (bin/"influxdb3").chmod(0755) + + # Create necessary directories for storing data, plugins, and the config + data_dir = var/"lib/influxdb3" + data_dir.mkpath + + plugin_dir = var/"lib/influxdb3/plugins" + plugin_dir.mkpath + + config_dir = etc/"influxdb3" + config_dir.mkpath + + generate_default_config(config_dir) + + (var/"log/influxdb3").mkpath + + create_startup_script + + (data_dir/"README.txt").write <<~EOS + InfluxDB 3 Core Data Directory + + This directory contains your InfluxDB 3 Core data when using file-based storage. + + Configuration: #{config_dir}/influxdb3.conf + Logs: #{var}/log/influxdb3/ + + To start the server: brew services start influxdb3-core + To view logs: tail -f #{var}/log/influxdb3/influxdb3.log + EOS + end + + # Generate a default configuration file with common settings + def generate_default_config(config_dir) + config_file = config_dir/"influxdb3.conf" + + return if config_file.exist? + + node_id = ENV.fetch("INFLUXDB3_NODE_ID", "node0") + + config_content = <<~EOS + # InfluxDB 3 Core Configuration File + # Generated by Homebrew on #{Time.now} + # Edit this file to customize your InfluxDB 3 setup + + # ============================================================================= + # Node Configuration + # ============================================================================= + NODE_ID=#{ENV.fetch("INFLUXDB3_NODE_ID", node_id)} + + # ============================================================================= + # Object Store Configuration + # ============================================================================= + # Options: memory, file, s3, google, azure + OBJECT_STORE_TYPE=#{ENV.fetch("INFLUXDB3_OBJECT_STORE", "file")} + + # File Object Store Settings (used when OBJECT_STORE_TYPE=file) + DATA_DIR=#{var}/lib/influxdb3 + + # File Object Store Settings (used when OBJECT_STORE_TYPE=file) + PLUGIN_DIR=#{var}/lib/influxdb3/plugins + + # S3 Object Store Settings (used when OBJECT_STORE_TYPE=s3) + # Uncomment and configure these if using S3: + # S3_BUCKET=your-bucket-name + # AWS_ACCESS_KEY_ID=your-access-key + # AWS_SECRET_ACCESS_KEY=your-secret-key + # AWS_REGION=your-region (default: us-east-1 if not uncommented) + # AWS_ENDPOINT= # Optional: for MinIO or other S3-compatible stores + # AWS_ALLOW_HTTP=false # Set to true for non-HTTPS endpoints + + # Google Cloud Storage Settings (used when OBJECT_STORE_TYPE=google) + # GOOGLE_SERVICE_ACCOUNT_PATH=/path/to/service-account.json + # GOOGLE_BUCKET=your-gcs-bucket + + # Azure Blob Storage Settings (used when OBJECT_STORE_TYPE=azure) + # AZURE_STORAGE_ACCOUNT=your-account + # AZURE_STORAGE_ACCESS_KEY=your-key + # AZURE_CONTAINER=your-container + + # ============================================================================= + # Server Configuration + # ============================================================================= + HTTP_BIND_ADDRESS=127.0.0.1:8181 + EOS + + config_file.write(config_content) + end + + # Create a dynamic startup script that reads config and starts influxdb3 + def create_startup_script + startup_script = bin/"influxdb3-core" + startup_script.write <<~EOS + #!/bin/bash + + # InfluxDB 3 Core Dynamic Startup Script + # This script reads configuration and starts influxdb3 with the right parameters + + CONFIG_FILE="#{etc}/influxdb3/influxdb3.conf" + + # Function to read config value + read_config() { + local key="$1" + local default="$2" + if [ -f "$CONFIG_FILE" ]; then + value=$(grep "^$key=" "$CONFIG_FILE" 2>/dev/null | cut -d'=' -f2- | tr -d '"') + echo "${value:-$default}" + else + echo "$default" + fi + } + + # Read configuration + NODE_ID="${INFLUXDB3_NODE_ID:-$(read_config "NODE_ID" "node0")}" + OBJECT_STORE="${INFLUXDB3_OBJECT_STORE:-$(read_config "OBJECT_STORE_TYPE" "file")}" + HTTP_BIND="${INFLUXDB3_HTTP_BIND:-$(read_config "HTTP_BIND_ADDRESS" "127.0.0.1:8181")}" + PLUGIN_DIR="${INFLUXDB3_PLUGIN_DIR:-$(read_config "PLUGIN_DIR" "/opt/homebrew/lib/influxdb3/plugins")}" + + # Start building command + ARGS=("#{HOMEBREW_PREFIX}/bin/influxdb3" "serve" "--node-id" "$NODE_ID" "--object-store" "$OBJECT_STORE") + + # Add object store specific arguments + case "$OBJECT_STORE" in + "file") + DATA_DIR="${INFLUXDB3_DATA_DIR:-$(read_config "DATA_DIR" "#{var}/lib/influxdb3")}" + ARGS+=("--data-dir" "$DATA_DIR") + ;; + "s3") + S3_BUCKET="$(read_config "S3_BUCKET" "")" + AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(read_config "AWS_ACCESS_KEY_ID" "")}" + AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(read_config "AWS_SECRET_ACCESS_KEY" "")}" + AWS_REGION="${AWS_REGION:-$(read_config "AWS_REGION" "")}" + AWS_ENDPOINT="${AWS_ENDPOINT:-$(read_config "AWS_ENDPOINT" "")}" + AWS_ALLOW_HTTP="$(read_config "AWS_ALLOW_HTTP" "")" + + [ -n "$S3_BUCKET" ] && ARGS+=("--bucket" "$S3_BUCKET") + [ -n "$AWS_ACCESS_KEY_ID" ] && ARGS+=("--aws-access-key-id" "$AWS_ACCESS_KEY_ID") + [ -n "$AWS_SECRET_ACCESS_KEY" ] && ARGS+=("--aws-secret-access-key" "$AWS_SECRET_ACCESS_KEY") + [ -n "$AWS_REGION" ] && ARGS+=("--aws-region" "$AWS_REGION") + [ -n "$AWS_ENDPOINT" ] && ARGS+=("--aws-endpoint" "$AWS_ENDPOINT") + [ "$AWS_ALLOW_HTTP" = "true" ] && ARGS+=("--aws-allow-http") + ;; + "google") + GOOGLE_SERVICE_ACCOUNT_PATH="${GOOGLE_SERVICE_ACCOUNT_PATH:-$(read_config "GOOGLE_SERVICE_ACCOUNT_PATH" "")}" + GOOGLE_BUCKET="${GOOGLE_BUCKET:-$(read_config "GOOGLE_BUCKET" "")}" + + [ -n "$GOOGLE_SERVICE_ACCOUNT_PATH" ] && ARGS+=("--google-service-account-path" "$GOOGLE_SERVICE_ACCOUNT_PATH") + [ -n "$GOOGLE_BUCKET" ] && ARGS+=("--google-bucket" "$GOOGLE_BUCKET") + ;; + "azure") + AZURE_STORAGE_ACCOUNT="${AZURE_STORAGE_ACCOUNT:-$(read_config "AZURE_STORAGE_ACCOUNT" "")}" + AZURE_STORAGE_ACCESS_KEY="${AZURE_STORAGE_ACCESS_KEY:-$(read_config "AZURE_STORAGE_ACCESS_KEY" "")}" + AZURE_CONTAINER="${AZURE_CONTAINER:-$(read_config "AZURE_CONTAINER" "")}" + + [ -n "$AZURE_STORAGE_ACCOUNT" ] && ARGS+=("--azure-storage-account" "$AZURE_STORAGE_ACCOUNT") + [ -n "$AZURE_STORAGE_ACCESS_KEY" ] && ARGS+=("--azure-storage-access-key" "$AZURE_STORAGE_ACCESS_KEY") + [ -n "$AZURE_CONTAINER" ] && ARGS+=("--azure-container" "$AZURE_CONTAINER") + ;; + esac + + # Add HTTP bind address + ARGS+=("--http-bind" "$HTTP_BIND") + + # Add Processing Engine + ARGS+=("--plugin-dir" "$PLUGIN_DIR") + + # Execute the command + echo "Starting InfluxDB 3 Core with configuration:" + echo " Node ID: $NODE_ID" + echo " Object Store: $OBJECT_STORE" + echo " HTTP Bind: $HTTP_BIND" + echo "" + echo "Full command: ${ARGS[*]}" + echo "" + + exec "${ARGS[@]}" + EOS + + startup_script.chmod(0755) + end + + # Provide user guidance after installation + def caveats + <<~EOS + + ┌────────────────────────────────────────────────────┐ + │ InfluxDB 3 Core has been installed and configured! │ + ├────────────────────────────────────────────────────┘ + │ + │ 📁 Config file: #{etc}/influxdb3/influxdb3.conf + │ 📁 Data directory: #{var}/lib/influxdb3 + │ 📁 Logs directory: #{var}/log/influxdb3 + │ 📁 Plugins directory: #{var}/lib/influxdb3/plugins + │ + │ Current Configuration: + │ ├─ Node ID: node0 + │ ├─ Storage: file + │ └─ Address: 127.0.0.1:8181 + │ + │ To customize storage (memory, s3, google, azure) or other + │ settings, edit the config file and restart the service: + │ ├─ vim #{etc}/influxdb3/influxdb3.conf + │ └─ brew services restart influxdb3-core + │ + │ Next Steps: + │ 1. Start the service: brew services start influxdb3-core + │ 2. Create admin token: influxdb3 create token --admin + │ 3. Set the token: export INFLUXDB3_AUTH_TOKEN="your-token" + │ 4. Create a database: influxdb3 create database mydata + │ 5. Start writing data! + │ + │ Documentation: https://docs.influxdata.com/influxdb3/core/get-started/ + │ + └─────────────────────────────────────────────────────\n + EOS + end + + service do + run opt_bin/"influxdb3-core" + keep_alive true + working_dir HOMEBREW_PREFIX + log_path var/"log/influxdb3/influxdb3.log" + error_log_path var/"log/influxdb3/influxdb3.log" + environment_variables INFLUXDB3_CONFIG_FILE: etc/"influxdb3/influxdb3.conf" + end + + test do + # Test wrapper script exists and is executable + assert_path_exists bin/"influxdb3" + assert_predicate bin/"influxdb3", :executable? + + # Test actual binary exists in libexec + assert_path_exists libexec/"influxdb3" + assert_predicate libexec/"influxdb3", :executable? + + # Test bundled Python exists in libexec + assert_path_exists libexec/"python" + + # Test startup script exists + assert_path_exists bin/"influxdb3-core" + assert_predicate bin/"influxdb3-core", :executable? + + # Test wrapper script forwards help command + output = shell_output("#{bin}/influxdb3 --help") + assert_match "InfluxDB 3", output + + # Test configuration file + assert_path_exists etc/"influxdb3/influxdb3.conf" + config_content = (etc/"influxdb3/influxdb3.conf").read + assert_match "NODE_ID=", config_content + assert_match "OBJECT_STORE_TYPE=", config_content + + # Test directory structure + assert_path_exists var/"lib/influxdb3" + assert_path_exists var/"lib/influxdb3/README.txt" + assert_path_exists var/"log/influxdb3" + + # Test that we can start the server briefly (memory mode for testing) + influxdb_port = free_port + pid = fork do + exec bin/"influxdb3", "serve", + "--node-id", "test-node", + "--object-store", "memory", + "--http-bind", "127.0.0.1:#{influxdb_port}", + "--disable-authz", "health" + end + + sleep 5 + + # Test that the server responds (basic health check) + begin + output = shell_output("curl -s http://127.0.0.1:#{influxdb_port}/health") + # With --disable-authz health, we should get "OK" + assert_match "OK", output + ensure + Process.kill("TERM", pid) if pid + Process.wait(pid) if pid + end + end +end