...

Source file src/github.com/emissary-ingress/emissary/v3/cmd/entrypoint/testutil_fake_syntheticratelimit_test.go

Documentation: github.com/emissary-ingress/emissary/v3/cmd/entrypoint

     1  package entrypoint_test
     2  
     3  import (
     4  	"strings"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/assert"
     8  	"github.com/stretchr/testify/require"
     9  
    10  	"github.com/emissary-ingress/emissary/v3/cmd/entrypoint"
    11  	v3bootstrap "github.com/emissary-ingress/emissary/v3/pkg/api/envoy/config/bootstrap/v3"
    12  	v3cluster "github.com/emissary-ingress/emissary/v3/pkg/api/envoy/config/cluster/v3"
    13  	"github.com/emissary-ingress/emissary/v3/pkg/snapshot/v1"
    14  )
    15  
    16  // This predicate is used to check k8s snapshots for an RateLimitService matching the provided name and
    17  // namespace.
    18  func HasRateLimitService(namespace, name string) func(snapshot *snapshot.Snapshot) bool {
    19  	return func(snapshot *snapshot.Snapshot) bool {
    20  		for _, m := range snapshot.Kubernetes.RateLimitServices {
    21  			if m.Namespace == namespace && m.Name == name {
    22  				return true
    23  			}
    24  		}
    25  		return false
    26  	}
    27  }
    28  
    29  // Tests the synthetic rateLimit generation when a valid RateLimitService is created.  This RateLimitService has
    30  // `protocol_version: v3` and should not be replaced by the synthetic RateLimitService.
    31  func TestSyntheticRateLimitValid(t *testing.T) {
    32  	for _, apiVersion := range []string{"v2", "v3alpha1"} {
    33  		apiVersion := apiVersion // capture loop variable
    34  		t.Run(apiVersion, func(t *testing.T) {
    35  			t.Setenv("EDGE_STACK", "true")
    36  
    37  			f := entrypoint.RunFake(t, entrypoint.FakeConfig{EnvoyConfig: true}, nil)
    38  
    39  			err := f.UpsertYAML(`
    40  ---
    41  apiVersion: getambassador.io/` + apiVersion + `
    42  kind: RateLimitService
    43  metadata:
    44    name: edge-stack-ratelimit-test
    45    namespace: foo
    46  spec:
    47    protocol_version: "v3"
    48    service: 127.0.0.1:8500
    49  `)
    50  			assert.NoError(t, err)
    51  			f.Flush()
    52  
    53  			// Use the predicate above to check that the snapshot contains the
    54  			// RateLimitService defined above.  The RateLimitService has `protocol_version: v3` so
    55  			// it should not be removed/replaced by the synthetic RateLimitService injected
    56  			// by syntheticratelimit.go
    57  			snap, err := f.GetSnapshot(HasRateLimitService("foo", "edge-stack-ratelimit-test"))
    58  			assert.NoError(t, err)
    59  			assert.NotNil(t, snap)
    60  
    61  			// In edge-stack we should only ever have 1 RateLimitService.
    62  			assert.Equal(t, 1, len(snap.Kubernetes.RateLimitServices))
    63  			assert.Equal(t, "edge-stack-ratelimit-test", snap.Kubernetes.RateLimitServices[0].Name)
    64  
    65  			// Check for a cluster name matching the provided RateLimitService
    66  			isRateLimitCluster := func(c *v3cluster.Cluster) bool {
    67  				return strings.Contains(c.Name, "cluster_127_0_0_1_8500_foo")
    68  			}
    69  
    70  			// Grab the next Envoy config that has an Edge Stack ratelimit cluster on
    71  			// 127.0.0.1:8500
    72  			envoyConfig, err := f.GetEnvoyConfig(func(envoy *v3bootstrap.Bootstrap) bool {
    73  				return FindCluster(envoy, isRateLimitCluster) != nil
    74  			})
    75  			require.NoError(t, err)
    76  
    77  			// Make sure an Envoy Config containing a cluster for the
    78  			// RateLimitService that was defined.
    79  			assert.NotNil(t, envoyConfig)
    80  		})
    81  	}
    82  }
    83  
    84  // This tests with a provided RateLimitService that has no protocol_version (which defaults to v2).  It
    85  // should get forcibly overridden to be v3.
    86  func TestSyntheticRateLimitReplace(t *testing.T) {
    87  	for _, apiVersion := range []string{"v2", "v3alpha1"} {
    88  		apiVersion := apiVersion // capture loop variable
    89  		t.Run(apiVersion, func(t *testing.T) {
    90  			t.Setenv("EDGE_STACK", "true")
    91  
    92  			f := entrypoint.RunFake(t, entrypoint.FakeConfig{EnvoyConfig: true}, nil)
    93  
    94  			err := f.UpsertYAML(`
    95  ---
    96  apiVersion: getambassador.io/` + apiVersion + `
    97  kind: RateLimitService
    98  metadata:
    99    name: edge-stack-ratelimit-test
   100    namespace: foo
   101  spec:
   102    service: 127.0.0.1:8500
   103  `)
   104  			assert.NoError(t, err)
   105  			f.Flush()
   106  
   107  			// The RateLimitService does not have `protocol_version: v3` so it should be
   108  			// forcibly edited to say `protocol_version: v3` by syntheticratelimit.go
   109  			snap, err := f.GetSnapshot(HasRateLimitService("foo", "edge-stack-ratelimit-test"))
   110  			assert.NoError(t, err)
   111  			assert.NotNil(t, snap)
   112  
   113  			// In edge-stack we should only ever have 1 RateLimitService.
   114  			assert.Equal(t, 1, len(snap.Kubernetes.RateLimitServices))
   115  			// The snapshot should only have the one defined above.
   116  			assert.Equal(t, "edge-stack-ratelimit-test", snap.Kubernetes.RateLimitServices[0].Name)
   117  			// The protocol version should be forcibly set to v3.
   118  			assert.Equal(t, "v3", snap.Kubernetes.RateLimitServices[0].Spec.ProtocolVersion)
   119  
   120  			// Check for a cluster name matching the provided RateLimitService
   121  			isRateLimitCluster := func(c *v3cluster.Cluster) bool {
   122  				return strings.Contains(c.Name, "cluster_127_0_0_1_8500_foo")
   123  			}
   124  
   125  			// Grab the next Envoy config that has an Edge Stack rateLimit cluster on
   126  			// 127.0.0.1:8500
   127  			envoyConfig, err := f.GetEnvoyConfig(func(envoy *v3bootstrap.Bootstrap) bool {
   128  				return FindCluster(envoy, isRateLimitCluster) != nil
   129  			})
   130  			require.NoError(t, err)
   131  
   132  			// Make sure an Envoy Config containing a cluster for the
   133  			// RateLimitService that was defined.
   134  			assert.NotNil(t, envoyConfig)
   135  		})
   136  	}
   137  }
   138  
   139  // Tests the synthetic rateLimit generation when an invalid RateLimitService is created.  This RateLimitService has
   140  // `protocol_version: v3` and should not be replaced by the synthetic RateLimitService even though it has
   141  // a bogus value because the bogus field will be dropped when it is loaded, and we will be left with
   142  // a valid RateLimitService.
   143  func TestSyntheticRateLimitBogusField(t *testing.T) {
   144  	for _, apiVersion := range []string{"v2", "v3alpha1"} {
   145  		apiVersion := apiVersion // capture loop variable
   146  		t.Run(apiVersion, func(t *testing.T) {
   147  			t.Setenv("EDGE_STACK", "true")
   148  
   149  			f := entrypoint.RunFake(t, entrypoint.FakeConfig{EnvoyConfig: true}, nil)
   150  
   151  			err := f.UpsertYAML(`
   152  ---
   153  apiVersion: getambassador.io/` + apiVersion + `
   154  kind: RateLimitService
   155  metadata:
   156    name: edge-stack-ratelimit-test
   157    namespace: foo
   158  spec:
   159    service: 127.0.0.1:8500
   160    bogus_field: "foo"
   161  `)
   162  			assert.NoError(t, err)
   163  			f.Flush()
   164  
   165  			// Use the predicate above to check that the snapshot contains the
   166  			// RateLimitService defined above.  The RateLimitService has `protocol_version: v3` so
   167  			// it should not be removed/replaced by the synthetic RateLimitService injected
   168  			// by syntheticratelimit.go
   169  			snap, err := f.GetSnapshot(HasRateLimitService("foo", "edge-stack-ratelimit-test"))
   170  			assert.NoError(t, err)
   171  			assert.NotNil(t, snap)
   172  
   173  			// In edge-stack we should only ever have 1 RateLimitService.
   174  			assert.Equal(t, 1, len(snap.Kubernetes.RateLimitServices))
   175  			assert.Equal(t, "edge-stack-ratelimit-test", snap.Kubernetes.RateLimitServices[0].Name)
   176  
   177  			// Check for a cluster name matching the provided RateLimitService
   178  			isRateLimitCluster := func(c *v3cluster.Cluster) bool {
   179  				return strings.Contains(c.Name, "cluster_127_0_0_1_8500_foo")
   180  			}
   181  
   182  			// Grab the next Envoy config that has an Edge Stack rateLimit cluster on
   183  			// 127.0.0.1:8500
   184  			envoyConfig, err := f.GetEnvoyConfig(func(envoy *v3bootstrap.Bootstrap) bool {
   185  				return FindCluster(envoy, isRateLimitCluster) != nil
   186  			})
   187  			require.NoError(t, err)
   188  
   189  			// Make sure an Envoy Config containing a cluster for the
   190  			// RateLimitService that was defined.
   191  			assert.NotNil(t, envoyConfig)
   192  		})
   193  	}
   194  }
   195  
   196  // Tests the synthetic rateLimit generation when an invalid RateLimitService (because the protocol_version is
   197  // invalid for the supported enums).  This RateLimitService should be tossed out and the synthetic
   198  // RateLimitService should be injected.
   199  func TestSyntheticRateLimitInvalidProtocolVer(t *testing.T) {
   200  	t.Setenv("EDGE_STACK", "true")
   201  
   202  	f := entrypoint.RunFake(t, entrypoint.FakeConfig{EnvoyConfig: true}, nil)
   203  
   204  	err := f.UpsertYAML(`
   205  ---
   206  apiVersion: getambassador.io/v2
   207  kind: RateLimitService
   208  metadata:
   209    name: edge-stack-ratelimit-test
   210    namespace: foo
   211  spec:
   212    protocol_version: "vBogus"
   213    bogus_field: "foo"
   214  `)
   215  	assert.NoError(t, err)
   216  	f.Flush()
   217  
   218  	// Use the predicate above to check that the snapshot contains the synthetic RateLimitService.
   219  	// The RateLimitService has `protocol_version: v3`, but it has a bogus field, so it should not be
   220  	// validated, and instead we inject the synthetic RateLimitService.
   221  	snap, err := f.GetSnapshot(HasRateLimitService("default", "synthetic_edge_stack_rate_limit"))
   222  	assert.NoError(t, err)
   223  	assert.NotNil(t, snap)
   224  
   225  	// In edge-stack we should only ever have 1 RateLimitService.
   226  	assert.Equal(t, 1, len(snap.Kubernetes.RateLimitServices))
   227  	// The snapshot should only have the synthetic RateLimitService and not the one defined above.
   228  	assert.Equal(t, "synthetic_edge_stack_rate_limit", snap.Kubernetes.RateLimitServices[0].Name)
   229  
   230  	// Check for a cluster name matching the provided RateLimitService
   231  	isRateLimitCluster := func(c *v3cluster.Cluster) bool {
   232  		return strings.Contains(c.Name, "cluster_127_0_0_1_8500_default")
   233  	}
   234  
   235  	// Grab the next Envoy config that has an Edge Stack rateLimit cluster on
   236  	// 127.0.0.1:8500
   237  	envoyConfig, err := f.GetEnvoyConfig(func(envoy *v3bootstrap.Bootstrap) bool {
   238  		return FindCluster(envoy, isRateLimitCluster) != nil
   239  	})
   240  	require.NoError(t, err)
   241  
   242  	// Make sure an Envoy Config containing a cluster for the
   243  	// RateLimitService that was defined.
   244  	assert.NotNil(t, envoyConfig)
   245  }
   246  
   247  // Tests the synthetic rateLimit generation when an invalid RateLimitService is created and edited several
   248  // times in succession.  After the config is edited several times, we should see that the final
   249  // result is our provided valid RateLimitService.  There should not be any duplicate RateLimitService
   250  // resources, and the synthetic RateLimitService that gets created when the first invalid RateLimitService is
   251  // applied should be removed when the final edit makes it a valid RateLimitService.
   252  func TestSyntheticRateLimitChurn(t *testing.T) {
   253  	t.Setenv("EDGE_STACK", "true")
   254  
   255  	f := entrypoint.RunFake(t, entrypoint.FakeConfig{EnvoyConfig: true}, nil)
   256  	f.AutoFlush(true)
   257  
   258  	err := f.UpsertYAML(`
   259  ---
   260  apiVersion: getambassador.io/v3alpha1
   261  kind: RateLimitService
   262  metadata:
   263    name: edge-stack-ratelimit-test
   264    namespace: foo
   265  spec:
   266    service: 127.0.0.1:8500
   267  `)
   268  	assert.NoError(t, err)
   269  	err = f.UpsertYAML(`
   270  ---
   271  apiVersion: getambassador.io/v3alpha1
   272  kind: RateLimitService
   273  metadata:
   274    name: edge-stack-ratelimit-test
   275    namespace: foo
   276  spec:
   277    service: 127.0.0.1:8500
   278    protocol_version: "v3"
   279  `)
   280  	assert.NoError(t, err)
   281  	err = f.UpsertYAML(`
   282  ---
   283  apiVersion: getambassador.io/v3alpha1
   284  kind: RateLimitService
   285  metadata:
   286    name: edge-stack-ratelimit-test
   287    namespace: foo
   288  spec:
   289    service: 127.0.0.1:8500
   290  `)
   291  	assert.NoError(t, err)
   292  	err = f.UpsertYAML(`
   293  ---
   294  apiVersion: getambassador.io/v3alpha1
   295  kind: RateLimitService
   296  metadata:
   297    name: edge-stack-ratelimit-test
   298    namespace: foo
   299  spec:
   300    service: 127.0.0.1:8500
   301    protocol_version: "v3"
   302  `)
   303  	assert.NoError(t, err)
   304  
   305  	// Use the predicate above to check that the snapshot contains the RateLimitService defined
   306  	// above.  The RateLimitService has `protocol_version: v3` so it should not be removed/replaced
   307  	// by the synthetic RateLimitService injected by syntheticratelimit.go
   308  	snap, err := f.GetSnapshot(HasRateLimitService("foo", "edge-stack-ratelimit-test"))
   309  	assert.NoError(t, err)
   310  	assert.NotNil(t, snap)
   311  
   312  	// In edge-stack we should only ever have 1 RateLimitService.
   313  	assert.Equal(t, 1, len(snap.Kubernetes.RateLimitServices))
   314  	// The snapshot should only have the synthetic RateLimitService and not the one defined above.
   315  	assert.Equal(t, "edge-stack-ratelimit-test", snap.Kubernetes.RateLimitServices[0].Name)
   316  
   317  	// Check for a cluster name matching the provided RateLimitService
   318  	isRateLimitCluster := func(c *v3cluster.Cluster) bool {
   319  		return strings.Contains(c.Name, "cluster_127_0_0_1_8500_foo")
   320  	}
   321  
   322  	// Grab the next Envoy config that has an Edge Stack rateLimit cluster on
   323  	// 127.0.0.1:8500
   324  	envoyConfig, err := f.GetEnvoyConfig(func(envoy *v3bootstrap.Bootstrap) bool {
   325  		return FindCluster(envoy, isRateLimitCluster) != nil
   326  	})
   327  	require.NoError(t, err)
   328  
   329  	// Make sure an Envoy Config containing a cluster for the
   330  	// RateLimitService that was defined.
   331  	assert.NotNil(t, envoyConfig)
   332  }
   333  
   334  // Tests the synthetic rateLimit generation by first creating an invalid RateLimitService and confirming that
   335  // the synthetic RateLimitService gets injected.  Afterwards, a valid RateLimitService is applied and we
   336  // expect the synthetic RateLimitService to be removed in favor of the new valid RateLimitService.
   337  func TestSyntheticRateLimitInjectAndRemove(t *testing.T) {
   338  	t.Setenv("EDGE_STACK", "true")
   339  
   340  	f := entrypoint.RunFake(t, entrypoint.FakeConfig{EnvoyConfig: true}, nil)
   341  	f.AutoFlush(true)
   342  
   343  	// This will cause a synthethic RateLimitService to be injected.
   344  	err := f.UpsertYAML(`
   345  ---
   346  apiVersion: getambassador.io/v3alpha1
   347  kind: RateLimitService
   348  metadata:
   349    name: edge-stack-ratelimit-test
   350    namespace: foo
   351  spec:
   352    service: 127.0.0.1:8500
   353    protocol_version: "vBogus"
   354  `)
   355  	assert.NoError(t, err)
   356  
   357  	// Use the predicate above to check that the snapshot contains the synthetic RateLimitService.
   358  	// The user-provided RateLimitService is invalid and so it should be ignored and instead we
   359  	// inject the synthetic RateLimitService.
   360  	snap, err := f.GetSnapshot(HasRateLimitService("default", "synthetic_edge_stack_rate_limit"))
   361  	assert.NoError(t, err)
   362  	assert.NotNil(t, snap)
   363  
   364  	// We should only have 1 RateLimitService.
   365  	assert.Equal(t, 1, len(snap.Kubernetes.RateLimitServices))
   366  	// The snapshot should only have the synthetic RateLimitService and not the one defined above.
   367  	assert.Equal(t, "synthetic_edge_stack_rate_limit", snap.Kubernetes.RateLimitServices[0].Name)
   368  
   369  	// Check for a cluster name matching the provided RateLimitService
   370  	isRateLimitCluster := func(c *v3cluster.Cluster) bool {
   371  		return strings.Contains(c.Name, "cluster_127_0_0_1_8500_default")
   372  	}
   373  
   374  	// Grab the next Envoy config that has an Edge Stack rateLimit cluster on
   375  	// 127.0.0.1:8500
   376  	envoyConfig, err := f.GetEnvoyConfig(func(envoy *v3bootstrap.Bootstrap) bool {
   377  		return FindCluster(envoy, isRateLimitCluster) != nil
   378  	})
   379  	require.NoError(t, err)
   380  
   381  	// Make sure an Envoy Config containing a cluster for the
   382  	// RateLimitService that was defined.
   383  	assert.NotNil(t, envoyConfig)
   384  
   385  	// Updating the yaml for that RateLimitService to include `protocol_version: v3` should make it
   386  	// valid and then remove our synthetic RateLimitService and allow the now valid RateLimitService to be
   387  	// used.
   388  	err = f.UpsertYAML(`
   389  ---
   390  apiVersion: getambassador.io/v3alpha1
   391  kind: RateLimitService
   392  metadata:
   393    name: edge-stack-ratelimit-test
   394    namespace: foo
   395  spec:
   396    service: 127.0.0.1:8500
   397    protocol_version: "v3"
   398  `)
   399  	assert.NoError(t, err)
   400  
   401  	// Use the predicate above to check that the snapshot contains the RateLimitService defined
   402  	// above.  The RateLimitService has `protocol_version: v3` so it should not be removed/replaced
   403  	// by the synthetic RateLimitService injected by syntheticratelimit.go
   404  	snap, err = f.GetSnapshot(HasRateLimitService("foo", "edge-stack-ratelimit-test"))
   405  	assert.NoError(t, err)
   406  	assert.NotNil(t, snap)
   407  
   408  	// In edge-stack we should only ever have 1 RateLimitService.
   409  	assert.Equal(t, 1, len(snap.Kubernetes.RateLimitServices))
   410  	assert.Equal(t, "edge-stack-ratelimit-test", snap.Kubernetes.RateLimitServices[0].Name)
   411  
   412  	// Check for a cluster name matching the provided RateLimitService
   413  	isRateLimitCluster = func(c *v3cluster.Cluster) bool {
   414  		return strings.Contains(c.Name, "cluster_127_0_0_1_8500_foo")
   415  	}
   416  
   417  	// Grab the next Envoy config that has an Edge Stack rateLimit cluster on
   418  	// 127.0.0.1:8500
   419  	envoyConfig, err = f.GetEnvoyConfig(func(envoy *v3bootstrap.Bootstrap) bool {
   420  		return FindCluster(envoy, isRateLimitCluster) != nil
   421  	})
   422  	require.NoError(t, err)
   423  
   424  	// Make sure an Envoy Config containing a cluster for the
   425  	// RateLimitService that was defined.
   426  	assert.NotNil(t, envoyConfig)
   427  }
   428  
   429  // This RateLimitService points at 127.0.0.1:8500, but it does not have `protocol_version: v3`.  It also
   430  // has additional fields set.  The correct action is to edit the RateLimitService to say
   431  // `protocol_version: v3`.
   432  func TestSyntheticRateLimitCopyFields(t *testing.T) {
   433  	t.Setenv("EDGE_STACK", "true")
   434  
   435  	f := entrypoint.RunFake(t, entrypoint.FakeConfig{EnvoyConfig: true}, nil)
   436  
   437  	err := f.UpsertYAML(`
   438  ---
   439  apiVersion: getambassador.io/v2
   440  kind: RateLimitService
   441  metadata:
   442    name: edge-stack-ratelimit-test
   443    namespace: foo
   444  spec:
   445    service: 127.0.0.1:8500
   446    timeout_ms: 12345
   447  `)
   448  	assert.NoError(t, err)
   449  	f.Flush()
   450  
   451  	// Use the predicate above to check that the snapshot contains the RateLimitService.
   452  	snap, err := f.GetSnapshot(HasRateLimitService("foo", "edge-stack-ratelimit-test"))
   453  	assert.NoError(t, err)
   454  	assert.NotNil(t, snap)
   455  
   456  	// In edge-stack we should only ever have 1 RateLimitService.
   457  	assert.Equal(t, 1, len(snap.Kubernetes.RateLimitServices))
   458  	// It should be that user-provided RateLimitService...
   459  	assert.Equal(t, "edge-stack-ratelimit-test", snap.Kubernetes.RateLimitServices[0].Name)
   460  	assert.Equal(t, int64(12345), snap.Kubernetes.RateLimitServices[0].Spec.Timeout.Duration.Milliseconds())
   461  	// ... but with `protocol_version: v3` set.
   462  	assert.Equal(t, "v3", snap.Kubernetes.RateLimitServices[0].Spec.ProtocolVersion)
   463  
   464  	// Check for a cluster name matching the provided RateLimitService
   465  	isRateLimitCluster := func(c *v3cluster.Cluster) bool {
   466  		return strings.Contains(c.Name, "cluster_127_0_0_1_8500_foo")
   467  	}
   468  
   469  	// Grab the next Envoy config that has an Edge Stack rateLimit cluster on
   470  	// 127.0.0.1:8500
   471  	envoyConfig, err := f.GetEnvoyConfig(func(envoy *v3bootstrap.Bootstrap) bool {
   472  		return FindCluster(envoy, isRateLimitCluster) != nil
   473  	})
   474  	require.NoError(t, err)
   475  
   476  	// Make sure an Envoy Config containing a cluster for the
   477  	// RateLimitService that was defined.
   478  	assert.NotNil(t, envoyConfig)
   479  }
   480  

View as plain text