Skip to content
Open
Show file tree
Hide file tree
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
15 changes: 10 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,17 @@ If you need to change dashboards or datasources, set `GF_AUTH_ANONYMOUS_ORG_ROLE
#### Accessing InfluxDB

You can access the InfluxDB CLI using `docker compose exec -it influxdb influx`.
For example, to export all receiver telemetry as a CSV, run:
For example, to list buckets, run:
```shell
$ docker compose exec influxdb influx \
-database "gsw" \
-format csv \
-execute "SELECT * FROM receiver"
docker compose exec influxdb influx bucket list
```

To query the `gsw` bucket directly, run:
```shell
docker compose exec influxdb influx query \
--org gsw \
--token gsw-local-dev-token \
Comment on lines +65 to +69

Copilot AI Apr 3, 2026

Copy link

Choose a reason for hiding this comment

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

The README hardcodes --token gsw-local-dev-token in the query example. If someone overrides the admin token via compose env (which the compose files support), this command will fail and can be confusing. Consider updating the docs to reference the configured env var (e.g. $GSW_INFLUXDB_ADMIN_TOKEN) or instructing how to retrieve the token used by the current compose setup.

Suggested change
To query the `gsw` bucket directly, run:
```shell
docker compose exec influxdb influx query \
--org gsw \
--token gsw-local-dev-token \
To query the `gsw` bucket directly, use the admin token configured for your current compose setup (for example, `$GSW_INFLUXDB_ADMIN_TOKEN` if you exported that environment variable before starting the containers):
```shell
docker compose exec influxdb influx query \
--org gsw \
--token "$GSW_INFLUXDB_ADMIN_TOKEN" \

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

hardcoded token is probably fine in the docs

'from(bucket: "gsw") |> range(start: -15m)'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this could potentially be changed to start: 0 to maintain compatibility with previous docs but it's like no big deal

```

### Attaching to the container
Expand Down
6 changes: 4 additions & 2 deletions cmd/Containerfile
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,10 @@

ENV GSW_LOGGER_OUTPUT_PATHS=stdout

ENV GSW_DATABASE_HOST_NAME=influxdb
ENV GSW_DATABASE_PORT_NUMBER=8089
ENV GSW_DATABASE_V2_URL=http://influxdb:8086
ENV GSW_DATABASE_V2_TOKEN=gsw-local-dev-token

Check warning on line 47 in cmd/Containerfile

View workflow job for this annotation

GitHub Actions / build-and-push-image

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ENV "GSW_DATABASE_V2_TOKEN") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/

Copilot AI Apr 3, 2026

Copy link

Choose a reason for hiding this comment

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

The Containerfile bakes a fixed InfluxDB admin token into the image layer via ENV GSW_DATABASE_V2_TOKEN=.... This is effectively a hardcoded credential and can be unintentionally reused outside local dev (and is retrievable from the built image metadata/history). Prefer leaving the token unset in the image and requiring it to be provided at runtime (e.g., via docker run -e ... / compose env), or set a placeholder value that forces explicit override.

Suggested change
ENV GSW_DATABASE_V2_TOKEN=gsw-local-dev-token
ENV GSW_DATABASE_V2_TOKEN=CHANGE_ME_AT_RUNTIME

Copilot uses AI. Check for mistakes.
ENV GSW_DATABASE_V2_ORG=gsw
ENV GSW_DATABASE_V2_BUCKET=gsw
Comment on lines -46 to +49

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I believe I had the database environment variables in the Dockerfile so that the GSW container could start without a database config, but that was patched previously so now I think all of the database config could be left out of the Dockerfile.

ENV GSW_LOGGING_CONFIG=data/config/logger.yaml
ENV GSW_TELEMETRY_CONFIG=data/config/backplane.yaml

Expand Down
12 changes: 10 additions & 2 deletions cmd/gsw_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,16 @@ func dbInitialize(ctx context.Context, channelMap map[int]chan []byte, cfg resol
}

func resolveDBConfig(config *viper.Viper) (resolvedDBConfig, error) {
v2Map := config.GetStringMap("database_v2")
if len(v2Map) > 0 {
v2Configured := config.IsSet("database_v2") ||
config.IsSet("database_v2.url") ||
config.IsSet("database_v2.token") ||
config.IsSet("database_v2.org") ||
config.IsSet("database_v2.bucket") ||
config.IsSet("database_v2.batch_size") ||
config.IsSet("database_v2.flush_interval_ms") ||
config.IsSet("database_v2.precision")

if v2Configured {
precision, err := db.ParsePrecision(config.GetString("database_v2.precision"))
if err != nil {
return resolvedDBConfig{}, fmt.Errorf("invalid database_v2.precision: %w", err)
Expand Down
74 changes: 74 additions & 0 deletions cmd/gsw_service_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package main

import (
"strings"
"testing"

"github.com/AarC10/GSW-V2/lib/db"
"github.com/spf13/viper"
)

func newTestConfig() *viper.Viper {
cfg := viper.New()
cfg.SetEnvPrefix("GSW")
cfg.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
cfg.AutomaticEnv()
return cfg
}

func TestResolveDBConfigFromEnvironmentUsesV2(t *testing.T) {
t.Setenv("GSW_DATABASE_V2_URL", "http://influxdb:8086")
t.Setenv("GSW_DATABASE_V2_TOKEN", "test-token")
t.Setenv("GSW_DATABASE_V2_ORG", "gsw")
t.Setenv("GSW_DATABASE_V2_BUCKET", "gsw")
t.Setenv("GSW_DATABASE_V2_BATCH_SIZE", "250")
t.Setenv("GSW_DATABASE_V2_FLUSH_INTERVAL_MS", "1500")
t.Setenv("GSW_DATABASE_V2_PRECISION", "ms")

cfg := newTestConfig()

got, err := resolveDBConfig(cfg)
if err != nil {
t.Fatalf("resolveDBConfig returned error: %v", err)
}
if got.v2 == nil {
t.Fatal("expected v2 config to be selected")
}
if got.v1 != nil {
t.Fatal("expected v1 config to remain unset")
}

want := db.InfluxDBV2Config{
URL: "http://influxdb:8086",
Token: "test-token",
Org: "gsw",
Bucket: "gsw",
BatchSize: 250,
FlushInterval: 1500,
Precision: db.PrecisionMS,
}
if *got.v2 != want {
t.Fatalf("got %+v, want %+v", *got.v2, want)
}
}

func TestResolveDBConfigPrefersV2OverV1(t *testing.T) {
cfg := viper.New()
cfg.Set("database_host_name", "legacy")
cfg.Set("database_port_number", 8089)
cfg.Set("database_v2.url", "http://influxdb:8086")
cfg.Set("database_v2.token", "token")
cfg.Set("database_v2.org", "gsw")
cfg.Set("database_v2.bucket", "gsw")

got, err := resolveDBConfig(cfg)
if err != nil {
t.Fatalf("resolveDBConfig returned error: %v", err)
}
if got.v2 == nil {
t.Fatal("expected v2 config to be selected")
}
if got.v1 != nil {
t.Fatal("expected v1 config to remain unset when v2 is configured")
}
}
45 changes: 36 additions & 9 deletions compose-services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,52 @@ services:
ports:
- "127.0.0.1:3000:3000/tcp"
depends_on:
influxdb:
condition: service_started
influxdb-init:
condition: service_completed_successfully
environment:
- GSW_GRAFANA_INFLUXDB_URL=http://influxdb:8086
- GSW_GRAFANA_INFLUXDB_USER=root
- GSW_GRAFANA_INFLUXDB_PASSWORD=root
- GSW_GRAFANA_INFLUXDB_DB_NAME=${GSW_INFLUXDB_BUCKET:-gsw}
- GSW_GRAFANA_INFLUXDB_TOKEN=${GSW_INFLUXDB_ADMIN_TOKEN:-gsw-local-dev-token}
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_ANONYMOUS_ORG_ROLE=Viewer
- GF_AUTH_BASIC_ENABLED=false
volumes:
- grafana-data:/var/lib/grafana
influxdb:
image: influxdb:1.11-alpine
image: influxdb:2.7-alpine

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

2.8-alpine is latest, could probably be bumped unless there are breaking changes in 2.8.

volumes:
- influxdb-data:/var/lib/influxdb
- influxdb-data:/var/lib/influxdb2
- influxdb-config:/etc/influxdb2
environment:
- INFLUXDB_UDP_ENABLED=true
- INFLUXDB_UDP_DATABASE=gsw
- DOCKER_INFLUXDB_INIT_MODE=setup
- DOCKER_INFLUXDB_INIT_USERNAME=${GSW_INFLUXDB_USERNAME:-gsw}
- DOCKER_INFLUXDB_INIT_PASSWORD=${GSW_INFLUXDB_PASSWORD:-gsw-password}
- DOCKER_INFLUXDB_INIT_ORG=${GSW_INFLUXDB_ORG:-gsw}
- DOCKER_INFLUXDB_INIT_BUCKET=${GSW_INFLUXDB_BUCKET:-gsw}
- DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=${GSW_INFLUXDB_ADMIN_TOKEN:-gsw-local-dev-token}
ports:
- "127.0.0.1:8089:8089/udp"
- "127.0.0.1:8086:8086/tcp"
healthcheck:
test: ["CMD", "influx", "ping", "--host", "http://localhost:8086"]
interval: 5s
timeout: 5s
retries: 20
start_period: 10s
influxdb-init:
image: influxdb:2.7-alpine
depends_on:
influxdb:
condition: service_healthy
environment:
- INFLUX_HOST=http://influxdb:8086
- DOCKER_INFLUXDB_INIT_ORG=${GSW_INFLUXDB_ORG:-gsw}
- DOCKER_INFLUXDB_INIT_BUCKET=${GSW_INFLUXDB_BUCKET:-gsw}
- DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=${GSW_INFLUXDB_ADMIN_TOKEN:-gsw-local-dev-token}
- GSW_INFLUXDB_V1_DATABASE=${GSW_INFLUXDB_BUCKET:-gsw}
- GSW_INFLUXDB_V1_RETENTION_POLICY=autogen
volumes:
- ./data/influxdb/init-v1-compat.sh:/docker-entrypoint-initdb.d/init-v1-compat.sh:ro
entrypoint: ["/bin/sh", "/docker-entrypoint-initdb.d/init-v1-compat.sh"]

mosquitto:
build:
Expand All @@ -38,3 +64,4 @@ services:
volumes:
grafana-data:
influxdb-data:
influxdb-config:
7 changes: 5 additions & 2 deletions compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ services:
dockerfile: cmd/Containerfile
depends_on:
influxdb:
condition: service_started
condition: service_healthy
grafana:
condition: service_started
environment:
- GSW_DATABASE_HOST_NAME=influxdb
- GSW_DATABASE_V2_URL=http://influxdb:8086
- GSW_DATABASE_V2_TOKEN=${GSW_INFLUXDB_ADMIN_TOKEN:-gsw-local-dev-token}
- GSW_DATABASE_V2_ORG=${GSW_INFLUXDB_ORG:-gsw}
- GSW_DATABASE_V2_BUCKET=${GSW_INFLUXDB_BUCKET:-gsw}
- GSW_TELEMETRY_CONFIG=data/config/backplane.yaml
ports:
- 11020:11020/udp
Expand Down
4 changes: 2 additions & 2 deletions data/config/gsw_service.yaml.example
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ telemetry_config: data/config/backplane.yaml
# If database_v2 is set, V2 will be used and V1 settings are ignored
database_v2:
url: http://localhost:8086
token: your-token-here
token: gsw-local-dev-token

Copilot AI Apr 3, 2026

Copy link

Choose a reason for hiding this comment

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

This example config now includes a concrete token value (gsw-local-dev-token). Even in example files, committing an actual token encourages copying it into real deployments and makes it easy to end up with shared credentials. Consider using a clear placeholder (e.g. your-token-here) and/or referencing the GSW_INFLUXDB_ADMIN_TOKEN/GSW_DATABASE_V2_TOKEN environment variable so users are nudged to set their own.

Suggested change
token: gsw-local-dev-token
token: your-token-here

Copilot uses AI. Check for mistakes.
org: gsw
bucket: gsw
batch_size: 100 # points buffered before auto-flush
flush_interval_ms: 1000 # max ms before flushing partial batch
precision: ns # ns | us | ms | s

# Path to GSW service logging config
logging_config: data/config/logger.yaml
logging_config: data/config/logger.yaml
10 changes: 7 additions & 3 deletions data/grafana/provisioning/datasources/gsw-influxdb-provider.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ datasources:
uid: gsw-influxdb
type: influxdb
access: proxy

user: $GSW_GRAFANA_INFLUXDB_USER

url: $GSW_GRAFANA_INFLUXDB_URL
jsonData:
dbName: $GSW_GRAFANA_INFLUXDB_DB_NAME
httpHeaderName1: Authorization
httpMode: POST
version: InfluxQL
secureJsonData:
password: $GSW_GRAFANA_INFLUXDB_PASSWORD
httpHeaderValue1: Token $GSW_GRAFANA_INFLUXDB_TOKEN
46 changes: 46 additions & 0 deletions data/influxdb/init-v1-compat.sh

@AarC10 AarC10 Apr 3, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Note this file should be blasted once we blast influx v1 from existence. This is just a bandaid to maintain compatibility with the current Grafana dashboards (technically can blast when that happens actually)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

honestly wouldn't hurt to have this in general

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could this be moved to an init script in the influx container? Would make it a lot simpler on the docker-compose and config side of things.

https://docs.influxdata.com/influxdb/v2/install/use-docker-compose/#:~:text=Runs%20any%20custom%20initialization%20scripts%20mounted%20inside%20the%20container%E2%80%99s%20/docker%2Dentrypoint%2Dinitdb.d/%20path.

Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/bin/sh
set -eu

host="${INFLUX_HOST:-http://influxdb:8086}"
org="${DOCKER_INFLUXDB_INIT_ORG:?DOCKER_INFLUXDB_INIT_ORG is required}"
bucket="${DOCKER_INFLUXDB_INIT_BUCKET:?DOCKER_INFLUXDB_INIT_BUCKET is required}"
token="${DOCKER_INFLUXDB_INIT_ADMIN_TOKEN:?DOCKER_INFLUXDB_INIT_ADMIN_TOKEN is required}"
database="${GSW_INFLUXDB_V1_DATABASE:-$bucket}"
retention_policy="${GSW_INFLUXDB_V1_RETENTION_POLICY:-autogen}"

echo "Waiting for InfluxDB at ${host}"
until influx ping --host "${host}" >/dev/null 2>&1; do
sleep 1
done

bucket_id="$(influx bucket list \
--host "${host}" \
--org "${org}" \
--token "${token}" \
--name "${bucket}" \
--hide-headers | awk 'NR == 1 { print $1 }')"

if [ -z "${bucket_id}" ]; then
echo "Unable to resolve bucket ID for ${bucket}" >&2
exit 1
fi

if influx v1 dbrp list \
--host "${host}" \
--org "${org}" \
--token "${token}" \
--db "${database}" \
--hide-headers | grep -Eq "[[:space:]]${retention_policy}([[:space:]]|$)"; then

Copilot AI Apr 3, 2026

Copy link

Choose a reason for hiding this comment

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

retention_policy is interpolated directly into an extended regex passed to grep -E. If the value contains regex metacharacters, the check can produce false positives/negatives (or even fail to compile). Consider avoiding regex here (e.g., parse columns with awk and compare exact fields, or use fixed-string matching) so the existence check is reliable for any retention policy name.

Suggested change
--hide-headers | grep -Eq "[[:space:]]${retention_policy}([[:space:]]|$)"; then
--hide-headers | awk -v database="${database}" -v retention_policy="${retention_policy}" '
$2 == database && $3 == retention_policy { found = 1; exit }
END { exit found ? 0 : 1 }
'; then

Copilot uses AI. Check for mistakes.
echo "DBRP mapping already exists for ${database}/${retention_policy}"
exit 0
fi

echo "Creating DBRP mapping for ${database}/${retention_policy}"
influx v1 dbrp create \
--host "${host}" \
--org "${org}" \
--token "${token}" \
--bucket-id "${bucket_id}" \
--db "${database}" \
--rp "${retention_policy}" \
--default
Loading