Skip to content

Commit

Permalink
test(meshtimeout): e2e test that checks golden envoy configs (#12650)
Browse files Browse the repository at this point in the history
## Motivation

When we test `BuildRules` we pass MeshTimeout policies as input and
check `outbound.ResourceRules` Go struct.

When we test MeshTimeout plugin we pass `outbound.ResourceRules` Go
struct as input and check envoy configs.

We need an easy way to pass MeshTimeout policies and check envoy
configs. It's sort of an integration test that involves 2 packages. The
easiest way to do that is to add another universal e2e test with golden
files. This way we have running Kuma CP and we basically test it as a
black box.

I managed to find 2 bugs using these tests:
* #12648
* #12649

## Implementation information

We check DPP `config` (marshalled Snapshot in Kuma CP) instead of the
actual Envoy config dump for a few reasons:
* no need to add logic that waits while config is propagated on DPP
* less setup-specific values we have to reduct to have stable golden
file (only IP addresses)
* we can use `kuma.io/effect: shadow` and quickly review the `diff`
policies introduces

---------

Signed-off-by: Ilya Lobkov <[email protected]>
  • Loading branch information
lobkovilya authored Jan 27, 2025
1 parent 0d48dbe commit 645d0de
Show file tree
Hide file tree
Showing 12 changed files with 3,240 additions and 2 deletions.
4 changes: 2 additions & 2 deletions pkg/test/ginkgo.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ func runSpecs(t *testing.T, description string) {

// EntriesForFolder returns all files in the folder as gingko table entries for files *.input.yaml this makes it easier to add test by only adding input and golden files
// if you prefix the file with a `F` we'll focus this specific test
func EntriesForFolder(folder string) []ginkgo.TableEntry {
func EntriesForFolder(folder string, pathPrefix ...string) []ginkgo.TableEntry {
ginkgo.GinkgoHelper()
var entries []ginkgo.TableEntry
testDir := path.Join("testdata", folder)
testDir := path.Join(append(pathPrefix, "testdata", folder)...)
files, err := os.ReadDir(testDir)
if err != nil {
panic(err)
Expand Down
101 changes: 101 additions & 0 deletions test/e2e_env/universal/envoyconfig/envoyconfig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package envoyconfig

import (
"encoding/json"
"os"
"regexp"
"strings"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
"github.com/kumahq/kuma/pkg/test"
"github.com/kumahq/kuma/pkg/test/matchers"
"github.com/kumahq/kuma/pkg/test/resources/builders"
. "github.com/kumahq/kuma/test/framework"
"github.com/kumahq/kuma/test/framework/client"
"github.com/kumahq/kuma/test/framework/envs/universal"
)

func EnvoyConfigTest() {
meshName := "envoyconfig"

BeforeAll(func() {
err := NewClusterSetup().
Install(
Yaml(
builders.Mesh().
WithName(meshName).
WithoutInitialPolicies().
WithMeshServicesEnabled(mesh_proto.Mesh_MeshServices_Exclusive),
),
).
Install(DemoClientUniversal("demo-client", meshName,
WithTransparentProxy(true)),
).
Install(TestServerUniversal("test-server", meshName,
WithArgs([]string{"echo", "--instance", "universal-1"})),
).
Setup(universal.Cluster)
Expect(err).ToNot(HaveOccurred())

Eventually(func(g Gomega) {
_, err := client.CollectEchoResponse(universal.Cluster, "demo-client", "test-server.svc.mesh.local")
g.Expect(err).ToNot(HaveOccurred())
}).Should(Succeed())
})

AfterEachFailure(func() {
DebugUniversal(universal.Cluster, meshName)
})

E2EAfterAll(func() {
Expect(universal.Cluster.DeleteMeshApps(meshName)).To(Succeed())
Expect(universal.Cluster.DeleteMesh(meshName)).To(Succeed())
})

E2EAfterEach(func() {
// delete all meshtimeout policies
out, err := universal.Cluster.GetKumactlOptions().RunKumactlAndGetOutput("get", "meshtimeouts", "--mesh", meshName, "-o", "json")
Expect(err).ToNot(HaveOccurred())
var output struct {
Items []struct {
Name string `json:"name"`
} `json:"items"`
}
Expect(json.Unmarshal([]byte(out), &output)).To(Succeed())
for _, item := range output.Items {
Expect(universal.Cluster.GetKumactlOptions().RunKumactl("delete", "meshtimeout", item.Name, "--mesh", meshName)).To(Succeed())
}
})

redactIPs := func(jsonStr string) string {
ipRegex := regexp.MustCompile(`\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b`)
return ipRegex.ReplaceAllString(jsonStr, "IP_REDACTED")
}

getConfig := func(dpp string) string {
output, err := universal.Cluster.GetKumactlOptions().
RunKumactlAndGetOutput("inspect", "dataplane", dpp, "--type", "config", "--mesh", meshName, "--shadow", "--include=diff")
Expect(err).ToNot(HaveOccurred())
return redactIPs(output)
}

DescribeTable("should generate proper Envoy config",
func(inputFile string) {
// given
input, err := os.ReadFile(inputFile)
Expect(err).ToNot(HaveOccurred())

// when
if len(input) > 0 {
Expect(universal.Cluster.Install(YamlUniversal(string(input)))).To(Succeed())
}

// then
Expect(getConfig("demo-client")).To(matchers.MatchGoldenJSON(strings.Replace(inputFile, "input.yaml", "demo-client.golden.json", 1)))
Expect(getConfig("test-server")).To(matchers.MatchGoldenJSON(strings.Replace(inputFile, "input.yaml", "test-server.golden.json", 1)))
}, test.EntriesForFolder("meshtimeout", "envoyconfig"),
)
}
Loading

0 comments on commit 645d0de

Please sign in to comment.