Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add flag to request and gather a flamegraph #299

Merged
merged 1 commit into from
Apr 3, 2024
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
1 change: 1 addition & 0 deletions cmd/dependabot/internal/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type SharedFlags struct {
file string
cache string
debugging bool
flamegraph bool
proxyCertPath string
collectorConfigPath string
extraHosts []string
Expand Down
2 changes: 2 additions & 0 deletions cmd/dependabot/internal/cmd/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ func NewUpdateCommand() *cobra.Command {
CollectorImage: collectorImage,
Creds: input.Credentials,
Debug: flags.debugging,
Flamegraph: flags.flamegraph,
Expected: nil, // update subcommand doesn't use expectations
ExtraHosts: flags.extraHosts,
InputName: flags.file,
Expand Down Expand Up @@ -126,6 +127,7 @@ func NewUpdateCommand() *cobra.Command {
cmd.Flags().StringVar(&flags.collectorConfigPath, "collector-config", "", "path to an OpenTelemetry collector config file")
cmd.Flags().BoolVar(&flags.pullImages, "pull", true, "pull the image if it isn't present")
cmd.Flags().BoolVar(&flags.debugging, "debug", false, "run an interactive shell inside the updater")
cmd.Flags().BoolVar(&flags.flamegraph, "flamegraph", false, "generate a flamegraph and other metrics")
cmd.Flags().StringArrayVarP(&flags.volumes, "volume", "v", nil, "mount volumes in Docker")
cmd.Flags().StringArrayVar(&flags.extraHosts, "extra-hosts", nil, "Docker extra hosts setting on the proxy")
cmd.Flags().DurationVarP(&flags.timeout, "timeout", "t", 0, "max time to run an update")
Expand Down
33 changes: 32 additions & 1 deletion internal/infra/run.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package infra

import (
"archive/tar"
"context"
"encoding/base64"
"encoding/json"
Expand Down Expand Up @@ -48,6 +49,8 @@ type RunParams struct {
PullImages bool
// run an interactive shell?
Debug bool
// generate performance metrics?
Flamegraph bool
// Volumes are used to mount directories in Docker
Volumes []string
// Timeout specifies an optional maximum duration the CLI will run an update.
Expand Down Expand Up @@ -408,10 +411,17 @@ func runContainers(ctx context.Context, params RunParams) (err error) {
return err
}
} else {
env := userEnv(prox.url, params.ApiUrl)
if params.Flamegraph {
env = append(env, "FLAMEGRAPH=1")
}
const cmd = "update-ca-certificates && bin/run fetch_files && bin/run update_files"
if err := updater.RunCmd(ctx, cmd, dependabot, userEnv(prox.url, params.ApiUrl)...); err != nil {
if err := updater.RunCmd(ctx, cmd, dependabot, env...); err != nil {
return err
}
if params.Flamegraph {
getFromContainer(ctx, cli, updater.containerID, "/tmp/dependabot-flamegraph.html")
}
// If the exit code is non-zero, error when using the `update` subcommand, but not the `test` subcommand.
if params.Expected == nil && *updater.ExitCode != 0 {
return fmt.Errorf("updater exited with code %d", *updater.ExitCode)
Expand All @@ -421,6 +431,27 @@ func runContainers(ctx context.Context, params RunParams) (err error) {
return nil
}

func getFromContainer(ctx context.Context, cli *client.Client, containerID, srcPath string) {
reader, _, err := cli.CopyFromContainer(ctx, containerID, srcPath)
if err != nil {
log.Println("Failed to get from container:", err)
return
}
defer reader.Close()
outFile, err := os.Create("flamegraph.html")
if err != nil {
log.Println("Failed to create file while getting from container:", err)
return
}
defer outFile.Close()
tarReader := tar.NewReader(reader)
tarReader.Next()
_, err = io.Copy(outFile, tarReader)
if err != nil {
log.Printf("Failed copy while getting from container %v: %v\n", srcPath, err)
}
}

func putCloneDir(ctx context.Context, cli *client.Client, updater *Updater, dir string) error {
// Docker won't create the directory, so we have to do it first.
const cmd = "mkdir -p " + guestRepoDir
Expand Down
32 changes: 32 additions & 0 deletions testdata/scripts/flamegraph.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Build the dummy Dockerfile
exec docker build -qt flamegraph-updater .

# Run the dependabot command
dependabot update go_modules dependabot/cli --updater-image flamegraph-updater --flamegraph

# There should be a flamegraph file in the current directory
exists flamegraph.html

exec docker rmi -f flamegraph-updater

-- Dockerfile --
FROM ubuntu:22.04

RUN useradd dependabot

COPY --chown=dependabot --chmod=755 update-ca-certificates /usr/bin/update-ca-certificates
COPY --chown=dependabot --chmod=755 run bin/run

-- update-ca-certificates --
#!/usr/bin/env bash

echo "Updated those certificates for ya"

-- run --
#!/usr/bin/env bash

# if there's an environment variable "FLAMEGRAPH" set, create a fake flamegraph in tmp
if [ -n "$FLAMEGRAPH" ]; then
echo "Creating flamegraph"
echo "fake flamegraph" > /tmp/dependabot-flamegraph.html
fi