mirror of
https://github.com/alibaba/higress.git
synced 2026-06-09 04:37:31 +08:00
feat: add e2e test for envoy filter (#710)
This commit is contained in:
@@ -26,7 +26,7 @@ header:
|
|||||||
- 'VERSION'
|
- 'VERSION'
|
||||||
- 'tools/'
|
- 'tools/'
|
||||||
- 'test/README.md'
|
- 'test/README.md'
|
||||||
- 'pkg/cmd/hgctl/testdata/config'
|
- 'cmd/hgctl/config/testdata/config'
|
||||||
- 'pkg/cmd/hgctl/manifests'
|
- 'pkg/cmd/hgctl/manifests'
|
||||||
|
|
||||||
comment: on-failure
|
comment: on-failure
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package hgctl
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@@ -27,26 +27,61 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
output string
|
BootstrapEnvoyConfigType EnvoyConfigType = "bootstrap"
|
||||||
podName string
|
ClusterEnvoyConfigType EnvoyConfigType = "cluster"
|
||||||
podNamespace string
|
EndpointEnvoyConfigType EnvoyConfigType = "endpoint"
|
||||||
|
ListenerEnvoyConfigType EnvoyConfigType = "listener"
|
||||||
|
RouteEnvoyConfigType EnvoyConfigType = "route"
|
||||||
|
AllEnvoyConfigType EnvoyConfigType = "all"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultProxyAdminPort = 15000
|
defaultProxyAdminPort = 15000
|
||||||
containerName = "envoy"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func retrieveConfigDump(args []string, includeEds bool) ([]byte, error) {
|
type EnvoyConfigType string
|
||||||
if len(args) != 0 {
|
|
||||||
podName = args[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
type GetEnvoyConfigOptions struct {
|
||||||
|
IncludeEds bool
|
||||||
|
PodName string
|
||||||
|
PodNamespace string
|
||||||
|
BindAddress string
|
||||||
|
Output string
|
||||||
|
EnvoyConfigType EnvoyConfigType
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDefaultGetEnvoyConfigOptions() *GetEnvoyConfigOptions {
|
||||||
|
return &GetEnvoyConfigOptions{
|
||||||
|
IncludeEds: true,
|
||||||
|
PodName: "",
|
||||||
|
PodNamespace: "higress-system",
|
||||||
|
BindAddress: "localhost",
|
||||||
|
Output: "json",
|
||||||
|
EnvoyConfigType: AllEnvoyConfigType,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetEnvoyConfig(config *GetEnvoyConfigOptions) ([]byte, error) {
|
||||||
|
configDump, err := retrieveConfigDump(config.PodName, config.PodNamespace, config.BindAddress, config.IncludeEds)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if config.EnvoyConfigType == AllEnvoyConfigType {
|
||||||
|
return configDump, nil
|
||||||
|
}
|
||||||
|
resource, err := getXDSResource(config.EnvoyConfigType, configDump)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return formatGatewayConfig(resource, config.Output)
|
||||||
|
}
|
||||||
|
|
||||||
|
func retrieveConfigDump(podName, podNamespace, bindAddress string, includeEds bool) ([]byte, error) {
|
||||||
if podNamespace == "" {
|
if podNamespace == "" {
|
||||||
return nil, fmt.Errorf("pod namespace is required")
|
return nil, fmt.Errorf("pod namespace is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
if podName == "" || len(args) == 0 {
|
if podName == "" {
|
||||||
c, err := kubernetes.NewCLIClient(options.DefaultConfigFlags.ToRawKubeConfigLoader())
|
c, err := kubernetes.NewCLIClient(options.DefaultConfigFlags.ToRawKubeConfigLoader())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to build kubernetes client: %w", err)
|
return nil, fmt.Errorf("failed to build kubernetes client: %w", err)
|
||||||
@@ -65,7 +100,7 @@ func retrieveConfigDump(args []string, includeEds bool) ([]byte, error) {
|
|||||||
fw, err := portForwarder(types.NamespacedName{
|
fw, err := portForwarder(types.NamespacedName{
|
||||||
Namespace: podNamespace,
|
Namespace: podNamespace,
|
||||||
Name: podName,
|
Name: podName,
|
||||||
})
|
}, bindAddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -82,7 +117,7 @@ func retrieveConfigDump(args []string, includeEds bool) ([]byte, error) {
|
|||||||
return configDump, nil
|
return configDump, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func portForwarder(nn types.NamespacedName) (kubernetes.PortForwarder, error) {
|
func portForwarder(nn types.NamespacedName, bindAddress string) (kubernetes.PortForwarder, error) {
|
||||||
c, err := kubernetes.NewCLIClient(options.DefaultConfigFlags.ToRawKubeConfigLoader())
|
c, err := kubernetes.NewCLIClient(options.DefaultConfigFlags.ToRawKubeConfigLoader())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("build CLI client fail: %w", err)
|
return nil, fmt.Errorf("build CLI client fail: %w", err)
|
||||||
@@ -149,3 +184,53 @@ func configDumpRequest(address string, includeEds bool) ([]byte, error) {
|
|||||||
|
|
||||||
return io.ReadAll(resp.Body)
|
return io.ReadAll(resp.Body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getXDSResource(resourceType EnvoyConfigType, configDump []byte) (any, error) {
|
||||||
|
cd := map[string]any{}
|
||||||
|
if err := json.Unmarshal(configDump, &cd); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if resourceType == AllEnvoyConfigType {
|
||||||
|
return cd, nil
|
||||||
|
}
|
||||||
|
configs := cd["configs"]
|
||||||
|
globalConfigs := configs.([]any)
|
||||||
|
|
||||||
|
switch resourceType {
|
||||||
|
case BootstrapEnvoyConfigType:
|
||||||
|
for _, config := range globalConfigs {
|
||||||
|
if config.(map[string]interface{})["@type"] == "type.googleapis.com/envoy.admin.v3.BootstrapConfigDump" {
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case EndpointEnvoyConfigType:
|
||||||
|
for _, config := range globalConfigs {
|
||||||
|
if config.(map[string]interface{})["@type"] == "type.googleapis.com/envoy.admin.v3.EndpointsConfigDump" {
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case ClusterEnvoyConfigType:
|
||||||
|
for _, config := range globalConfigs {
|
||||||
|
if config.(map[string]interface{})["@type"] == "type.googleapis.com/envoy.admin.v3.ClustersConfigDump" {
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case ListenerEnvoyConfigType:
|
||||||
|
for _, config := range globalConfigs {
|
||||||
|
if config.(map[string]interface{})["@type"] == "type.googleapis.com/envoy.admin.v3.ListenersConfigDump" {
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case RouteEnvoyConfigType:
|
||||||
|
for _, config := range globalConfigs {
|
||||||
|
if config.(map[string]interface{})["@type"] == "type.googleapis.com/envoy.admin.v3.RoutesConfigDump" {
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown resourceType %s", resourceType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("unknown resourceType %s", resourceType)
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package hgctl
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -109,7 +109,7 @@ func TestExtractAllConfigDump(t *testing.T) {
|
|||||||
t.Run(tc.output, func(t *testing.T) {
|
t.Run(tc.output, func(t *testing.T) {
|
||||||
configDump, err := fetchGatewayConfig(fw, true)
|
configDump, err := fetchGatewayConfig(fw, true)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
data, err := GetXDSResource(AllEnvoyConfigType, configDump)
|
data, err := getXDSResource(AllEnvoyConfigType, configDump)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
got, err := formatGatewayConfig(data, tc.output)
|
got, err := formatGatewayConfig(data, tc.output)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@@ -137,7 +137,7 @@ func TestExtractSubResourcesConfigDump(t *testing.T) {
|
|||||||
cases := []struct {
|
cases := []struct {
|
||||||
output string
|
output string
|
||||||
expected string
|
expected string
|
||||||
resourceType envoyConfigType
|
resourceType EnvoyConfigType
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
output: "json",
|
output: "json",
|
||||||
@@ -192,7 +192,7 @@ func TestExtractSubResourcesConfigDump(t *testing.T) {
|
|||||||
t.Run(tc.output, func(t *testing.T) {
|
t.Run(tc.output, func(t *testing.T) {
|
||||||
configDump, err := fetchGatewayConfig(fw, false)
|
configDump, err := fetchGatewayConfig(fw, false)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
resource, err := GetXDSResource(tc.resourceType, configDump)
|
resource, err := getXDSResource(tc.resourceType, configDump)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
got, err := formatGatewayConfig(resource, tc.output)
|
got, err := formatGatewayConfig(resource, tc.output)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
3
go.mod
3
go.mod
@@ -238,6 +238,8 @@ require (
|
|||||||
github.com/spf13/cast v1.3.1 // indirect
|
github.com/spf13/cast v1.3.1 // indirect
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||||
github.com/theupdateframework/notary v0.7.0 // indirect
|
github.com/theupdateframework/notary v0.7.0 // indirect
|
||||||
|
github.com/tidwall/match v1.1.1 // indirect
|
||||||
|
github.com/tidwall/pretty v1.2.0 // indirect
|
||||||
github.com/tonistiigi/fsutil v0.0.0-20220930225714-4638ad635be5 // indirect
|
github.com/tonistiigi/fsutil v0.0.0-20220930225714-4638ad635be5 // indirect
|
||||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
|
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
|
||||||
github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3 // indirect
|
github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3 // indirect
|
||||||
@@ -302,6 +304,7 @@ require (
|
|||||||
github.com/evanphx/json-patch/v5 v5.6.0
|
github.com/evanphx/json-patch/v5 v5.6.0
|
||||||
github.com/google/yamlfmt v0.10.0
|
github.com/google/yamlfmt v0.10.0
|
||||||
github.com/kylelemons/godebug v1.1.0
|
github.com/kylelemons/godebug v1.1.0
|
||||||
|
github.com/tidwall/gjson v1.17.0
|
||||||
helm.sh/helm/v3 v3.7.1
|
helm.sh/helm/v3 v3.7.1
|
||||||
k8s.io/apiextensions-apiserver v0.25.4
|
k8s.io/apiextensions-apiserver v0.25.4
|
||||||
knative.dev/networking v0.0.0-20220302134042-e8b2eb995165
|
knative.dev/networking v0.0.0-20220302134042-e8b2eb995165
|
||||||
|
|||||||
6
go.sum
6
go.sum
@@ -1582,6 +1582,12 @@ github.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIU
|
|||||||
github.com/theupdateframework/notary v0.6.1/go.mod h1:MOfgIfmox8s7/7fduvB2xyPPMJCrjRLRizA8OFwpnKY=
|
github.com/theupdateframework/notary v0.6.1/go.mod h1:MOfgIfmox8s7/7fduvB2xyPPMJCrjRLRizA8OFwpnKY=
|
||||||
github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c=
|
github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c=
|
||||||
github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw=
|
github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw=
|
||||||
|
github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
|
||||||
|
github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
|
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||||
|
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||||
|
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||||
|
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ package hgctl
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/alibaba/higress/cmd/hgctl/config"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||||
)
|
)
|
||||||
@@ -45,21 +46,20 @@ func bootstrapConfigCmd() *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runBootstrapConfig(c *cobra.Command, args []string) error {
|
func runBootstrapConfig(c *cobra.Command, args []string) error {
|
||||||
configDump, err := retrieveConfigDump(args, false)
|
if len(args) != 0 {
|
||||||
|
podName = args[0]
|
||||||
|
}
|
||||||
|
envoyConfig, err := config.GetEnvoyConfig(&config.GetEnvoyConfigOptions{
|
||||||
|
PodName: podName,
|
||||||
|
PodNamespace: podNamespace,
|
||||||
|
BindAddress: bindAddress,
|
||||||
|
Output: output,
|
||||||
|
EnvoyConfigType: config.BootstrapEnvoyConfigType,
|
||||||
|
IncludeEds: true,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
_, err = fmt.Fprintln(c.OutOrStdout(), string(envoyConfig))
|
||||||
bootstrap, err := GetXDSResource(BootstrapEnvoyConfigType, configDump)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
out, err := formatGatewayConfig(bootstrap, output)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = fmt.Fprintln(c.OutOrStdout(), string(out))
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,11 +11,13 @@
|
|||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package hgctl
|
package hgctl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/alibaba/higress/cmd/hgctl/config"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||||
)
|
)
|
||||||
@@ -44,21 +46,20 @@ func clusterConfigCmd() *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runClusterConfig(c *cobra.Command, args []string) error {
|
func runClusterConfig(c *cobra.Command, args []string) error {
|
||||||
configDump, err := retrieveConfigDump(args, false)
|
if len(args) != 0 {
|
||||||
|
podName = args[0]
|
||||||
|
}
|
||||||
|
envoyConfig, err := config.GetEnvoyConfig(&config.GetEnvoyConfigOptions{
|
||||||
|
PodName: podName,
|
||||||
|
PodNamespace: podNamespace,
|
||||||
|
BindAddress: bindAddress,
|
||||||
|
Output: output,
|
||||||
|
EnvoyConfigType: config.ClusterEnvoyConfigType,
|
||||||
|
IncludeEds: true,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
_, err = fmt.Fprintln(c.OutOrStdout(), string(envoyConfig))
|
||||||
cluster, err := GetXDSResource(ClusterEnvoyConfigType, configDump)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
out, err := formatGatewayConfig(cluster, output)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = fmt.Fprintln(c.OutOrStdout(), string(out))
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,11 +17,23 @@ package hgctl
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/alibaba/higress/cmd/hgctl/config"
|
||||||
"github.com/alibaba/higress/pkg/cmd/options"
|
"github.com/alibaba/higress/pkg/cmd/options"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
output string
|
||||||
|
podName string
|
||||||
|
podNamespace string
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultProxyAdminPort = 15000
|
||||||
|
containerName = "envoy"
|
||||||
|
)
|
||||||
|
|
||||||
func newConfigCommand() *cobra.Command {
|
func newConfigCommand() *cobra.Command {
|
||||||
cfgCommand := &cobra.Command{
|
cfgCommand := &cobra.Command{
|
||||||
Use: "gateway-config",
|
Use: "gateway-config",
|
||||||
@@ -69,11 +81,20 @@ func allConfigCmd() *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runAllConfig(c *cobra.Command, args []string) error {
|
func runAllConfig(c *cobra.Command, args []string) error {
|
||||||
configDump, err := retrieveConfigDump(args, true)
|
if len(args) != 0 {
|
||||||
|
podName = args[0]
|
||||||
|
}
|
||||||
|
envoyConfig, err := config.GetEnvoyConfig(&config.GetEnvoyConfigOptions{
|
||||||
|
PodName: podName,
|
||||||
|
PodNamespace: podNamespace,
|
||||||
|
BindAddress: bindAddress,
|
||||||
|
Output: output,
|
||||||
|
EnvoyConfigType: config.AllEnvoyConfigType,
|
||||||
|
IncludeEds: true,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
_, err = fmt.Fprintln(c.OutOrStdout(), string(envoyConfig))
|
||||||
_, err = fmt.Fprintln(c.OutOrStdout(), string(configDump))
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ package hgctl
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/alibaba/higress/cmd/hgctl/config"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||||
)
|
)
|
||||||
@@ -45,21 +46,20 @@ func endpointConfigCmd() *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runEndpointConfig(c *cobra.Command, args []string) error {
|
func runEndpointConfig(c *cobra.Command, args []string) error {
|
||||||
configDump, err := retrieveConfigDump(args, true)
|
if len(args) != 0 {
|
||||||
|
podName = args[0]
|
||||||
|
}
|
||||||
|
envoyConfig, err := config.GetEnvoyConfig(&config.GetEnvoyConfigOptions{
|
||||||
|
PodName: podName,
|
||||||
|
PodNamespace: podNamespace,
|
||||||
|
BindAddress: bindAddress,
|
||||||
|
Output: output,
|
||||||
|
EnvoyConfigType: config.EndpointEnvoyConfigType,
|
||||||
|
IncludeEds: true,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
_, err = fmt.Fprintln(c.OutOrStdout(), string(envoyConfig))
|
||||||
endpoint, err := GetXDSResource(EndpointEnvoyConfigType, configDump)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
out, err := formatGatewayConfig(endpoint, output)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = fmt.Fprintln(c.OutOrStdout(), string(out))
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ package hgctl
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/alibaba/higress/cmd/hgctl/config"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||||
)
|
)
|
||||||
@@ -45,21 +46,20 @@ func listenerConfigCmd() *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runListenerConfig(c *cobra.Command, args []string) error {
|
func runListenerConfig(c *cobra.Command, args []string) error {
|
||||||
configDump, err := retrieveConfigDump(args, false)
|
if len(args) != 0 {
|
||||||
|
podName = args[0]
|
||||||
|
}
|
||||||
|
envoyConfig, err := config.GetEnvoyConfig(&config.GetEnvoyConfigOptions{
|
||||||
|
PodName: podName,
|
||||||
|
PodNamespace: podNamespace,
|
||||||
|
BindAddress: bindAddress,
|
||||||
|
Output: output,
|
||||||
|
EnvoyConfigType: config.ListenerEnvoyConfigType,
|
||||||
|
IncludeEds: true,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
_, err = fmt.Fprintln(c.OutOrStdout(), string(envoyConfig))
|
||||||
listener, err := GetXDSResource(ListenerEnvoyConfigType, configDump)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
out, err := formatGatewayConfig(listener, output)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = fmt.Fprintln(c.OutOrStdout(), string(out))
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ package hgctl
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/alibaba/higress/cmd/hgctl/config"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||||
)
|
)
|
||||||
@@ -45,21 +46,20 @@ func routeConfigCmd() *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runRouteConfig(c *cobra.Command, args []string) error {
|
func runRouteConfig(c *cobra.Command, args []string) error {
|
||||||
configDump, err := retrieveConfigDump(args, false)
|
if len(args) != 0 {
|
||||||
|
podName = args[0]
|
||||||
|
}
|
||||||
|
envoyConfig, err := config.GetEnvoyConfig(&config.GetEnvoyConfigOptions{
|
||||||
|
PodName: podName,
|
||||||
|
PodNamespace: podNamespace,
|
||||||
|
BindAddress: bindAddress,
|
||||||
|
Output: output,
|
||||||
|
EnvoyConfigType: config.RouteEnvoyConfigType,
|
||||||
|
IncludeEds: true,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
_, err = fmt.Fprintln(c.OutOrStdout(), string(envoyConfig))
|
||||||
route, err := GetXDSResource(RouteEnvoyConfigType, configDump)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
out, err := formatGatewayConfig(route, output)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = fmt.Fprintln(c.OutOrStdout(), string(out))
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/alibaba/higress/pkg/ingress/kube/configmap"
|
"github.com/alibaba/higress/pkg/ingress/kube/configmap"
|
||||||
|
"github.com/alibaba/higress/test/e2e/conformance/utils/envoy"
|
||||||
"github.com/alibaba/higress/test/e2e/conformance/utils/http"
|
"github.com/alibaba/higress/test/e2e/conformance/utils/http"
|
||||||
"github.com/alibaba/higress/test/e2e/conformance/utils/kubernetes"
|
"github.com/alibaba/higress/test/e2e/conformance/utils/kubernetes"
|
||||||
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
|
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
|
||||||
@@ -25,6 +26,266 @@ import (
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
Register(ConfigmapGzip)
|
Register(ConfigmapGzip)
|
||||||
|
Register(ConfigMapGzipEnvoy)
|
||||||
|
}
|
||||||
|
|
||||||
|
var testCases = []struct {
|
||||||
|
higressConfig *configmap.HigressConfig
|
||||||
|
envoyAssertion envoy.Assertion
|
||||||
|
httpAssert http.Assertion
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
higressConfig: &configmap.HigressConfig{
|
||||||
|
Gzip: &configmap.Gzip{
|
||||||
|
Enable: false,
|
||||||
|
MinContentLength: 1024,
|
||||||
|
ContentType: []string{"text/html", "text/css", "text/plain", "text/xml", "application/json", "application/javascript", "application/xhtml+xml", "image/svg+xml"},
|
||||||
|
DisableOnEtagHeader: true,
|
||||||
|
MemoryLevel: 5,
|
||||||
|
WindowBits: 12,
|
||||||
|
ChunkSize: 4096,
|
||||||
|
CompressionLevel: "BEST_COMPRESSION",
|
||||||
|
CompressionStrategy: "DEFAULT_STRATEGY",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
httpAssert: http.Assertion{
|
||||||
|
Meta: http.AssertionMeta{
|
||||||
|
TestCaseName: "case1: disable gzip output",
|
||||||
|
TargetBackend: "web-backend",
|
||||||
|
TargetNamespace: "higress-conformance-infra",
|
||||||
|
},
|
||||||
|
Request: http.AssertionRequest{
|
||||||
|
ActualRequest: http.Request{
|
||||||
|
Host: "foo.com",
|
||||||
|
Path: "/foo",
|
||||||
|
Method: "GET",
|
||||||
|
Headers: map[string]string{
|
||||||
|
"Accept-Encoding": "*",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Response: http.AssertionResponse{
|
||||||
|
ExpectedResponseNoRequest: true,
|
||||||
|
ExpectedResponse: http.Response{
|
||||||
|
StatusCode: 200,
|
||||||
|
AbsentHeaders: []string{"content-encoding"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
envoyAssertion: envoy.Assertion{
|
||||||
|
Path: "configs.#.dynamic_listeners.#.active_state.listener.filter_chains",
|
||||||
|
TargetNamespace: "higress-system",
|
||||||
|
CheckType: envoy.CheckTypeNotExist,
|
||||||
|
ExpectEnvoyConfig: map[string]interface{}{
|
||||||
|
"memory_level": 5,
|
||||||
|
"compression_level": "COMPRESSION_LEVEL_9",
|
||||||
|
"window_bits": 12,
|
||||||
|
"min_content_length": 1024,
|
||||||
|
"disable_on_etag_header": true,
|
||||||
|
"content_type": []interface{}{
|
||||||
|
"text/html",
|
||||||
|
"text/css",
|
||||||
|
"text/plain",
|
||||||
|
"text/xml",
|
||||||
|
"application/json",
|
||||||
|
"application/javascript",
|
||||||
|
"application/xhtml+xml",
|
||||||
|
"image/svg+xml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
higressConfig: &configmap.HigressConfig{
|
||||||
|
Gzip: &configmap.Gzip{
|
||||||
|
Enable: true,
|
||||||
|
MinContentLength: 100,
|
||||||
|
ContentType: []string{"text/html", "text/css", "text/plain", "text/xml", "application/json", "application/javascript", "application/xhtml+xml", "image/svg+xml"},
|
||||||
|
DisableOnEtagHeader: true,
|
||||||
|
MemoryLevel: 5,
|
||||||
|
WindowBits: 12,
|
||||||
|
ChunkSize: 4096,
|
||||||
|
CompressionLevel: "BEST_COMPRESSION",
|
||||||
|
CompressionStrategy: "DEFAULT_STRATEGY",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
httpAssert: http.Assertion{
|
||||||
|
Meta: http.AssertionMeta{
|
||||||
|
TestCaseName: "case2: enable gzip output",
|
||||||
|
TargetBackend: "web-backend",
|
||||||
|
TargetNamespace: "higress-conformance-infra",
|
||||||
|
},
|
||||||
|
Request: http.AssertionRequest{
|
||||||
|
ActualRequest: http.Request{
|
||||||
|
Host: "foo.com",
|
||||||
|
Path: "/foo",
|
||||||
|
Method: "GET",
|
||||||
|
Headers: map[string]string{
|
||||||
|
"Accept-Encoding": "*",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Response: http.AssertionResponse{
|
||||||
|
ExpectedResponseNoRequest: true,
|
||||||
|
ExpectedResponse: http.Response{
|
||||||
|
StatusCode: 200,
|
||||||
|
},
|
||||||
|
AdditionalResponseHeaders: map[string]string{"content-encoding": "gzip"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
envoyAssertion: envoy.Assertion{
|
||||||
|
Path: "configs.#.dynamic_listeners.#.active_state.listener.filter_chains",
|
||||||
|
TargetNamespace: "higress-system",
|
||||||
|
CheckType: envoy.CheckTypeExist,
|
||||||
|
ExpectEnvoyConfig: map[string]interface{}{
|
||||||
|
"name": "envoy.filters.network.http_connection_manager",
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
|
||||||
|
"stat_prefix": "outbound_0.0.0.0_80",
|
||||||
|
"memory_level": 5,
|
||||||
|
"compression_level": "COMPRESSION_LEVEL_9",
|
||||||
|
"window_bits": 12,
|
||||||
|
"min_content_length": 100,
|
||||||
|
"disable_on_etag_header": true,
|
||||||
|
"content_type": []interface{}{
|
||||||
|
"text/html",
|
||||||
|
"text/css",
|
||||||
|
"text/plain",
|
||||||
|
"text/xml",
|
||||||
|
"application/json",
|
||||||
|
"application/javascript",
|
||||||
|
"application/xhtml+xml",
|
||||||
|
"image/svg+xml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
higressConfig: &configmap.HigressConfig{
|
||||||
|
Gzip: &configmap.Gzip{
|
||||||
|
Enable: true,
|
||||||
|
MinContentLength: 4096,
|
||||||
|
ContentType: []string{"text/html", "text/css", "text/plain", "text/xml", "application/json", "application/javascript", "application/xhtml+xml", "image/svg+xml"},
|
||||||
|
DisableOnEtagHeader: true,
|
||||||
|
MemoryLevel: 5,
|
||||||
|
WindowBits: 12,
|
||||||
|
ChunkSize: 4096,
|
||||||
|
CompressionLevel: "BEST_COMPRESSION",
|
||||||
|
CompressionStrategy: "DEFAULT_STRATEGY",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
httpAssert: http.Assertion{
|
||||||
|
Meta: http.AssertionMeta{
|
||||||
|
TestCaseName: "case3: disable gzip output because content length less hhan 4096 ",
|
||||||
|
TargetBackend: "web-backend",
|
||||||
|
TargetNamespace: "higress-conformance-infra",
|
||||||
|
},
|
||||||
|
Request: http.AssertionRequest{
|
||||||
|
ActualRequest: http.Request{
|
||||||
|
Host: "foo.com",
|
||||||
|
Path: "/foo",
|
||||||
|
Method: "GET",
|
||||||
|
Headers: map[string]string{
|
||||||
|
"Accept-Encoding": "*",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Response: http.AssertionResponse{
|
||||||
|
ExpectedResponseNoRequest: true,
|
||||||
|
ExpectedResponse: http.Response{
|
||||||
|
StatusCode: 200,
|
||||||
|
AbsentHeaders: []string{"content-encoding"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
envoyAssertion: envoy.Assertion{
|
||||||
|
Path: "configs.#.dynamic_listeners.#.active_state.listener.filter_chains",
|
||||||
|
TargetNamespace: "higress-system",
|
||||||
|
CheckType: envoy.CheckTypeExist,
|
||||||
|
ExpectEnvoyConfig: map[string]interface{}{
|
||||||
|
"name": "envoy.filters.network.http_connection_manager",
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
|
||||||
|
"stat_prefix": "outbound_0.0.0.0_80",
|
||||||
|
"memory_level": 5,
|
||||||
|
"compression_level": "COMPRESSION_LEVEL_9",
|
||||||
|
"window_bits": 12,
|
||||||
|
"min_content_length": 4096,
|
||||||
|
"disable_on_etag_header": true,
|
||||||
|
"content_type": []interface{}{
|
||||||
|
"text/html",
|
||||||
|
"text/css",
|
||||||
|
"text/plain",
|
||||||
|
"text/xml",
|
||||||
|
"application/json",
|
||||||
|
"application/javascript",
|
||||||
|
"application/xhtml+xml",
|
||||||
|
"image/svg+xml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
higressConfig: &configmap.HigressConfig{
|
||||||
|
Gzip: &configmap.Gzip{
|
||||||
|
Enable: true,
|
||||||
|
MinContentLength: 100,
|
||||||
|
ContentType: []string{"text/html", "text/css", "text/plain", "text/xml", "application/javascript", "application/xhtml+xml", "image/svg+xml"},
|
||||||
|
DisableOnEtagHeader: true,
|
||||||
|
MemoryLevel: 5,
|
||||||
|
WindowBits: 12,
|
||||||
|
ChunkSize: 4096,
|
||||||
|
CompressionLevel: "BEST_COMPRESSION",
|
||||||
|
CompressionStrategy: "DEFAULT_STRATEGY",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
httpAssert: http.Assertion{
|
||||||
|
Meta: http.AssertionMeta{
|
||||||
|
TestCaseName: "case4: disable gzip output because application/json missed in content types ",
|
||||||
|
TargetBackend: "web-backend",
|
||||||
|
TargetNamespace: "higress-conformance-infra",
|
||||||
|
},
|
||||||
|
Request: http.AssertionRequest{
|
||||||
|
ActualRequest: http.Request{
|
||||||
|
Host: "foo.com",
|
||||||
|
Path: "/foo",
|
||||||
|
Method: "GET",
|
||||||
|
Headers: map[string]string{
|
||||||
|
"Accept-Encoding": "*",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Response: http.AssertionResponse{
|
||||||
|
ExpectedResponseNoRequest: true,
|
||||||
|
ExpectedResponse: http.Response{
|
||||||
|
StatusCode: 200,
|
||||||
|
AbsentHeaders: []string{"content-encoding"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
envoyAssertion: envoy.Assertion{
|
||||||
|
Path: "configs.#.dynamic_listeners.#.active_state.listener.filter_chains",
|
||||||
|
TargetNamespace: "higress-system",
|
||||||
|
CheckType: envoy.CheckTypeExist,
|
||||||
|
ExpectEnvoyConfig: map[string]interface{}{
|
||||||
|
"name": "envoy.filters.network.http_connection_manager",
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
|
||||||
|
"stat_prefix": "outbound_0.0.0.0_80",
|
||||||
|
"memory_level": 5,
|
||||||
|
"compression_level": "COMPRESSION_LEVEL_9",
|
||||||
|
"window_bits": 12,
|
||||||
|
"min_content_length": 100,
|
||||||
|
"disable_on_etag_header": true,
|
||||||
|
"content_type": []interface{}{
|
||||||
|
"text/html",
|
||||||
|
"text/css",
|
||||||
|
"text/plain",
|
||||||
|
"text/xml",
|
||||||
|
"application/javascript",
|
||||||
|
"application/xhtml+xml",
|
||||||
|
"image/svg+xml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var ConfigmapGzip = suite.ConformanceTest{
|
var ConfigmapGzip = suite.ConformanceTest{
|
||||||
@@ -33,171 +294,9 @@ var ConfigmapGzip = suite.ConformanceTest{
|
|||||||
Manifests: []string{"tests/configmap-gzip.yaml"},
|
Manifests: []string{"tests/configmap-gzip.yaml"},
|
||||||
Features: []suite.SupportedFeature{suite.HTTPConformanceFeature},
|
Features: []suite.SupportedFeature{suite.HTTPConformanceFeature},
|
||||||
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||||
testcases := []struct {
|
|
||||||
higressConfig *configmap.HigressConfig
|
|
||||||
httpAssert http.Assertion
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
higressConfig: &configmap.HigressConfig{
|
|
||||||
Gzip: &configmap.Gzip{
|
|
||||||
Enable: false,
|
|
||||||
MinContentLength: 1024,
|
|
||||||
ContentType: []string{"text/html", "text/css", "text/plain", "text/xml", "application/json", "application/javascript", "application/xhtml+xml", "image/svg+xml"},
|
|
||||||
DisableOnEtagHeader: true,
|
|
||||||
MemoryLevel: 5,
|
|
||||||
WindowBits: 12,
|
|
||||||
ChunkSize: 4096,
|
|
||||||
CompressionLevel: "BEST_COMPRESSION",
|
|
||||||
CompressionStrategy: "DEFAULT_STRATEGY",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
httpAssert: http.Assertion{
|
|
||||||
Meta: http.AssertionMeta{
|
|
||||||
TestCaseName: "case1: disable gzip output",
|
|
||||||
TargetBackend: "web-backend",
|
|
||||||
TargetNamespace: "higress-conformance-infra",
|
|
||||||
},
|
|
||||||
Request: http.AssertionRequest{
|
|
||||||
ActualRequest: http.Request{
|
|
||||||
Host: "foo.com",
|
|
||||||
Path: "/foo",
|
|
||||||
Method: "GET",
|
|
||||||
Headers: map[string]string{
|
|
||||||
"Accept-Encoding": "*",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Response: http.AssertionResponse{
|
|
||||||
ExpectedResponseNoRequest: true,
|
|
||||||
ExpectedResponse: http.Response{
|
|
||||||
StatusCode: 200,
|
|
||||||
AbsentHeaders: []string{"content-encoding"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
higressConfig: &configmap.HigressConfig{
|
|
||||||
Gzip: &configmap.Gzip{
|
|
||||||
Enable: true,
|
|
||||||
MinContentLength: 100,
|
|
||||||
ContentType: []string{"text/html", "text/css", "text/plain", "text/xml", "application/json", "application/javascript", "application/xhtml+xml", "image/svg+xml"},
|
|
||||||
DisableOnEtagHeader: true,
|
|
||||||
MemoryLevel: 5,
|
|
||||||
WindowBits: 12,
|
|
||||||
ChunkSize: 4096,
|
|
||||||
CompressionLevel: "BEST_COMPRESSION",
|
|
||||||
CompressionStrategy: "DEFAULT_STRATEGY",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
httpAssert: http.Assertion{
|
|
||||||
Meta: http.AssertionMeta{
|
|
||||||
TestCaseName: "case2: enable gzip output",
|
|
||||||
TargetBackend: "web-backend",
|
|
||||||
TargetNamespace: "higress-conformance-infra",
|
|
||||||
},
|
|
||||||
Request: http.AssertionRequest{
|
|
||||||
ActualRequest: http.Request{
|
|
||||||
Host: "foo.com",
|
|
||||||
Path: "/foo",
|
|
||||||
Method: "GET",
|
|
||||||
Headers: map[string]string{
|
|
||||||
"Accept-Encoding": "*",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Response: http.AssertionResponse{
|
|
||||||
ExpectedResponseNoRequest: true,
|
|
||||||
ExpectedResponse: http.Response{
|
|
||||||
StatusCode: 200,
|
|
||||||
},
|
|
||||||
AdditionalResponseHeaders: map[string]string{"content-encoding": "gzip"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
higressConfig: &configmap.HigressConfig{
|
|
||||||
Gzip: &configmap.Gzip{
|
|
||||||
Enable: true,
|
|
||||||
MinContentLength: 4096,
|
|
||||||
ContentType: []string{"text/html", "text/css", "text/plain", "text/xml", "application/json", "application/javascript", "application/xhtml+xml", "image/svg+xml"},
|
|
||||||
DisableOnEtagHeader: true,
|
|
||||||
MemoryLevel: 5,
|
|
||||||
WindowBits: 12,
|
|
||||||
ChunkSize: 4096,
|
|
||||||
CompressionLevel: "BEST_COMPRESSION",
|
|
||||||
CompressionStrategy: "DEFAULT_STRATEGY",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
httpAssert: http.Assertion{
|
|
||||||
Meta: http.AssertionMeta{
|
|
||||||
TestCaseName: "case3: disable gzip output because content length less hhan 4096 ",
|
|
||||||
TargetBackend: "web-backend",
|
|
||||||
TargetNamespace: "higress-conformance-infra",
|
|
||||||
},
|
|
||||||
Request: http.AssertionRequest{
|
|
||||||
ActualRequest: http.Request{
|
|
||||||
Host: "foo.com",
|
|
||||||
Path: "/foo",
|
|
||||||
Method: "GET",
|
|
||||||
Headers: map[string]string{
|
|
||||||
"Accept-Encoding": "*",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Response: http.AssertionResponse{
|
|
||||||
ExpectedResponseNoRequest: true,
|
|
||||||
ExpectedResponse: http.Response{
|
|
||||||
StatusCode: 200,
|
|
||||||
AbsentHeaders: []string{"content-encoding"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
higressConfig: &configmap.HigressConfig{
|
|
||||||
Gzip: &configmap.Gzip{
|
|
||||||
Enable: true,
|
|
||||||
MinContentLength: 100,
|
|
||||||
ContentType: []string{"text/html", "text/css", "text/plain", "text/xml", "application/javascript", "application/xhtml+xml", "image/svg+xml"},
|
|
||||||
DisableOnEtagHeader: true,
|
|
||||||
MemoryLevel: 5,
|
|
||||||
WindowBits: 12,
|
|
||||||
ChunkSize: 4096,
|
|
||||||
CompressionLevel: "BEST_COMPRESSION",
|
|
||||||
CompressionStrategy: "DEFAULT_STRATEGY",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
httpAssert: http.Assertion{
|
|
||||||
Meta: http.AssertionMeta{
|
|
||||||
TestCaseName: "case4: disable gzip output because application/json missed in content types ",
|
|
||||||
TargetBackend: "web-backend",
|
|
||||||
TargetNamespace: "higress-conformance-infra",
|
|
||||||
},
|
|
||||||
Request: http.AssertionRequest{
|
|
||||||
ActualRequest: http.Request{
|
|
||||||
Host: "foo.com",
|
|
||||||
Path: "/foo",
|
|
||||||
Method: "GET",
|
|
||||||
Headers: map[string]string{
|
|
||||||
"Accept-Encoding": "*",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Response: http.AssertionResponse{
|
|
||||||
ExpectedResponseNoRequest: true,
|
|
||||||
ExpectedResponse: http.Response{
|
|
||||||
StatusCode: 200,
|
|
||||||
AbsentHeaders: []string{"content-encoding"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("Configmap Gzip", func(t *testing.T) {
|
t.Run("Configmap Gzip", func(t *testing.T) {
|
||||||
for _, testcase := range testcases {
|
for _, testcase := range testCases {
|
||||||
err := kubernetes.ApplyConfigmapDataWithYaml(suite.Client, "higress-system", "higress-config", "higress", testcase.higressConfig)
|
err := kubernetes.ApplyConfigmapDataWithYaml(t, suite.Client, "higress-system", "higress-config", "higress", testcase.higressConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("can't apply conifgmap %s in namespace %s for data key %s", "higress-config", "higress-system", "higress")
|
t.Fatalf("can't apply conifgmap %s in namespace %s for data key %s", "higress-config", "higress-system", "higress")
|
||||||
}
|
}
|
||||||
@@ -206,3 +305,22 @@ var ConfigmapGzip = suite.ConformanceTest{
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ConfigMapGzipEnvoy = suite.ConformanceTest{
|
||||||
|
ShortName: "ConfigMapGzipEnvoy",
|
||||||
|
Description: "The Envoy config should contain gzip config",
|
||||||
|
Manifests: []string{"tests/configmap-gzip.yaml"},
|
||||||
|
Features: []suite.SupportedFeature{suite.EnvoyConfigConformanceFeature},
|
||||||
|
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
|
||||||
|
t.Run("ConfigMap Gzip Envoy", func(t *testing.T) {
|
||||||
|
for _, testcase := range testCases {
|
||||||
|
// apply config
|
||||||
|
err := kubernetes.ApplyConfigmapDataWithYaml(t, suite.Client, "higress-system", "higress-config", "higress", testcase.higressConfig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("can't apply conifgmap %s in namespace %s for data key %s", "higress-config", "higress-system", "higress")
|
||||||
|
}
|
||||||
|
envoy.AssertEnvoyConfig(t, suite.TimeoutConfig, testcase.envoyAssertion)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|||||||
@@ -29,4 +29,5 @@ spec:
|
|||||||
service:
|
service:
|
||||||
name: infra-backend-v3
|
name: infra-backend-v3
|
||||||
port:
|
port:
|
||||||
number: 8080
|
number: 8080
|
||||||
|
|
||||||
|
|||||||
287
test/e2e/conformance/utils/envoy/envoy.go
Normal file
287
test/e2e/conformance/utils/envoy/envoy.go
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2022 The Kubernetes Authors.
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package envoy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/alibaba/higress/cmd/hgctl/config"
|
||||||
|
cfg "github.com/alibaba/higress/test/e2e/conformance/utils/config"
|
||||||
|
"github.com/tidwall/gjson"
|
||||||
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CheckType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// CheckTypeMatch checks if the actual value matches the expected value.
|
||||||
|
CheckTypeMatch CheckType = "match"
|
||||||
|
// CheckTypeExist checks if the actual value exists.
|
||||||
|
CheckTypeExist CheckType = "exist"
|
||||||
|
// CheckTypeNotExist checks if the actual value does not exist.
|
||||||
|
CheckTypeNotExist CheckType = "notexist"
|
||||||
|
|
||||||
|
// defaultSuccessThreshold is the default number of times the assertion must succeed in a row.
|
||||||
|
defaultSuccessThreshold = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// Assertion defines the assertion to be made on the Envoy config.
|
||||||
|
// TODO: It can support localization judgment so that this configuration check function will be more universal.
|
||||||
|
// TODO: Can be used for general e2e tests, rather than just envoy filter scenarios.
|
||||||
|
type Assertion struct {
|
||||||
|
// Path is the path of gjson to the value to be asserted.
|
||||||
|
Path string
|
||||||
|
// CheckType is the type of assertion to be made.
|
||||||
|
CheckType CheckType
|
||||||
|
// ExpectEnvoyConfig is the expected value of the Envoy config.
|
||||||
|
ExpectEnvoyConfig map[string]interface{}
|
||||||
|
// TargetNamespace is the namespace of the Envoy pod.
|
||||||
|
TargetNamespace string
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertEnvoyConfig asserts the Envoy config.
|
||||||
|
func AssertEnvoyConfig(t *testing.T, timeoutConfig cfg.TimeoutConfig, expected Assertion) {
|
||||||
|
options := config.NewDefaultGetEnvoyConfigOptions()
|
||||||
|
options.PodNamespace = expected.TargetNamespace
|
||||||
|
waitForEnvoyConfig(t, timeoutConfig, options, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
// waitForEnvoyConfig waits for the Envoy config to be ready and asserts it.
|
||||||
|
func waitForEnvoyConfig(t *testing.T, timeoutConfig cfg.TimeoutConfig, options *config.GetEnvoyConfigOptions, expected Assertion) {
|
||||||
|
awaitConvergence(t, defaultSuccessThreshold, timeoutConfig.MaxTimeToConsistency, func(elapsed time.Duration) bool {
|
||||||
|
allEnvoyConfig := ""
|
||||||
|
err := wait.Poll(1*time.Second, 10*time.Second, func() (bool, error) {
|
||||||
|
out, err := config.GetEnvoyConfig(options)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
allEnvoyConfig = string(out)
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
switch expected.CheckType {
|
||||||
|
case CheckTypeMatch:
|
||||||
|
err = assertEnvoyConfigMatch(t, allEnvoyConfig, expected)
|
||||||
|
case CheckTypeExist:
|
||||||
|
err = assertEnvoyConfigExist(t, allEnvoyConfig, expected)
|
||||||
|
case CheckTypeNotExist:
|
||||||
|
err = assertEnvoyConfigNotExist(t, allEnvoyConfig, expected)
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("unsupported check type %s", expected.CheckType)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
t.Logf("✅ Envoy config checked")
|
||||||
|
}
|
||||||
|
|
||||||
|
// assertEnvoyConfigNotExist asserts the Envoy config does not exist.
|
||||||
|
func assertEnvoyConfigNotExist(t *testing.T, envoyConfig string, expected Assertion) error {
|
||||||
|
result := gjson.Get(envoyConfig, expected.Path).Value()
|
||||||
|
if result == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if !findMustNotExist(t, result, expected.ExpectEnvoyConfig) {
|
||||||
|
return fmt.Errorf("the expected value %s exists in path '%s'", expected.ExpectEnvoyConfig, expected.Path)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// assertEnvoyConfigExist asserts the Envoy config exists.
|
||||||
|
func assertEnvoyConfigExist(t *testing.T, envoyConfig string, expected Assertion) error {
|
||||||
|
result := gjson.Get(envoyConfig, expected.Path).Value()
|
||||||
|
if result == nil {
|
||||||
|
return fmt.Errorf("failed to get value from path '%s'", expected.Path)
|
||||||
|
}
|
||||||
|
if !findMustExist(t, result, expected.ExpectEnvoyConfig) {
|
||||||
|
return fmt.Errorf("the expected value %s does not exist in path '%s'", expected.ExpectEnvoyConfig, expected.Path)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// assertEnvoyConfigMatch asserts the Envoy config matches the expected value.
|
||||||
|
func assertEnvoyConfigMatch(t *testing.T, envoyConfig string, expected Assertion) error {
|
||||||
|
result := gjson.Get(envoyConfig, expected.Path).Value()
|
||||||
|
if result == nil {
|
||||||
|
return fmt.Errorf("failed to get value from path '%s'", expected.Path)
|
||||||
|
}
|
||||||
|
if !match(t, result, expected.ExpectEnvoyConfig) {
|
||||||
|
return fmt.Errorf("failed to match value from path '%s'", expected.Path)
|
||||||
|
}
|
||||||
|
t.Logf("✅ Matched value %s in path '%s'", expected.ExpectEnvoyConfig, expected.Path)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// awaitConvergence runs the given function until it returns 'true' `threshold` times in a row.
|
||||||
|
// Each failed attempt has a 1s delay; successful attempts have no delay.
|
||||||
|
func awaitConvergence(t *testing.T, threshold int, maxTimeToConsistency time.Duration, fn func(elapsed time.Duration) bool) {
|
||||||
|
successes := 0
|
||||||
|
attempts := 0
|
||||||
|
start := time.Now()
|
||||||
|
to := time.After(maxTimeToConsistency)
|
||||||
|
delay := time.Second
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-to:
|
||||||
|
t.Fatalf("timeout while waiting after %d attempts", attempts)
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
completed := fn(time.Now().Sub(start))
|
||||||
|
attempts++
|
||||||
|
if completed {
|
||||||
|
successes++
|
||||||
|
if successes >= threshold {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Skip delay if we have a success
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
successes = 0
|
||||||
|
select {
|
||||||
|
// Capture the overall timeout
|
||||||
|
case <-to:
|
||||||
|
t.Fatalf("timeout while waiting after %d attempts, %d/%d sucessess", attempts, successes, threshold)
|
||||||
|
// And the per-try delay
|
||||||
|
case <-time.After(delay):
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// match
|
||||||
|
// 1. interface{} is a slice: if one of the slice elements matches, the assertion passes
|
||||||
|
// Notice: can recursively find slices
|
||||||
|
// 2. interface{} is a map: if all the map elements match, the assertion passes
|
||||||
|
// 3. interface{} is a field: if the field matches, the assertion passes
|
||||||
|
func match(t *testing.T, actual interface{}, expected map[string]interface{}) bool {
|
||||||
|
reflectValue := reflect.ValueOf(actual)
|
||||||
|
kind := reflectValue.Kind()
|
||||||
|
switch kind {
|
||||||
|
case reflect.Slice:
|
||||||
|
actualValueSlice := actual.([]interface{})
|
||||||
|
for _, v := range actualValueSlice {
|
||||||
|
if match(t, v, expected) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
case reflect.Map:
|
||||||
|
actualValueMap := actual.(map[string]interface{})
|
||||||
|
for key, expectValue := range expected {
|
||||||
|
actualValue, ok := actualValueMap[key]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(actualValue, expectValue) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return reflect.DeepEqual(actual, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// findMustExist finds the value of the given path in the given Envoy config.
|
||||||
|
func findMustExist(t *testing.T, actual interface{}, expected map[string]interface{}) bool {
|
||||||
|
for key, expectValue := range expected {
|
||||||
|
// If the key does not exist, the assertion fails.
|
||||||
|
t.Logf("🔍 Finding key %s", key)
|
||||||
|
if !findKey(actual, key, expectValue) {
|
||||||
|
t.Logf("❌ Not found key %s", key)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
t.Logf("✅ Found key %s", key)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// findMustNotExist finds the value of the given path in the given Envoy config.
|
||||||
|
func findMustNotExist(t *testing.T, actual interface{}, expected map[string]interface{}) bool {
|
||||||
|
for key, expectValue := range expected {
|
||||||
|
// If the key exists, the assertion fails.
|
||||||
|
t.Logf("🔍 Finding key %s", key)
|
||||||
|
if findKey(actual, key, expectValue) {
|
||||||
|
t.Logf("❌ Found key %s", key)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
t.Logf("✅ Not found key %s", key)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// findKey finds the value of the given key in the given Envoy config.
|
||||||
|
func findKey(actual interface{}, key string, expectValue interface{}) bool {
|
||||||
|
reflectValue := reflect.ValueOf(actual)
|
||||||
|
kind := reflectValue.Kind()
|
||||||
|
switch kind {
|
||||||
|
case reflect.Slice:
|
||||||
|
actualValueSlice := actual.([]interface{})
|
||||||
|
for _, v := range actualValueSlice {
|
||||||
|
if findKey(v, key, expectValue) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
case reflect.Map:
|
||||||
|
actualValueMap := actual.(map[string]interface{})
|
||||||
|
for actualKey, actualValue := range actualValueMap {
|
||||||
|
if actualKey == key && reflect.DeepEqual(convertType(actualValue, expectValue), expectValue) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if findKey(actualValue, key, expectValue) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
if reflectValue.String() == key && reflect.DeepEqual(convertType(actual, expectValue), expectValue) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertType converts the type of the given value to the type of the given target value.
|
||||||
|
func convertType(value interface{}, targetType interface{}) interface{} {
|
||||||
|
targetTypeValue := reflect.ValueOf(targetType)
|
||||||
|
targetTypeKind := targetTypeValue.Kind()
|
||||||
|
|
||||||
|
switch targetTypeKind {
|
||||||
|
case reflect.Int:
|
||||||
|
switch value.(type) {
|
||||||
|
case int:
|
||||||
|
return value
|
||||||
|
case float64:
|
||||||
|
return int(value.(float64))
|
||||||
|
}
|
||||||
|
case reflect.Float64:
|
||||||
|
switch value.(type) {
|
||||||
|
case int:
|
||||||
|
return float64(value.(int))
|
||||||
|
case float64:
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
462
test/e2e/conformance/utils/envoy/envoy_test.go
Normal file
462
test/e2e/conformance/utils/envoy/envoy_test.go
Normal file
@@ -0,0 +1,462 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2022 The Kubernetes Authors.
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package envoy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_match(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
actual interface{}
|
||||||
|
expected map[string]interface{}
|
||||||
|
expectResult bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "case 1",
|
||||||
|
actual: []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "baz",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
expectResult: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "case 2",
|
||||||
|
actual: []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "baz",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: map[string]interface{}{
|
||||||
|
"foo": "bay",
|
||||||
|
},
|
||||||
|
expectResult: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "case 3",
|
||||||
|
actual: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
"bar": "baz",
|
||||||
|
"baz": "bay",
|
||||||
|
},
|
||||||
|
expected: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
expectResult: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "case 4",
|
||||||
|
actual: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
expected: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
expectResult: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "case 5",
|
||||||
|
actual: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
expected: map[string]interface{}{
|
||||||
|
"foo": "baz",
|
||||||
|
},
|
||||||
|
expectResult: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "case 6",
|
||||||
|
actual: []interface{}{
|
||||||
|
[]interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
expectResult: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "case 7",
|
||||||
|
actual: []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
[]interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "baz",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: map[string]interface{}{
|
||||||
|
"foo": "baz",
|
||||||
|
},
|
||||||
|
expectResult: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
|
result := match(t, testCase.actual, testCase.expected)
|
||||||
|
if result != testCase.expectResult {
|
||||||
|
t.Errorf("expected %v, got %v", testCase.expectResult, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_findMustExist(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
actual interface{}
|
||||||
|
expected map[string]interface{}
|
||||||
|
expectResult bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "case 1",
|
||||||
|
actual: []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
expectResult: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "case 2",
|
||||||
|
actual: []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: map[string]interface{}{
|
||||||
|
"foo": "baz",
|
||||||
|
},
|
||||||
|
expectResult: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "case 3",
|
||||||
|
actual: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
"bar": "baz",
|
||||||
|
"baz": "bay",
|
||||||
|
},
|
||||||
|
expected: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
expectResult: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "case 4",
|
||||||
|
actual: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
expected: map[string]interface{}{
|
||||||
|
"foo": "baz",
|
||||||
|
},
|
||||||
|
expectResult: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "case 5",
|
||||||
|
actual: []interface{}{
|
||||||
|
[]interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
expectResult: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "case 6",
|
||||||
|
actual: []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
[]interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "baz",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: map[string]interface{}{
|
||||||
|
"foo": "baz",
|
||||||
|
},
|
||||||
|
expectResult: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "case 7",
|
||||||
|
actual: []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
[]interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"test": "baz",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
"test": "baz",
|
||||||
|
},
|
||||||
|
expectResult: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "case 8",
|
||||||
|
actual: []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
"test": "baz",
|
||||||
|
},
|
||||||
|
[]interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "baz",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: map[string]interface{}{
|
||||||
|
"foo": "baz",
|
||||||
|
"test": "baz",
|
||||||
|
},
|
||||||
|
expectResult: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "case 9",
|
||||||
|
actual: []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
[]interface{}{
|
||||||
|
[]interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "baz",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: map[string]interface{}{
|
||||||
|
"foo": "baz",
|
||||||
|
},
|
||||||
|
expectResult: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "case 9",
|
||||||
|
actual: []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
[]interface{}{
|
||||||
|
[]interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "baz",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"content": []interface{}{
|
||||||
|
"one",
|
||||||
|
"two",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: map[string]interface{}{
|
||||||
|
"foo": "baz",
|
||||||
|
"content": []interface{}{
|
||||||
|
"one",
|
||||||
|
"two",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectResult: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
|
result := findMustExist(t, testCase.actual, testCase.expected)
|
||||||
|
if result != testCase.expectResult {
|
||||||
|
t.Errorf("expected %v, got %v", testCase.expectResult, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_findMustNotExist(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
actual interface{}
|
||||||
|
expected map[string]interface{}
|
||||||
|
expectResult bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "case 1",
|
||||||
|
actual: []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
expectResult: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "case 2",
|
||||||
|
actual: []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: map[string]interface{}{
|
||||||
|
"foo": "baz",
|
||||||
|
},
|
||||||
|
expectResult: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "case 3",
|
||||||
|
actual: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
"bar": "baz",
|
||||||
|
"baz": "bay",
|
||||||
|
},
|
||||||
|
expected: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
expectResult: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "case 4",
|
||||||
|
actual: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
expected: map[string]interface{}{
|
||||||
|
"foo": "baz",
|
||||||
|
},
|
||||||
|
expectResult: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "case 5",
|
||||||
|
actual: []interface{}{
|
||||||
|
[]interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
expectResult: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "case 6",
|
||||||
|
actual: []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
[]interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "baz",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: map[string]interface{}{
|
||||||
|
"foo": "baz",
|
||||||
|
},
|
||||||
|
expectResult: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "case 7",
|
||||||
|
actual: []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
[]interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"test": "baz",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
expectResult: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "case 8",
|
||||||
|
actual: []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
"test": "baz",
|
||||||
|
},
|
||||||
|
[]interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "baz",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: map[string]interface{}{
|
||||||
|
"foo": "baz",
|
||||||
|
"test": "baz",
|
||||||
|
},
|
||||||
|
expectResult: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "case 9",
|
||||||
|
actual: []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
[]interface{}{
|
||||||
|
[]interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "baz",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: map[string]interface{}{
|
||||||
|
"foo": "baz",
|
||||||
|
},
|
||||||
|
expectResult: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
|
result := findMustNotExist(t, testCase.actual, testCase.expected)
|
||||||
|
if result != testCase.expectResult {
|
||||||
|
t.Errorf("expected %v, got %v", testCase.expectResult, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,4 +26,5 @@ var (
|
|||||||
IsWasmPluginTest = flag.Bool("isWasmPluginTest", false, "Determine if run wasm plugin conformance test")
|
IsWasmPluginTest = flag.Bool("isWasmPluginTest", false, "Determine if run wasm plugin conformance test")
|
||||||
WasmPluginType = flag.String("wasmPluginType", "GO", "Define wasm plugin type, currently supports GO, CPP")
|
WasmPluginType = flag.String("wasmPluginType", "GO", "Define wasm plugin type, currently supports GO, CPP")
|
||||||
WasmPluginName = flag.String("wasmPluginName", "", "Define wasm plugin name")
|
WasmPluginName = flag.String("wasmPluginName", "", "Define wasm plugin name")
|
||||||
|
IsEnvoyConfigTest = flag.Bool("isEnvoyConfigTest", false, "Determine if run envoy config conformance test")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ func FindPodConditionInList(t *testing.T, conditions []v1.PodCondition, condName
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func ApplyConfigmapDataWithYaml(c client.Client, namespace string, name string, key string, val any) error {
|
func ApplyConfigmapDataWithYaml(t *testing.T, c client.Client, namespace string, name string, key string, val any) error {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
@@ -140,8 +140,11 @@ func ApplyConfigmapDataWithYaml(c client.Client, namespace string, name string,
|
|||||||
}
|
}
|
||||||
cm.Data[key] = data
|
cm.Data[key] = data
|
||||||
|
|
||||||
|
t.Logf("🏗 Updating %s %s", name, namespace)
|
||||||
|
|
||||||
if err := c.Update(ctx, cm); err != nil {
|
if err := c.Update(ctx, cm); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ const (
|
|||||||
EurekaConformanceFeature SupportedFeature = "eureka"
|
EurekaConformanceFeature SupportedFeature = "eureka"
|
||||||
ConsulConformanceFeature SupportedFeature = "consul"
|
ConsulConformanceFeature SupportedFeature = "consul"
|
||||||
NacosConformanceFeature SupportedFeature = "nacos"
|
NacosConformanceFeature SupportedFeature = "nacos"
|
||||||
|
|
||||||
|
// extended: envoy config
|
||||||
|
EnvoyConfigConformanceFeature SupportedFeature = "envoy-config"
|
||||||
)
|
)
|
||||||
|
|
||||||
var AllFeatures = sets.Set{}.
|
var AllFeatures = sets.Set{}.
|
||||||
@@ -38,7 +41,8 @@ var AllFeatures = sets.Set{}.
|
|||||||
Insert(string(DubboConformanceFeature)).
|
Insert(string(DubboConformanceFeature)).
|
||||||
Insert(string(EurekaConformanceFeature)).
|
Insert(string(EurekaConformanceFeature)).
|
||||||
Insert(string(ConsulConformanceFeature)).
|
Insert(string(ConsulConformanceFeature)).
|
||||||
Insert(string(NacosConformanceFeature))
|
Insert(string(NacosConformanceFeature)).
|
||||||
|
Insert(string(EnvoyConfigConformanceFeature))
|
||||||
|
|
||||||
var ExperimentFeatures = sets.Set{}.
|
var ExperimentFeatures = sets.Set{}.
|
||||||
Insert(string(WASMGoConformanceFeature)).
|
Insert(string(WASMGoConformanceFeature)).
|
||||||
|
|||||||
@@ -60,6 +60,9 @@ type Options struct {
|
|||||||
// resources such as Gateways should be cleaned up after the run.
|
// resources such as Gateways should be cleaned up after the run.
|
||||||
CleanupBaseResources bool
|
CleanupBaseResources bool
|
||||||
TimeoutConfig config.TimeoutConfig
|
TimeoutConfig config.TimeoutConfig
|
||||||
|
|
||||||
|
// IsEnvoyConfigTest indicates whether or not the test is for envoy config
|
||||||
|
IsEnvoyConfigTest bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type WASMOptions struct {
|
type WASMOptions struct {
|
||||||
@@ -87,6 +90,8 @@ func New(s Options) *ConformanceTestSuite {
|
|||||||
} else {
|
} else {
|
||||||
s.SupportedFeatures.Insert(string(WASMGoConformanceFeature))
|
s.SupportedFeatures.Insert(string(WASMGoConformanceFeature))
|
||||||
}
|
}
|
||||||
|
} else if s.IsEnvoyConfigTest {
|
||||||
|
s.SupportedFeatures.Insert(string(EnvoyConfigConformanceFeature))
|
||||||
} else if s.EnableAllSupportedFeatures {
|
} else if s.EnableAllSupportedFeatures {
|
||||||
s.SupportedFeatures = AllFeatures
|
s.SupportedFeatures = AllFeatures
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ func TestHigressConformanceTests(t *testing.T) {
|
|||||||
},
|
},
|
||||||
GatewayAddress: "localhost",
|
GatewayAddress: "localhost",
|
||||||
EnableAllSupportedFeatures: true,
|
EnableAllSupportedFeatures: true,
|
||||||
|
IsEnvoyConfigTest: *flags.IsEnvoyConfigTest,
|
||||||
})
|
})
|
||||||
|
|
||||||
cSuite.Setup(t)
|
cSuite.Setup(t)
|
||||||
|
|||||||
Reference in New Issue
Block a user