Skip to content
Open
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
371 changes: 371 additions & 0 deletions influxdb3-core.rb
Original file line number Diff line number Diff line change
@@ -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"]


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you remove one of these 2 newlines, then brew audit --strict ... will come back clean.

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/<VERSION> 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)
Copy link
Contributor

@jdstrand jdstrand Aug 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hiltontj - I mentioned this in another issue, but what @peterbarnett03 is doing here suggests we should support a proper configuration file in addition to cli args and env vars. Whenever we update our deb/rpm scripts for the systemd unit, a configuration file would be useful there as well (though, admittedly, systemd has an EnvironmentFile that can be used similarly to how this brew formula is doing things).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do currently support configuration via a .env file, but I am not entirely sure how one points the service at a particular .env file. My understanding is it needs to be in the directory the binary is run from.

Copy link
Contributor

@jdstrand jdstrand Aug 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hiltontj - the type of configuration file I'm talking about would be something like /etc/influxdb3/influxdb.conf where the serve process would look at it. Eg:

This lists configuration preference: https://docs.influxdata.com/influxdb/v2/reference/config-options/?t=JSON#configuration-precedence

1.x and 2.x use have various file formats for the config file, but I'm not suggesting anything particular. Using an env file format with an option like influxdb3 serve -c|--config /path/to/config/file would be fine IME. Then we could ship a nicely formatted example file that comes from official repos in our packages rather then all the different package formats coming up with something bespoke. This would ease maintenance, provide a consistent user experience and the config file could be self-documenting, which would help users (it could be organized in a way that is easy for folks to read too).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not entirely sure how one points the service at a particular .env file

if you can forgive an interloper's presence (I was just looking at how cli flags are setup in core), claude suggests

  1. dotenv crate looks in the current directory and parents -- doesn't seem to have a way to point it at a file
  2. manually parsing for -c|--config , loading env vars, and then parsing with clap
  3. using clap's try_parse and only access -c|--config|INFLUXDB3_CONFIG_FILE_PATH if it is present, load the env vars, and then parsing again with claps::parse
  4. using the config crate, but it looks tricky to combine that with clap since you want the env vars defined before clap::parse is called.

looks slightly unpleasant either way especially since we might like an env var like INFLUXDB3_CONFIG_FILE_PATH to work too!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using an env file format with an option like influxdb3 serve -c|--config /path/to/config/file would be fine IME.

Agreed. env file format would also make it consistent with the .env files that are currently supported, so we can provide a nicely formatted/commented example that covers both use cases.

looks slightly unpleasant either way especially since we might like an env var like INFLUXDB3_CONFIG_FILE_PATH to work too!

😭


Okay, I can open an issue for a configuration file feature.

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