Skip to content

Commit

Permalink
Move glooctl debug unit tests to e2e tests (#10511)
Browse files Browse the repository at this point in the history
  • Loading branch information
arianaw66 authored Dec 20, 2024
1 parent 0cdebe4 commit a8f776e
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 99 deletions.
13 changes: 13 additions & 0 deletions changelog/v1.19.0-beta3/fix-glooctl-debug-tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
changelog:
- type: NON_USER_FACING
issueLink: https://github.com/k8sgateway/k8sgateway/issues/10400
description: >-
fixes flaky glooctl debug tests by moving them from unit to e2e tests
skipCI-docs-build:true
- type: NON_USER_FACING
issueLink: https://github.com/solo-io/gloo/issues/10482
description: >-
fixes flaky glooctl debug tests by moving them from unit to e2e tests
skipCI-docs-build:true
99 changes: 0 additions & 99 deletions projects/gloo/cli/pkg/cmd/debug/root_test.go

This file was deleted.

24 changes: 24 additions & 0 deletions projects/gloo/cli/pkg/testutils/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/spf13/cobra"

"github.com/solo-io/gloo/pkg/cliutil/testutil"
cli "github.com/solo-io/gloo/projects/gloo/cli/pkg/cmd"
)

Expand Down Expand Up @@ -65,6 +66,29 @@ func (c *GlooCli) DebugLogs(ctx context.Context, extraArgs ...string) error {
return ExecuteCommandWithArgs(c.NewCommand(ctx), debugLogsArgs...)
}

// Debug attempts to write debug information to a specified directory, depending on the consent of the user to do so.
// It runs a function passed by the caller on the returned error (even if nil),
// the intention of which is to allow the caller to make assertions about the error, e.g. by passing s.NoError as the errAssertions func
func (c *GlooCli) Debug(ctx context.Context, userConsent bool, errAssertions func(error, ...interface{}) bool, extraArgs ...string) {
debugLogsArgs := append([]string{
"debug",
}, extraArgs...)

testutil.ExpectInteractive(func(c *testutil.Console) {
c.ExpectString("This command will overwrite")
c.SendLine(func() string {
if userConsent {
return "y"
}
return "N"
}())
c.ExpectEOF()
}, func() {
err := ExecuteCommandWithArgs(c.NewCommand(ctx), debugLogsArgs...)
errAssertions(err)
}, nil)
}

// IstioInject inject istio-proxy and sds
func (c *GlooCli) IstioInject(ctx context.Context, installNamespace, kubeContext string, extraArgs ...string) (string, error) {
injectArgs := append([]string{
Expand Down
142 changes: 142 additions & 0 deletions test/kubernetes/e2e/features/glooctl/debug_suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,18 @@ import (
"context"
"os"
"path/filepath"
"strings"

"github.com/solo-io/gloo/test/kubernetes/e2e"
"github.com/stretchr/testify/suite"
)

var _ e2e.NewSuiteFunc = NewDebugSuite

var kubeStateFile = func(outDir string) string {
return filepath.Join(outDir, "kube-state.log")
}

// debugSuite contains the set of tests to validate the behavior of `glooctl debug`
// These tests attempt to mirror: https://github.com/solo-io/gloo/blob/v1.16.x/test/kube2e/glooctl/debug_test.go
type debugSuite struct {
Expand Down Expand Up @@ -68,3 +73,140 @@ func (s *debugSuite) TestLogsFile() {
_, err = os.Stat(outputFile)
s.NoError(err, "Output file should have been generated")
}

func (s *debugSuite) TestDebugDeny() {
outputDir := filepath.Join(s.tmpDir, "debug")

// should error and abort if the user does not consent
s.testInstallation.Actions.Glooctl().Debug(s.ctx, false,
func(err error, msgAndArgs ...interface{}) bool {
return s.ErrorContains(err, "Aborting: cannot proceed without overwriting \""+outputDir+"\" directory")
},
"-N", s.testInstallation.Metadata.InstallNamespace, "--directory", outputDir)

s.NoDirExists(outputDir)
}

func (s *debugSuite) TestDebugFile() {
outputDir := filepath.Join(s.tmpDir, "debug")

s.testInstallation.Actions.Glooctl().Debug(s.ctx, true, s.NoError,
"-N", s.testInstallation.Metadata.InstallNamespace, "--directory", outputDir)

// should populate the kube-state.log file
s.checkNonEmptyFile(kubeStateFile(outputDir))

// should populate ns directory
s.DirExists(filepath.Join(outputDir, s.testInstallation.Metadata.InstallNamespace))

// _pods directory should contain expected logs
s.checkPodLogsDir(filepath.Join(outputDir, s.testInstallation.Metadata.InstallNamespace, "_pods"))

// ns dir should contain envoy or gloo controller info for each pod
s.checkEnvoyAndGlooControllerInfo(filepath.Join(outputDir, s.testInstallation.Metadata.InstallNamespace))

// ns dir should contain dirs for expected CRDs with expected files for each CR
s.checkCRs(filepath.Join(outputDir, s.testInstallation.Metadata.InstallNamespace))

// default dir should not exist
s.NoDirExists("debug")
}

func (s *debugSuite) checkPodLogsDir(podsDir string) {
files, err := os.ReadDir(podsDir)
s.NoError(err)

s.GreaterOrEqual(len(files), 4)
var glooPods, gatewayProxyPods, publicGwPods int
for _, file := range files {
fileName := file.Name()
s.True(strings.HasSuffix(fileName, ".log"))

if strings.HasPrefix(fileName, "gloo-") && !strings.HasPrefix(fileName, "gloo-resource-") {
glooPods += 1
} else if strings.HasPrefix(file.Name(), "gateway-proxy-") {
gatewayProxyPods += 1
} else if strings.HasPrefix(file.Name(), "public-gw-") {
publicGwPods += 1
}

s.checkNonEmptyFile(filepath.Join(podsDir, fileName))
}
s.Equal(glooPods, 1)
s.Equal(gatewayProxyPods, 1)
s.Equal(publicGwPods, 2)
}

func (s *debugSuite) checkEnvoyAndGlooControllerInfo(nsDir string) {
files, err := os.ReadDir(nsDir)
s.NoError(err)

var fileIdx int
for _, file := range files {
if file.IsDir() {
continue
}

s.Less(fileIdx, 4*4) // 4 pods, 4 pieces of info each

// rely on os.ReadDir returns dir sorted by filename
var prefix string
switch fileIdx / 4 {
case 0:
prefix = "gateway-proxy-"
case 1:
prefix = "gloo-"
case 2, 3:
prefix = "public-gw-"
}

var gwSuffix string
var glooSuffix string
switch fileIdx % 4 {
case 0:
gwSuffix = ".clusters.log"
glooSuffix = ".controller.log"
case 1:
gwSuffix = ".config.log"
glooSuffix = ".krt_snapshot.log"
case 2:
gwSuffix = ".listeners.log"
glooSuffix = ".metrics.log"
case 3:
gwSuffix = ".stats.log"
glooSuffix = ".xds_snapshot.log"
}

fileName := file.Name()
s.True(strings.HasPrefix(fileName, prefix), "expected file %s at index %d to have prefix %s", fileName, fileIdx, prefix)
if prefix == "gloo-" {
s.True(strings.HasSuffix(fileName, glooSuffix), "expected file %s at index %d to have suffix %s", fileName, fileIdx, glooSuffix)
} else {
s.True(strings.HasSuffix(fileName, gwSuffix), "expected file %s at index %d to have suffix %s", fileName, fileIdx, gwSuffix)
}

s.checkNonEmptyFile(filepath.Join(nsDir, fileName))

fileIdx++
}
}

func (s *debugSuite) checkCRs(nsDir string) {
gws, err := os.ReadDir(filepath.Join(nsDir, "gateways.gateway.solo.io"))
s.NoError(err)
s.Len(gws, 3)
s.checkNonEmptyFile(filepath.Join(nsDir, "gateways.gateway.solo.io", "gateway-proxy.yaml"))
s.checkNonEmptyFile(filepath.Join(nsDir, "gateways.gateway.solo.io", "gateway-proxy-ssl.yaml"))
s.checkNonEmptyFile(filepath.Join(nsDir, "gateways.gateway.solo.io", "public-gw-ssl.yaml"))

settings, err := os.ReadDir(filepath.Join(nsDir, "settings.gloo.solo.io"))
s.NoError(err)
s.Len(settings, 1)
s.checkNonEmptyFile(filepath.Join(nsDir, "settings.gloo.solo.io", "default.yaml"))
}

func (s *debugSuite) checkNonEmptyFile(filepath string) {
bytes, err := os.ReadFile(filepath)
s.NoError(err, filepath+" file should have been generated")
s.NotEmpty(bytes, filepath+" file should not be empty")
}

0 comments on commit a8f776e

Please sign in to comment.