Skip to content

Commit

Permalink
feat: add support-bundle command (#1351)
Browse files Browse the repository at this point in the history
* feat: add support-bundle command

add support-bundle command. this command generates a support bundle
using the in-cluster support bundle definitions and the local host
support bundle definitions.

* feat: disable interactive support-bundle

we just want to generate a support bundle and save it to disk so we
don't need to have the interactive mode enabled.

* chore: support-bundle isn't always a json

we can't rely on parsing the output as json, it might contain error
messages mixed up.
  • Loading branch information
ricardomaraschini authored Oct 22, 2024
1 parent cfd6d15 commit 2f44e63
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 13 deletions.
1 change: 1 addition & 0 deletions cmd/embedded-cluster/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func main() {
updateCommand(),
restoreCommand(),
adminConsoleCommand(),
supportBundleCommand(),
},
}
if err := app.RunContext(ctx, os.Args); err != nil {
Expand Down
67 changes: 67 additions & 0 deletions cmd/embedded-cluster/support_bundle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package main

import (
"bytes"
"fmt"
"io"
"os"

"github.com/urfave/cli/v2"

"github.com/replicatedhq/embedded-cluster/pkg/defaults"
"github.com/replicatedhq/embedded-cluster/pkg/helpers"
"github.com/replicatedhq/embedded-cluster/pkg/spinner"
)

func supportBundleCommand() *cli.Command {
return &cli.Command{
Name: "support-bundle",
Usage: fmt.Sprintf("Generate a %s support bundle", defaults.BinaryName()),
Before: func(c *cli.Context) error {
if os.Getuid() != 0 {
return fmt.Errorf("support-bundle command must be run as root")
}
return nil
},
Action: func(c *cli.Context) error {
provider := discoverBestProvider(c.Context)
os.Setenv("TMPDIR", provider.EmbeddedClusterTmpSubDir())

supportBundle := provider.PathToEmbeddedClusterBinary("kubectl-support_bundle")
if _, err := os.Stat(supportBundle); err != nil {
return fmt.Errorf("unable to find support bundle binary")
}

kubeConfig := provider.PathToKubeConfig()
hostSupportBundle := provider.PathToEmbeddedClusterSupportFile("host-support-bundle.yaml")

spin := spinner.Start()
spin.Infof("Collecting support bundle (this may take a while)")

stdout := bytes.NewBuffer(nil)
stderr := bytes.NewBuffer(nil)
if err := helpers.RunCommandWithOptions(
helpers.RunCommandOptions{
Writer: stdout,
ErrWriter: stderr,
LogOnSuccess: true,
},
supportBundle,
"--interactive=false",
fmt.Sprintf("--kubeconfig=%s", kubeConfig),
"--load-cluster-specs",
hostSupportBundle,
); err != nil {
spin.Infof("Failed to collect support bundle")
spin.CloseWithError()
io.Copy(os.Stdout, stdout)
io.Copy(os.Stderr, stderr)
return ErrNothingElseToAdd
}

spin.Infof("Support bundle collected!")
spin.Close()
return nil
},
}
}
16 changes: 16 additions & 0 deletions e2e/scripts/validate-support-bundle.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,28 @@ main() {
echo "Failed to find 'k0s sysinfo' inside the host support bundle"
return 1
fi
rm -rf host.tar.gz

tar -zxvf cluster.tar.gz
if ! ls cluster/podlogs/embedded-cluster-operator; then
echo "Failed to find operator logs inside the cluster support bundle"
return 1
fi
rm -rf cluster.tar.gz

tar -zxvf support-bundle-*.tar.gz
rm -rf support-bundle-*.tar.gz

if ! ls support-bundle-*/host-collectors/run-host/k0s-sysinfo.txt; then
echo "Failed to find 'k0s sysinfo' inside the support bundle generated with the embedded cluster binary"
return 1
fi

if ! ls support-bundle-*/podlogs/embedded-cluster-operator; then
echo "Failed to find operator logs inside the support bundle generated with the embedded cluster binary"
return 1
fi

}

main "$@"
25 changes: 12 additions & 13 deletions e2e/support-bundle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"time"

"github.com/replicatedhq/embedded-cluster/e2e/cluster/docker"
"github.com/stretchr/testify/assert"
)

func TestCollectSupportBundle(t *testing.T) {
Expand All @@ -23,27 +24,25 @@ func TestCollectSupportBundle(t *testing.T) {

t.Logf("%s: installing embedded-cluster on node 0", time.Now().Format(time.RFC3339))
line := []string{"single-node-install.sh", "cli"}
if stdout, stderr, err := tc.RunCommandOnNode(0, line); err != nil {
t.Fatalf("fail to install embedded-cluster: %v: %s: %s", err, stdout, stderr)
}
stdout, stderr, err := tc.RunCommandOnNode(0, line)
assert.NoErrorf(t, err, "fail to install embedded-cluster: %v: %s: %s", err, stdout, stderr)

line = []string{"collect-support-bundle-host.sh"}
stdout, stderr, err := tc.RunCommandOnNode(0, line)
if err != nil {
t.Fatalf("fail to collect host support bundle: %v: %s: %s", err, stdout, stderr)
}
stdout, stderr, err = tc.RunCommandOnNode(0, line)
assert.NoErrorf(t, err, "fail to collect host support bundle: %v: %s: %s", err, stdout, stderr)

line = []string{"collect-support-bundle-cluster.sh"}
stdout, stderr, err = tc.RunCommandOnNode(0, line)
if err != nil {
t.Fatalf("fail to collect cluster support bundle: %v: %s: %s", err, stdout, stderr)
}
assert.NoErrorf(t, err, "fail to collect cluster support bundle: %v: %s: %s", err, stdout, stderr)

t.Logf("%s: collecting support bundle with the embedded-cluster binary", time.Now().Format(time.RFC3339))
line = []string{"embedded-cluster", "support-bundle"}
stdout, stderr, err = tc.RunCommandOnNode(0, line)
assert.NoErrorf(t, err, "fail to collect support bundle using embedded-cluster binary: %v: %s: %s", err, stdout, stderr)

line = []string{"validate-support-bundle.sh"}
stdout, stderr, err = tc.RunCommandOnNode(0, line)
if err != nil {
t.Fatalf("fail to validate support bundle: %v: %s: %s", err, stdout, stderr)
}
assert.NoErrorf(t, err, "fail to validate support bundle: %v: %s: %s", err, stdout, stderr)

t.Logf("%s: test complete", time.Now().Format(time.RFC3339))
}
15 changes: 15 additions & 0 deletions pkg/helpers/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ import (
type RunCommandOptions struct {
// Writer is an additional io.Writer to write the stdout of the command to.
Writer io.Writer
// ErrWriter is an additional io.Writer to write the stderr of the command to.
ErrWriter io.Writer
// Env is a map of additional environment variables to set for the command.
Env map[string]string
// Stdin is the standard input to be used when running the command.
Stdin io.Reader
// LogOnSuccess makes the command output to be logged even when it succeeds.
LogOnSuccess bool
}

// RunCommandWithOptions runs a the provided command with the options specified.
Expand All @@ -34,6 +38,9 @@ func RunCommandWithOptions(opts RunCommandOptions, bin string, args ...string) e
cmd.Stdin = opts.Stdin
}
cmd.Stderr = stderr
if opts.ErrWriter != nil {
cmd.Stderr = io.MultiWriter(opts.ErrWriter, stderr)
}
cmdEnv := cmd.Environ()
for k, v := range opts.Env {
cmdEnv = append(cmdEnv, fmt.Sprintf("%s=%s", k, v))
Expand All @@ -48,6 +55,14 @@ func RunCommandWithOptions(opts RunCommandOptions, bin string, args ...string) e
}
return err
}

if !opts.LogOnSuccess {
return nil
}

logrus.Debugf("command succeeded:")
logrus.Debugf("stdout: %s", stdout.String())
logrus.Debugf("stderr: %s", stderr.String())
return nil
}

Expand Down

0 comments on commit 2f44e63

Please sign in to comment.