Skip to content
Open
Changes from 3 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
303 changes: 303 additions & 0 deletions influxdb3-core.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
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"]

depends_on "[email protected]" => :recommended

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

# Install the pre-compiled binary and configure the system
def install
bin.install "influxdb3"

if File.directory?("python")
(bin/"python").install Dir["python/*"]
elsif File.directory?("plugins")
(bin/"plugins").install Dir["plugins/*"]
end

# If no bundled Python, create symlinks to system Python
unless (bin/"python").exist?
python_prefix = Formula["[email protected]"].opt_prefix
(bin/"python").mkpath
(bin/"python/lib").mkpath

python_lib = python_prefix/"lib/libpython3.13.dylib"
(bin/"python/lib/libpython3.13.dylib").make_symlink(python_lib) if python_lib.exist?
Copy link
Contributor

Choose a reason for hiding this comment

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

You should remove this. influxdb3 needs to use the libpython version it was compiled against. If this works at all today, it is by luck that there is a libpython3.13.dylib on the system (not all macOS systems will have this version) and the design of the processing engine is that it is NOT dependent on the system for python (if we changed the python version we want to use, we'd break these systems). Further, the system libpython3.13.dylib could potentially subtly incompatible with the one that we compiled against.

The brew file needs to unconditionally ship libpython in a location that influxdb3 can find it; when it does so, there is no reason to have fallback code (a user removing files from the brew install location can't possibly expect the code to work as designed; we need to just error).

end

# 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=("#{opt_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
assert_path_exists bin/"influxdb3"
assert_predicate bin/"influxdb3", :executable?

assert_path_exists bin/"influxdb3-core"
assert_predicate bin/"influxdb3-core", :executable?

output = shell_output("#{bin}/influxdb3 --help")
assert_match "InfluxDB 3", output

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

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