diff --git a/internal/cmd/server.go b/internal/cmd/server.go index 27d3c447205..6028026004b 100644 --- a/internal/cmd/server.go +++ b/internal/cmd/server.go @@ -155,16 +155,19 @@ func setupRunners(cfg *config.Server) error { return err } - // Start the Global RateLimit Runner - // It subscribes to the xds Resources and translates it to Envoy Ratelimit Service - // infrastructure and configuration. - rateLimitRunner := ratelimitrunner.New(&ratelimitrunner.Config{ - Server: *cfg, - XdsIR: xdsIR, - RateLimitInfraIR: rateLimitInfraIR, - }) - if err := rateLimitRunner.Start(ctx); err != nil { - return err + // Start the global rateLimit runner if it has been enabled through the config + if cfg.EnvoyGateway.RateLimit != nil { + // Start the Global RateLimit Runner + // It subscribes to the xds Resources and translates it to Envoy Ratelimit Service + // infrastructure and configuration. + rateLimitRunner := ratelimitrunner.New(&ratelimitrunner.Config{ + Server: *cfg, + XdsIR: xdsIR, + RateLimitInfraIR: rateLimitInfraIR, + }) + if err := rateLimitRunner.Start(ctx); err != nil { + return err + } } // Wait until done diff --git a/internal/globalratelimit/runner/runner.go b/internal/globalratelimit/runner/runner.go index 46ccd96e229..0f1ed3c995c 100644 --- a/internal/globalratelimit/runner/runner.go +++ b/internal/globalratelimit/runner/runner.go @@ -70,11 +70,6 @@ func (r *Runner) subscribeAndTranslate(ctx context.Context) { func (r *Runner) translate(xdsIRs []*ir.Xds) (*ir.RateLimitInfra, error) { rlInfra := new(ir.RateLimitInfra) - // Return empty IR if ratelimit has not been enabled yet in the EnvoyGateway API - if r.EnvoyGateway.RateLimit == nil { - return nil, nil - } - for _, xdsIR := range xdsIRs { for _, listener := range xdsIR.HTTP { config := translator.BuildRateLimitServiceConfig(listener) diff --git a/internal/infrastructure/kubernetes/configmap.go b/internal/infrastructure/kubernetes/configmap.go index 4e8dcfec97d..b1501978902 100644 --- a/internal/infrastructure/kubernetes/configmap.go +++ b/internal/infrastructure/kubernetes/configmap.go @@ -45,8 +45,8 @@ var ( `"private_key":{"filename":"%s"}}}]}`, xdsTLSCertFilename, xdsTLSKeyFilename) ) -// expectedConfigMap returns the expected ConfigMap based on the provided infra. -func (i *Infra) expectedConfigMap(infra *ir.Infra) (*corev1.ConfigMap, error) { +// expectedProxyConfigMap returns the expected ConfigMap based on the provided infra. +func (i *Infra) expectedProxyConfigMap(infra *ir.Infra) (*corev1.ConfigMap, error) { // Set the labels based on the owning gateway name. labels := envoyLabels(infra.GetProxyInfra().GetProxyMetadata().Labels) if len(labels[gatewayapi.OwningGatewayNamespaceLabel]) == 0 || len(labels[gatewayapi.OwningGatewayNameLabel]) == 0 { @@ -56,7 +56,7 @@ func (i *Infra) expectedConfigMap(infra *ir.Infra) (*corev1.ConfigMap, error) { return &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Namespace: i.Namespace, - Name: expectedConfigMapName(infra.Proxy.Name), + Name: expectedProxyConfigMapName(infra.Proxy.Name), Labels: labels, }, Data: map[string]string{ @@ -66,48 +66,99 @@ func (i *Infra) expectedConfigMap(infra *ir.Infra) (*corev1.ConfigMap, error) { }, nil } -// createOrUpdateConfigMap creates a ConfigMap in the Kube api server based on the provided +// createOrUpdateProxyConfigMap creates a ConfigMap in the Kube api server based on the provided // infra, if it doesn't exist and updates it if it does. -func (i *Infra) createOrUpdateConfigMap(ctx context.Context, infra *ir.Infra) (*corev1.ConfigMap, error) { - cm, err := i.expectedConfigMap(infra) +func (i *Infra) createOrUpdateProxyConfigMap(ctx context.Context, infra *ir.Infra) error { + cm, err := i.expectedProxyConfigMap(infra) if err != nil { - return nil, err + return err } + return i.createOrUpdateConfigMap(ctx, cm) +} + +// deleteProxyConfigMap deletes the Envoy ConfigMap in the kube api server, if it exists. +func (i *Infra) deleteProxyConfigMap(ctx context.Context, infra *ir.Infra) error { + cm := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: i.Namespace, + Name: expectedProxyConfigMapName(infra.Proxy.Name), + }, + } + + return i.deleteConfigMap(ctx, cm) +} + +func expectedProxyConfigMapName(proxyName string) string { + cMapName := utils.GetHashedName(proxyName) + return fmt.Sprintf("%s-%s", config.EnvoyPrefix, cMapName) +} + +// expectedRateLimitConfigMap returns the expected ConfigMap based on the provided infra. +func (i *Infra) expectedRateLimitConfigMap(infra *ir.RateLimitInfra) *corev1.ConfigMap { + labels := rateLimitLabels() + data := make(map[string]string) + + for _, config := range infra.Configs { + data[config.Name] = config.Config + } + + return &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: i.Namespace, + Name: rateLimitInfraName, + Labels: labels, + }, + Data: data, + } +} + +// createOrUpdateRateLimitConfigMap creates a ConfigMap in the Kube api server based on the provided +// infra, if it doesn't exist and updates it if it does. +func (i *Infra) createOrUpdateRateLimitConfigMap(ctx context.Context, infra *ir.RateLimitInfra) error { + cm := i.expectedRateLimitConfigMap(infra) + return i.createOrUpdateConfigMap(ctx, cm) +} + +// deleteProxyConfigMap deletes the Envoy ConfigMap in the kube api server, if it exists. +func (i *Infra) deleteRateLimitConfigMap(ctx context.Context, _ *ir.RateLimitInfra) error { + cm := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: i.Namespace, + Name: rateLimitInfraName, + }, + } + + return i.deleteConfigMap(ctx, cm) +} + +func (i *Infra) createOrUpdateConfigMap(ctx context.Context, cm *corev1.ConfigMap) error { current := &corev1.ConfigMap{} key := types.NamespacedName{ - Namespace: i.Namespace, - Name: expectedConfigMapName(infra.Proxy.Name), + Namespace: cm.Namespace, + Name: cm.Name, } if err := i.Client.Get(ctx, key, current); err != nil { // Create if not found. if kerrors.IsNotFound(err) { if err := i.Client.Create(ctx, cm); err != nil { - return nil, fmt.Errorf("failed to create configmap %s/%s: %w", cm.Namespace, cm.Name, err) + return fmt.Errorf("failed to create configmap %s/%s: %w", cm.Namespace, cm.Name, err) } } } else { // Update if current value is different. if !reflect.DeepEqual(cm.Data, current.Data) { if err := i.Client.Update(ctx, cm); err != nil { - return nil, fmt.Errorf("failed to update configmap %s/%s: %w", cm.Namespace, cm.Name, err) + return fmt.Errorf("failed to update configmap %s/%s: %w", cm.Namespace, cm.Name, err) } } } - return cm, nil + return nil } -// deleteConfigMap deletes the Envoy ConfigMap in the kube api server, if it exists. -func (i *Infra) deleteConfigMap(ctx context.Context, infra *ir.Infra) error { - cm := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: i.Namespace, - Name: expectedConfigMapName(infra.Proxy.Name), - }, - } - +func (i *Infra) deleteConfigMap(ctx context.Context, cm *corev1.ConfigMap) error { if err := i.Client.Delete(ctx, cm); err != nil { if kerrors.IsNotFound(err) { return nil @@ -117,8 +168,3 @@ func (i *Infra) deleteConfigMap(ctx context.Context, infra *ir.Infra) error { return nil } - -func expectedConfigMapName(proxyName string) string { - cMapName := utils.GetHashedName(proxyName) - return fmt.Sprintf("%s-%s", config.EnvoyPrefix, cMapName) -} diff --git a/internal/infrastructure/kubernetes/configmap_test.go b/internal/infrastructure/kubernetes/configmap_test.go index c476d3a64b9..ec27b6f645a 100644 --- a/internal/infrastructure/kubernetes/configmap_test.go +++ b/internal/infrastructure/kubernetes/configmap_test.go @@ -14,6 +14,7 @@ import ( corev1 "k8s.io/api/core/v1" apiequality "k8s.io/apimachinery/pkg/api/equality" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" "github.com/envoyproxy/gateway/internal/envoygateway" @@ -22,7 +23,7 @@ import ( "github.com/envoyproxy/gateway/internal/ir" ) -func TestExpectedConfigMap(t *testing.T) { +func TestExpectedProxyConfigMap(t *testing.T) { // Setup the infra. cli := fakeclient.NewClientBuilder().WithScheme(envoygateway.GetScheme()).WithObjects().Build() cfg, err := config.New() @@ -35,13 +36,13 @@ func TestExpectedConfigMap(t *testing.T) { // An infra without Gateway owner labels should trigger // an error. - _, err = kube.expectedConfigMap(infra) + _, err = kube.expectedProxyConfigMap(infra) require.NotNil(t, err) infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNamespaceLabel] = "default" infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNameLabel] = infra.Proxy.Name - cm, err := kube.expectedConfigMap(infra) + cm, err := kube.expectedProxyConfigMap(infra) require.NoError(t, err) require.Equal(t, "envoy-test-74657374", cm.Name) @@ -57,7 +58,7 @@ func TestExpectedConfigMap(t *testing.T) { assert.True(t, apiequality.Semantic.DeepEqual(wantLabels, cm.Labels)) } -func TestCreateOrUpdateConfigMap(t *testing.T) { +func TestCreateOrUpdateProxyConfigMap(t *testing.T) { cfg, err := config.New() require.NoError(t, err) kube := NewInfra(nil, cfg) @@ -123,17 +124,22 @@ func TestCreateOrUpdateConfigMap(t *testing.T) { } else { kube.Client = fakeclient.NewClientBuilder().WithScheme(envoygateway.GetScheme()).Build() } - cm, err := kube.createOrUpdateConfigMap(context.Background(), infra) + err := kube.createOrUpdateProxyConfigMap(context.Background(), infra) require.NoError(t, err) - require.Equal(t, tc.expect.Namespace, cm.Namespace) - require.Equal(t, tc.expect.Name, cm.Name) - assert.True(t, apiequality.Semantic.DeepEqual(tc.expect.Labels, cm.Labels)) - require.Equal(t, tc.expect.Data, cm.Data) + actual := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: tc.expect.Namespace, + Name: tc.expect.Name, + }, + } + require.NoError(t, kube.Client.Get(context.Background(), client.ObjectKeyFromObject(actual), actual)) + require.Equal(t, tc.expect.Data, actual.Data) + assert.True(t, apiequality.Semantic.DeepEqual(tc.expect.Labels, actual.Labels)) }) } } -func TestDeleteConfigMap(t *testing.T) { +func TestDeleteConfigProxyMap(t *testing.T) { cfg, err := config.New() require.NoError(t, err) @@ -173,7 +179,7 @@ func TestDeleteConfigMap(t *testing.T) { t.Parallel() cli := fakeclient.NewClientBuilder().WithScheme(envoygateway.GetScheme()).WithObjects(tc.current).Build() kube := NewInfra(cli, cfg) - err := kube.deleteConfigMap(context.Background(), infra) + err := kube.deleteProxyConfigMap(context.Background(), infra) require.NoError(t, err) }) } diff --git a/internal/infrastructure/kubernetes/deployment.go b/internal/infrastructure/kubernetes/deployment.go index 350377d9f44..ba31cd62789 100644 --- a/internal/infrastructure/kubernetes/deployment.go +++ b/internal/infrastructure/kubernetes/deployment.go @@ -50,6 +50,13 @@ const ( envoyAdminPort = 19000 // envoyAdminAccessLogPath is the path used to expose admin access log. envoyAdminAccessLogPath = "/dev/null" + + // rateLimitInfraName is the name for rate-limit resources. + rateLimitInfraName = "envoy-ratelimit" + // rateLimitInfraHTTPPort is the http port that the rate limit service listens on. + rateLimitInfraHTTPPort = 8080 + // rateLimitInfraImage is the container image for the rate limit service. + rateLimitInfraImage = "envoyproxy/ratelimit:latest" ) //go:embed bootstrap.yaml.tpl @@ -100,14 +107,14 @@ func (b *bootstrapConfig) render() error { return nil } -func expectedDeploymentName(proxyName string) string { +func expectedProxyDeploymentName(proxyName string) string { deploymentName := utils.GetHashedName(proxyName) return fmt.Sprintf("%s-%s", config.EnvoyPrefix, deploymentName) } -// expectedDeployment returns the expected Deployment based on the provided infra. -func (i *Infra) expectedDeployment(infra *ir.Infra) (*appsv1.Deployment, error) { - containers, err := expectedContainers(infra) +// expectedProxyDeployment returns the expected Deployment based on the provided infra. +func (i *Infra) expectedProxyDeployment(infra *ir.Infra) (*appsv1.Deployment, error) { + containers, err := expectedProxyContainers(infra) if err != nil { return nil, err } @@ -118,6 +125,7 @@ func (i *Infra) expectedDeployment(infra *ir.Infra) (*appsv1.Deployment, error) return nil, fmt.Errorf("missing owning gateway labels") } + selector := getSelector(labels) // Get the EnvoyProxy config to configure the ret. provider := infra.GetProxyInfra().GetProxyConfig().GetProvider() if provider.Type != egcfgv1a1.ProviderTypeKubernetes { @@ -132,19 +140,19 @@ func (i *Infra) expectedDeployment(infra *ir.Infra) (*appsv1.Deployment, error) }, ObjectMeta: metav1.ObjectMeta{ Namespace: i.Namespace, - Name: expectedDeploymentName(infra.Proxy.Name), + Name: expectedProxyDeploymentName(infra.Proxy.Name), Labels: labels, }, Spec: appsv1.DeploymentSpec{ Replicas: deployCfg.Replicas, - Selector: envoySelector(infra.GetProxyInfra().GetProxyMetadata().Labels), + Selector: selector, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: envoySelector(infra.GetProxyInfra().GetProxyMetadata().Labels).MatchLabels, + Labels: selector.MatchLabels, }, Spec: corev1.PodSpec{ Containers: containers, - ServiceAccountName: expectedServiceAccountName(infra.Proxy.Name), + ServiceAccountName: expectedProxyServiceAccountName(infra.Proxy.Name), AutomountServiceAccountToken: pointer.Bool(false), TerminationGracePeriodSeconds: pointer.Int64(int64(300)), DNSPolicy: corev1.DNSClusterFirst, @@ -164,7 +172,7 @@ func (i *Infra) expectedDeployment(infra *ir.Infra) (*appsv1.Deployment, error) VolumeSource: corev1.VolumeSource{ ConfigMap: &corev1.ConfigMapVolumeSource{ LocalObjectReference: corev1.LocalObjectReference{ - Name: expectedConfigMapName(infra.Proxy.Name), + Name: expectedProxyConfigMapName(infra.Proxy.Name), }, Items: []corev1.KeyToPath{ { @@ -190,7 +198,7 @@ func (i *Infra) expectedDeployment(infra *ir.Infra) (*appsv1.Deployment, error) return ret, nil } -func expectedContainers(infra *ir.Infra) ([]corev1.Container, error) { +func expectedProxyContainers(infra *ir.Infra) ([]corev1.Container, error) { ports := []corev1.ContainerPort{ { Name: "http", @@ -275,20 +283,172 @@ func expectedContainers(infra *ir.Infra) ([]corev1.Container, error) { return containers, nil } -// createDeployment creates a Deployment in the kube api server based on the provided +// createOrUpdateProxyDeployment creates a Deployment in the kube api server based on the provided // infra, if it doesn't exist and updates it if it does. -func (i *Infra) createOrUpdateDeployment(ctx context.Context, infra *ir.Infra) error { - deploy, err := i.expectedDeployment(infra) +func (i *Infra) createOrUpdateProxyDeployment(ctx context.Context, infra *ir.Infra) error { + deploy, err := i.expectedProxyDeployment(infra) if err != nil { return err } + return i.createOrUpdateDeployment(ctx, deploy) +} + +// deleteProxyDeployment deletes the Envoy Deployment in the kube api server, if it exists. +func (i *Infra) deleteProxyDeployment(ctx context.Context, infra *ir.Infra) error { + deploy := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: i.Namespace, + Name: expectedProxyDeploymentName(infra.Proxy.Name), + }, + } + + return i.deleteDeployment(ctx, deploy) +} + +// expectedRateLimitDeployment returns the expected rate limit Deployment based on the provided infra. +func (i *Infra) expectedRateLimitDeployment(infra *ir.RateLimitInfra) *appsv1.Deployment { + containers := expectedRateLimitContainers(infra) + labels := rateLimitLabels() + selector := getSelector(labels) + + ret := &appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + Kind: "Deployment", + APIVersion: "apps/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: i.Namespace, + Name: rateLimitInfraName, + Labels: labels, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: pointer.Int32(int32(1)), + Selector: selector, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: selector.MatchLabels, + }, + Spec: corev1.PodSpec{ + Containers: containers, + ServiceAccountName: rateLimitInfraName, + AutomountServiceAccountToken: pointer.Bool(false), + TerminationGracePeriodSeconds: pointer.Int64(int64(300)), + DNSPolicy: corev1.DNSClusterFirst, + RestartPolicy: corev1.RestartPolicyAlways, + SchedulerName: "default-scheduler", + Volumes: []corev1.Volume{ + { + Name: rateLimitInfraName, + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: rateLimitInfraName, + }, + DefaultMode: pointer.Int32(int32(420)), + Optional: pointer.Bool(false), + }, + }, + }, + }, + }, + }, + }, + } + + return ret +} +func expectedRateLimitContainers(infra *ir.RateLimitInfra) []corev1.Container { + ports := []corev1.ContainerPort{ + { + Name: "http", + ContainerPort: rateLimitInfraHTTPPort, + Protocol: corev1.ProtocolTCP, + }, + } + + containers := []corev1.Container{ + { + Name: rateLimitInfraName, + Image: rateLimitInfraImage, + ImagePullPolicy: corev1.PullIfNotPresent, + Command: []string{ + "/bin/ratelimit", + }, + Env: []corev1.EnvVar{ + { + Name: "REDIS_SOCKET_TYPE", + Value: "tcp", + }, + { + Name: "REDIS_URL", + Value: infra.Backend.Redis.URL, + }, + { + Name: "RUNTIME_ROOT", + Value: "/data", + }, + {Name: "RUNTIME_SUBDIRECTORY", + Value: "ratelimit", + }, + { + Name: "RUNTIME_IGNOREDOTFILES", + Value: "true", + }, + { + Name: "RUNTIME_WATCH_ROOT", + Value: "false", + }, + { + Name: "LOG_LEVEL", + Value: "info", + }, + { + Name: "USE_STATSD", + Value: "false", + }, + }, + Ports: ports, + VolumeMounts: []corev1.VolumeMount{ + { + Name: rateLimitInfraName, + MountPath: "/data/ratelimit/config", + ReadOnly: true, + }, + }, + TerminationMessagePolicy: corev1.TerminationMessageReadFile, + TerminationMessagePath: "/dev/termination-log", + }, + } + + return containers +} + +// createOrUpdateRateLimitDeployment creates a Deployment in the kube api server based on the provided +// infra, if it doesn't exist and updates it if it does. +func (i *Infra) createOrUpdateRateLimitDeployment(ctx context.Context, infra *ir.RateLimitInfra) error { + deploy := i.expectedRateLimitDeployment(infra) + return i.createOrUpdateDeployment(ctx, deploy) +} + +// deleteRateLimitDeployment deletes the Envoy RateLimit Deployment in the kube api server, if it exists. +func (i *Infra) deleteRateLimitDeployment(ctx context.Context, _ *ir.RateLimitInfra) error { + deploy := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: i.Namespace, + Name: rateLimitInfraName, + }, + } + + return i.deleteDeployment(ctx, deploy) +} + +func (i *Infra) createOrUpdateDeployment(ctx context.Context, deploy *appsv1.Deployment) error { current := &appsv1.Deployment{} key := types.NamespacedName{ - Namespace: i.Namespace, - Name: expectedDeploymentName(infra.Proxy.Name), + Namespace: deploy.Namespace, + Name: deploy.Name, } - if err := i.Client.Get(ctx, key, current); err != nil { // Create if not found. if kerrors.IsNotFound(err) { @@ -310,21 +470,12 @@ func (i *Infra) createOrUpdateDeployment(ctx context.Context, infra *ir.Infra) e return nil } -// deleteDeployment deletes the Envoy Deployment in the kube api server, if it exists. -func (i *Infra) deleteDeployment(ctx context.Context, infra *ir.Infra) error { - deploy := &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: i.Namespace, - Name: expectedDeploymentName(infra.Proxy.Name), - }, - } - +func (i *Infra) deleteDeployment(ctx context.Context, deploy *appsv1.Deployment) error { if err := i.Client.Delete(ctx, deploy); err != nil { if kerrors.IsNotFound(err) { return nil } return fmt.Errorf("failed to delete deployment %s/%s: %w", deploy.Namespace, deploy.Name, err) } - return nil } diff --git a/internal/infrastructure/kubernetes/deployment_test.go b/internal/infrastructure/kubernetes/deployment_test.go index d71c87553ca..f812d641802 100644 --- a/internal/infrastructure/kubernetes/deployment_test.go +++ b/internal/infrastructure/kubernetes/deployment_test.go @@ -117,11 +117,11 @@ func TestExpectedDeployment(t *testing.T) { infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNamespaceLabel] = "default" infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNameLabel] = infra.Proxy.Name - deploy, err := kube.expectedDeployment(infra) + deploy, err := kube.expectedProxyDeployment(infra) require.NoError(t, err) // Check the deployment name is as expected. - assert.Equal(t, deploy.Name, expectedDeploymentName(infra.Proxy.Name)) + assert.Equal(t, deploy.Name, expectedProxyDeploymentName(infra.Proxy.Name)) // Check container details, i.e. env vars, labels, etc. for the deployment are as expected. container := checkContainer(t, deploy, envoyContainerName, true) @@ -158,7 +158,7 @@ func TestExpectedDeployment(t *testing.T) { repl := int32(2) infra.Proxy.GetProxyConfig().GetProvider().GetKubeResourceProvider().EnvoyDeployment.Replicas = &repl - deploy, err = kube.expectedDeployment(infra) + deploy, err = kube.expectedProxyDeployment(infra) require.NoError(t, err) // Check the number of replicas is as expected. @@ -185,7 +185,7 @@ func TestCreateOrUpdateDeployment(t *testing.T) { infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNamespaceLabel] = "default" infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNameLabel] = infra.Proxy.Name - deploy, err := kube.expectedDeployment(infra) + deploy, err := kube.expectedProxyDeployment(infra) require.NoError(t, err) testCases := []struct { @@ -233,13 +233,13 @@ func TestCreateOrUpdateDeployment(t *testing.T) { } else { kube.Client = fakeclient.NewClientBuilder().WithScheme(envoygateway.GetScheme()).Build() } - err := kube.createOrUpdateDeployment(context.Background(), tc.in) + err := kube.createOrUpdateProxyDeployment(context.Background(), tc.in) require.NoError(t, err) actual := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Namespace: kube.Namespace, - Name: expectedDeploymentName(tc.in.Proxy.Name), + Name: expectedProxyDeploymentName(tc.in.Proxy.Name), }, } require.NoError(t, kube.Client.Get(context.Background(), client.ObjectKeyFromObject(actual), actual)) @@ -248,7 +248,7 @@ func TestCreateOrUpdateDeployment(t *testing.T) { } } -func TestDeleteDeployment(t *testing.T) { +func TestDeleteProxyDeployment(t *testing.T) { testCases := []struct { name string expect bool @@ -268,7 +268,7 @@ func TestDeleteDeployment(t *testing.T) { Namespace: "test", } infra := ir.NewInfra() - err := kube.deleteDeployment(context.Background(), infra) + err := kube.deleteProxyDeployment(context.Background(), infra) require.NoError(t, err) }) } diff --git a/internal/infrastructure/kubernetes/infra.go b/internal/infrastructure/kubernetes/infra.go index efce6a7779d..3d297ea4e00 100644 --- a/internal/infrastructure/kubernetes/infra.go +++ b/internal/infrastructure/kubernetes/infra.go @@ -32,8 +32,8 @@ func NewInfra(cli client.Client, cfg *config.Server) *Infra { } } -// CreateOrUpdateInfra creates the managed kube infra, if it doesn't exist. -func (i *Infra) CreateOrUpdateInfra(ctx context.Context, infra *ir.Infra) error { +// CreateOrUpdateProxyInfra creates the managed kube infra, if it doesn't exist. +func (i *Infra) CreateOrUpdateProxyInfra(ctx context.Context, infra *ir.Infra) error { if infra == nil { return errors.New("infra ir is nil") } @@ -42,44 +42,44 @@ func (i *Infra) CreateOrUpdateInfra(ctx context.Context, infra *ir.Infra) error return errors.New("infra proxy ir is nil") } - if err := i.createOrUpdateServiceAccount(ctx, infra); err != nil { + if err := i.createOrUpdateProxyServiceAccount(ctx, infra); err != nil { return err } - if _, err := i.createOrUpdateConfigMap(ctx, infra); err != nil { + if err := i.createOrUpdateProxyConfigMap(ctx, infra); err != nil { return err } - if err := i.createOrUpdateDeployment(ctx, infra); err != nil { + if err := i.createOrUpdateProxyDeployment(ctx, infra); err != nil { return err } - if err := i.createOrUpdateService(ctx, infra); err != nil { + if err := i.createOrUpdateProxyService(ctx, infra); err != nil { return err } return nil } -// DeleteInfra removes the managed kube infra, if it doesn't exist. -func (i *Infra) DeleteInfra(ctx context.Context, infra *ir.Infra) error { +// DeleteProxyInfra removes the managed kube infra, if it doesn't exist. +func (i *Infra) DeleteProxyInfra(ctx context.Context, infra *ir.Infra) error { if infra == nil { return errors.New("infra ir is nil") } - if err := i.deleteService(ctx, infra); err != nil { + if err := i.deleteProxyService(ctx, infra); err != nil { return err } - if err := i.deleteDeployment(ctx, infra); err != nil { + if err := i.deleteProxyDeployment(ctx, infra); err != nil { return err } - if err := i.deleteConfigMap(ctx, infra); err != nil { + if err := i.deleteProxyConfigMap(ctx, infra); err != nil { return err } - if err := i.deleteServiceAccount(ctx, infra); err != nil { + if err := i.deleteProxyServiceAccount(ctx, infra); err != nil { return err } @@ -88,12 +88,50 @@ func (i *Infra) DeleteInfra(ctx context.Context, infra *ir.Infra) error { // CreateOrUpdateRateLimitInfra creates the managed kube rate limit infra, if it doesn't exist. func (i *Infra) CreateOrUpdateRateLimitInfra(ctx context.Context, infra *ir.RateLimitInfra) error { - // TODO + if infra == nil { + return errors.New("ratelimit infra ir is nil") + } + + if err := i.deleteRateLimitService(ctx, infra); err != nil { + return err + } + + if err := i.deleteRateLimitDeployment(ctx, infra); err != nil { + return err + } + + if err := i.deleteRateLimitConfigMap(ctx, infra); err != nil { + return err + } + + if err := i.deleteRateLimitServiceAccount(ctx, infra); err != nil { + return err + } + return nil } // DeleteRateLimitInfra removes the managed kube infra, if it doesn't exist. func (i *Infra) DeleteRateLimitInfra(ctx context.Context, infra *ir.RateLimitInfra) error { - // TODO + if infra == nil { + return errors.New("ratelimit infra ir is nil") + } + if err := i.createOrUpdateRateLimitServiceAccount(ctx, infra); err != nil { + return err + } + + if err := i.createOrUpdateRateLimitConfigMap(ctx, infra); err != nil { + return err + } + + if err := i.createOrUpdateRateLimitDeployment(ctx, infra); err != nil { + return err + } + + if err := i.createOrUpdateRateLimitService(ctx, infra); err != nil { + return err + } + return nil + } diff --git a/internal/infrastructure/kubernetes/infra_test.go b/internal/infrastructure/kubernetes/infra_test.go index 26f037a816e..5b8743e6b05 100644 --- a/internal/infrastructure/kubernetes/infra_test.go +++ b/internal/infrastructure/kubernetes/infra_test.go @@ -21,7 +21,7 @@ import ( "github.com/envoyproxy/gateway/internal/ir" ) -func TestCreateInfra(t *testing.T) { +func TestCreateProxyInfra(t *testing.T) { // Infra with Gateway owner labels. infraWithLabels := ir.NewInfra() infraWithLabels.GetProxyInfra().GetProxyMetadata().Labels = envoyAppLabel() @@ -66,7 +66,7 @@ func TestCreateInfra(t *testing.T) { Namespace: "default", } // Create or update the proxy infra. - err := kube.CreateOrUpdateInfra(context.Background(), tc.in) + err := kube.CreateOrUpdateProxyInfra(context.Background(), tc.in) if !tc.expect { require.Error(t, err) } else { @@ -76,7 +76,7 @@ func TestCreateInfra(t *testing.T) { sa := &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Namespace: kube.Namespace, - Name: expectedServiceAccountName(tc.in.Proxy.Name), + Name: expectedProxyServiceAccountName(tc.in.Proxy.Name), }, } require.NoError(t, kube.Client.Get(context.Background(), client.ObjectKeyFromObject(sa), sa)) @@ -84,7 +84,7 @@ func TestCreateInfra(t *testing.T) { cm := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Namespace: kube.Namespace, - Name: expectedConfigMapName(tc.in.Proxy.Name), + Name: expectedProxyConfigMapName(tc.in.Proxy.Name), }, } require.NoError(t, kube.Client.Get(context.Background(), client.ObjectKeyFromObject(cm), cm)) @@ -92,7 +92,7 @@ func TestCreateInfra(t *testing.T) { deploy := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Namespace: kube.Namespace, - Name: expectedDeploymentName(tc.in.Proxy.Name), + Name: expectedProxyDeploymentName(tc.in.Proxy.Name), }, } require.NoError(t, kube.Client.Get(context.Background(), client.ObjectKeyFromObject(deploy), deploy)) @@ -100,7 +100,7 @@ func TestCreateInfra(t *testing.T) { svc := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Namespace: kube.Namespace, - Name: expectedServiceName(tc.in.Proxy.Name), + Name: expectedProxyServiceName(tc.in.Proxy.Name), }, } require.NoError(t, kube.Client.Get(context.Background(), client.ObjectKeyFromObject(svc), svc)) @@ -109,7 +109,7 @@ func TestCreateInfra(t *testing.T) { } } -func TestDeleteInfra(t *testing.T) { +func TestDeleteProxyInfra(t *testing.T) { testCases := []struct { name string in *ir.Infra @@ -134,7 +134,7 @@ func TestDeleteInfra(t *testing.T) { kube := &Infra{ Client: fakeclient.NewClientBuilder().WithScheme(envoygateway.GetScheme()).Build(), } - err := kube.DeleteInfra(context.Background(), tc.in) + err := kube.DeleteProxyInfra(context.Background(), tc.in) if !tc.expect { require.Error(t, err) } else { diff --git a/internal/infrastructure/kubernetes/labels.go b/internal/infrastructure/kubernetes/labels.go index 263bb9d97d2..dfe26074e84 100644 --- a/internal/infrastructure/kubernetes/labels.go +++ b/internal/infrastructure/kubernetes/labels.go @@ -16,11 +16,18 @@ func envoyAppLabel() map[string]string { } } -// envoySelector returns a label selector used to select resources +// rateLimitLabels returns the labels used for all envoy rate limit resources. +func rateLimitLabels() map[string]string { + return map[string]string{ + "app.gateway.envoyproxy.io/name": rateLimitInfraName, + } +} + +// getSelector returns a label selector used to select resources // based on the provided lbls. -func envoySelector(extraLbls map[string]string) *metav1.LabelSelector { +func getSelector(labels map[string]string) *metav1.LabelSelector { return &metav1.LabelSelector{ - MatchLabels: envoyLabels(extraLbls), + MatchLabels: labels, } } diff --git a/internal/infrastructure/kubernetes/labels_test.go b/internal/infrastructure/kubernetes/labels_test.go index 621efaa872b..8fcc3902317 100644 --- a/internal/infrastructure/kubernetes/labels_test.go +++ b/internal/infrastructure/kubernetes/labels_test.go @@ -30,7 +30,7 @@ func TestEnvoyPodSelector(t *testing.T) { for _, tc := range cases { tc := tc t.Run("", func(t *testing.T) { - got := envoySelector(tc.in) + got := getSelector(envoyLabels(tc.in)) require.Equal(t, tc.expected, got.MatchLabels) }) } diff --git a/internal/infrastructure/kubernetes/service.go b/internal/infrastructure/kubernetes/service.go index d69e3fab55f..10740bb1ff6 100644 --- a/internal/infrastructure/kubernetes/service.go +++ b/internal/infrastructure/kubernetes/service.go @@ -22,13 +22,13 @@ import ( "github.com/envoyproxy/gateway/internal/provider/utils" ) -func expectedServiceName(proxyName string) string { +func expectedProxyServiceName(proxyName string) string { svcName := utils.GetHashedName(proxyName) return fmt.Sprintf("%s-%s", config.EnvoyPrefix, svcName) } -// expectedService returns the expected Service based on the provided infra. -func (i *Infra) expectedService(infra *ir.Infra) (*corev1.Service, error) { +// expectedproxyService returns the expected Service based on the provided infra. +func (i *Infra) expectedProxyService(infra *ir.Infra) (*corev1.Service, error) { var ports []corev1.ServicePort for _, listener := range infra.Proxy.Listeners { for _, port := range listener.Ports { @@ -56,13 +56,13 @@ func (i *Infra) expectedService(infra *ir.Infra) (*corev1.Service, error) { svc := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Namespace: i.Namespace, - Name: expectedServiceName(infra.Proxy.Name), + Name: expectedProxyServiceName(infra.Proxy.Name), Labels: labels, }, Spec: corev1.ServiceSpec{ Type: corev1.ServiceTypeLoadBalancer, Ports: ports, - Selector: envoySelector(infra.GetProxyInfra().GetProxyMetadata().Labels).MatchLabels, + Selector: getSelector(labels).MatchLabels, SessionAffinity: corev1.ServiceAffinityNone, // Preserve the client source IP and avoid a second hop for LoadBalancer. ExternalTrafficPolicy: corev1.ServiceExternalTrafficPolicyTypeLocal, @@ -72,10 +72,10 @@ func (i *Infra) expectedService(infra *ir.Infra) (*corev1.Service, error) { return svc, nil } -// createOrUpdateService creates a Service in the kube api server based on the provided infra, +// createOrUpdateproxyService creates a Service in the kube api server based on the provided infra, // if it doesn't exist or updates it if it does. -func (i *Infra) createOrUpdateService(ctx context.Context, infra *ir.Infra) error { - svc, err := i.expectedService(infra) +func (i *Infra) createOrUpdateProxyService(ctx context.Context, infra *ir.Infra) error { + svc, err := i.expectedProxyService(infra) if err != nil { return fmt.Errorf("failed to generate expected service: %w", err) } @@ -83,7 +83,7 @@ func (i *Infra) createOrUpdateService(ctx context.Context, infra *ir.Infra) erro current := &corev1.Service{} key := types.NamespacedName{ Namespace: i.Namespace, - Name: expectedServiceName(infra.Proxy.Name), + Name: expectedProxyServiceName(infra.Proxy.Name), } if err := i.Client.Get(ctx, key, current); err != nil { @@ -107,15 +107,98 @@ func (i *Infra) createOrUpdateService(ctx context.Context, infra *ir.Infra) erro return nil } -// deleteService deletes the Envoy Service in the kube api server, if it exists. -func (i *Infra) deleteService(ctx context.Context, infra *ir.Infra) error { +// deleteProxyService deletes the Envoy Service in the kube api server, if it exists. +func (i *Infra) deleteProxyService(ctx context.Context, infra *ir.Infra) error { svc := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Namespace: i.Namespace, - Name: expectedServiceName(infra.Proxy.Name), + Name: expectedProxyServiceName(infra.Proxy.Name), }, } + return i.deleteService(ctx, svc) +} + +// expectedRateLimitInfraService returns the expected rate limit Service based on the provided infra. +func (i *Infra) expectedRateLimitService(_ *ir.RateLimitInfra) *corev1.Service { + ports := []corev1.ServicePort{ + { + Name: "http", + Protocol: corev1.ProtocolTCP, + Port: rateLimitInfraHTTPPort, + TargetPort: intstr.IntOrString{IntVal: rateLimitInfraHTTPPort}, + }, + } + + labels := rateLimitLabels() + + svc := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: i.Namespace, + Name: rateLimitInfraName, + Labels: labels, + }, + Spec: corev1.ServiceSpec{ + Type: corev1.ServiceTypeLoadBalancer, + Ports: ports, + Selector: getSelector(labels).MatchLabels, + SessionAffinity: corev1.ServiceAffinityNone, + // Preserve the client source IP and avoid a second hop for LoadBalancer. + ExternalTrafficPolicy: corev1.ServiceExternalTrafficPolicyTypeLocal, + }, + } + + return svc +} + +// createOrUpdateRateLimitService creates a Service in the kube api server based on the provided infra, +// if it doesn't exist or updates it if it does. +func (i *Infra) createOrUpdateRateLimitService(ctx context.Context, infra *ir.RateLimitInfra) error { + svc := i.expectedRateLimitService(infra) + return i.createOrUpdateService(ctx, svc) +} + +// deleteRateLimitService deletes the rate limit Service in the kube api server, if it exists. +func (i *Infra) deleteRateLimitService(ctx context.Context, _ *ir.RateLimitInfra) error { + svc := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: i.Namespace, + Name: rateLimitInfraName, + }, + } + + return i.deleteService(ctx, svc) +} + +func (i *Infra) createOrUpdateService(ctx context.Context, svc *corev1.Service) error { + current := &corev1.Service{} + key := types.NamespacedName{ + Namespace: svc.Namespace, + Name: svc.Name, + } + + if err := i.Client.Get(ctx, key, current); err != nil { + // Create if not found. + if kerrors.IsNotFound(err) { + if err := i.Client.Create(ctx, svc); err != nil { + return fmt.Errorf("failed to create service %s/%s: %w", + svc.Namespace, svc.Name, err) + } + } + } else { + // Update if current value is different. + if !reflect.DeepEqual(svc.Spec, current.Spec) { + if err := i.Client.Update(ctx, svc); err != nil { + return fmt.Errorf("failed to update service %s/%s: %w", + svc.Namespace, svc.Name, err) + } + } + } + + return nil +} + +func (i *Infra) deleteService(ctx context.Context, svc *corev1.Service) error { if err := i.Client.Delete(ctx, svc); err != nil { if kerrors.IsNotFound(err) { return nil diff --git a/internal/infrastructure/kubernetes/service_test.go b/internal/infrastructure/kubernetes/service_test.go index f4500adc241..733bd6a3684 100644 --- a/internal/infrastructure/kubernetes/service_test.go +++ b/internal/infrastructure/kubernetes/service_test.go @@ -66,7 +66,7 @@ func checkServiceHasLabels(t *testing.T, svc *corev1.Service, expected map[strin t.Errorf("service has unexpected %q labels", svc.Labels) } -func TestDesiredService(t *testing.T) { +func TestDesiredProxyService(t *testing.T) { cfg, err := config.New() require.NoError(t, err) cli := fakeclient.NewClientBuilder().WithScheme(envoygateway.GetScheme()).WithObjects().Build() @@ -88,11 +88,11 @@ func TestDesiredService(t *testing.T) { ContainerPort: 2443, }, } - svc, err := kube.expectedService(infra) + svc, err := kube.expectedProxyService(infra) require.NoError(t, err) // Check the service name is as expected. - assert.Equal(t, svc.Name, expectedDeploymentName(infra.Proxy.Name)) + assert.Equal(t, svc.Name, expectedProxyDeploymentName(infra.Proxy.Name)) checkServiceHasPort(t, svc, 80) checkServiceHasPort(t, svc, 443) @@ -110,7 +110,7 @@ func TestDesiredService(t *testing.T) { } } -func TestDeleteService(t *testing.T) { +func TestDeleteProxyService(t *testing.T) { testCases := []struct { name string }{ @@ -127,7 +127,7 @@ func TestDeleteService(t *testing.T) { Namespace: "test", } infra := ir.NewInfra() - err := kube.deleteService(context.Background(), infra) + err := kube.deleteProxyService(context.Background(), infra) require.NoError(t, err) }) } diff --git a/internal/infrastructure/kubernetes/serviceaccount.go b/internal/infrastructure/kubernetes/serviceaccount.go index 97110c1d2ca..11dc0d2d844 100644 --- a/internal/infrastructure/kubernetes/serviceaccount.go +++ b/internal/infrastructure/kubernetes/serviceaccount.go @@ -20,13 +20,13 @@ import ( "github.com/envoyproxy/gateway/internal/provider/utils" ) -func expectedServiceAccountName(proxyName string) string { +func expectedProxyServiceAccountName(proxyName string) string { svcActName := utils.GetHashedName(proxyName) return fmt.Sprintf("%s-%s", config.EnvoyPrefix, svcActName) } -// expectedServiceAccount returns the expected proxy serviceAccount. -func (i *Infra) expectedServiceAccount(infra *ir.Infra) (*corev1.ServiceAccount, error) { +// expectedProxyServiceAccount returns the expected proxy serviceAccount. +func (i *Infra) expectedProxyServiceAccount(infra *ir.Infra) (*corev1.ServiceAccount, error) { // Set the labels based on the owning gateway name. labels := envoyLabels(infra.GetProxyInfra().GetProxyMetadata().Labels) if len(labels[gatewayapi.OwningGatewayNamespaceLabel]) == 0 || len(labels[gatewayapi.OwningGatewayNameLabel]) == 0 { @@ -40,24 +40,74 @@ func (i *Infra) expectedServiceAccount(infra *ir.Infra) (*corev1.ServiceAccount, }, ObjectMeta: metav1.ObjectMeta{ Namespace: i.Namespace, - Name: expectedServiceAccountName(infra.Proxy.Name), + Name: expectedProxyServiceAccountName(infra.Proxy.Name), Labels: labels, }, }, nil } -// createOrUpdateServiceAccount creates the Envoy ServiceAccount in the kube api server, +// createOrUpdateProxyServiceAccount creates the Envoy ServiceAccount in the kube api server, // if it doesn't exist and updates it if it does. -func (i *Infra) createOrUpdateServiceAccount(ctx context.Context, infra *ir.Infra) error { - sa, err := i.expectedServiceAccount(infra) +func (i *Infra) createOrUpdateProxyServiceAccount(ctx context.Context, infra *ir.Infra) error { + sa, err := i.expectedProxyServiceAccount(infra) if err != nil { return err } + return i.createOrUpdateServiceAccount(ctx, sa) +} + +// deleteProxyServiceAccount deletes the Envoy ServiceAccount in the kube api server, +// if it exists. +func (i *Infra) deleteProxyServiceAccount(ctx context.Context, infra *ir.Infra) error { + sa := &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: i.Namespace, + Name: expectedProxyServiceAccountName(infra.Proxy.Name), + }, + } + + return i.deleteServiceAccount(ctx, sa) +} +// expectedRateLimitServiceAccount returns the expected ratelimit serviceAccount. +func (i *Infra) expectedRateLimitServiceAccount(_ *ir.RateLimitInfra) *corev1.ServiceAccount { + return &corev1.ServiceAccount{ + TypeMeta: metav1.TypeMeta{ + Kind: "ServiceAccount", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: i.Namespace, + Name: rateLimitInfraName, + }, + } +} + +// createOrUpdateRateLimitServiceAccount creates the Envoy RateLimit ServiceAccount in the kube api server, +// if it doesn't exist and updates it if it does. +func (i *Infra) createOrUpdateRateLimitServiceAccount(ctx context.Context, infra *ir.RateLimitInfra) error { + sa := i.expectedRateLimitServiceAccount(infra) + return i.createOrUpdateServiceAccount(ctx, sa) +} + +// deleteRateLimitServiceAccount deletes the Envoy RateLimit ServiceAccount in the kube api server, +// if it exists. +func (i *Infra) deleteRateLimitServiceAccount(ctx context.Context, _ *ir.RateLimitInfra) error { + sa := &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: i.Namespace, + Name: rateLimitInfraName, + }, + } + + return i.deleteServiceAccount(ctx, sa) +} + +func (i *Infra) createOrUpdateServiceAccount(ctx context.Context, sa *corev1.ServiceAccount) error { current := &corev1.ServiceAccount{} key := types.NamespacedName{ - Namespace: i.Namespace, - Name: expectedServiceAccountName(infra.Proxy.Name), + Namespace: sa.Namespace, + Name: sa.Name, } if err := i.Client.Get(ctx, key, current); err != nil { @@ -80,15 +130,7 @@ func (i *Infra) createOrUpdateServiceAccount(ctx context.Context, infra *ir.Infr return nil } -// deleteServiceAccount deletes the Envoy ServiceAccount in the kube api server, -// if it exists. -func (i *Infra) deleteServiceAccount(ctx context.Context, infra *ir.Infra) error { - sa := &corev1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: i.Namespace, - Name: expectedServiceAccountName(infra.Proxy.Name), - }, - } +func (i *Infra) deleteServiceAccount(ctx context.Context, sa *corev1.ServiceAccount) error { if err := i.Client.Delete(ctx, sa); err != nil { if kerrors.IsNotFound(err) { return nil diff --git a/internal/infrastructure/kubernetes/serviceaccount_test.go b/internal/infrastructure/kubernetes/serviceaccount_test.go index b9918e8a1d7..1f9127a9234 100644 --- a/internal/infrastructure/kubernetes/serviceaccount_test.go +++ b/internal/infrastructure/kubernetes/serviceaccount_test.go @@ -25,7 +25,7 @@ import ( "github.com/envoyproxy/gateway/internal/ir" ) -func TestExpectedServiceAccount(t *testing.T) { +func TestExpectedProxyServiceAccount(t *testing.T) { cfg, err := config.New() require.NoError(t, err) cli := fakeclient.NewClientBuilder().WithScheme(envoygateway.GetScheme()).WithObjects().Build() @@ -34,17 +34,17 @@ func TestExpectedServiceAccount(t *testing.T) { // An infra without Gateway owner labels should trigger // an error. - _, err = kube.expectedServiceAccount(infra) + _, err = kube.expectedProxyServiceAccount(infra) require.NotNil(t, err) infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNamespaceLabel] = "default" infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNameLabel] = infra.Proxy.Name - sa, err := kube.expectedServiceAccount(infra) + sa, err := kube.expectedProxyServiceAccount(infra) require.NoError(t, err) // Check the serviceaccount name is as expected. - assert.Equal(t, sa.Name, expectedServiceAccountName(infra.Proxy.Name)) + assert.Equal(t, sa.Name, expectedProxyServiceAccountName(infra.Proxy.Name)) wantLabels := envoyAppLabel() wantLabels[gatewayapi.OwningGatewayNamespaceLabel] = "default" @@ -52,7 +52,7 @@ func TestExpectedServiceAccount(t *testing.T) { assert.True(t, apiequality.Semantic.DeepEqual(wantLabels, sa.Labels)) } -func TestCreateOrUpdateServiceAccount(t *testing.T) { +func TestCreateOrUpdateProxyServiceAccount(t *testing.T) { testCases := []struct { name string ns string @@ -194,13 +194,13 @@ func TestCreateOrUpdateServiceAccount(t *testing.T) { } else { kube.Client = fakeclient.NewClientBuilder().WithScheme(envoygateway.GetScheme()).Build() } - err := kube.createOrUpdateServiceAccount(context.Background(), tc.in) + err := kube.createOrUpdateProxyServiceAccount(context.Background(), tc.in) require.NoError(t, err) actual := &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Namespace: kube.Namespace, - Name: expectedServiceAccountName(tc.in.Proxy.Name), + Name: expectedProxyServiceAccountName(tc.in.Proxy.Name), }, } require.NoError(t, kube.Client.Get(context.Background(), client.ObjectKeyFromObject(actual), actual)) @@ -211,7 +211,7 @@ func TestCreateOrUpdateServiceAccount(t *testing.T) { } } -func TestDeleteServiceAccount(t *testing.T) { +func TestDeleteProxyServiceAccount(t *testing.T) { testCases := []struct { name string expect bool @@ -230,7 +230,7 @@ func TestDeleteServiceAccount(t *testing.T) { Namespace: "test", } infra := ir.NewInfra() - err := kube.deleteServiceAccount(context.Background(), infra) + err := kube.deleteProxyServiceAccount(context.Background(), infra) require.NoError(t, err) }) } diff --git a/internal/infrastructure/manager.go b/internal/infrastructure/manager.go index 94cf8c5531d..f281a732f69 100644 --- a/internal/infrastructure/manager.go +++ b/internal/infrastructure/manager.go @@ -23,10 +23,10 @@ var _ Manager = (*kubernetes.Infra)(nil) // Manager provides the scaffolding for managing infrastructure. type Manager interface { - // CreateOrUpdateInfra creates or updates infra. - CreateOrUpdateInfra(ctx context.Context, infra *ir.Infra) error - // DeleteInfra deletes infra. - DeleteInfra(ctx context.Context, infra *ir.Infra) error + // CreateOrUpdateProxyInfra creates or updates infra. + CreateOrUpdateProxyInfra(ctx context.Context, infra *ir.Infra) error + // DeleteProxyInfra deletes infra. + DeleteProxyInfra(ctx context.Context, infra *ir.Infra) error // CreateOrUpdateRateLimitInfra creates or updates rate limit infra. CreateOrUpdateRateLimitInfra(ctx context.Context, infra *ir.RateLimitInfra) error // DeleteRateLimitInfra deletes rate limit infra. diff --git a/internal/infrastructure/runner/runner.go b/internal/infrastructure/runner/runner.go index 10c122e19f9..514a6ea34e8 100644 --- a/internal/infrastructure/runner/runner.go +++ b/internal/infrastructure/runner/runner.go @@ -41,25 +41,29 @@ func (r *Runner) Start(ctx context.Context) error { if err != nil { r.Logger.Error(err, "failed to create new manager") } - go r.subscribeToInfraIR(ctx) - go r.subscribeToRateLimitInfraIR(ctx) + go r.subscribeToProxyInfraIR(ctx) + + // subscribe to rate limit infra IR if ratelimit has been enabled in the config. + if r.EnvoyGateway.RateLimit != nil { + go r.subscribeToRateLimitInfraIR(ctx) + } r.Logger.Info("started") return nil } -func (r *Runner) subscribeToInfraIR(ctx context.Context) { +func (r *Runner) subscribeToProxyInfraIR(ctx context.Context) { // Subscribe to resources message.HandleSubscription(r.InfraIR.Subscribe(ctx), func(update message.Update[string, *ir.Infra]) { val := update.Value if update.Delete { - if err := r.mgr.DeleteInfra(ctx, val); err != nil { + if err := r.mgr.DeleteProxyInfra(ctx, val); err != nil { r.Logger.Error(err, "failed to delete infra") } } else { // Manage the proxy infra. - if err := r.mgr.CreateOrUpdateInfra(ctx, val); err != nil { + if err := r.mgr.CreateOrUpdateProxyInfra(ctx, val); err != nil { r.Logger.Error(err, "failed to create new infra") } }