Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions internal/infrastructure/host/ratelimit_infra.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ func (i *Infra) CreateOrUpdateRateLimitInfra(_ context.Context) error {
return fmt.Errorf("create/update ratelimit infrastructure is not supported yet for host infrastructure")
}

// DeleteRateLimitInfra removes the managed host rate limit process, if it doesn't exist.
// DeleteRateLimitInfra is a no-op for host infrastructure since rate limiting
// is not yet supported, so there is nothing to clean up.
func (i *Infra) DeleteRateLimitInfra(_ context.Context) error {
return fmt.Errorf("delete ratelimit infrastructure is not supported yet for host infrastructure")
return nil
}
18 changes: 12 additions & 6 deletions internal/provider/file/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ func TestFileProvider(t *testing.T) {
want := &resource.Resources{}
mustUnmarshal(t, "testdata/resources.1.yaml", want)
// Ignore GatewayClass status as it's set asynchronously and creates race conditions
testutil.CmpResources(t, want, resources, cmpopts.IgnoreFields(gwapiv1.GatewayClassStatus{}, "Conditions"))
testutil.CmpResources(t, want, resources, cmpopts.IgnoreFields(gwapiv1.GatewayClassStatus{}, "Conditions"),
cmpopts.IgnoreFields(resource.Resources{}, "Secrets"))
})

t.Run("rename the watched file then rename it back", func(t *testing.T) {
Expand All @@ -154,7 +155,8 @@ func TestFileProvider(t *testing.T) {
want := &resource.Resources{}
mustUnmarshal(t, "testdata/resources.1.yaml", want)
// Ignore GatewayClass status as it's set asynchronously and creates race conditions
testutil.CmpResources(t, want, resources, cmpopts.IgnoreFields(gwapiv1.GatewayClassStatus{}, "Conditions"))
testutil.CmpResources(t, want, resources, cmpopts.IgnoreFields(gwapiv1.GatewayClassStatus{}, "Conditions"),
cmpopts.IgnoreFields(resource.Resources{}, "Secrets"))
})

t.Run("remove the watched file", func(t *testing.T) {
Expand All @@ -177,7 +179,8 @@ func TestFileProvider(t *testing.T) {
want := &resource.Resources{}
mustUnmarshal(t, "testdata/resources.1.yaml", want)
// Ignore GatewayClass status as it's set asynchronously and creates race conditions
testutil.CmpResources(t, want, resources, cmpopts.IgnoreFields(gwapiv1.GatewayClassStatus{}, "Conditions"))
testutil.CmpResources(t, want, resources, cmpopts.IgnoreFields(gwapiv1.GatewayClassStatus{}, "Conditions"),
cmpopts.IgnoreFields(resource.Resources{}, "Secrets"))
})

t.Run("rename the file then rename it back in watched dir", func(t *testing.T) {
Expand All @@ -201,7 +204,8 @@ func TestFileProvider(t *testing.T) {
want := &resource.Resources{}
mustUnmarshal(t, "testdata/resources.1.yaml", want)
// Ignore GatewayClass status as it's set asynchronously and creates race conditions
testutil.CmpResources(t, want, resources, cmpopts.IgnoreFields(gwapiv1.GatewayClassStatus{}, "Conditions"))
testutil.CmpResources(t, want, resources, cmpopts.IgnoreFields(gwapiv1.GatewayClassStatus{}, "Conditions"),
cmpopts.IgnoreFields(resource.Resources{}, "Secrets"))
})

t.Run("update file content in watched dir", func(t *testing.T) {
Expand Down Expand Up @@ -229,13 +233,15 @@ func TestFileProvider(t *testing.T) {
want1 := &resource.Resources{}
mustUnmarshal(t, "testdata/resources.1.yaml", want1)
// Ignore GatewayClass status as it's set asynchronously and creates race conditions
testutil.CmpResources(t, want1, resources1, cmpopts.IgnoreFields(gwapiv1.GatewayClassStatus{}, "Conditions"))
testutil.CmpResources(t, want1, resources1, cmpopts.IgnoreFields(gwapiv1.GatewayClassStatus{}, "Conditions"),
cmpopts.IgnoreFields(resource.Resources{}, "Secrets"))

resources2 := pResources.GetResourcesByGatewayClass("eg-2")
want2 := &resource.Resources{}
mustUnmarshal(t, "testdata/resources.2.yaml", want2)
// Ignore GatewayClass status as it's set asynchronously and creates race conditions
testutil.CmpResources(t, want2, resources2, cmpopts.IgnoreFields(gwapiv1.GatewayClassStatus{}, "Conditions"))
testutil.CmpResources(t, want2, resources2, cmpopts.IgnoreFields(gwapiv1.GatewayClassStatus{}, "Conditions"),
cmpopts.IgnoreFields(resource.Resources{}, "Secrets"))
})

t.Run("remove all files in watched dir", func(t *testing.T) {
Expand Down
16 changes: 16 additions & 0 deletions internal/provider/kubernetes/controller_offline.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1"

egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
"github.com/envoyproxy/gateway/internal/crypto"
"github.com/envoyproxy/gateway/internal/envoygateway"
"github.com/envoyproxy/gateway/internal/envoygateway/config"
"github.com/envoyproxy/gateway/internal/message"
Expand Down Expand Up @@ -74,6 +75,21 @@ func NewOfflineGatewayAPIController(
allExtensions = append(allExtensions, extBackendPoliciesGVKs...)

cli := newOfflineGatewayAPIClient(allExtensions)

// Seed the fake client with the secrets that the CertGen job would create
// in Kubernetes mode. This prevents spurious errors during reconciliation
// when the controller tries to look up OIDC HMAC and Envoy TLS secrets.
certs, err := crypto.GenerateCerts(cfg)
if err != nil {
return nil, fmt.Errorf("failed to generate certificates: %w", err)
}
secrets := CertsToSecret(cfg.ControllerNamespace, certs)
for i := range secrets {
if err := cli.Create(ctx, &secrets[i]); err != nil {
return nil, fmt.Errorf("failed to seed secret %s: %w", secrets[i].Name, err)
}
}

r := &gatewayAPIReconciler{
client: cli,
log: cfg.Logger,
Expand Down
60 changes: 60 additions & 0 deletions internal/provider/kubernetes/controller_offline_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import (

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
client "sigs.k8s.io/controller-runtime/pkg/client"
gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
Expand Down Expand Up @@ -246,3 +248,61 @@ func TestNewOfflineGatewayAPIControllerIndexRegistration(t *testing.T) {
require.NoError(t, err)
})
}

// TestOfflineControllerSeedsSecrets verifies that the offline controller seeds
// the fake Kubernetes client with the secrets normally created by the CertGen
// job. Without these, the reconciler logs spurious errors on every startup.
// See https://github.com/envoyproxy/gateway/issues/6596
func TestOfflineControllerSeedsSecrets(t *testing.T) {
cfg, err := config.New(os.Stdout, os.Stderr)
require.NoError(t, err)

cfg.EnvoyGateway.Provider = &egv1a1.EnvoyGatewayProvider{
Type: egv1a1.ProviderTypeCustom,
}
pResources := new(message.ProviderResources)
reconciler, err := NewOfflineGatewayAPIController(context.Background(), cfg, nil, pResources)
require.NoError(t, err)

cases := []struct {
name string
secretType corev1.SecretType
keys []string
}{
{
name: "envoy-gateway",
secretType: corev1.SecretTypeTLS,
keys: []string{corev1.TLSCertKey, corev1.TLSPrivateKeyKey, "ca.crt"},
},
{
name: "envoy",
secretType: corev1.SecretTypeTLS,
keys: []string{corev1.TLSCertKey, corev1.TLSPrivateKeyKey, "ca.crt"},
},
{
name: "envoy-rate-limit",
secretType: corev1.SecretTypeTLS,
keys: []string{corev1.TLSCertKey, corev1.TLSPrivateKeyKey, "ca.crt"},
},
{
name: "envoy-oidc-hmac",
secretType: corev1.SecretTypeOpaque,
keys: []string{"hmac-secret"},
},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
var secret corev1.Secret
err := reconciler.Client.Get(context.Background(), types.NamespacedName{
Namespace: cfg.ControllerNamespace,
Name: tc.name,
}, &secret)
require.NoError(t, err)
assert.Equal(t, tc.secretType, secret.Type)
for _, key := range tc.keys {
assert.NotEmpty(t, secret.Data[key], "expected key %s to have data", key)
}
})
}
}
1 change: 1 addition & 0 deletions release-notes/current.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ bug fixes: |
Fixed validation of XListenerSet certificateRefs
Fixed XListenerSet not allowing xRoutes from the same namespace when configured to allow them
Fixed API key authentication dropping non-first client IDs when credential Secrets contain multiple keys.
Fixed standalone mode emitting non-actionable error logs for missing secrets and unsupported ratelimit deletion on every startup.

# Enhancements that improve performance.
performance improvements: |
Expand Down
Loading