Skip to content

Commit

Permalink
tests: Allow running TestWatchNamespaceSelector in Enterprise (#10096)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidjumani committed Sep 24, 2024
1 parent 2362f6f commit df90c9d
Show file tree
Hide file tree
Showing 11 changed files with 60 additions and 142 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 @@ -74,7 +74,7 @@ jobs:
# September 19, 2024: 22 minutes
- cluster-name: 'cluster-five'
go-test-args: '-v -timeout=25m'
go-test-run-regex: '^TestValidationStrict$$|^TestValidationAlwaysAccept$$|^TestTransformationValidationDisabled$$|^TestGloomtlsGatewayEdgeGateway$$|^TestWatchNamespaceSelector$$'
go-test-run-regex: '^TestWatchNamespaceSelector$$|^TestValidationStrict$$|^TestValidationAlwaysAccept$$|^TestTransformationValidationDisabled$$|^TestGloomtlsGatewayEdgeGateway$$'

# In our PR tests, we run the suite of tests using the upper ends of versions that we claim to support
# The versions should mirror: https://docs.solo.io/gloo-edge/latest/reference/support/
Expand Down
4 changes: 4 additions & 0 deletions changelog/v1.17.10/watch-ns-tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
changelog:
- type: NON_USER_FACING
description: Allow running TestWatchNamespaceSelector in Enterprise by avoiding upgrades and other fixes

5 changes: 1 addition & 4 deletions docs/content/reference/values.txt
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,7 @@
|kubeGateway.gatewayParameters.glooGateway.stats.statsRoutePrefixRewrite|string|/stats|Set the prefix rewrite used for the stats endpoint|
|kubeGateway.gatewayParameters.glooGateway.floatingUserId|bool||If true, allows the cluster to dynamically assign a user ID for the processes running in the container. Default is false.|
|settings.watchNamespaces[]|string||whitelist of namespaces for Gloo Edge to watch for services and CRDs. Empty list means all namespaces. If this and WatchNamespaceSelectors are specified, this takes precedence and WatchNamespaceSelectors is ignored|
|settings.watchNamespaceSelectors[].match_labels.NAME|string|||
|settings.watchNamespaceSelectors[].match_expressions[].key|string|||
|settings.watchNamespaceSelectors[].match_expressions[].operator|string|||
|settings.watchNamespaceSelectors[].match_expressions[].values[]|string|||
|settings.watchNamespaceSelectors|interface||A list of Kubernetes selectors that specify the set of namespaces to restrict the namespaces that Gloo controllers take into consideration when watching for resources. Elements in the list are disjunctive (OR semantics), i.e. a namespace will be included if it matches any selector. An empty list means all namespaces. If this and WatchNamespaces are specified, WatchNamespaces takes precedence and this is ignored|
|settings.writeNamespace|string||namespace where intermediary CRDs will be written to, e.g. Upstreams written by Gloo Edge Discovery.|
|settings.integrations.knative.enabled|bool|false|enabled knative components|
|settings.integrations.knative.version|string|0.10.0|the version of knative installed to the cluster. if using version < 0.8.0, Gloo Edge will use Knative's ClusterIngress API for configuration rather than the namespace-scoped Ingress|
Expand Down
3 changes: 1 addition & 2 deletions install/helm/gloo/generate/values.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package generate

import (
v1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1"
corev1 "k8s.io/api/core/v1"
)

Expand Down Expand Up @@ -216,7 +215,7 @@ type KnativeProxyInternal struct {

type Settings struct {
WatchNamespaces []string `json:"watchNamespaces,omitempty" desc:"whitelist of namespaces for Gloo Edge to watch for services and CRDs. Empty list means all namespaces. If this and WatchNamespaceSelectors are specified, this takes precedence and WatchNamespaceSelectors is ignored"`
WatchNamespaceSelectors []*v1.LabelSelector `json:"watchNamespaceSelectors,omitempty" desc:"A list of Kubernetes selectors that specify the set of namespaces to restrict the namespaces that Gloo controllers take into consideration when watching for resources. Elements in the list are disjunctive (OR semantics), i.e. a namespace will be included if it matches any selector. An empty list means all namespaces. If this and WatchNamespaces are specified, WatchNamespaces takes precedence and this is ignored"`
WatchNamespaceSelectors interface{} `json:"watchNamespaceSelectors,omitempty" desc:"A list of Kubernetes selectors that specify the set of namespaces to restrict the namespaces that Gloo controllers take into consideration when watching for resources. Elements in the list are disjunctive (OR semantics), i.e. a namespace will be included if it matches any selector. An empty list means all namespaces. If this and WatchNamespaces are specified, WatchNamespaces takes precedence and this is ignored"`
WriteNamespace *string `json:"writeNamespace,omitempty" desc:"namespace where intermediary CRDs will be written to, e.g. Upstreams written by Gloo Edge Discovery."`
Integrations *Integrations `json:"integrations,omitempty"`
Create *bool `json:"create,omitempty" desc:"create a Settings CRD which provides bootstrap configuration to Gloo Edge controllers"`
Expand Down
69 changes: 36 additions & 33 deletions test/kubernetes/e2e/features/watch_namespace_selector/suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,40 +19,43 @@ type testingSuite struct {

func NewTestingSuite(ctx context.Context, testInst *e2e.TestInstallation) suite.TestingSuite {
return &testingSuite{
base.NewBaseTestingSuite(ctx, testInst, e2e.MustTestHelper(ctx, testInst), setupSuite, testCases),
base.NewBaseTestingSuite(ctx, testInst, setupSuite, testCases),
}
}

func (s *testingSuite) SetupSuite() {
s.BaseTestingSuite.SetupSuite()

// Apply a VS in the install namespace
s.applyFile(installNamespaceVSManifest, "-n", s.TestHelper.InstallNamespace)
s.applyFile(installNamespaceVSManifest, "-n", s.TestInstallation.Metadata.InstallNamespace)
}

func (s *testingSuite) TearDownSuite() {
// Delete VS in the install namespace
s.deleteFile(installNamespaceVSManifest, "-n", s.TestHelper.InstallNamespace)
s.deleteFile(installNamespaceVSManifest, "-n", s.TestInstallation.Metadata.InstallNamespace)

s.BaseTestingSuite.TearDownSuite()
}

func (s *testingSuite) TestMatchLabels() {
s.testWatchNamespaceSelector()
s.testWatchNamespaceSelector("label", "match")
}

func (s *testingSuite) TestMatchExpressions() {
s.testWatchNamespaceSelector()
s.testWatchNamespaceSelector("expression", "match")
}

func (s *testingSuite) testWatchNamespaceSelector() {
func (s *testingSuite) testWatchNamespaceSelector(key, value string) {
// Ensure the install namespace is watched even if not specified
s.TestInstallation.Assertions.CurlEventuallyRespondsWithStatus(s.Ctx, "install-ns/", http.StatusOK)

// Ensure the namespace is not watched
s.TestInstallation.Actions.Kubectl().Execute(s.Ctx, "label", "ns", "random", key+"-")

// Ensure CRs defined in non watched-namespaces are not translated
s.TestInstallation.Assertions.CurlConsistentlyRespondsWithStatus(s.Ctx, "random/", http.StatusNotFound)

s.labelSecondNamespaceAsWatched()
s.labelSecondNamespaceAsWatched(key, value)

// The VS defined in the random namespace should be translated
s.TestInstallation.Assertions.CurlEventuallyRespondsWithStatus(s.Ctx, "random/", http.StatusOK)
Expand All @@ -63,8 +66,8 @@ func (s *testingSuite) TestUnwatchedNamespaceValidation() {
s.applyFile(randomUpstreamManifest)

// It should successfully apply inconsequential labels to a ns without validation errors
s.addLabelToSecondNamespace()
s.removeLabelFromSecondNamespace()
s.addInconsequentialLabelToSecondNamespace()
s.removeInconsequentialLabelFromSecondNamespace()

// Deleting resources in the namespace and the namespace itself should not error out
s.deleteFile(randomUpstreamManifest)
Expand All @@ -75,90 +78,90 @@ func (s *testingSuite) TestWatchedNamespaceValidation() {
s.applyFile(unlabeledRandomNamespaceManifest)
s.applyFile(randomUpstreamManifest)

s.labelSecondNamespaceAsWatched()
s.labelSecondNamespaceAsWatched("label", "match")

// It should successfully apply inconsequential labels to a ns we watch without validation errors
s.addLabelToSecondNamespace()
s.removeLabelFromSecondNamespace()
s.addInconsequentialLabelToSecondNamespace()
s.removeInconsequentialLabelFromSecondNamespace()

s.Eventually(func() bool {
err := s.TestHelper.ApplyFile(s.Ctx, installNamespaceWithRandomUpstreamVSManifest, "-n", s.TestHelper.InstallNamespace)
err := s.TestInstallation.Actions.Kubectl().ApplyFile(s.Ctx, installNamespaceWithRandomUpstreamVSManifest, "-n", s.TestInstallation.Metadata.InstallNamespace)
return err == nil
}, time.Minute*2, time.Second*10)

// The upstream defined in the random namespace should be translated and referenced
s.TestInstallation.Assertions.CurlEventuallyRespondsWithStatus(s.Ctx, "/get", http.StatusOK)

// Trying to unwatch the namespace that has an upstream referenced in another namespace leads to an error
_, errOut, err := s.TestHelper.Execute(s.Ctx, "label", "ns", "random", "watch-")
_, errOut, err := s.TestInstallation.Actions.Kubectl().Execute(s.Ctx, "label", "ns", "random", "label-")

s.Contains(errOut, `admission webhook "gloo.namespace-selector.svc" denied the request: resource incompatible with current Gloo snapshot`)
s.Contains(errOut, `denied the request: resource incompatible with current Gloo snapshot`)
s.Contains(errOut, `Route Warning: InvalidDestinationWarning. Reason: *v1.Upstream { random.postman-echo } not found`)
s.Error(err)

// Trying to delete the namespace also errors out
_, errOut, err = s.TestHelper.Execute(s.Ctx, "delete", "ns", "random")
s.Contains(errOut, `admission webhook "gloo.namespace-selector.svc" denied the request: resource incompatible with current Gloo snapshot`)
_, errOut, err = s.TestInstallation.Actions.Kubectl().Execute(s.Ctx, "delete", "ns", "random")
s.Contains(errOut, `denied the request: resource incompatible with current Gloo snapshot`)
s.Contains(errOut, `Route Warning: InvalidDestinationWarning. Reason: *v1.Upstream { random.postman-echo } not found`)
s.Error(err)

// Ensure we didn't break the validation server while we're at it
s.addLabelToSecondNamespace()
s.removeLabelFromSecondNamespace()
s.addInconsequentialLabelToSecondNamespace()
s.removeInconsequentialLabelFromSecondNamespace()

s.deleteFile(installNamespaceWithRandomUpstreamVSManifest, "-n", s.TestHelper.InstallNamespace)
s.deleteFile(installNamespaceWithRandomUpstreamVSManifest, "-n", s.TestInstallation.Metadata.InstallNamespace)

s.unwatchNamespace()
s.unwatchNamespace("label")

// The upstream defined in the random namespace should be translated and referenced
s.TestInstallation.Assertions.CurlEventuallyRespondsWithStatus(s.Ctx, "/get", http.StatusNotFound)

// Optimists invent airplanes; pessimists invent parachutes
s.addLabelToSecondNamespace()
s.removeLabelFromSecondNamespace()
s.addInconsequentialLabelToSecondNamespace()
s.removeInconsequentialLabelFromSecondNamespace()

s.deleteFile(randomUpstreamManifest)
s.deleteFile(unlabeledRandomNamespaceManifest)
}

func (s *testingSuite) labelSecondNamespaceAsWatched() {
func (s *testingSuite) labelSecondNamespaceAsWatched(key, value string) {
// Label the `random` namespace with the watchNamespaceSelector labels
// kubectl label ns random watch=this
out, _, err := s.TestHelper.Execute(s.Ctx, "label", "ns", "random", "watch=this")
out, _, err := s.TestInstallation.Actions.Kubectl().Execute(s.Ctx, "label", "ns", "random", key+"="+value)
s.Assertions.Contains(out, "namespace/random labeled")
s.NoError(err)
}

func (s *testingSuite) unwatchNamespace() {
func (s *testingSuite) unwatchNamespace(key string) {
// Label the `random` namespace with the watchNamespaceSelector labels
// kubectl label ns random watch-
out, _, err := s.TestHelper.Execute(s.Ctx, "label", "ns", "random", "watch-")
out, _, err := s.TestInstallation.Actions.Kubectl().Execute(s.Ctx, "label", "ns", "random", key+"-")
s.Assertions.Contains(out, "namespace/random unlabeled")
s.NoError(err)
}

func (s *testingSuite) addLabelToSecondNamespace() {
func (s *testingSuite) addInconsequentialLabelToSecondNamespace() {
// label the `random` namespace
// kubectl label ns random inconsequential=label
out, _, err := s.TestHelper.Execute(s.Ctx, "label", "ns", "random", "inconsequential=label")
out, _, err := s.TestInstallation.Actions.Kubectl().Execute(s.Ctx, "label", "ns", "random", "inconsequential=label")
s.Assertions.Contains(out, "namespace/random labeled")
s.NoError(err)
}

func (s *testingSuite) removeLabelFromSecondNamespace() {
func (s *testingSuite) removeInconsequentialLabelFromSecondNamespace() {
// unlabel the `random` namespace
// kubectl label ns random inconsequential-
out, _, err := s.TestHelper.Execute(s.Ctx, "label", "ns", "random", "inconsequential-")
out, _, err := s.TestInstallation.Actions.Kubectl().Execute(s.Ctx, "label", "ns", "random", "inconsequential-")
s.Assertions.Contains(out, "namespace/random unlabeled")
s.NoError(err)
}

func (s *testingSuite) applyFile(filename string, extraArgs ...string) {
err := s.TestHelper.ApplyFile(s.Ctx, filename, extraArgs...)
err := s.TestInstallation.Actions.Kubectl().ApplyFile(s.Ctx, filename, extraArgs...)
s.NoError(err)
}

func (s *testingSuite) deleteFile(filename string, extraArgs ...string) {
err := s.TestHelper.DeleteFile(s.Ctx, filename, extraArgs...)
err := s.TestInstallation.Actions.Kubectl().DeleteFile(s.Ctx, filename, extraArgs...)
s.NoError(err)
}

This file was deleted.

This file was deleted.

This file was deleted.

21 changes: 6 additions & 15 deletions test/kubernetes/e2e/features/watch_namespace_selector/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@ import (
var (
installNamespaceVSManifest = filepath.Join(util.MustGetThisDir(), "testdata", "vs-install-ns.yaml")

matchLabelsSetup = filepath.Join(util.MustGetThisDir(), "testdata", "match-labels.yaml")
matchExpressionsSetup = filepath.Join(util.MustGetThisDir(), "testdata", "match-expressions.yaml")

unlabeledRandomNamespaceManifest = filepath.Join(util.MustGetThisDir(), "testdata", "random-ns-unlabeled.yaml")
randomVSManifest = filepath.Join(util.MustGetThisDir(), "testdata", "vs-random.yaml")

Expand All @@ -38,27 +35,21 @@ var (
testCases = map[string]*base.TestCase{
"TestMatchLabels": {
SimpleTestCase: base.SimpleTestCase{
UpgradeValues: matchLabelsSetup,
Manifests: []string{unlabeledRandomNamespaceManifest, randomVSManifest},
Resources: []client.Object{randomNamespaceVS},
Manifests: []string{unlabeledRandomNamespaceManifest, randomVSManifest},
Resources: []client.Object{randomNamespaceVS},
},
},
"TestMatchExpressions": {
SimpleTestCase: base.SimpleTestCase{
UpgradeValues: matchExpressionsSetup,
Manifests: []string{unlabeledRandomNamespaceManifest, randomVSManifest},
Resources: []client.Object{randomNamespaceVS},
Manifests: []string{unlabeledRandomNamespaceManifest, randomVSManifest},
Resources: []client.Object{randomNamespaceVS},
},
},
"TestUnwatchedNamespaceValidation": {
SimpleTestCase: base.SimpleTestCase{
UpgradeValues: matchLabelsSetup,
},
SimpleTestCase: base.SimpleTestCase{},
},
"TestWatchedNamespaceValidation": {
SimpleTestCase: base.SimpleTestCase{
UpgradeValues: matchExpressionsSetup,
},
SimpleTestCase: base.SimpleTestCase{},
},
}
)
Loading

0 comments on commit df90c9d

Please sign in to comment.