Skip to content

Commit

Permalink
feat: adds support bundles
Browse files Browse the repository at this point in the history
Signed-off-by: Sam Berning <[email protected]>
  • Loading branch information
sam-berning committed Feb 15, 2023
1 parent 7aaa381 commit d2fb229
Show file tree
Hide file tree
Showing 7 changed files with 474 additions and 2 deletions.
8 changes: 8 additions & 0 deletions cmd/finch/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"io"
"os"

"github.com/runfinch/finch/pkg/support"

"github.com/runfinch/finch/pkg/disk"

"github.com/runfinch/finch/pkg/command"
Expand Down Expand Up @@ -88,13 +90,19 @@ var newApp = func(logger flog.Logger, fp path.Finch, fs afero.Fs, fc *config.Fin
fp.QEMUBinDir(),
system.NewStdLib(),
)
supportBundleBuilder := support.NewBundleBuilder(
logger,
fs,
support.NewBundleConfig(fp, system.NewStdLib().Env("HOME"), ecc),
)

// append nerdctl commands
allCommands := initializeNerdctlCommands(lcc, logger)
// append finch specific commands
allCommands = append(allCommands,
newVersionCommand(lcc, logger, stdOut),
virtualMachineCommands(logger, fp, lcc, ecc, fs, fc),
newSupportBundleCommand(logger, supportBundleBuilder, lcc),
)

rootCmd.AddCommand(allCommands...)
Expand Down
4 changes: 2 additions & 2 deletions cmd/finch/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,8 @@ func TestNewApp(t *testing.T) {
assert.Equal(t, cmd.Version, version.Version)
assert.Equal(t, cmd.SilenceUsage, true)
assert.Equal(t, cmd.SilenceErrors, true)
// confirm the number of command, comprised of nerdctl commands + finch commands (version, vm)
assert.Equal(t, len(cmd.Commands()), len(nerdctlCmds)+2)
// confirm the number of command, comprised of nerdctl commands + finch commands (version, vm, support-bundle)
assert.Equal(t, len(cmd.Commands()), len(nerdctlCmds)+3)

// PersistentPreRunE should set logger level to debug if the debug flag exists.
mockCmd := &cobra.Command{}
Expand Down
87 changes: 87 additions & 0 deletions cmd/finch/support_bundle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package main

import (
"fmt"

"github.com/spf13/cobra"

"github.com/runfinch/finch/pkg/command"
"github.com/runfinch/finch/pkg/flog"
"github.com/runfinch/finch/pkg/lima"
"github.com/runfinch/finch/pkg/support"
)

func newSupportBundleCommand(logger flog.Logger, builder *support.BundleBuilder, lcc command.LimaCmdCreator) *cobra.Command {
supportBundleCommand := &cobra.Command{
Use: "support-bundle",
Short: "Gathers logs and info into a bundle to help debug issues",
}
supportBundleCommand.AddCommand(
newSupportBundleGenerateCommand(logger, builder, lcc),
)
return supportBundleCommand
}

func newSupportBundleGenerateCommand(logger flog.Logger, builder *support.BundleBuilder, lcc command.LimaCmdCreator) *cobra.Command {
supportBundleGenerateCommand := &cobra.Command{
Use: "generate",
Args: cobra.NoArgs,
Short: "Generate support bundle",
RunE: newGenerateSupportBundleAction(logger, builder, lcc).runAdapter,
}

return supportBundleGenerateCommand
}

type generateSupportBundleAction struct {
logger flog.Logger
builder *support.BundleBuilder
lcc command.LimaCmdCreator
}

func newGenerateSupportBundleAction(
logger flog.Logger,
builder *support.BundleBuilder,
lcc command.LimaCmdCreator,
) *generateSupportBundleAction {
return &generateSupportBundleAction{
logger: logger,
builder: builder,
lcc: lcc,
}
}

func (a *generateSupportBundleAction) runAdapter(cmd *cobra.Command, args []string) error {
return a.run()
}

func (a *generateSupportBundleAction) run() error {
err := a.assertVMExists()
if err != nil {
return err
}
a.logger.Info("Generating support bundle...")
bundleFile, err := a.builder.GenerateSupportBundle()
if err != nil {
return err
}
a.logger.Infof("Bundle created: %s", bundleFile)
return nil
}

func (a *generateSupportBundleAction) assertVMExists() error {
status, err := lima.GetVMStatus(a.lcc, a.logger, limaInstanceName)
if err != nil {
return err
}
switch status {
case lima.Nonexistent:
return fmt.Errorf("cannot create support bundle for nonexistent VM, run `finch %s init` to create a new instance",
virtualMachineRootCmd)
default:
return nil
}
}
24 changes: 24 additions & 0 deletions cmd/finch/support_bundle_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package main

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestNewSupportBundleCommand(t *testing.T) {
t.Parallel()

cmd := newSupportBundleCommand(nil, nil, nil)
assert.Equal(t, cmd.Name(), "support-bundle")
}

func TestNewSupportBundleGenerateCommand(t *testing.T) {
t.Parallel()

cmd := newSupportBundleGenerateCommand(nil, nil, nil)
assert.Equal(t, cmd.Name(), "generate")
}
100 changes: 100 additions & 0 deletions pkg/support/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package support

import (
"github.com/runfinch/finch/pkg/command"
fpath "github.com/runfinch/finch/pkg/path"
"github.com/runfinch/finch/pkg/version"
"path"
"strings"
)

type platformData struct {
Os string `yaml:"os"`
Arch string `yaml:"arch"`
Finch string `yaml:"finch"`
}

// BundleConfig provides methods that configure what is included in a support bundle
type BundleConfig struct {
finch fpath.Finch
homeDir string
ecc command.Creator
}

// NewBundleConfig creates a new BundleConfig
func NewBundleConfig(finch fpath.Finch, homeDir string, ecc command.Creator) BundleConfig {
return BundleConfig{
finch: finch,
homeDir: homeDir,
ecc: ecc,
}
}

func (c *BundleConfig) logFiles() []string {
return []string{
path.Join(c.finch.LimaInstancePath(), "ha.stderr.log"),
path.Join(c.finch.LimaInstancePath(), "ha.stdout.log"),
path.Join(c.finch.LimaInstancePath(), "serial.log"),
}
}

func (c *BundleConfig) configFiles() []string {
return []string{
path.Join(c.finch.LimaInstancePath(), "lima.yaml"),
c.finch.ConfigFilePath(c.homeDir),
}
}

func (c *BundleConfig) platformData() (*platformData, error) {
platform := &platformData{}

// populate OS version
os, err := c.getOSVersion()
if err != nil {
return nil, err
}
platform.Os = os

// populate arch
arch, err := c.getArch()
if err != nil {
return nil, err
}
platform.Arch = arch

// populate Finch version
platform.Finch = getFinchVersion()

return platform, nil
}

func (c *BundleConfig) getOSVersion() (string, error) {
cmd := c.ecc.Create("sw_vers", "-productVersion")
out, err := cmd.Output()
if err != nil {
return "", err
}

os := strings.TrimSuffix(string(out), "\n")

return os, nil
}

func (c *BundleConfig) getArch() (string, error) {
cmd := c.ecc.Create("uname", "-m")
out, err := cmd.Output()
if err != nil {
return "", err
}

arch := strings.TrimSuffix(string(out), "\n")

return arch, nil
}

func getFinchVersion() string {
return version.Version
}
135 changes: 135 additions & 0 deletions pkg/support/support.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

// Package support provides functions and methods to produce Finch support bundles
package support

import (
"archive/zip"
"fmt"
"io"
"path"
"time"

"github.com/runfinch/finch/pkg/flog"
"github.com/spf13/afero"
"gopkg.in/yaml.v3"
)

const (
bundlePrefix = "finch-support"
platformFileName = "platform.yaml"
logPrefix = "logs"
configPrefix = "configs"
)

// BundleBuilder provides methods to generate support bundles.
type BundleBuilder struct {
logger flog.Logger
fs afero.Fs
config BundleConfig
}

// NewBundleBuilder produces a new BundleBuilder.
func NewBundleBuilder(logger flog.Logger, fs afero.Fs, config BundleConfig) *BundleBuilder {
return &BundleBuilder{
logger: logger,
fs: fs,
config: config,
}
}

// GenerateSupportBundle generates a new support bundle.
func (b *BundleBuilder) GenerateSupportBundle() (string, error) {
zipFileName := bundleFileName()
b.logger.Debugf("Creating %s...", zipFileName)
zipFile, err := b.fs.Create(zipFileName)
if err != nil {
return "", err
}

writer := zip.NewWriter(zipFile)

_, err = writer.Create(fmt.Sprintf("%s/", bundlePrefix))
if err != nil {
return "", err
}

platform, err := b.config.platformData()
if err != nil {
return "", err
}

b.logger.Debugln("Gathering platform data...")
err = b.writePlatformData(writer, platform)
if err != nil {
return "", err
}

b.logger.Debugln("Copying in log files...")
for _, file := range b.config.logFiles() {
err := b.copyInFile(writer, file, logPrefix)
if err != nil {
return "", err
}
}

b.logger.Debugln("Copying in config files...")
for _, file := range b.config.configFiles() {
err := b.copyInFile(writer, file, configPrefix)
if err != nil {
return "", err
}
}

err = writer.Close()
if err != nil {
return "", err
}

return zipFileName, nil
}

func (b *BundleBuilder) writePlatformData(writer *zip.Writer, platform *platformData) error {
platformFile, err := writer.Create(path.Join(bundlePrefix, platformFileName))
if err != nil {
return err
}

toWrite, err := yaml.Marshal(&platform)
if err != nil {
return err
}

_, err = platformFile.Write(toWrite)
if err != nil {
return err
}

return nil
}

func (b *BundleBuilder) copyInFile(writer *zip.Writer, fileName string, prefix string) error {
f, err := b.fs.Open(fileName)
if err != nil {
return err
}

baseName := path.Base(fileName)
zipCopy, err := writer.Create(path.Join(bundlePrefix, prefix, baseName))
if err != nil {
return err
}

_, err = io.Copy(zipCopy, f)
if err != nil {
return err
}

return nil
}

func bundleFileName() string {
timestamp := time.Now().Format("20060102150405")
return fmt.Sprintf("%s-%s.zip", bundlePrefix, timestamp)
}
Loading

0 comments on commit d2fb229

Please sign in to comment.