Skip to content

Commit

Permalink
Backport 1.17 Move glooctl and gloomtls tests to new framework (#9777)
Browse files Browse the repository at this point in the history
* cherrypick gloomtls tests

* Move glooctl tests to new framework (#9718)

* initial move for glooctl tests

* combine glooctl helm manifest and edge gw manifest

* fix check crds

* move test helper values in glooContext

* fix crd check for not gloo-system ns

* passing tests

* passing eventually error test

* changelog, cleanup

* update references to old glooctl tests

* regen docs

* remove old glooctl test from workflow

* Adding changelog file to new location

* Deleting changelog file from old location

* fix race debug

* remove old glooctl tests from nightlys

* Adding changelog file to new location

* Deleting changelog file from old location

* Add back nightlys for 1.15 and 1.16

* add glooctl tests back

* remove tmp dir for check-crds

* Adding changelog file to new location

* Deleting changelog file from old location

* istio feedback from prev pr

* helm manifest typo

* chart uri

---------

Co-authored-by: soloio-bulldozer[bot] <48420018+soloio-bulldozer[bot]@users.noreply.github.com>
Co-authored-by: changelog-bot <changelog-bot>

* changelog

* remove unnecessary workflow changes

* combine changelogs

---------

Co-authored-by: soloio-bulldozer[bot] <48420018+soloio-bulldozer[bot]@users.noreply.github.com>
Co-authored-by: Sam Heilbron <SamHeilbron@gmail.com>
  • Loading branch information
3 people committed Jul 18, 2024
1 parent 751e637 commit 17ebe72
Show file tree
Hide file tree
Showing 43 changed files with 1,018 additions and 115 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pr-kubernetes-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
# May 14th: ~ minutes execution time (see load_balancing_tests.md)
- cluster-name: 'cluster-one'
go-test-args: '-v -timeout=25m'
go-test-run-regex: '^TestK8sGateway$$/^RouteDelegation$$|^TestK8sGateway$$/^Glooctl$$|^TestK8sGateway$$/^Services$$'
go-test-run-regex: '^TestGloomtlsGatewayEdgeGateway$$|^TestK8sGateway$$/^RouteDelegation$$|^TestK8sGateway$$/^Services$$|^TestGlooctlGlooGatewayEdgeGateway$$|^TestGlooctlK8sGateway$$'

# May 14th: ~ minutes execution time (see load_balancing_tests.md)
- cluster-name: 'cluster-two'
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/regression-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ jobs:
# upgrade tests are run on LTS but not on main branch, for main they are run nightly
# ingress is deprecated from 1.17. Ref: https://solo-io-corp.slack.com/archives/G01EERAK3KJ/p1716389614777799
# this is the github action version of ternary op
# TODO(npolshak): remove gloomtls, glooctl after 3 days of successful runs of new test suite
kube-e2e-test-type: [ 'gateway', 'gloo', 'helm', 'gloomtls', 'glooctl', 'upgrade' ]
kube-version: [ { node: 'v1.29.2@sha256:51a1434a5397193442f0be2a297b488b6c919ce8a3931be0ce822606ea5ca245', kubectl: 'v1.29.2', kind: 'v0.20.0', helm: 'v3.14.4' } ]
image-variant:
Expand Down
14 changes: 14 additions & 0 deletions changelog/v1.17.1/add-glooctl-tests-new-framework.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
changelog:
- type: NON_USER_FACING
issueLink: https://github.com/solo-io/solo-projects/issues/6303
resolvesIssue: false
description: >-
Migrate glooctl tests from kube2e to new framework.
skipCI-docs-build:true
- type: NON_USER_FACING
issueLink: https://github.com/solo-io/solo-projects/issues/6303
resolvesIssue: false
description: >-
Migrate gloomtls tests from kube2e to new framework.
skipCI-docs-build:true
1 change: 1 addition & 0 deletions docs/content/reference/cli/glooctl_check-crds.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ glooctl check-crds [flags]
```
-h, --help help for check-crds
--local-chart string check against CRDs in helm chart at path specified by this flag (supersedes --version)
-n, --namespace string namespace for reading or writing resources (default "gloo-system")
--show-yaml show full yaml of both CRDs that differ
--version string version of gloo's CRDs to check against
```
Expand Down
1 change: 1 addition & 0 deletions projects/gloo/cli/pkg/cmd/check-crds/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func RootCmd(opts *options.Options, optionsFunc ...cliutils.OptionsFunc) *cobra.
flagutils.AddVersionFlag(pflags, &opts.CheckCRD.Version)
flagutils.AddLocalChartFlag(pflags, &opts.CheckCRD.LocalChart)
flagutils.AddShowYamlFlag(pflags, &opts.CheckCRD.ShowYaml)
flagutils.AddNamespaceFlag(pflags, &opts.Metadata.Namespace)
cliutils.ApplyOptions(cmd, optionsFunc)
return cmd
}
Expand Down
10 changes: 9 additions & 1 deletion projects/gloo/cli/pkg/cmd/debug/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"io"
"os"
"strings"
"sync"

"github.com/solo-io/gloo/pkg/cliutil/install"
installcmd "github.com/solo-io/gloo/projects/gloo/cli/pkg/cmd/install"
Expand Down Expand Up @@ -41,7 +42,11 @@ func DebugLogs(opts *options.Options, w io.Writer) error {
storageClient := debugutils.NewFileStorageClient(fs)

// if writing to a non-zipped file, create a channel to collect logs
var fileBuf chan string
// Need fileBufLock here because eg.Go() can create concurrent goroutines, which can access shared fileBuf concurrently.
var (
fileBuf chan string
fileBufLock sync.Mutex
)
if !opts.Top.Zip && opts.Top.File != "" {
fileBuf = make(chan string, len(responses))
}
Expand All @@ -50,6 +55,9 @@ func DebugLogs(opts *options.Options, w io.Writer) error {
for _, response := range responses {
response := response
eg.Go(func() error {
fileBufLock.Lock()
defer fileBufLock.Unlock()

defer response.Response.Close()
var logs strings.Builder
if opts.Top.ErrorsOnly {
Expand Down
2 changes: 1 addition & 1 deletion test/kube2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ The below table contains the environment variables that can be used to configure

| Name | Default | Description |
|------------------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| KUBE2E_TESTS | gateway | Name of the test suite to be run. Options: `'gateway', 'gloo', 'ingress', 'helm', 'gloomtls', 'glooctl', 'upgrade', 'istio'` |
| KUBE2E_TESTS | gateway | Name of the test suite to be run. Options: `'gateway', 'gloo', 'ingress', 'helm', 'glooctl', 'upgrade', 'istio'` |
| DEBUG | 0 | Set to 1 for debug log output |
| WAIT_ON_FAIL | 0 | Set to 1 to prevent Ginkgo from cleaning up the Gloo Edge installation in case of failure. Useful to exec into inspect resources created by the test. A command to resume the test run (and thus clean up resources) will be logged to the output. |
| TEAR_DOWN | false | Set to true to uninstall Gloo after the test suite completes |
Expand Down
5 changes: 4 additions & 1 deletion test/kubernetes/e2e/features/glooctl/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type checkOutput struct {
}

var (
checkOutputByKey = map[string]checkOutput{
checkCommonGlooGatewayOutputByKey = map[string]checkOutput{
"deployments": {
include: ContainSubstring("Checking Deployments... OK"),
exclude: And(
Expand Down Expand Up @@ -93,6 +93,9 @@ var (
exclude: gstruct.Ignore(), // We have not had historical tests for this, it would be good to add
readOnly: ContainSubstring("Warning: checking proxies with port forwarding is disabled"),
},
}

checkK8sGatewayOutputByKey = map[string]checkOutput{
"kube-gateway-classes": {
include: ContainSubstring("Checking Kubernetes GatewayClasses... OK"),
exclude: Not(ContainSubstring("Checking Kubernetes GatewayClasses...")),
Expand Down
50 changes: 50 additions & 0 deletions test/kubernetes/e2e/features/glooctl/check_crds_suite.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package glooctl

import (
"context"

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

var _ e2e.NewSuiteFunc = NewDebugSuite

// checkCrdsSuite contains the set of tests to validate the behavior of `glooctl check-crds`
type checkCrdsSuite struct {
suite.Suite

ctx context.Context
testInstallation *e2e.TestInstallation
}

func NewCheckCrdsSuite(ctx context.Context, testInst *e2e.TestInstallation) suite.TestingSuite {
return &checkCrdsSuite{
ctx: ctx,
testInstallation: testInst,
}
}

func (s *checkCrdsSuite) TestValidatesCorrectCrds() {
if s.testInstallation.Metadata.ReleasedVersion != "" {
err := s.testInstallation.Actions.Glooctl().CheckCrds(s.ctx,
"-n", s.testInstallation.Metadata.InstallNamespace,
"--kube-context", s.testInstallation.ClusterContext.KubeContext,
"--version", s.testInstallation.Metadata.ChartVersion)
s.NoError(err)
} else {
err := s.testInstallation.Actions.Glooctl().CheckCrds(s.ctx,
"-n", s.testInstallation.Metadata.InstallNamespace,
"--kube-context", s.testInstallation.ClusterContext.KubeContext,
"--local-chart", s.testInstallation.Metadata.ChartUri)
s.NoError(err)
}
}

func (s *checkCrdsSuite) TestCrdMismatch() {
err := s.testInstallation.Actions.Glooctl().CheckCrds(s.ctx,
"-n", s.testInstallation.Metadata.InstallNamespace,
"--kube-context", s.testInstallation.ClusterContext.KubeContext,
"--version", "1.9.0")
s.Error(err)
s.Contains(err.Error(), "One or more CRDs are out of date")
}
194 changes: 190 additions & 4 deletions test/kubernetes/e2e/features/glooctl/check_suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ package glooctl
import (
"context"
"fmt"
"os"
"time"

"github.com/onsi/gomega"

"github.com/solo-io/gloo/test/kubernetes/e2e"
"github.com/stretchr/testify/suite"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var _ e2e.NewSuiteFunc = NewCheckSuite
Expand All @@ -33,31 +35,55 @@ func (s *checkSuite) TestCheck() {
"-n", s.testInstallation.Metadata.InstallNamespace, "-x", "xds-metrics")
s.NoError(err)

for _, expectedOutput := range checkOutputByKey {
for _, expectedOutput := range checkCommonGlooGatewayOutputByKey {
gomega.Expect(output).To(expectedOutput.include)
}

if s.testInstallation.Metadata.K8sGatewayEnabled {
for _, expectedOutput := range checkK8sGatewayOutputByKey {
gomega.Expect(output).To(expectedOutput.include)
}
}
}

func (s *checkSuite) TestCheckExclude() {
for excludeKey, expectedOutput := range checkOutputByKey {
for excludeKey, expectedOutput := range checkCommonGlooGatewayOutputByKey {
output, err := s.testInstallation.Actions.Glooctl().Check(s.ctx,
"-n", s.testInstallation.Metadata.InstallNamespace, "-x", fmt.Sprintf("xds-metrics,%s", excludeKey))
s.NoError(err)
gomega.Expect(output).To(expectedOutput.exclude)
}

if s.testInstallation.Metadata.K8sGatewayEnabled {
for excludeKey, expectedOutput := range checkK8sGatewayOutputByKey {
output, err := s.testInstallation.Actions.Glooctl().Check(s.ctx,
"-n", s.testInstallation.Metadata.InstallNamespace, "-x", fmt.Sprintf("xds-metrics,%s", excludeKey))
s.NoError(err)
gomega.Expect(output).To(expectedOutput.exclude)
}
}
}

func (s *checkSuite) TestCheckReadOnly() {
output, err := s.testInstallation.Actions.Glooctl().Check(s.ctx,
"-n", s.testInstallation.Metadata.InstallNamespace, "--read-only")
s.NoError(err)

for _, expectedOutput := range checkOutputByKey {
for _, expectedOutput := range checkCommonGlooGatewayOutputByKey {
gomega.Expect(output).To(gomega.And(
expectedOutput.include,
expectedOutput.readOnly,
))
}

if s.testInstallation.Metadata.K8sGatewayEnabled {
for _, expectedOutput := range checkK8sGatewayOutputByKey {
gomega.Expect(output).To(gomega.And(
expectedOutput.include,
expectedOutput.readOnly,
))
}
}
}

func (s *checkSuite) TestCheckKubeContext() {
Expand All @@ -72,3 +98,163 @@ func (s *checkSuite) TestCheckKubeContext() {
"-n", s.testInstallation.Metadata.InstallNamespace, "--kube-context", s.testInstallation.ClusterContext.KubeContext)
s.NoError(err)
}

func (s *checkSuite) TestCheckTimeout() {
// When passing short timeout, check will fail
shortTimeoutValues, err := os.CreateTemp("", "*.yaml")
s.NoError(err)
_, err = shortTimeoutValues.Write([]byte(`checkTimeoutSeconds: 1ns`))
s.NoError(err)

_, err = s.testInstallation.Actions.Glooctl().Check(s.ctx,
"-n", s.testInstallation.Metadata.InstallNamespace,
"-c", shortTimeoutValues.Name())
s.Error(err)
s.Contains(err.Error(), "context deadline exceeded")

// When passing valid timeout, check should succeed
normalTimeoutValues, err := os.CreateTemp("", "*.yaml")
s.NoError(err)
_, err = normalTimeoutValues.Write([]byte(`checkTimeoutSeconds: 300s`))
s.NoError(err)

_, err = s.testInstallation.Actions.Glooctl().Check(s.ctx,
"-n", s.testInstallation.Metadata.InstallNamespace,
"-c", normalTimeoutValues.Name())
s.NoError(err)
}

func (s *checkSuite) TestCheckNamespace() {
// namespace does not exist
output, err := s.testInstallation.Actions.Glooctl().Check(s.ctx, "-n", "not-gloo-system")
s.Error(err)
s.Contains(output, "Could not communicate with kubernetes cluster: namespaces \"not-gloo-system\" not found")

// gloo not in namespace
output, err = s.testInstallation.Actions.Glooctl().Check(s.ctx, "-n", "default")
s.Error(err)
s.Contains(output, "Warning: The provided label selector (gloo) applies to no pods")

// pod does not exist
output, err = s.testInstallation.Actions.Glooctl().Check(s.ctx,
"-n", s.testInstallation.Metadata.InstallNamespace,
"-p", "not-gloo")
s.NoError(err)
s.Contains(output, "Warning: The provided label selector (not-gloo) applies to no pods")
s.Contains(output, "No problems detected.")

// resource namespace does not exist
output, err = s.testInstallation.Actions.Glooctl().Check(s.ctx,
"-n", s.testInstallation.Metadata.InstallNamespace,
"-r", "not-gloo-system")
s.Error(err)
s.Contains(output, fmt.Sprintf("No namespaces specified are currently being watched (defaulting to '%s' namespace)", s.testInstallation.Metadata.InstallNamespace))
}

func (s *checkSuite) TestNoGateways() {
s.T().Cleanup(func() {
// Scale gateways back
err := s.testInstallation.ClusterContext.Cli.Scale(s.ctx, s.testInstallation.Metadata.InstallNamespace, "deploy/gateway-proxy", 1)
s.NoError(err)
s.testInstallation.Assertions.EventuallyRunningReplicas(s.ctx, metav1.ObjectMeta{
Name: "gateway-proxy",
Namespace: s.testInstallation.Metadata.InstallNamespace,
}, gomega.Equal(1))

err = s.testInstallation.ClusterContext.Cli.Scale(s.ctx, s.testInstallation.Metadata.InstallNamespace, "deploy/public-gw", 2)
s.NoError(err)
s.testInstallation.Assertions.EventuallyRunningReplicas(s.ctx, metav1.ObjectMeta{
Name: "public-gw",
Namespace: s.testInstallation.Metadata.InstallNamespace,
}, gomega.Equal(2)) // helm has replicas=2 defined
})

// Scale gateways down
err := s.testInstallation.ClusterContext.Cli.Scale(s.ctx, s.testInstallation.Metadata.InstallNamespace, "deploy/gateway-proxy", 0)
s.NoError(err)
s.testInstallation.Assertions.EventuallyRunningReplicas(s.ctx, metav1.ObjectMeta{
Name: "gateway-proxy",
Namespace: s.testInstallation.Metadata.InstallNamespace,
}, gomega.Equal(0))

// public-gw is defined in the helm chart
err = s.testInstallation.ClusterContext.Cli.Scale(s.ctx, s.testInstallation.Metadata.InstallNamespace, "deploy/public-gw", 0)
s.NoError(err)
s.testInstallation.Assertions.EventuallyRunningReplicas(s.ctx, metav1.ObjectMeta{
Name: "public-gw",
Namespace: s.testInstallation.Metadata.InstallNamespace,
}, gomega.Equal(0))

_, err = s.testInstallation.Actions.Glooctl().Check(s.ctx,
"-n", s.testInstallation.Metadata.InstallNamespace, "--kube-context", s.testInstallation.ClusterContext.KubeContext, "-x", "xds-metrics")
s.Error(err)
s.Contains(err.Error(), "Gloo installation is incomplete: no active gateway-proxy pods exist in cluster")
}

func (s *checkSuite) TestEdgeGatewayScaled() {
s.T().Cleanup(func() {
// Scale gateway back
err := s.testInstallation.ClusterContext.Cli.Scale(s.ctx, s.testInstallation.Metadata.InstallNamespace, "deploy/gateway-proxy", 1)
s.NoError(err)
s.testInstallation.Assertions.EventuallyRunningReplicas(s.ctx, metav1.ObjectMeta{
Name: "gateway-proxy",
Namespace: s.testInstallation.Metadata.InstallNamespace,
}, gomega.Equal(1))
})

// Scale gateway down
err := s.testInstallation.ClusterContext.Cli.Scale(s.ctx, s.testInstallation.Metadata.InstallNamespace, "deploy/gateway-proxy", 0)
s.NoError(err)

// Wait for gateway to be gone
s.testInstallation.Assertions.EventuallyRunningReplicas(s.ctx, metav1.ObjectMeta{
Name: "gateway-proxy",
Namespace: s.testInstallation.Metadata.InstallNamespace,
}, gomega.Equal(0))

output, err := s.testInstallation.Actions.Glooctl().Check(s.ctx,
"-n", s.testInstallation.Metadata.InstallNamespace, "--kube-context", s.testInstallation.ClusterContext.KubeContext, "-x", "xds-metrics")
s.NoError(err)
s.Contains(output, fmt.Sprintf("Warning: %s:gateway-proxy has zero replicas", s.testInstallation.Metadata.InstallNamespace))
s.Contains(output, "No problems detected.")
// Check healthy output
for _, expectedOutput := range checkCommonGlooGatewayOutputByKey {
gomega.Expect(output).To(expectedOutput.include)
}

if s.testInstallation.Metadata.K8sGatewayEnabled {
for _, expectedOutput := range checkK8sGatewayOutputByKey {
gomega.Expect(output).To(expectedOutput.include)
}
}
}

func (s *checkSuite) TestEdgeResourceError() {
s.T().Cleanup(func() {
// Delete invalid config
err := s.testInstallation.ClusterContext.Cli.DeleteFileSafe(s.ctx, invalidVSKubeDest)
s.NoError(err)
err = s.testInstallation.ClusterContext.Cli.DeleteFileSafe(s.ctx, invalidVSUpstreamDest, "-n", s.testInstallation.Metadata.InstallNamespace)
s.NoError(err)
})

// Apply invalid config
err := s.testInstallation.ClusterContext.Cli.ApplyFile(s.ctx, invalidVSKubeDest)
s.NoError(err)
err = s.testInstallation.ClusterContext.Cli.ApplyFile(s.ctx, invalidVSUpstreamDest, "-n", s.testInstallation.Metadata.InstallNamespace)
s.NoError(err)

// Run check. This needs to run in eventually to get all errors reported
gomega.Eventually(func() error {
_, err = s.testInstallation.Actions.Glooctl().Check(s.ctx,
"-n", s.testInstallation.Metadata.InstallNamespace, "--kube-context", s.testInstallation.ClusterContext.KubeContext)
return err
}, time.Minute*2, time.Second*10).Should(gomega.SatisfyAll(
gomega.MatchError(gomega.ContainSubstring(fmt.Sprintf("* Found rejected virtual service by '%s': default reject-me-too (Reason: 2 errors occurred:", s.testInstallation.Metadata.InstallNamespace))),
gomega.MatchError(gomega.ContainSubstring(fmt.Sprintf("* domain conflict: other virtual services that belong to the same Gateway as this one don't specify a domain (and thus default to '*'): [%s.reject-me]", s.testInstallation.Metadata.InstallNamespace))),
gomega.MatchError(gomega.ContainSubstring(fmt.Sprintf("* VirtualHost Error: DomainsNotUniqueError. Reason: domain * is shared by the following virtual hosts: [default.reject-me-too %s.reject-me]", s.testInstallation.Metadata.InstallNamespace))),
gomega.MatchError(gomega.ContainSubstring(fmt.Sprintf("* Found rejected virtual service by '%s': %s reject-me (Reason: 2 errors occurred:", s.testInstallation.Metadata.InstallNamespace, s.testInstallation.Metadata.InstallNamespace))),
gomega.MatchError(gomega.ContainSubstring(fmt.Sprintf("* domain conflict: other virtual services that belong to the same Gateway as this one don't specify a domain (and thus default to '*'): [default.reject-me-too]"))),
gomega.MatchError(gomega.ContainSubstring(fmt.Sprintf("* VirtualHost Error: DomainsNotUniqueError. Reason: domain * is shared by the following virtual hosts: [default.reject-me-too %s.reject-me]", s.testInstallation.Metadata.InstallNamespace))),
))
}
Loading

0 comments on commit 17ebe72

Please sign in to comment.