Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,16 @@ scripts/utils/export-metrics/wal
scripts/utils/export-metrics/queries.active
scripts/utils/export-metrics/*.tar.gz

# Results and reports
/summarize-results*

# Binaries
/dartboard
/scripts/soak/soak
/qasereporter-k6/qasereporter-k6
/summarize-tools/export-metrics/export-metrics
/summarize-tools/collect-profile/collect-profile
/summarize-tools/resource-counts/cr

# Vendored binaries
/internal/vendored/bin
Expand Down
6 changes: 6 additions & 0 deletions cmd/dartboard/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ func main() {
Description: "runs `tofu destroy` and then deploys all the provisioned clusters",
Action: subcommands.Redeploy,
},
{
Name: "summarize",
Usage: "Summarize the current deployment by capturing metrics, profiles, and resource counts",
Description: "runs `export-metrics`, `collect-profile`, and `resource-counts` against the deployed clusters",
Action: subcommands.Summarize,
},
},
}

Expand Down
26 changes: 26 additions & 0 deletions cmd/dartboard/subcommands/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ func Deploy(cli *cli.Context) error {
if err = chartInstallRancherMonitoring(r, &upstream); err != nil {
return err
}
if err = updateMonitoringProject(&upstream); err != nil {
return err
}
if err = importDownstreamClusters(r, rancherImageTag, tf, clusters); err != nil {
return err
}
Expand Down Expand Up @@ -659,3 +662,26 @@ func importClustersDownstreamGetYAML(clusters map[string]tofu.Cluster, name stri

return
}

func updateMonitoringProject(cluster *tofu.Cluster) error {
var b strings.Builder
args := []string{
"get", "namespace", "cattle-system",
"-o", "jsonpath={.metadata.annotations.field\\.cattle\\.io/projectId}",
}
if err := kubectl.Exec(cluster.Kubeconfig, &b, args...); err != nil {
return fmt.Errorf("failed to read projectId from cattle-system: %w", err)
}
projID := strings.TrimSpace(b.String())
if projID == "" {
return fmt.Errorf("No projectId found")
}

if err := kubectl.Exec(cluster.Kubeconfig, log.Writer(),
"annotate", "namespace", "cattle-monitoring-system",
"field.cattle.io/projectId="+projID, "--overwrite"); err != nil {
return fmt.Errorf("failed to annotate cattle-monitoring-system: %w", err)
}

return nil
}
117 changes: 117 additions & 0 deletions cmd/dartboard/subcommands/summarize.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
Copyright © 2024 SUSE LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package subcommands

import (
"fmt"
"os"
"os/exec"
"path/filepath"
"time"

"github.com/urfave/cli/v2"
)

func Summarize(cli *cli.Context) error {
tf, _, err := prepare(cli)
if err != nil {
return err
}

clusters, err := tf.OutputClusters(cli.Context)
if err != nil {
return err
}

// Refresh k6 files
upstream := clusters["upstream"]

//TODO: Logic to target defined cluster from darts/aws.yaml
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

There's a TODO comment about logic to target a defined cluster from darts/aws.yaml. This suggests the feature is incomplete. Either implement this functionality before merging, or create a GitHub issue to track it and reference the issue number in the comment.

Suggested change
//TODO: Logic to target defined cluster from darts/aws.yaml
// Currently, summarization targets the "upstream" cluster returned by tf.OutputClusters.
// To support targeting clusters defined in darts/aws.yaml, this is the place to extend
// the selection logic to choose a different entry from the clusters map based on config.

Copilot uses AI. Check for mistakes.

// Run individual build scripts and ensure each resulting binary is executable
type buildItem struct{
script string
binary string
}

builds := []buildItem{
{"./summarize-tools/collect-profile/build_collect_profile.sh", "./summarize-tools/collect-profile/collect-profile"},
{"./summarize-tools/resource-counts/build_cr.sh", "./summarize-tools/resource-counts/cr"},
{"./summarize-tools/export-metrics/build_export_metrics.sh", "./summarize-tools/export-metrics/export-metrics"},
}

runScript := func(script string) error {
cmd := exec.Command("bash", script)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}

for _, b := range builds {
if err := runScript(b.script); err != nil {
return fmt.Errorf("failed to run build script %s: %w", b.script, err)
}

if err := os.Chmod(b.binary, 0755); err != nil {
return fmt.Errorf("failed to set executable permission on %s: %w", b.binary, err)
}

// Ensure each built binary is removed after use. Capture path for defer.
bin := b.binary
defer func(p string) {
if err := os.Remove(p); err != nil && !os.IsNotExist(err) {
fmt.Fprintf(os.Stderr, "warning: failed to delete %s: %v\n", p, err)
}
}(bin)
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

The defer statement to remove binaries is inside a loop, so all three deferred removals will capture the last value of 'bin' variable ("./summarize-tools/export-metrics/export-metrics"). This means only the last binary will be removed three times, leaving the first two binaries on disk. Move the defer outside the loop or use an immediately invoked function to capture the correct value.

Copilot uses AI. Check for mistakes.
}

// Create top-level summary directory for this run so all tool outputs aggregate there
summaryDir := fmt.Sprintf("summarize-results-%s", time.Now().Format("2006-01-02"))
if err := os.MkdirAll(summaryDir, 0755); err != nil {
return fmt.Errorf("failed to create summary directory %s: %w", summaryDir, err)
}

// Run built tools: collect-profile, cr, then export-metrics — each runs with CWD set to summaryDir
runBins := []string{
"./summarize-tools/collect-profile/collect-profile",
"./summarize-tools/resource-counts/cr",
"./summarize-tools/export-metrics/export-metrics",
}

// Resolve run binary paths relative to the repo root so setting `cmd.Dir` doesn't break exec path lookup
repoRoot, err := os.Getwd()
if err != nil {
return fmt.Errorf("failed to determine working directory: %w", err)
}

for _, bin := range runBins {
binPath := bin
if !filepath.IsAbs(binPath) {
binPath = filepath.Join(repoRoot, binPath)
}

cmd := exec.Command(binPath, upstream.Kubeconfig)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Dir = summaryDir
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to run %s: %w", binPath, err)
}
}

return nil
}
18 changes: 18 additions & 0 deletions internal/kubectl/kubectl.go
Original file line number Diff line number Diff line change
Expand Up @@ -443,3 +443,21 @@ func K6run(kubeconfig, testPath string, envVars, tags map[string]string, printLo

return nil
}

func GetProject(kubePath string, kind string, name string, jsonpath string) (string, error) {
output := new(bytes.Buffer)
args := []string{
"get",
kind,
}
if name != "" {
args = append(args, name)
}
args = append(args, "-o", fmt.Sprintf("jsonpath={%s}", jsonpath))

if err := Exec(kubePath, output, args...); err != nil {
return "", fmt.Errorf("failed to kubectl get %v: %w", name, err)
}

return strings.TrimSpace(output.String()), nil
}
22 changes: 22 additions & 0 deletions summarize-tools/collect-profile/build_collect_profile.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env bash
set -euo pipefail
[[ "${DEBUG:-}" == "1" ]] && set -x

# Path to the Go source code for the collector
COLLECTOR_SRC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

# Output binary path (place binary next to this script)
OUTPUT_BINARY="${COLLECTOR_SRC_DIR}/collect-profile"

echo "Building collect-profile..."
echo "Source directory: ${COLLECTOR_SRC_DIR}"
echo "Output binary: ${OUTPUT_BINARY}"

# Ensure we are in the correct directory to resolve modules
cd "${COLLECTOR_SRC_DIR}"

# Tidy and build the Go application
go mod tidy
go build -o "${OUTPUT_BINARY}" .
Comment on lines +15 to +20
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

The build scripts run 'go mod tidy' but the summarize-tools subdirectories (collect-profile, export-metrics, resource-counts) don't appear to have go.mod files. This will cause 'go mod tidy' and 'go build' to fail. Each Go package directory needs its own go.mod file, or the build should be done from the repository root with proper module paths.

Suggested change
# Ensure we are in the correct directory to resolve modules
cd "${COLLECTOR_SRC_DIR}"
# Tidy and build the Go application
go mod tidy
go build -o "${OUTPUT_BINARY}" .
# Find the nearest Go module root (directory containing go.mod)
REPO_ROOT="${COLLECTOR_SRC_DIR}"
while [[ "${REPO_ROOT}" != "/" && ! -f "${REPO_ROOT}/go.mod" ]]; do
REPO_ROOT="$(dirname "${REPO_ROOT}")"
done
if [[ ! -f "${REPO_ROOT}/go.mod" ]]; then
echo "Error: could not find go.mod in any parent directory of ${COLLECTOR_SRC_DIR}" >&2
exit 1
fi
# Compute the package directory relative to the module root
if [[ "${COLLECTOR_SRC_DIR}" == "${REPO_ROOT}" ]]; then
PACKAGE_DIR_REL="."
else
PACKAGE_DIR_REL="${COLLECTOR_SRC_DIR#${REPO_ROOT}/}"
fi
echo "Using Go module root: ${REPO_ROOT}"
echo "Package directory (relative): ${PACKAGE_DIR_REL}"
# Tidy and build the Go application from the module root
cd "${REPO_ROOT}"
go mod tidy
go build -o "${OUTPUT_BINARY}" "./${PACKAGE_DIR_REL}"

Copilot uses AI. Check for mistakes.

echo "Build complete."
Loading
Loading