diff --git a/.drone/drone.yml b/.drone/drone.yml index f521c097fe5f..204767a96375 100644 --- a/.drone/drone.yml +++ b/.drone/drone.yml @@ -110,7 +110,7 @@ steps: - commands: - apt-get update -y && apt-get install -y libsystemd-dev - make lint - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Lint trigger: event: @@ -128,7 +128,7 @@ steps: - ERR_MSG="Dashboard definitions are out of date. Please run 'make generate-dashboards' and commit changes!" - if [ ! -z "$(git status --porcelain)" ]; then echo $ERR_MSG >&2; exit 1; fi - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Regenerate dashboards trigger: event: @@ -146,7 +146,7 @@ steps: - ERR_MSG="Custom Resource Definitions are out of date. Please run 'make generate-crds' and commit changes!" - if [ ! -z "$(git status --porcelain)" ]; then echo $ERR_MSG >&2; exit 1; fi - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Regenerate crds trigger: event: @@ -161,7 +161,7 @@ platform: steps: - commands: - make GO_TAGS="nodocker" test - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Run Go tests trigger: event: @@ -176,7 +176,7 @@ platform: steps: - commands: - K8S_USE_DOCKER_NETWORK=1 make test - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Run Go tests volumes: - name: docker @@ -199,7 +199,7 @@ platform: steps: - commands: - go test -tags="nodocker,nonetwork" ./... - image: grafana/agent-build-image:0.31.0-windows + image: grafana/agent-build-image:0.33.0-windows name: Run Go tests trigger: ref: @@ -214,7 +214,7 @@ platform: steps: - commands: - make agent-image - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build container volumes: - name: docker @@ -239,7 +239,7 @@ platform: steps: - commands: - make agentctl-image - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build container volumes: - name: docker @@ -264,7 +264,7 @@ platform: steps: - commands: - make operator-image - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build container volumes: - name: docker @@ -290,7 +290,7 @@ platform: steps: - commands: - '& "C:/Program Files/git/bin/bash.exe" ./tools/ci/docker-containers-windows agent' - image: grafana/agent-build-image:0.31.0-windows + image: grafana/agent-build-image:0.33.0-windows name: Build container volumes: - name: docker @@ -316,7 +316,7 @@ platform: steps: - commands: - '& "C:/Program Files/git/bin/bash.exe" ./tools/ci/docker-containers-windows agentctl' - image: grafana/agent-build-image:0.31.0-windows + image: grafana/agent-build-image:0.33.0-windows name: Build container volumes: - name: docker @@ -343,7 +343,7 @@ steps: - make generate-ui - GO_TAGS="builtinassets promtail_journal_enabled" GOOS=linux GOARCH=amd64 GOARM= make agent - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -360,7 +360,7 @@ steps: - make generate-ui - GO_TAGS="builtinassets promtail_journal_enabled" GOOS=linux GOARCH=arm64 GOARM= make agent - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -377,7 +377,7 @@ steps: - make generate-ui - GO_TAGS="builtinassets promtail_journal_enabled" GOOS=linux GOARCH=ppc64le GOARM= make agent - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -394,7 +394,7 @@ steps: - make generate-ui - GO_TAGS="builtinassets promtail_journal_enabled" GOOS=linux GOARCH=s390x GOARM= make agent - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -410,7 +410,7 @@ steps: - commands: - make generate-ui - GO_TAGS="builtinassets" GOOS=darwin GOARCH=amd64 GOARM= make agent - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -426,7 +426,7 @@ steps: - commands: - make generate-ui - GO_TAGS="builtinassets" GOOS=darwin GOARCH=arm64 GOARM= make agent - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -442,7 +442,7 @@ steps: - commands: - make generate-ui - GO_TAGS="builtinassets" GOOS=windows GOARCH=amd64 GOARM= make agent - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -458,7 +458,7 @@ steps: - commands: - make generate-ui - GO_TAGS="builtinassets" GOOS=freebsd GOARCH=amd64 GOARM= make agent - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -475,7 +475,7 @@ steps: - make generate-ui - GO_TAGS="builtinassets promtail_journal_enabled" GOOS=linux GOARCH=amd64 GOARM= make agent-flow - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -492,7 +492,7 @@ steps: - make generate-ui - GO_TAGS="builtinassets promtail_journal_enabled" GOOS=linux GOARCH=arm64 GOARM= make agent-flow - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -509,7 +509,7 @@ steps: - make generate-ui - GO_TAGS="builtinassets promtail_journal_enabled" GOOS=linux GOARCH=ppc64le GOARM= make agent-flow - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -526,7 +526,7 @@ steps: - make generate-ui - GO_TAGS="builtinassets promtail_journal_enabled" GOOS=linux GOARCH=s390x GOARM= make agent-flow - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -542,7 +542,7 @@ steps: - commands: - make generate-ui - GO_TAGS="builtinassets" GOOS=darwin GOARCH=amd64 GOARM= make agent-flow - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -558,7 +558,7 @@ steps: - commands: - make generate-ui - GO_TAGS="builtinassets" GOOS=darwin GOARCH=arm64 GOARM= make agent-flow - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -574,7 +574,7 @@ steps: - commands: - make generate-ui - GO_TAGS="builtinassets" GOOS=windows GOARCH=amd64 GOARM= make agent-flow - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -590,7 +590,7 @@ steps: - commands: - make generate-ui - GO_TAGS="builtinassets" GOOS=freebsd GOARCH=amd64 GOARM= make agent-flow - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -607,7 +607,7 @@ steps: - make generate-ui - GO_TAGS="builtinassets promtail_journal_enabled" GOOS=linux GOARCH=amd64 GOARM= make agentctl - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -624,7 +624,7 @@ steps: - make generate-ui - GO_TAGS="builtinassets promtail_journal_enabled" GOOS=linux GOARCH=arm64 GOARM= make agentctl - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -641,7 +641,7 @@ steps: - make generate-ui - GO_TAGS="builtinassets promtail_journal_enabled" GOOS=linux GOARCH=ppc64le GOARM= make agentctl - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -658,7 +658,7 @@ steps: - make generate-ui - GO_TAGS="builtinassets promtail_journal_enabled" GOOS=linux GOARCH=s390x GOARM= make agentctl - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -674,7 +674,7 @@ steps: - commands: - make generate-ui - GO_TAGS="builtinassets" GOOS=darwin GOARCH=amd64 GOARM= make agentctl - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -690,7 +690,7 @@ steps: - commands: - make generate-ui - GO_TAGS="builtinassets" GOOS=darwin GOARCH=arm64 GOARM= make agentctl - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -706,7 +706,7 @@ steps: - commands: - make generate-ui - GO_TAGS="builtinassets" GOOS=windows GOARCH=amd64 GOARM= make agentctl - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -722,7 +722,7 @@ steps: - commands: - make generate-ui - GO_TAGS="builtinassets" GOOS=freebsd GOARCH=amd64 GOARM= make agentctl - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -739,7 +739,7 @@ steps: - make generate-ui - GO_TAGS="builtinassets promtail_journal_enabled" GOOS=linux GOARCH=amd64 GOARM= make operator - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -756,7 +756,7 @@ steps: - make generate-ui - GO_TAGS="builtinassets promtail_journal_enabled" GOOS=linux GOARCH=arm64 GOARM= make operator - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -773,7 +773,7 @@ steps: - make generate-ui - GO_TAGS="builtinassets promtail_journal_enabled" GOOS=linux GOARCH=ppc64le GOARM= make operator - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -790,7 +790,7 @@ steps: - make generate-ui - GO_TAGS="builtinassets promtail_journal_enabled" GOOS=linux GOARCH=s390x GOARM= make operator - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -806,7 +806,7 @@ steps: - commands: - make generate-ui - GO_TAGS="builtinassets" GOOS=darwin GOARCH=amd64 GOARM= make operator - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -822,7 +822,7 @@ steps: - commands: - make generate-ui - GO_TAGS="builtinassets" GOOS=darwin GOARCH=arm64 GOARM= make operator - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -838,7 +838,7 @@ steps: - commands: - make generate-ui - GO_TAGS="builtinassets" GOOS=windows GOARCH=amd64 GOARM= make operator - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -854,7 +854,7 @@ steps: - commands: - make generate-ui - GO_TAGS="builtinassets" GOOS=freebsd GOARCH=amd64 GOARM= make operator - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -871,7 +871,7 @@ steps: - make generate-ui - GO_TAGS="builtinassets promtail_journal_enabled" GOOS=linux GOARCH=amd64 GOARM= GOEXPERIMENT=boringcrypto make agent-boringcrypto - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -888,7 +888,7 @@ steps: - make generate-ui - GO_TAGS="builtinassets promtail_journal_enabled" GOOS=linux GOARCH=arm64 GOARM= GOEXPERIMENT=boringcrypto make agent-boringcrypto - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Build trigger: event: @@ -904,7 +904,7 @@ steps: - commands: - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes failure: ignore - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Configure QEMU volumes: - name: docker @@ -924,7 +924,7 @@ steps: from_secret: docker_password GCR_CREDS: from_secret: gcr_admin - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Publish container volumes: - name: docker @@ -948,7 +948,7 @@ steps: - commands: - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes failure: ignore - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Configure QEMU volumes: - name: docker @@ -968,7 +968,7 @@ steps: from_secret: docker_password GCR_CREDS: from_secret: gcr_admin - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Publish container volumes: - name: docker @@ -992,7 +992,7 @@ steps: - commands: - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes failure: ignore - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Configure QEMU volumes: - name: docker @@ -1012,7 +1012,7 @@ steps: from_secret: docker_password GCR_CREDS: from_secret: gcr_admin - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Publish container volumes: - name: docker @@ -1036,7 +1036,7 @@ steps: - commands: - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes failure: ignore - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Configure QEMU volumes: - name: docker @@ -1056,7 +1056,7 @@ steps: from_secret: docker_password GCR_CREDS: from_secret: gcr_admin - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Publish container volumes: - name: docker @@ -1085,7 +1085,7 @@ steps: from_secret: docker_login DOCKER_PASSWORD: from_secret: docker_password - image: grafana/agent-build-image:0.31.0-windows + image: grafana/agent-build-image:0.33.0-windows name: Build containers volumes: - name: docker @@ -1114,7 +1114,7 @@ steps: from_secret: docker_login DOCKER_PASSWORD: from_secret: docker_password - image: grafana/agent-build-image:0.31.0-windows + image: grafana/agent-build-image:0.33.0-windows name: Build containers volumes: - name: docker @@ -1231,7 +1231,7 @@ steps: from_secret: gpg_private_key GPG_PUBLIC_KEY: from_secret: gpg_public_key - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Publish release volumes: - name: docker @@ -1256,7 +1256,7 @@ steps: - DOCKER_OPTS="" make dist/grafana-agentctl-linux-amd64 - DOCKER_OPTS="" make dist.temp/grafana-agent-flow-linux-amd64 - DOCKER_OPTS="" make test-packages - image: grafana/agent-build-image:0.31.0 + image: grafana/agent-build-image:0.33.0 name: Test Linux system packages volumes: - name: docker @@ -1352,6 +1352,6 @@ kind: secret name: updater_private_key --- kind: signature -hmac: 2e439110a89f33a78d745a71635d47f9b1a99de6028bb84c258a0be9c09840f2 +hmac: 251627e59296f743a58112edccf6a03ba1e226a57d59720e3894d2ff9c7e520d ... diff --git a/CHANGELOG.md b/CHANGELOG.md index 48f592c8165a..888c219e08dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,28 @@ This document contains a historical list of changes between releases. Only changes that impact end-user behavior are listed; changes to documentation or internal API changes are not present. +v0.40.2 (2024-03-05) +-------------------- + +### Bugfixes + +- Set permissions on the `Grafana Agent [Flow]` folder when installing via the + windows installer rather than relying on the parent folder permissions. (@erikbaranowski) + +- Set restricted viewing permissions on the `agent-config.yaml` (static mode) or + `config.river` (flow mode) when installing via the Windows installer if the + configuration file does not already exist. (@erikbaranowski) + +- Fix an issue where the import config node would not run after a config reload. (@wildum) + +- Fix an issue where Loki could reject a batch of logs when structured metadata feature is used. (@thampiotr) + +- Fix a duplicate metrics registration panic when recreating static + mode metric instance's write handler. (@rfratto, @hainenber) + +### Other changes +- Change the Docker base image for Linux containers to `public.ecr.aws/ubuntu/ubuntu:mantic`. (@hainenber) v0.40.1 (2024-02-27) -------------------- diff --git a/cmd/grafana-agent-operator/Dockerfile b/cmd/grafana-agent-operator/Dockerfile index a86af13209bb..6ba2a6218cd7 100644 --- a/cmd/grafana-agent-operator/Dockerfile +++ b/cmd/grafana-agent-operator/Dockerfile @@ -4,7 +4,7 @@ # default when running `docker buildx build` or when DOCKER_BUILDKIT=1 is set # in environment variables. -FROM --platform=$BUILDPLATFORM grafana/agent-build-image:0.30.4 as build +FROM --platform=$BUILDPLATFORM grafana/agent-build-image:0.33.0 as build ARG BUILDPLATFORM ARG TARGETPLATFORM ARG TARGETOS @@ -22,7 +22,7 @@ RUN --mount=type=cache,target=/root/.cache/go-build \ RELEASE_BUILD=${RELEASE_BUILD} VERSION=${VERSION} \ make operator -FROM ubuntu:mantic +FROM public.ecr.aws/ubuntu/ubuntu:mantic LABEL org.opencontainers.image.source="https://github.com/grafana/agent" diff --git a/cmd/grafana-agent/Dockerfile b/cmd/grafana-agent/Dockerfile index f151d43fd080..132d1317495c 100644 --- a/cmd/grafana-agent/Dockerfile +++ b/cmd/grafana-agent/Dockerfile @@ -4,7 +4,7 @@ # default when running `docker buildx build` or when DOCKER_BUILDKIT=1 is set # in environment variables. -FROM --platform=$BUILDPLATFORM grafana/agent-build-image:0.30.4 as build +FROM --platform=$BUILDPLATFORM grafana/agent-build-image:0.33.0 as build ARG BUILDPLATFORM ARG TARGETPLATFORM ARG TARGETOS @@ -30,7 +30,7 @@ RUN --mount=type=cache,target=/root/.cache/go-build \ GOEXPERIMENT=${GOEXPERIMENT} \ make agent -FROM ubuntu:mantic +FROM public.ecr.aws/ubuntu/ubuntu:mantic LABEL org.opencontainers.image.source="https://github.com/grafana/agent" diff --git a/cmd/grafana-agent/Dockerfile.windows b/cmd/grafana-agent/Dockerfile.windows index 454c93450eea..6f99a6138fdb 100644 --- a/cmd/grafana-agent/Dockerfile.windows +++ b/cmd/grafana-agent/Dockerfile.windows @@ -1,4 +1,4 @@ -FROM grafana/agent-build-image:0.30.4-windows as builder +FROM grafana/agent-build-image:0.33.0-windows as builder ARG VERSION ARG RELEASE_BUILD=1 diff --git a/cmd/grafana-agentctl/Dockerfile b/cmd/grafana-agentctl/Dockerfile index d04f1816ef99..67fe64c6f3bf 100644 --- a/cmd/grafana-agentctl/Dockerfile +++ b/cmd/grafana-agentctl/Dockerfile @@ -4,7 +4,7 @@ # default when running `docker buildx build` or when DOCKER_BUILDKIT=1 is set # in environment variables. -FROM --platform=$BUILDPLATFORM grafana/agent-build-image:0.30.4 as build +FROM --platform=$BUILDPLATFORM grafana/agent-build-image:0.33.0 as build ARG BUILDPLATFORM ARG TARGETPLATFORM ARG TARGETOS @@ -23,7 +23,7 @@ RUN --mount=type=cache,target=/root/.cache/go-build \ GO_TAGS="netgo promtail_journal_enabled" \ make agentctl -FROM ubuntu:mantic +FROM public.ecr.aws/ubuntu/ubuntu:mantic LABEL org.opencontainers.image.source="https://github.com/grafana/agent" diff --git a/cmd/grafana-agentctl/Dockerfile.windows b/cmd/grafana-agentctl/Dockerfile.windows index 80d32f866736..8cf3c34a0ace 100644 --- a/cmd/grafana-agentctl/Dockerfile.windows +++ b/cmd/grafana-agentctl/Dockerfile.windows @@ -1,4 +1,4 @@ -FROM grafana/agent-build-image:0.30.4-windows as builder +FROM grafana/agent-build-image:0.33.0-windows as builder ARG VERSION ARG RELEASE_BUILD=1 diff --git a/component/common/loki/client/batch.go b/component/common/loki/client/batch.go index 66a2ac7859de..473ada73e831 100644 --- a/component/common/loki/client/batch.go +++ b/component/common/loki/client/batch.go @@ -61,7 +61,7 @@ func newBatch(maxStreams int, entries ...loki.Entry) *batch { // add an entry to the batch func (b *batch) add(entry loki.Entry) error { - b.totalBytes += len(entry.Line) + b.totalBytes += entrySize(entry.Entry) // Append the entry to an already existing stream (if any) labels := labelsMapToString(entry.Labels, ReservedLabelTenantID) @@ -150,8 +150,8 @@ func (b *batch) sizeBytes() int { // sizeBytesAfter returns the size of the batch after the input entry // will be added to the batch itself -func (b *batch) sizeBytesAfter(line string) int { - return b.totalBytes + len(line) +func (b *batch) sizeBytesAfter(entry logproto.Entry) int { + return b.totalBytes + entrySize(entry) } // age of the batch since its creation @@ -201,3 +201,11 @@ func (b *batch) reportAsSentData(h SentDataMarkerHandler) { h.UpdateSentData(seg, data) } } + +func entrySize(entry logproto.Entry) int { + structuredMetadataSize := 0 + for _, label := range entry.StructuredMetadata { + structuredMetadataSize += label.Size() + } + return len(entry.Line) + structuredMetadataSize +} diff --git a/component/common/loki/client/batch_test.go b/component/common/loki/client/batch_test.go index 2701f0a0c717..a495470cfc15 100644 --- a/component/common/loki/client/batch_test.go +++ b/component/common/loki/client/batch_test.go @@ -57,8 +57,9 @@ func TestBatch_add(t *testing.T) { inputEntries: []loki.Entry{ {Labels: model.LabelSet{}, Entry: logEntries[0].Entry}, {Labels: model.LabelSet{}, Entry: logEntries[1].Entry}, + {Labels: model.LabelSet{}, Entry: logEntries[7].Entry}, }, - expectedSizeBytes: len(logEntries[0].Entry.Line) + len(logEntries[1].Entry.Line), + expectedSizeBytes: entrySize(logEntries[0].Entry) + entrySize(logEntries[0].Entry) + entrySize(logEntries[7].Entry), }, "multiple streams with multiple log entries": { inputEntries: []loki.Entry{ diff --git a/component/common/loki/client/client.go b/component/common/loki/client/client.go index 5b426b4a1873..428066e862b9 100644 --- a/component/common/loki/client/client.go +++ b/component/common/loki/client/client.go @@ -308,7 +308,7 @@ func (c *client) run() { // If adding the entry to the batch will increase the size over the max // size allowed, we do send the current batch and then create a new one - if batch.sizeBytesAfter(e.Line) > c.cfg.BatchSize { + if batch.sizeBytesAfter(e.Entry) > c.cfg.BatchSize { c.sendBatch(tenantID, batch) batches[tenantID] = newBatch(c.maxStreams, e) diff --git a/component/common/loki/client/client_test.go b/component/common/loki/client/client_test.go index a253fdf61335..aad46e4fa1ea 100644 --- a/component/common/loki/client/client_test.go +++ b/component/common/loki/client/client_test.go @@ -9,6 +9,8 @@ import ( "testing" "time" + "github.com/grafana/loki/pkg/push" + "github.com/go-kit/log" "github.com/grafana/dskit/backoff" "github.com/grafana/dskit/flagext" @@ -34,6 +36,16 @@ var logEntries = []loki.Entry{ {Labels: model.LabelSet{"__tenant_id__": "tenant-1"}, Entry: logproto.Entry{Timestamp: time.Unix(5, 0).UTC(), Line: "line5"}}, {Labels: model.LabelSet{"__tenant_id__": "tenant-2"}, Entry: logproto.Entry{Timestamp: time.Unix(6, 0).UTC(), Line: "line6"}}, {Labels: model.LabelSet{}, Entry: logproto.Entry{Timestamp: time.Unix(6, 0).UTC(), Line: "line0123456789"}}, + { + Labels: model.LabelSet{}, + Entry: logproto.Entry{ + Timestamp: time.Unix(7, 0).UTC(), + Line: "line7", + StructuredMetadata: push.LabelsAdapter{ + {Name: "trace_id", Value: "12345"}, + }, + }, + }, } func TestClient_Handle(t *testing.T) { diff --git a/component/common/loki/client/queue_client.go b/component/common/loki/client/queue_client.go index edd9c25bbd37..edb0bbf9a816 100644 --- a/component/common/loki/client/queue_client.go +++ b/component/common/loki/client/queue_client.go @@ -344,7 +344,7 @@ func (c *queueClient) appendSingleEntry(segmentNum int, lbs model.LabelSet, e lo // If adding the entry to the batch will increase the size over the max // size allowed, we do send the current batch and then create a new one - if batch.sizeBytesAfter(e.Line) > c.cfg.BatchSize { + if batch.sizeBytesAfter(e) > c.cfg.BatchSize { c.sendQueue.enqueue(queuedBatch{ TenantID: tenantID, Batch: batch, diff --git a/docs/sources/_index.md b/docs/sources/_index.md index 556ea167a769..6300bb5df2db 100644 --- a/docs/sources/_index.md +++ b/docs/sources/_index.md @@ -9,7 +9,7 @@ title: Grafana Agent description: Grafana Agent is a flexible, performant, vendor-neutral, telemetry collector weight: 350 cascade: - AGENT_RELEASE: v0.40.1 + AGENT_RELEASE: v0.40.2 OTEL_VERSION: v0.87.0 --- diff --git a/packaging/grafana-agent-flow/windows/install_script.nsis b/packaging/grafana-agent-flow/windows/install_script.nsis index 469a2cbd97b0..44ae1a78b5c5 100644 --- a/packaging/grafana-agent-flow/windows/install_script.nsis +++ b/packaging/grafana-agent-flow/windows/install_script.nsis @@ -63,7 +63,13 @@ Section "install" Pop $0 # Configure the out path and copy files to it. - SetOutPath "$INSTDIR" + IfFileExists "$INSTDIR" Exists NotExists + NotExists: + SetOutPath "$INSTDIR" + Call SetFolderPermissions + Exists: + SetOutPath "$INSTDIR" + File "..\..\..\dist.temp\grafana-agent-flow-windows-amd64.exe" File "..\..\..\dist.temp\grafana-agent-service-windows-amd64.exe" File "logo.ico" @@ -109,6 +115,14 @@ Function CreateConfig Return CreateNewConfig: File "config.river" + + # Set permissions on the config file + AccessControl::DisableFileInheritance "$INSTDIR\config.river" + AccessControl::SetFileOwner "$INSTDIR\config.river" "Administrators" + AccessControl::ClearOnFile "$INSTDIR\config.river" "Administrators" "FullAccess" + AccessControl::SetOnFile "$INSTDIR\config.river" "SYSTEM" "FullAccess" + AccessControl::GrantOnFile "$INSTDIR\config.river" "Everyone" "ListDirectory" + AccessControl::GrantOnFile "$INSTDIR\config.river" "Everyone" "ReadAttributes" Return FunctionEnd @@ -164,6 +178,18 @@ Function InitializeRegistry Return FunctionEnd +Function SetFolderPermissions + # Set permissions on the install directory + AccessControl::DisableFileInheritance $INSTDIR + AccessControl::SetFileOwner $INSTDIR "Administrators" + AccessControl::ClearOnFile $INSTDIR "Administrators" "FullAccess" + AccessControl::SetOnFile $INSTDIR "SYSTEM" "FullAccess" + AccessControl::GrantOnFile $INSTDIR "Everyone" "ListDirectory" + AccessControl::GrantOnFile $INSTDIR "Everyone" "GenericExecute" + AccessControl::GrantOnFile $INSTDIR "Everyone" "GenericRead" + AccessControl::GrantOnFile $INSTDIR "Everyone" "ReadAttributes" +FunctionEnd + # Automatically called when uninstalling. Function un.onInit SetShellVarContext all diff --git a/packaging/grafana-agent/windows/install_script.nsis b/packaging/grafana-agent/windows/install_script.nsis index b08a8216c691..14814475197b 100644 --- a/packaging/grafana-agent/windows/install_script.nsis +++ b/packaging/grafana-agent/windows/install_script.nsis @@ -114,7 +114,12 @@ Function Install nsExec::ExecToLog 'sc stop "Grafana Agent"' Pop $0 # Files for the install directory - to build the installer, these should be in the same directory as the install script (this file) - setOutPath $INSTDIR + IfFileExists "$INSTDIR" Exists NotExists + NotExists: + SetOutPath "$INSTDIR" + Call SetFolderPermissions + Exists: + SetOutPath "$INSTDIR" # Files added here should be removed by the uninstaller (see section "uninstall") file "grafana-agent-windows-amd64.exe" file "logo.ico" @@ -186,9 +191,29 @@ Function WriteConfig FileWrite $9 ` enabled: true` ${EndIf} FileClose $9 # and close the file + + # Set permissions on the config file + AccessControl::DisableFileInheritance "$INSTDIR\agent-config.yaml" + AccessControl::SetFileOwner "$INSTDIR\agent-config.yaml" "Administrators" + AccessControl::ClearOnFile "$INSTDIR\agent-config.yaml" "Administrators" "FullAccess" + AccessControl::SetOnFile "$INSTDIR\agent-config.yaml" "SYSTEM" "FullAccess" + AccessControl::GrantOnFile "$INSTDIR\agent-config.yaml" "Everyone" "ListDirectory" + AccessControl::GrantOnFile "$INSTDIR\agent-config.yaml" "Everyone" "ReadAttributes" Return FunctionEnd +Function SetFolderPermissions + # Set permissions on the install directory + AccessControl::DisableFileInheritance $INSTDIR + AccessControl::SetFileOwner $INSTDIR "Administrators" + AccessControl::ClearOnFile $INSTDIR "Administrators" "FullAccess" + AccessControl::SetOnFile $INSTDIR "SYSTEM" "FullAccess" + AccessControl::GrantOnFile $INSTDIR "Everyone" "ListDirectory" + AccessControl::GrantOnFile $INSTDIR "Everyone" "GenericExecute" + AccessControl::GrantOnFile $INSTDIR "Everyone" "GenericRead" + AccessControl::GrantOnFile $INSTDIR "Everyone" "ReadAttributes" +FunctionEnd + # Uninstaller Function un.onInit SetShellVarContext all diff --git a/pkg/flow/internal/controller/block_node.go b/pkg/flow/internal/controller/block_node.go index 0bb20a33eb67..49ac08c971aa 100644 --- a/pkg/flow/internal/controller/block_node.go +++ b/pkg/flow/internal/controller/block_node.go @@ -11,14 +11,15 @@ import ( type BlockNode interface { dag.Node - // Block returns the current block of the managed config node. + // Block returns the current block managed by the node. Block() *ast.BlockStmt - // Evaluate updates the arguments for the managed component - // by re-evaluating its River block with the provided scope. The managed component - // will be built the first time Evaluate is called. + // Evaluate updates the arguments by re-evaluating the River block with the provided scope. // // Evaluate will return an error if the River block cannot be evaluated or if // decoding to arguments fails. Evaluate(scope *vm.Scope) error + + // UpdateBlock updates the River block used to construct arguments. + UpdateBlock(b *ast.BlockStmt) } diff --git a/pkg/flow/internal/controller/component_node.go b/pkg/flow/internal/controller/component_node.go index 1a6e41605316..64eda47b42a6 100644 --- a/pkg/flow/internal/controller/component_node.go +++ b/pkg/flow/internal/controller/component_node.go @@ -2,7 +2,6 @@ package controller import ( "github.com/grafana/agent/component" - "github.com/grafana/river/ast" ) // ComponentNode is a generic representation of a Flow component. @@ -27,9 +26,6 @@ type ComponentNode interface { // ID returns the component ID of the managed component from its River block. ID() ComponentID - // UpdateBlock updates the River block used to construct arguments for the managed component. - UpdateBlock(b *ast.BlockStmt) - // ModuleIDs returns the current list of modules managed by the component. ModuleIDs() []string } diff --git a/pkg/flow/internal/controller/loader.go b/pkg/flow/internal/controller/loader.go index 79779837cce9..0523ec93d44a 100644 --- a/pkg/flow/internal/controller/loader.go +++ b/pkg/flow/internal/controller/loader.go @@ -338,9 +338,15 @@ func (l *Loader) splitComponentBlocks(blocks []*ast.BlockStmt) (componentBlocks, } func (l *Loader) populateDeclareNodes(g *dag.Graph, declareBlocks []*ast.BlockStmt) diag.Diagnostics { - var diags diag.Diagnostics + var ( + diags diag.Diagnostics + node *DeclareNode + blockMap = make(map[string]*ast.BlockStmt, len(declareBlocks)) + ) l.declareNodes = map[string]*DeclareNode{} for _, declareBlock := range declareBlocks { + id := BlockComponentID(declareBlock).String() + if declareBlock.Label == declareType { diags.Add(diag.Diagnostic{ Severity: diag.SeverityLevelError, @@ -350,16 +356,18 @@ func (l *Loader) populateDeclareNodes(g *dag.Graph, declareBlocks []*ast.BlockSt }) continue } - // TODO: if node already exists in the graph, update the block - // instead of copying it. - node := NewDeclareNode(declareBlock) - if g.GetByID(node.NodeID()) != nil { - diags.Add(diag.Diagnostic{ - Severity: diag.SeverityLevelError, - Message: fmt.Sprintf("cannot add declare node %q; node with same ID already exists", node.NodeID()), - }) + + if diag, defined := blockAlreadyDefined(blockMap, id, declareBlock); defined { + diags = append(diags, diag) continue } + + if exist := l.graph.GetByID(id); exist != nil { + node = exist.(*DeclareNode) + node.UpdateBlock(declareBlock) + } else { + node = NewDeclareNode(declareBlock) + } l.componentNodeManager.customComponentReg.registerDeclare(declareBlock) l.declareNodes[node.label] = node g.Add(node) @@ -367,6 +375,21 @@ func (l *Loader) populateDeclareNodes(g *dag.Graph, declareBlocks []*ast.BlockSt return diags } +// blockAlreadyDefined returns (diag, true) if the given id is already in the provided blockMap. +// else it adds the block to the map and returns (empty diag, false). +func blockAlreadyDefined(blockMap map[string]*ast.BlockStmt, id string, block *ast.BlockStmt) (diag.Diagnostic, bool) { + if orig, redefined := blockMap[id]; redefined { + return diag.Diagnostic{ + Severity: diag.SeverityLevelError, + Message: fmt.Sprintf("block %s already declared at %s", id, ast.StartPos(orig).Position()), + StartPos: block.NamePos.Position(), + EndPos: block.NamePos.Add(len(id) - 1).Position(), + }, true + } + blockMap[id] = block + return diag.Diagnostic{}, false +} + // populateServiceNodes adds service nodes to the graph. func (l *Loader) populateServiceNodes(g *dag.Graph, serviceBlocks []*ast.BlockStmt) diag.Diagnostics { var diags diag.Diagnostics @@ -441,25 +464,33 @@ func (l *Loader) populateServiceNodes(g *dag.Graph, serviceBlocks []*ast.BlockSt // populateConfigBlockNodes adds any config blocks to the graph. func (l *Loader) populateConfigBlockNodes(args map[string]any, g *dag.Graph, configBlocks []*ast.BlockStmt) diag.Diagnostics { var ( - diags diag.Diagnostics - nodeMap = NewConfigNodeMap() + diags diag.Diagnostics + nodeMap = NewConfigNodeMap() + blockMap = make(map[string]*ast.BlockStmt, len(configBlocks)) ) for _, block := range configBlocks { - node, newConfigNodeDiags := NewConfigNode(block, l.globals) - diags = append(diags, newConfigNodeDiags...) - - if g.GetByID(node.NodeID()) != nil { - configBlockStartPos := ast.StartPos(block).Position() - diags.Add(diag.Diagnostic{ - Severity: diag.SeverityLevelError, - Message: fmt.Sprintf("%q block already declared at %s", node.NodeID(), configBlockStartPos), - StartPos: configBlockStartPos, - EndPos: ast.EndPos(block).Position(), - }) - + var ( + node BlockNode + newConfigNodeDiags diag.Diagnostics + ) + id := BlockComponentID(block).String() + if diag, defined := blockAlreadyDefined(blockMap, id, block); defined { + diags = append(diags, diag) continue } + // Check the graph from the previous call to Load to see we can copy an + // existing instance of BlockNode. + if exist := l.graph.GetByID(id); exist != nil { + node = exist.(BlockNode) + node.UpdateBlock(block) + } else { + node, newConfigNodeDiags = NewConfigNode(block, l.globals) + diags = append(diags, newConfigNodeDiags...) + if diags.HasErrors() { + continue + } + } nodeMapDiags := nodeMap.Append(node) diags = append(diags, nodeMapDiags...) @@ -502,18 +533,10 @@ func (l *Loader) populateComponentNodes(g *dag.Graph, componentBlocks []*ast.Blo ) for _, block := range componentBlocks { id := BlockComponentID(block).String() - - if orig, redefined := blockMap[id]; redefined { - diags.Add(diag.Diagnostic{ - Severity: diag.SeverityLevelError, - Message: fmt.Sprintf("Component %s already declared at %s", id, ast.StartPos(orig).Position()), - StartPos: block.NamePos.Position(), - EndPos: block.NamePos.Add(len(id) - 1).Position(), - }) + if diag, defined := blockAlreadyDefined(blockMap, id, block); defined { + diags = append(diags, diag) continue } - blockMap[id] = block - // Check the graph from the previous call to Load to see if we can copy an // existing instance of ComponentNode. if exist := l.graph.GetByID(id); exist != nil { diff --git a/pkg/flow/internal/controller/loader_test.go b/pkg/flow/internal/controller/loader_test.go index 703ad1480e65..5080abec9ea2 100644 --- a/pkg/flow/internal/controller/loader_test.go +++ b/pkg/flow/internal/controller/loader_test.go @@ -84,14 +84,30 @@ func TestLoader(t *testing.T) { t.Run("New Graph", func(t *testing.T) { l := controller.NewLoader(newLoaderOptions()) - diags := applyFromContent(t, l, []byte(testFile), []byte(testConfig)) + diags := applyFromContent(t, l, []byte(testFile), []byte(testConfig), nil) require.NoError(t, diags.ErrorOrNil()) requireGraph(t, l.Graph(), testGraphDefinition) }) + t.Run("Reload Graph New Config", func(t *testing.T) { + l := controller.NewLoader(newLoaderOptions()) + diags := applyFromContent(t, l, []byte(testFile), []byte(testConfig), nil) + require.NoError(t, diags.ErrorOrNil()) + requireGraph(t, l.Graph(), testGraphDefinition) + updatedTestConfig := ` + tracing { + sampling_fraction = 2 + } + ` + diags = applyFromContent(t, l, []byte(testFile), []byte(updatedTestConfig), nil) + require.NoError(t, diags.ErrorOrNil()) + // Expect the same graph because tracing is still there and logging will be added by default. + requireGraph(t, l.Graph(), testGraphDefinition) + }) + t.Run("New Graph No Config", func(t *testing.T) { l := controller.NewLoader(newLoaderOptions()) - diags := applyFromContent(t, l, []byte(testFile), nil) + diags := applyFromContent(t, l, []byte(testFile), nil, nil) require.NoError(t, diags.ErrorOrNil()) requireGraph(t, l.Graph(), testGraphDefinition) }) @@ -109,11 +125,11 @@ func TestLoader(t *testing.T) { } ` l := controller.NewLoader(newLoaderOptions()) - diags := applyFromContent(t, l, []byte(startFile), []byte(testConfig)) + diags := applyFromContent(t, l, []byte(startFile), []byte(testConfig), nil) origGraph := l.Graph() require.NoError(t, diags.ErrorOrNil()) - diags = applyFromContent(t, l, []byte(testFile), []byte(testConfig)) + diags = applyFromContent(t, l, []byte(testFile), []byte(testConfig), nil) require.NoError(t, diags.ErrorOrNil()) newGraph := l.Graph() @@ -128,7 +144,7 @@ func TestLoader(t *testing.T) { } ` l := controller.NewLoader(newLoaderOptions()) - diags := applyFromContent(t, l, []byte(invalidFile), nil) + diags := applyFromContent(t, l, []byte(invalidFile), nil, nil) require.ErrorContains(t, diags.ErrorOrNil(), `cannot retrieve the definition of component name "doesnotexist`) }) @@ -139,7 +155,7 @@ func TestLoader(t *testing.T) { } ` l := controller.NewLoader(newLoaderOptions()) - diags := applyFromContent(t, l, []byte(invalidFile), nil) + diags := applyFromContent(t, l, []byte(invalidFile), nil, nil) require.ErrorContains(t, diags.ErrorOrNil(), `component "testcomponents.tick" must have a label`) }) @@ -158,7 +174,7 @@ func TestLoader(t *testing.T) { } ` l := controller.NewLoader(newLoaderOptions()) - diags := applyFromContent(t, l, []byte(invalidFile), nil) + diags := applyFromContent(t, l, []byte(invalidFile), nil, nil) require.Error(t, diags.ErrorOrNil()) requireGraph(t, l.Graph(), graphDefinition{ @@ -186,9 +202,94 @@ func TestLoader(t *testing.T) { } ` l := controller.NewLoader(newLoaderOptions()) - diags := applyFromContent(t, l, []byte(invalidFile), nil) + diags := applyFromContent(t, l, []byte(invalidFile), nil, nil) require.Error(t, diags.ErrorOrNil()) }) + + t.Run("Config block redefined", func(t *testing.T) { + invalidFile := ` + logging {} + logging {} + ` + l := controller.NewLoader(newLoaderOptions()) + diags := applyFromContent(t, l, nil, []byte(invalidFile), nil) + require.ErrorContains(t, diags.ErrorOrNil(), `block logging already declared at TestLoader/Config_block_redefined:2:4`) + }) + + t.Run("Config block redefined after reload", func(t *testing.T) { + file := ` + logging {} + ` + l := controller.NewLoader(newLoaderOptions()) + diags := applyFromContent(t, l, nil, []byte(file), nil) + require.NoError(t, diags.ErrorOrNil()) + invalidFile := ` + logging {} + logging {} + ` + diags = applyFromContent(t, l, nil, []byte(invalidFile), nil) + require.ErrorContains(t, diags.ErrorOrNil(), `block logging already declared at TestLoader/Config_block_redefined_after_reload:2:4`) + }) + + t.Run("Component block redefined", func(t *testing.T) { + invalidFile := ` + testcomponents.tick "ticker" { + frequency = "1s" + } + testcomponents.tick "ticker" { + frequency = "1s" + } + ` + l := controller.NewLoader(newLoaderOptions()) + diags := applyFromContent(t, l, []byte(invalidFile), nil, nil) + require.ErrorContains(t, diags.ErrorOrNil(), `block testcomponents.tick.ticker already declared at TestLoader/Component_block_redefined:2:4`) + }) + + t.Run("Component block redefined after reload", func(t *testing.T) { + file := ` + testcomponents.tick "ticker" { + frequency = "1s" + } + ` + l := controller.NewLoader(newLoaderOptions()) + diags := applyFromContent(t, l, []byte(file), nil, nil) + require.NoError(t, diags.ErrorOrNil()) + invalidFile := ` + testcomponents.tick "ticker" { + frequency = "1s" + } + testcomponents.tick "ticker" { + frequency = "1s" + } + ` + diags = applyFromContent(t, l, []byte(invalidFile), nil, nil) + require.ErrorContains(t, diags.ErrorOrNil(), `block testcomponents.tick.ticker already declared at TestLoader/Component_block_redefined_after_reload:2:4`) + }) + + t.Run("Declare block redefined", func(t *testing.T) { + invalidFile := ` + declare "a" {} + declare "a" {} + ` + l := controller.NewLoader(newLoaderOptions()) + diags := applyFromContent(t, l, nil, nil, []byte(invalidFile)) + require.ErrorContains(t, diags.ErrorOrNil(), `block declare.a already declared at TestLoader/Declare_block_redefined:2:4`) + }) + + t.Run("Declare block redefined after reload", func(t *testing.T) { + file := ` + declare "a" {} + ` + l := controller.NewLoader(newLoaderOptions()) + diags := applyFromContent(t, l, nil, nil, []byte(file)) + require.NoError(t, diags.ErrorOrNil()) + invalidFile := ` + declare "a" {} + declare "a" {} + ` + diags = applyFromContent(t, l, nil, nil, []byte(invalidFile)) + require.ErrorContains(t, diags.ErrorOrNil(), `block declare.a already declared at TestLoader/Declare_block_redefined_after_reload:2:4`) + }) } // TestScopeWithFailingComponent is used to ensure that the scope is filled out, even if the component @@ -228,13 +329,13 @@ func TestScopeWithFailingComponent(t *testing.T) { } l := controller.NewLoader(newLoaderOptions()) - diags := applyFromContent(t, l, []byte(testFile), nil) + diags := applyFromContent(t, l, []byte(testFile), nil, nil) require.Error(t, diags.ErrorOrNil()) require.Len(t, diags, 1) require.True(t, strings.Contains(diags.Error(), `unrecognized attribute name "frequenc"`)) } -func applyFromContent(t *testing.T, l *controller.Loader, componentBytes []byte, configBytes []byte) diag.Diagnostics { +func applyFromContent(t *testing.T, l *controller.Loader, componentBytes []byte, configBytes []byte, declareBytes []byte) diag.Diagnostics { t.Helper() var ( @@ -256,6 +357,13 @@ func applyFromContent(t *testing.T, l *controller.Loader, componentBytes []byte, } } + if string(declareBytes) != "" { + declareBlocks, diags = fileToBlock(t, declareBytes) + if diags.HasErrors() { + return diags + } + } + applyOptions := controller.ApplyOptions{ ComponentBlocks: componentBlocks, ConfigBlocks: configBlocks, diff --git a/pkg/flow/internal/controller/node_config_argument.go b/pkg/flow/internal/controller/node_config_argument.go index 5b979503f805..23c57987cce0 100644 --- a/pkg/flow/internal/controller/node_config_argument.go +++ b/pkg/flow/internal/controller/node_config_argument.go @@ -2,6 +2,7 @@ package controller import ( "fmt" + "strings" "sync" "github.com/grafana/river/ast" @@ -85,3 +86,19 @@ func (cn *ArgumentConfigNode) Block() *ast.BlockStmt { // NodeID implements dag.Node and returns the unique ID for the config node. func (cn *ArgumentConfigNode) NodeID() string { return cn.nodeID } + +// UpdateBlock updates the River block used to construct arguments. +// The new block isn't used until the next time Evaluate is invoked. +// +// UpdateBlock will panic if the block does not match the component ID of the +// ArgumentConfigNode. +func (cn *ArgumentConfigNode) UpdateBlock(b *ast.BlockStmt) { + if !BlockComponentID(b).Equals(strings.Split(cn.nodeID, ".")) { + panic("UpdateBlock called with an River block with a different ID") + } + + cn.mut.Lock() + defer cn.mut.Unlock() + cn.block = b + cn.eval = vm.New(b.Body) +} diff --git a/pkg/flow/internal/controller/node_config_export.go b/pkg/flow/internal/controller/node_config_export.go index 027335e629b4..314e9a3dd983 100644 --- a/pkg/flow/internal/controller/node_config_export.go +++ b/pkg/flow/internal/controller/node_config_export.go @@ -2,6 +2,7 @@ package controller import ( "fmt" + "strings" "sync" "github.com/grafana/river/ast" @@ -74,3 +75,19 @@ func (cn *ExportConfigNode) Block() *ast.BlockStmt { // NodeID implements dag.Node and returns the unique ID for the config node. func (cn *ExportConfigNode) NodeID() string { return cn.nodeID } + +// UpdateBlock updates the River block used to construct arguments. +// The new block isn't used until the next time Evaluate is invoked. +// +// UpdateBlock will panic if the block does not match the component ID of the +// ExportConfigNode. +func (cn *ExportConfigNode) UpdateBlock(b *ast.BlockStmt) { + if !BlockComponentID(b).Equals(strings.Split(cn.nodeID, ".")) { + panic("UpdateBlock called with an River block with a different ID") + } + + cn.mut.Lock() + defer cn.mut.Unlock() + cn.block = b + cn.eval = vm.New(b.Body) +} diff --git a/pkg/flow/internal/controller/node_config_import.go b/pkg/flow/internal/controller/node_config_import.go index 10ef832a96b6..60e57c1a54e8 100644 --- a/pkg/flow/internal/controller/node_config_import.go +++ b/pkg/flow/internal/controller/node_config_import.go @@ -385,6 +385,22 @@ func (cn *ImportConfigNode) run(errChan chan error, updateTasks func() error) er } } +// UpdateBlock updates the River block used to construct arguments. +// The new block isn't used until the next time Evaluate is invoked. +// +// UpdateBlock will panic if the block does not match the component ID of the +// ImportConfigNode. +func (cn *ImportConfigNode) UpdateBlock(b *ast.BlockStmt) { + if !BlockComponentID(b).Equals(strings.Split(cn.nodeID, ".")) { + panic("UpdateBlock called with an River block with a different ID") + } + + cn.mut.Lock() + defer cn.mut.Unlock() + cn.block = b + cn.source.SetEval(vm.New(b.Body)) +} + func (cn *ImportConfigNode) Label() string { return cn.label } // Block implements BlockNode and returns the current block of the managed config node. diff --git a/pkg/flow/internal/controller/node_config_logging.go b/pkg/flow/internal/controller/node_config_logging.go index 74a35fe6612c..00405459b015 100644 --- a/pkg/flow/internal/controller/node_config_logging.go +++ b/pkg/flow/internal/controller/node_config_logging.go @@ -2,6 +2,7 @@ package controller import ( "fmt" + "strings" "sync" "github.com/go-kit/log" @@ -80,3 +81,19 @@ func (cn *LoggingConfigNode) Block() *ast.BlockStmt { // NodeID implements dag.Node and returns the unique ID for the config node. func (cn *LoggingConfigNode) NodeID() string { return cn.nodeID } + +// UpdateBlock updates the River block used to construct arguments. +// The new block isn't used until the next time Evaluate is invoked. +// +// UpdateBlock will panic if the block does not match the component ID of the +// LoggingConfigNode. +func (cn *LoggingConfigNode) UpdateBlock(b *ast.BlockStmt) { + if !BlockComponentID(b).Equals(strings.Split(cn.nodeID, ".")) { + panic("UpdateBlock called with an River block with a different ID") + } + + cn.mut.Lock() + defer cn.mut.Unlock() + cn.block = b + cn.eval = vm.New(b.Body) +} diff --git a/pkg/flow/internal/controller/node_config_tracing.go b/pkg/flow/internal/controller/node_config_tracing.go index 4fb70e92c9dd..d64f68fbc5b3 100644 --- a/pkg/flow/internal/controller/node_config_tracing.go +++ b/pkg/flow/internal/controller/node_config_tracing.go @@ -2,6 +2,7 @@ package controller import ( "fmt" + "strings" "sync" "github.com/grafana/agent/pkg/flow/tracing" @@ -84,3 +85,19 @@ func (cn *TracingConfigNode) Block() *ast.BlockStmt { // NodeID implements dag.Node and returns the unique ID for the config node. func (cn *TracingConfigNode) NodeID() string { return cn.nodeID } + +// UpdateBlock updates the River block used to construct arguments. +// The new block isn't used until the next time Evaluate is invoked. +// +// UpdateBlock will panic if the block does not match the component ID of the +// LoggingConfigNode. +func (cn *TracingConfigNode) UpdateBlock(b *ast.BlockStmt) { + if !BlockComponentID(b).Equals(strings.Split(cn.nodeID, ".")) { + panic("UpdateBlock called with an River block with a different ID") + } + + cn.mut.Lock() + defer cn.mut.Unlock() + cn.block = b + cn.eval = vm.New(b.Body) +} diff --git a/pkg/flow/internal/controller/node_declare.go b/pkg/flow/internal/controller/node_declare.go index b67350eb5540..0dec60283908 100644 --- a/pkg/flow/internal/controller/node_declare.go +++ b/pkg/flow/internal/controller/node_declare.go @@ -1,6 +1,9 @@ package controller import ( + "strings" + "sync" + "github.com/grafana/river/ast" "github.com/grafana/river/vm" ) @@ -10,6 +13,7 @@ type DeclareNode struct { label string nodeID string componentName string + mut sync.RWMutex block *ast.BlockStmt } @@ -42,3 +46,17 @@ func (cn *DeclareNode) Block() *ast.BlockStmt { // NodeID implements dag.Node and returns the unique ID for the config node. func (cn *DeclareNode) NodeID() string { return cn.nodeID } + +// UpdateBlock updates the managed River block. +// +// UpdateBlock will panic if the block does not match the component ID of the +// DeclareNode. +func (cn *DeclareNode) UpdateBlock(b *ast.BlockStmt) { + if !BlockComponentID(b).Equals(strings.Split(cn.nodeID, ".")) { + panic("UpdateBlock called with an River block with a different ID") + } + + cn.mut.Lock() + defer cn.mut.Unlock() + cn.block = b +} diff --git a/pkg/flow/internal/controller/scheduler_test.go b/pkg/flow/internal/controller/scheduler_test.go index c965d99c9db7..57c6c65c97d1 100644 --- a/pkg/flow/internal/controller/scheduler_test.go +++ b/pkg/flow/internal/controller/scheduler_test.go @@ -99,6 +99,7 @@ func (fr fakeRunnable) NodeID() string { return fr.ID } func (fr fakeRunnable) Run(ctx context.Context) error { return fr.Component.Run(ctx) } func (fr fakeRunnable) Block() *ast.BlockStmt { return nil } func (fr fakeRunnable) Evaluate(scope *vm.Scope) error { return nil } +func (fr fakeRunnable) UpdateBlock(b *ast.BlockStmt) {} type mockComponent struct { RunFunc func(ctx context.Context) error diff --git a/pkg/flow/internal/importsource/import_file.go b/pkg/flow/internal/importsource/import_file.go index bde2f635b6f9..97ce7a5c4085 100644 --- a/pkg/flow/internal/importsource/import_file.go +++ b/pkg/flow/internal/importsource/import_file.go @@ -96,3 +96,8 @@ func (im *ImportFile) Run(ctx context.Context) error { func (im *ImportFile) CurrentHealth() component.Health { return im.fileComponent.CurrentHealth() } + +// Update the evaluator. +func (im *ImportFile) SetEval(eval *vm.Evaluator) { + im.eval = eval +} diff --git a/pkg/flow/internal/importsource/import_git.go b/pkg/flow/internal/importsource/import_git.go index ba7a5aa3081f..f27c4ffc2edf 100644 --- a/pkg/flow/internal/importsource/import_git.go +++ b/pkg/flow/internal/importsource/import_git.go @@ -254,3 +254,8 @@ func (im *ImportGit) CurrentHealth() component.Health { defer im.healthMut.RUnlock() return im.health } + +// Update the evaluator. +func (im *ImportGit) SetEval(eval *vm.Evaluator) { + im.eval = eval +} diff --git a/pkg/flow/internal/importsource/import_http.go b/pkg/flow/internal/importsource/import_http.go index 1d0673445c70..9fae17db4f5d 100644 --- a/pkg/flow/internal/importsource/import_http.go +++ b/pkg/flow/internal/importsource/import_http.go @@ -101,3 +101,8 @@ func (im *ImportHTTP) Run(ctx context.Context) error { func (im *ImportHTTP) CurrentHealth() component.Health { return im.managedRemoteHTTP.CurrentHealth() } + +// Update the evaluator. +func (im *ImportHTTP) SetEval(eval *vm.Evaluator) { + im.eval = eval +} diff --git a/pkg/flow/internal/importsource/import_source.go b/pkg/flow/internal/importsource/import_source.go index f1ef6e72ac1a..aed03ca20406 100644 --- a/pkg/flow/internal/importsource/import_source.go +++ b/pkg/flow/internal/importsource/import_source.go @@ -32,6 +32,8 @@ type ImportSource interface { Run(ctx context.Context) error // CurrentHealth returns the current Health status of the running source. CurrentHealth() component.Health + // Update evaluator + SetEval(eval *vm.Evaluator) } // NewImportSource creates a new ImportSource depending on the type. diff --git a/pkg/flow/internal/importsource/import_string.go b/pkg/flow/internal/importsource/import_string.go index aae1ebc07040..826f3e096dc4 100644 --- a/pkg/flow/internal/importsource/import_string.go +++ b/pkg/flow/internal/importsource/import_string.go @@ -58,3 +58,8 @@ func (im *ImportString) CurrentHealth() component.Health { Health: component.HealthTypeHealthy, } } + +// Update the evaluator. +func (im *ImportString) SetEval(eval *vm.Evaluator) { + im.eval = eval +} diff --git a/pkg/flow/module_eval_test.go b/pkg/flow/module_eval_test.go index 81bd1b381fab..8e7b0f341313 100644 --- a/pkg/flow/module_eval_test.go +++ b/pkg/flow/module_eval_test.go @@ -209,6 +209,111 @@ func TestUpdates_TwoModules_SameCompNames(t *testing.T) { }, 3*time.Second, 10*time.Millisecond) } +func TestUpdates_ReloadConfig(t *testing.T) { + // We use this module in a Flow config below. + module := ` + argument "input" { + optional = false + } + + testcomponents.passthrough "pt" { + input = argument.input.value + lag = "1ms" + } + + export "output" { + value = testcomponents.passthrough.pt.output + } +` + + // We send the count increments via module and to the summation component and verify that the updates propagate. + config := ` + testcomponents.count "inc" { + frequency = "10ms" + max = 10 + } + + module.string "test" { + content = ` + strconv.Quote(module) + ` + arguments { + input = testcomponents.count.inc.count + } + } + + testcomponents.summation "sum" { + input = module.string.test.exports.output + } +` + + ctrl := flow.New(testOptions(t)) + f, err := flow.ParseSource(t.Name(), []byte(config)) + require.NoError(t, err) + require.NotNil(t, f) + + err = ctrl.LoadSource(f, nil) + require.NoError(t, err) + + ctx, cancel := context.WithCancel(context.Background()) + done := make(chan struct{}) + go func() { + ctrl.Run(ctx) + close(done) + }() + defer func() { + cancel() + <-done + }() + + require.Eventually(t, func() bool { + export := getExport[testcomponents.SummationExports](t, ctrl, "", "testcomponents.summation.sum") + return export.LastAdded == 10 + }, 3*time.Second, 10*time.Millisecond) + + // Reload with a new export. + module = ` + argument "input" { + optional = false + } + + testcomponents.passthrough "pt" { + input = argument.input.value + lag = "1ms" + } + + export "output" { + value = -10 + } +` + config = ` + testcomponents.count "inc" { + frequency = "10ms" + max = 10 + } + + module.string "test" { + content = ` + strconv.Quote(module) + ` + arguments { + input = testcomponents.count.inc.count + } + } + + testcomponents.summation "sum" { + input = module.string.test.exports.output + } +` + f, err = flow.ParseSource(t.Name(), []byte(config)) + require.NoError(t, err) + require.NotNil(t, f) + + err = ctrl.LoadSource(f, nil) + require.NoError(t, err) + + require.Eventually(t, func() bool { + export := getExport[testcomponents.SummationExports](t, ctrl, "", "testcomponents.summation.sum") + return export.LastAdded == -10 + }, 3*time.Second, 10*time.Millisecond) +} + func testOptions(t *testing.T) flow.Options { t.Helper() s, err := logging.New(os.Stderr, logging.DefaultOptions) diff --git a/pkg/flow/module_test.go b/pkg/flow/module_test.go index c5f4417c84c3..f35b18b80273 100644 --- a/pkg/flow/module_test.go +++ b/pkg/flow/module_test.go @@ -102,13 +102,13 @@ func TestModule(t *testing.T) { name: "Duplicate argument config", argumentModuleContent: argumentConfig + argumentConfig, exportModuleContent: exportStringConfig, - expectedErrorContains: "\"argument.username\" block already declared", + expectedErrorContains: "block argument.username already declared at t1:2:2", }, { name: "Duplicate export config", argumentModuleContent: argumentConfig, exportModuleContent: exportStringConfig + exportStringConfig, - expectedErrorContains: "\"export.username\" block already declared", + expectedErrorContains: "block export.username already declared at t1:7:2", }, { name: "Multiple exports but none are used but still exported", diff --git a/pkg/metrics/instance/instance.go b/pkg/metrics/instance/instance.go index c81c8e620622..e4cc1113f7b1 100644 --- a/pkg/metrics/instance/instance.go +++ b/pkg/metrics/instance/instance.go @@ -409,7 +409,7 @@ func (i *Instance) initialize(ctx context.Context, reg prometheus.Registerer, cf return fmt.Errorf("error creating WAL: %w", err) } - i.writeHandler = remote.NewWriteHandler(i.logger, i.reg, i.wal) + i.writeHandler = remote.NewWriteHandler(i.logger, reg, i.wal) i.discovery, err = i.newDiscoveryManager(ctx, cfg) if err != nil { diff --git a/pkg/metrics/instance/instance_test.go b/pkg/metrics/instance/instance_test.go index e82117e797df..4ba6e4d3eb4d 100644 --- a/pkg/metrics/instance/instance_test.go +++ b/pkg/metrics/instance/instance_test.go @@ -258,7 +258,8 @@ func TestInstance_Recreate(t *testing.T) { cfg.RemoteFlushDeadline = time.Hour logger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)) - inst, err := New(prometheus.NewRegistry(), cfg, walDir, logger) + currentReg := prometheus.NewRegistry() + inst, err := New(currentReg, cfg, walDir, logger) require.NoError(t, err) ctx, cancel := context.WithCancel(context.Background()) @@ -278,7 +279,7 @@ func TestInstance_Recreate(t *testing.T) { // Recreate the instance, no panic should happen. require.NotPanics(t, func() { - inst, err := New(prometheus.NewRegistry(), cfg, walDir, logger) + inst, err := New(currentReg, cfg, walDir, logger) require.NoError(t, err) runInstance(t, inst) diff --git a/pkg/operator/defaults.go b/pkg/operator/defaults.go index e985937bb348..52005e52976a 100644 --- a/pkg/operator/defaults.go +++ b/pkg/operator/defaults.go @@ -2,7 +2,7 @@ package operator // Supported versions of the Grafana Agent. var ( - DefaultAgentVersion = "v0.40.1" + DefaultAgentVersion = "v0.40.2" DefaultAgentBaseImage = "grafana/agent" DefaultAgentImage = DefaultAgentBaseImage + ":" + DefaultAgentVersion ) diff --git a/tools/gen-versioned-files/agent-version.txt b/tools/gen-versioned-files/agent-version.txt index 01437515a7c3..1e24a0583af0 100644 --- a/tools/gen-versioned-files/agent-version.txt +++ b/tools/gen-versioned-files/agent-version.txt @@ -1 +1 @@ -v0.40.1 +v0.40.2 diff --git a/tools/make/build-container.mk b/tools/make/build-container.mk index be1d4be9ca49..409d284e1f10 100644 --- a/tools/make/build-container.mk +++ b/tools/make/build-container.mk @@ -34,7 +34,7 @@ # variable names should be passed through to the container. USE_CONTAINER ?= 0 -BUILD_IMAGE_VERSION ?= 0.31.0 +BUILD_IMAGE_VERSION ?= 0.33.0 BUILD_IMAGE ?= grafana/agent-build-image:$(BUILD_IMAGE_VERSION) DOCKER_OPTS ?= -it diff --git a/tools/make/packaging.mk b/tools/make/packaging.mk index c9421433e6ae..d150926e48e4 100644 --- a/tools/make/packaging.mk +++ b/tools/make/packaging.mk @@ -388,7 +388,7 @@ ifeq ($(USE_CONTAINER),1) else cp ./dist/grafana-agent-windows-amd64.exe ./packaging/grafana-agent/windows cp LICENSE ./packaging/grafana-agent/windows - # quotes around mkdir are manadory. ref: https://github.com/grafana/agent/pull/5664#discussion_r1378796371 + # quotes around mkdir are mandatory. ref: https://github.com/grafana/agent/pull/5664#discussion_r1378796371 "mkdir" -p dist makensis -V4 -DVERSION=$(VERSION) -DOUT="../../../dist/grafana-agent-installer.exe" ./packaging/grafana-agent/windows/install_script.nsis endif @@ -398,7 +398,7 @@ dist-agent-flow-installer: dist.temp/grafana-agent-flow-windows-amd64.exe dist.t ifeq ($(USE_CONTAINER),1) $(RERUN_IN_CONTAINER) else - # quotes around mkdir are manadory. ref: https://github.com/grafana/agent/pull/5664#discussion_r1378796371 + # quotes around mkdir are mandatory. ref: https://github.com/grafana/agent/pull/5664#discussion_r1378796371 "mkdir" -p dist makensis -V4 -DVERSION=$(VERSION) -DOUT="../../../dist/grafana-agent-flow-installer.exe" ./packaging/grafana-agent-flow/windows/install_script.nsis endif