Files
fleet/tools/dibble/pkg/seed/scripts.go
T
George Karr a25ae3ddfc Adding initial tool - dibble the tapir for seeding data (#46122)
## Overview

`dibble` is a one-stop CLI for seeding a Fleet server with test data —
users, teams, policies, reports, labels, scripts, MDM profiles,
software, secrets, CAs, and vulns — replacing ~8 ad-hoc seeding tools
with a single binary.

It makes it easy to:
- **Spin up a populated dev/test server in one command** — `dibble all`
plants everything with sensible, idempotent defaults.
- **Skip the flag-memorization** — running `dibble` with no args
launches an interactive wizard that prompts for Fleet URL, API token,
theme, and which entities to seed, and offers to save the config to
`~/.dibble.yaml`.
- **Seed individual entity types** — `dibble users`, `dibble teams`,
`dibble policies`, etc., when you only need one slice.
- **Get themed, recognizable test data** — pick a theme (hitchhikers,
tng, lotr, ghibli, parksrec, …) so seeded names are easy to eyeball in
the UI.

Hosts are intentionally out of scope — `cmd/osquery-perf` still owns
that. `dibble hosts` is a thin convenience wrapper that picks a fleet,
fetches its enroll secret, and prints/runs the osquery-perf invocation
for you.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Dibble: a CLI tool to seed realistic test data, including an
interactive wizard and subcommands for
teams/users/software/policies/scripts/reports/profiles/labels/activities/enroll-secrets/hosts/vulns,
plus theme-driven “cas” and “ping”.
* Theme system: multiple curated themes to generate consistent seeded
identities, policies, software, labels, and scripts.
* **Chores**
* Ignored the built dibble binary and added a Makefile build target to
compile the dibble tool.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-17 11:07:05 -05:00

62 lines
1.8 KiB
Go

package seed
import (
"fmt"
"strings"
"github.com/fleetdm/fleet/v4/tools/dibble/pkg/themes"
)
// Scripts seeds saved scripts (global + per-team). The endpoint accepts a
// multipart upload with the script content as a file plus `team_id` (or
// none for global) as a form field.
//
// Script content varies by extension: .sh for macOS/Linux targets and .ps1
// for Windows — the two extensions Fleet's scripts endpoint accepts (along
// with .py, which we don't seed).
func Scripts(c Client, log Logger, theme themes.Theme, teams []Team, count int) Result {
res := Result{Entity: "scripts"}
exts := []string{".sh", ".ps1"}
body := func(ext, name string) string {
switch ext {
case ".ps1":
return fmt.Sprintf("# %s\nWrite-Host 'dibble was here'\n", name)
default:
return fmt.Sprintf("#!/usr/bin/env bash\n# %s\necho 'dibble was here'\n", name)
}
}
postOne := func(scope string, teamID uint, i int) {
n := themes.Pick(theme, "script", i)
ext := exts[i%len(exts)]
name := strings.TrimSuffix(n.Name, ".sh")
name = strings.TrimSuffix(name, ".ps1") + ext
fields := map[string]string{}
if teamID > 0 {
// Fleet's renamed "team" → "fleet" — the multipart key is fleet_id.
fields["fleet_id"] = fmt.Sprintf("%d", teamID)
}
// Form field must be exactly "script"; filename carries the extension.
files := []MultipartFile{{FieldName: "script", Filename: name, Content: []byte(body(ext, n.Name))}}
err := c.PostMultipart("/api/latest/fleet/scripts", fields, files, nil)
switch {
case err == nil:
res.Created++
log.Printf("script (%s) %s", scope, name)
case IsAlreadyExists(err):
res.Skipped++
default:
res.Errors = append(res.Errors, err)
}
}
for i := 0; i < count; i++ {
postOne("global", 0, i)
}
for _, t := range teams {
postOne("team="+t.Name, t.ID, 0)
}
return res
}