@@ -7,8 +7,7 @@ package remote
77
88import (
99 "context"
10-
11- "sigs.k8s.io/controller-runtime/pkg/client"
10+ "sync"
1211
1312 egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
1413 "github.com/envoyproxy/gateway/internal/envoygateway/config"
@@ -17,8 +16,13 @@ import (
1716 "github.com/envoyproxy/gateway/internal/message"
1817)
1918
20- // Infra manages the creation and deletion of Kubernetes infrastructure
21- // based on Infra IR resources.
19+ // Infra manages the creation and deletion of remotely managed proxy and rate
20+ // limit infrastructure by delegating to a remote provider over gRPC.
21+ //
22+ // The underlying InfraClient is constructed lazily on the first method call
23+ // that requires it. This avoids dialing the remote service or reading
24+ // Kubernetes secrets during process startup, where failures would crash the
25+ // pod before validation that the remote provider is actually being used.
2226type Infra struct {
2327 // EnvoyGateway is the configuration used to startup Envoy Gateway.
2428 EnvoyGateway * egv1a1.EnvoyGateway
@@ -28,44 +32,88 @@ type Infra struct {
2832 // errors is the notifier used to send async errors to the main control loop.
2933 errors message.RunnerErrorNotifier
3034
35+ // factory builds the InfraClient on demand. It must not be nil.
36+ factory InfraClientFactory
37+
38+ mu sync.Mutex
3139 ic InfraClient
3240}
3341
34- // NewInfra returns a new Infra.
35- func NewInfra (cfg * config.Server , k8sClient client.Client , errors message.RunnerErrorNotifier ) (* Infra , error ) {
36- // We initialize the client here, that way if the remote connection is misconfigured, then the pod
37- // crashes rather than silently failing when infrastructure changes happen.
38- infraClient , err := newRemoteInfraClient (cfg , k8sClient )
39- if err != nil {
40- if infraClient != nil {
41- _ = infraClient .Close ()
42- }
43- return nil , err
44- }
45- return & Infra {
42+ // NewInfra returns a new Infra that will lazily build its InfraClient via the
43+ // provided factory. The factory is invoked at most once for a successful
44+ // construction; if it returns an error, the next call will retry.
45+ func NewInfra (cfg * config.Server , factory InfraClientFactory , errors message.RunnerErrorNotifier ) * Infra {
46+ return new (Infra {
4647 EnvoyGateway: cfg .EnvoyGateway ,
4748 logger : cfg .Logger .WithName (string (egv1a1 .LogComponentInfrastructureRunner )),
4849 errors : errors ,
49- ic : infraClient ,
50- }, nil
50+ factory : factory ,
51+ })
5152}
5253
54+ // Close releases any resources held by the underlying InfraClient. It is a
55+ // no-op if the client was never constructed.
5356func (i * Infra ) Close () error {
54- return i .ic .Close ()
57+ i .mu .Lock ()
58+ defer i .mu .Unlock ()
59+ if i .ic == nil {
60+ return nil
61+ }
62+ err := i .ic .Close ()
63+ i.ic = nil
64+ return err
5565}
5666
67+ // CreateOrUpdateProxyInfra delegates to the underlying InfraClient.
5768func (i * Infra ) CreateOrUpdateProxyInfra (ctx context .Context , infra * ir .Infra ) error {
58- return i .ic .CreateOrUpdateProxyInfra (ctx , infra )
69+ ic , err := i.client (ctx )
70+ if err != nil {
71+ return err
72+ }
73+ return ic .CreateOrUpdateProxyInfra (ctx , infra )
5974}
6075
76+ // DeleteProxyInfra delegates to the underlying InfraClient.
6177func (i * Infra ) DeleteProxyInfra (ctx context .Context , infra * ir .Infra ) error {
62- return i .ic .DeleteProxyInfra (ctx , infra )
78+ ic , err := i.client (ctx )
79+ if err != nil {
80+ return err
81+ }
82+ return ic .DeleteProxyInfra (ctx , infra )
6383}
6484
85+ // CreateOrUpdateRateLimitInfra delegates to the underlying InfraClient.
6586func (i * Infra ) CreateOrUpdateRateLimitInfra (ctx context .Context ) error {
66- return i .ic .CreateOrUpdateRateLimitInfra (ctx )
87+ ic , err := i.client (ctx )
88+ if err != nil {
89+ return err
90+ }
91+ return ic .CreateOrUpdateRateLimitInfra (ctx )
6792}
6893
94+ // DeleteRateLimitInfra delegates to the underlying InfraClient.
6995func (i * Infra ) DeleteRateLimitInfra (ctx context .Context ) error {
70- return i .ic .DeleteRateLimitInfra (ctx )
96+ ic , err := i.client (ctx )
97+ if err != nil {
98+ return err
99+ }
100+ return ic .DeleteRateLimitInfra (ctx )
101+ }
102+
103+ // client returns the cached InfraClient, building it via the factory on the
104+ // first successful call. Failed factory invocations are not cached so that
105+ // transient errors during startup of the remote service are retried on the
106+ // next request.
107+ func (i * Infra ) client (ctx context .Context ) (InfraClient , error ) {
108+ i .mu .Lock ()
109+ defer i .mu .Unlock ()
110+ if i .ic != nil {
111+ return i .ic , nil
112+ }
113+ ic , err := i.factory (ctx )
114+ if err != nil {
115+ return nil , err
116+ }
117+ i.ic = ic
118+ return ic , nil
71119}
0 commit comments