Skip to content
Merged
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
35 changes: 35 additions & 0 deletions .github/workflows/nightly.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
name: "nightly"
on: # yamllint disable-line rule:truthy
push:
branches:
- "main"
permissions:
contents: "read"

jobs:
nightly:
runs-on: "ubuntu-latest"
permissions:
contents: "write"
packages: "write" # publish to GHCR
steps:
- uses: "actions/checkout@v6"
with:
fetch-depth: 0
- uses: "authzed/actions/setup-go@main"
- uses: "authzed/actions/docker-login@main"
with:
quayio_token: "${{ secrets.QUAYIO_PASSWORD }}"
github_token: "${{ secrets.GITHUB_TOKEN }}"
dockerhub_token: "${{ secrets.DOCKERHUB_ACCESS_TOKEN }}"
- uses: "goreleaser/goreleaser-action@v6"
with:
distribution: "goreleaser-pro"
# NOTE: keep in sync with goreleaser version in the release job.
# github actions don't allow yaml anchors.
version: "2.4.8"
args: "release -f .goreleaser.nightly.yaml --clean --nightly"
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
GORELEASER_KEY: "${{ secrets.GORELEASER_KEY }}"
52 changes: 52 additions & 0 deletions .goreleaser.nightly.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# This config builds nightly ("devel") images on every push to main. It mirrors
# .goreleaser.yaml but builds linux images only, omits the "latest" tag so that
# release images are never overwritten, and derives a commit-based version.
# yaml-language-server: $schema=https://goreleaser.com/static/schema-pro.json

---
version: 2

builds:
- id: "main-build"
env:
- "CGO_ENABLED=0"
goos:
- "linux"
goarch:
- "amd64"
- "arm64"
main: "./cmd/thumper/"
dir: "."
mod_timestamp: "{{ .CommitTimestamp }}"

kos:
- &ko-defaults
id: "dockerhub-ko"
build: "main-build"
platforms:
- "linux/amd64"
- "linux/arm64"
repository: "authzed"
tags:
- "v{{ .Version }}"
creation_time: "{{ .CommitTimestamp }}"
ko_data_creation_time: "{{ .CommitTimestamp }}"
sbom: "none"
# This prevents it from adding the md5sum after the name of
# the image
base_import_paths: true
- <<: *ko-defaults
id: "ghcr-build"
repository: "ghcr.io/authzed"
- <<: *ko-defaults
id: "quay-build"
repository: "quay.io/authzed"

checksum:
name_template: "checksums.txt"

# With --nightly, the version (and therefore the image tag) becomes
# "<next-patch>-<short-commit>", e.g. v0.1.1-1874ebe, which is unique per commit
# and never collides with a real release tag.
nightly:
version_template: "{{ incpatch .Version }}-{{ .ShortCommit }}"
6 changes: 6 additions & 0 deletions internal/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func RegisterRunFlags(cmd *cobra.Command) {
cmd.Flags().Int("qps", 1, "queries per second to generate")
cmd.Flags().Duration("step-timeout", 500*time.Millisecond, "maximum time a single step is allowed to run")
cmd.Flags().Bool("randomize-starting-step", false, "randomize the starting script step for each worker")
cmd.Flags().Bool("rerender", false, "re-render each script from its source file at the start of every cycle through its steps, regenerating template values such as randomObjectID for each cycle instead of once at startup")

// Register http flags
MetricsServerBuilder.RegisterFlags(cmd.Flags())
Expand Down Expand Up @@ -63,6 +64,7 @@ func runCmdFunc(cmd *cobra.Command, args []string) error {
qps := cobrautil.MustGetInt(cmd, "qps")
stepTimeout := cobrautil.MustGetDuration(cmd, "step-timeout")
stepRandomization := cobrautil.MustGetBool(cmd, "randomize-starting-step")
rerender := cobrautil.MustGetBool(cmd, "rerender")
psName := cobrautil.MustGetString(cmd, "permissions-system")
log.Info().Int("qps", qps).Str("permission-system", psName).Msg("starting run command")

Expand Down Expand Up @@ -102,6 +104,10 @@ func runCmdFunc(cmd *cobra.Command, args []string) error {
return fmt.Errorf("error preparing scripts for execution: %w", err)
}

if rerender {
thumperrunner.EnableRerender(preparedFileScripts, scriptFilename, scriptVars)
}

if !usedRandom {
scriptCache[scriptFilename] = preparedFileScripts
}
Expand Down
63 changes: 63 additions & 0 deletions internal/thumperrunner/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"fmt"
"time"

"github.com/authzed/internal/thumper/internal/config"

v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"
"github.com/authzed/authzed-go/v1"
"github.com/rs/zerolog/log"
Expand All @@ -22,6 +24,10 @@ type ExecutableScript struct {
name string
weight uint
steps []executableStep

// source, when set, allows the script to be re-rendered from its origin
// file at the start of each execution cycle (see EnableRerender).
source *scriptSource
}

type ExecutableContext struct {
Expand All @@ -30,10 +36,67 @@ type ExecutableContext struct {

numExecuted int
zedToken *v1.ZedToken

// source is copied from the script when the context is created; when
// non-nil the script is re-rendered at the start of each cycle.
source *scriptSource
}

// scriptSource records where a script was loaded from so it can be re-rendered
// (re-templated) on demand, regenerating template values such as randomObjectID.
type scriptSource struct {
filename string
vars config.ScriptVariables
docIndex int
}

// load re-renders the source file and returns a freshly prepared copy of the
// document at docIndex.
func (src *scriptSource) load() (*ExecutableScript, error) {
scripts, _, err := config.Load(src.filename, src.vars)
if err != nil {
return nil, fmt.Errorf("error re-loading script %s: %w", src.filename, err)
}

prepared, err := Prepare(scripts)
if err != nil {
return nil, fmt.Errorf("error re-preparing script %s: %w", src.filename, err)
}

if src.docIndex >= len(prepared) {
return nil, fmt.Errorf("script %s no longer contains document %d", src.filename, src.docIndex)
}

return prepared[src.docIndex], nil
}

// EnableRerender attaches a re-render source to each prepared script so that,
// at the start of every cycle through its steps, the script is re-rendered from
// filename. This regenerates template values (notably randomObjectID) for each
// cycle instead of baking them in once at load time.
func EnableRerender(scripts []*ExecutableScript, filename string, vars config.ScriptVariables) {
for idx, s := range scripts {
s.source = &scriptSource{filename: filename, vars: vars, docIndex: idx}
}
}

// StepForward advances the script one step and then stops.
func (s *ExecutableContext) StepForward(workerIndex int, stepTimeout time.Duration) {
// At the start of each cycle through the script's steps, re-render the
// script from its source (if enabled) so template values such as
// randomObjectID are regenerated for this cycle.
if s.source != nil && s.numExecuted%len(s.script.steps) == 0 {
if fresh, err := s.source.load(); err != nil {
log.Warn().
Err(err).
Str("script", s.script.name).
Int("worker", workerIndex).
Msg("failed to re-render script; reusing previous render")
} else {
s.script = fresh
}
}

ctx, cancel := context.WithTimeout(context.Background(), stepTimeout)
defer cancel()

Expand Down
1 change: 1 addition & 0 deletions internal/thumperrunner/thumper.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func RunWorker(options WorkerOptions) {
script: script,
client: options.Client,
numExecuted: numExecuted,
source: script.source,
}
choices = append(choices, weightedrand.NewChoice(executable, script.weight))
}
Expand Down
Loading