Skip to content

Commit ef84a6d

Browse files
authored
Set Gateway Programmed condition (#658)
* Set Gateway Programmed condition When the Gateway configuration has been successfully uploaded to nginx, we will set the status Programmed to true. If there's any error with updating nginx, then we set Programmed to false. We'll also set a negative status on the HTTPRoutes in the system to explain that the Gateway is not programmed. Moved the buildStatuses logic into the status package and removed redundant tests.
1 parent 64dda4c commit ef84a6d

22 files changed

+621
-1238
lines changed

docs/gateway-api-compatibility.md

+39-35
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,11 @@ of the [static-mode](./cli-help.md#static-mode) command.
3939

4040
Fields:
4141
* `spec`
42-
* `controllerName` - supported.
43-
* `parametersRef` - not supported.
44-
* `description` - supported.
42+
* `controllerName` - supported.
43+
* `parametersRef` - not supported.
44+
* `description` - supported.
4545
* `status`
46-
* `conditions` - partially supported.
46+
* `conditions` - partially supported.
4747

4848
### Gateway
4949

@@ -54,18 +54,18 @@ See [static-mode](./cli-help.md#static-mode) command for more info.
5454

5555
Fields:
5656
* `spec`
57-
* `gatewayClassName` - supported.
58-
* `listeners`
59-
* `name` - supported.
60-
* `hostname` - partially supported. Wildcard hostnames like `*.example.com` are not yet supported.
61-
* `port` - partially supported. Allowed values: `80` for HTTP listeners and `443` for HTTPS listeners.
62-
* `protocol` - partially supported. Allowed values: `HTTP`, `HTTPS`.
63-
* `tls`
64-
* `mode` - partially supported. Allowed value: `Terminate`.
65-
* `certificateRefs` - partially supported. The TLS certificate and key must be stored in a Secret resource of type `kubernetes.io/tls` in the same namespace as the Gateway resource. Only a single reference is supported. You must deploy the Secret before the Gateway resource. Secret rotation (watching for updates) is not supported.
66-
* `options` - not supported.
67-
* `allowedRoutes` - not supported.
68-
* `addresses` - not supported.
57+
* `gatewayClassName` - supported.
58+
* `listeners`
59+
* `name` - supported.
60+
* `hostname` - partially supported. Wildcard hostnames like `*.example.com` are not yet supported.
61+
* `port` - partially supported. Allowed values: `80` for HTTP listeners and `443` for HTTPS listeners.
62+
* `protocol` - partially supported. Allowed values: `HTTP`, `HTTPS`.
63+
* `tls`
64+
* `mode` - partially supported. Allowed value: `Terminate`.
65+
* `certificateRefs` - partially supported. The TLS certificate and key must be stored in a Secret resource of type `kubernetes.io/tls` in the same namespace as the Gateway resource. Only a single reference is supported. You must deploy the Secret before the Gateway resource. Secret rotation (watching for updates) is not supported.
66+
* `options` - not supported.
67+
* `allowedRoutes` - not supported.
68+
* `addresses` - not supported.
6969
* `status`
7070
* `addresses` - Pod IPAddress supported.
7171
* `conditions` - Supported (Condition/Status/Reason):
@@ -75,11 +75,14 @@ Fields:
7575
* `Accepted/False/Invalid`
7676
* `Accepted/False/UnsupportedValue`: Custom reason for when a value of a field in a Gateway is invalid or not supported.
7777
* `Accepted/False/GatewayConflict`: Custom reason for when the Gateway is ignored due to a conflicting Gateway. NKG only supports a single Gateway.
78+
* `Programmed/True/Programmed`
79+
* `Programmed/False/Invalid`
80+
* `Programmed/False/GatewayConflict`: Custom reason for when the Gateway is ignored due to a conflicting Gateway. NKG only supports a single Gateway.
7881
* `listeners`
79-
* `name` - supported.
80-
* `supportedKinds` - not supported.
81-
* `attachedRoutes` - supported.
82-
* `conditions` - Supported (Condition/Status/Reason):
82+
* `name` - supported.
83+
* `supportedKinds` - not supported.
84+
* `attachedRoutes` - supported.
85+
* `conditions` - Supported (Condition/Status/Reason):
8386
* `Accepted/True/Accepted`
8487
* `Accepted/False/UnsupportedProtocol`
8588
* `Accepted/False/InvalidCertificateRef`
@@ -101,26 +104,27 @@ Fields:
101104
* `parentRefs` - partially supported. Port not supported.
102105
* `hostnames` - partially supported. Wildcard binding is not supported: a hostname like `example.com` will not bind to a listener with the hostname `*.example.com`. However, `example.com` will bind to a listener with the empty hostname.
103106
* `rules`
104-
* `matches`
105-
* `path` - partially supported. Only `PathPrefix` and `Exact` types.
106-
* `headers` - partially supported. Only `Exact` type.
107-
* `queryParams` - partially supported. Only `Exact` type.
108-
* `method` - supported.
109-
* `filters`
110-
* `type` - supported.
111-
* `requestRedirect` - supported except for the experimental `path` field. If multiple filters with `requestRedirect` are configured, NGINX Kubernetes Gateway will choose the first one and ignore the rest.
112-
* `requestHeaderModifier`, `requestMirror`, `urlRewrite`, `extensionRef` - not supported.
113-
* `backendRefs` - partially supported. Backend ref `filters` are not supported.
107+
* `matches`
108+
* `path` - partially supported. Only `PathPrefix` and `Exact` types.
109+
* `headers` - partially supported. Only `Exact` type.
110+
* `queryParams` - partially supported. Only `Exact` type.
111+
* `method` - supported.
112+
* `filters`
113+
* `type` - supported.
114+
* `requestRedirect` - supported except for the experimental `path` field. If multiple filters with `requestRedirect` are configured, NGINX Kubernetes Gateway will choose the first one and ignore the rest.
115+
* `requestHeaderModifier`, `requestMirror`, `urlRewrite`, `extensionRef` - not supported.
116+
* `backendRefs` - partially supported. Backend ref `filters` are not supported.
114117
* `status`
115118
* `parents`
116-
* `parentRef` - supported.
117-
* `controllerName` - supported.
118-
* `conditions` - partially supported. Supported (Condition/Status/Reason):
119-
* `Accepted/True/Accepted`
120-
* `Accepted/False/NoMatchingListenerHostname`
119+
* `parentRef` - supported.
120+
* `controllerName` - supported.
121+
* `conditions` - partially supported. Supported (Condition/Status/Reason):
122+
* `Accepted/True/Accepted`
123+
* `Accepted/False/NoMatchingListenerHostname`
121124
* `Accepted/False/NoMatchingParent`
122125
* `Accepted/False/UnsupportedValue`: Custom reason for when the HTTPRoute includes an invalid or unsupported value.
123126
* `Accepted/False/InvalidListener`: Custom reason for when the HTTPRoute references an invalid listener.
127+
* `Accepted/False/GatewayNotProgrammed`: Custom reason for when the Gateway is not Programmed. HTTPRoute may be valid and configured, but will maintain this status as long as the Gateway is not Programmed.
124128
* `ResolvedRefs/True/ResolvedRefs`
125129
* `ResolvedRefs/False/InvalidKind`
126130
* `ResolvedRefs/False/RefNotPermitted`

internal/events/handler.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/nginxinc/nginx-kubernetes-gateway/internal/nginx/runtime"
1515
"github.com/nginxinc/nginx-kubernetes-gateway/internal/state"
1616
"github.com/nginxinc/nginx-kubernetes-gateway/internal/state/dataplane"
17+
"github.com/nginxinc/nginx-kubernetes-gateway/internal/state/resolver"
1718
"github.com/nginxinc/nginx-kubernetes-gateway/internal/state/secrets"
1819
"github.com/nginxinc/nginx-kubernetes-gateway/internal/status"
1920
)
@@ -35,6 +36,8 @@ type EventHandlerConfig struct {
3536
SecretStore secrets.SecretStore
3637
// SecretMemoryManager is the state SecretMemoryManager.
3738
SecretMemoryManager secrets.SecretDiskMemoryManager
39+
// ServiceResolver resolves Services to Endpoints.
40+
ServiceResolver resolver.ServiceResolver
3841
// Generator is the nginx config Generator.
3942
Generator config.Generator
4043
// NginxFileMgr is the file Manager for nginx.
@@ -74,20 +77,22 @@ func (h *EventHandlerImpl) HandleEventBatch(ctx context.Context, batch EventBatc
7477
}
7578
}
7679

77-
changed, conf, statuses := h.cfg.Processor.Process(ctx)
80+
changed, graph := h.cfg.Processor.Process()
7881
if !changed {
7982
h.cfg.Logger.Info("Handling events didn't result into NGINX configuration changes")
8083
return
8184
}
8285

83-
err := h.updateNginx(ctx, conf)
86+
var nginxReloadRes status.NginxReloadResult
87+
err := h.updateNginx(ctx, dataplane.BuildConfiguration(ctx, graph, h.cfg.ServiceResolver))
8488
if err != nil {
8589
h.cfg.Logger.Error(err, "Failed to update NGINX configuration")
90+
nginxReloadRes.Error = err
8691
} else {
8792
h.cfg.Logger.Info("NGINX configuration was successfully updated")
8893
}
8994

90-
h.cfg.StatusUpdater.Update(ctx, statuses)
95+
h.cfg.StatusUpdater.Update(ctx, status.BuildStatuses(graph, nginxReloadRes))
9196
}
9297

9398
func (h *EventHandlerImpl) updateNginx(ctx context.Context, conf dataplane.Configuration) error {

internal/events/handler_test.go

+6-12
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ import (
1919
"github.com/nginxinc/nginx-kubernetes-gateway/internal/nginx/config/configfakes"
2020
"github.com/nginxinc/nginx-kubernetes-gateway/internal/nginx/file/filefakes"
2121
"github.com/nginxinc/nginx-kubernetes-gateway/internal/nginx/runtime/runtimefakes"
22-
"github.com/nginxinc/nginx-kubernetes-gateway/internal/state"
2322
"github.com/nginxinc/nginx-kubernetes-gateway/internal/state/dataplane"
23+
"github.com/nginxinc/nginx-kubernetes-gateway/internal/state/graph"
2424
"github.com/nginxinc/nginx-kubernetes-gateway/internal/state/secrets/secretsfakes"
2525
"github.com/nginxinc/nginx-kubernetes-gateway/internal/state/statefakes"
2626
"github.com/nginxinc/nginx-kubernetes-gateway/internal/status/statusfakes"
@@ -50,7 +50,7 @@ var _ = Describe("EventHandler", func() {
5050
fakeStatusUpdater *statusfakes.FakeUpdater
5151
)
5252

53-
expectReconfig := func(expectedConf dataplane.Configuration, expectedCfg []byte, expectedStatuses state.Statuses) {
53+
expectReconfig := func(expectedConf dataplane.Configuration, expectedCfg []byte) {
5454
Expect(fakeProcessor.ProcessCallCount()).Should(Equal(1))
5555

5656
Expect(fakeGenerator.GenerateCallCount()).Should(Equal(1))
@@ -64,8 +64,6 @@ var _ = Describe("EventHandler", func() {
6464
Expect(fakeNginxRuntimeMgr.ReloadCallCount()).Should(Equal(1))
6565

6666
Expect(fakeStatusUpdater.UpdateCallCount()).Should(Equal(1))
67-
_, statuses := fakeStatusUpdater.UpdateArgsForCall(0)
68-
Expect(statuses).Should(Equal(expectedStatuses))
6967
}
7068

7169
BeforeEach(func() {
@@ -93,10 +91,8 @@ var _ = Describe("EventHandler", func() {
9391
DescribeTable(
9492
"A batch with one event",
9593
func(e interface{}) {
96-
fakeConf := dataplane.Configuration{}
97-
fakeStatuses := state.Statuses{}
9894
changed := true
99-
fakeProcessor.ProcessReturns(changed, fakeConf, fakeStatuses)
95+
fakeProcessor.ProcessReturns(changed, &graph.Graph{})
10096

10197
fakeCfg := []byte("fake")
10298
fakeGenerator.GenerateReturns(fakeCfg)
@@ -120,7 +116,7 @@ var _ = Describe("EventHandler", func() {
120116
}
121117

122118
// Check that a reconfig happened
123-
expectReconfig(fakeConf, fakeCfg, fakeStatuses)
119+
expectReconfig(dataplane.Configuration{}, fakeCfg)
124120
},
125121
Entry(
126122
"HTTPRoute upsert",
@@ -258,10 +254,8 @@ var _ = Describe("EventHandler", func() {
258254
batch = append(batch, upserts...)
259255
batch = append(batch, deletes...)
260256

261-
fakeConf := dataplane.Configuration{}
262257
changed := true
263-
fakeStatuses := state.Statuses{}
264-
fakeProcessor.ProcessReturns(changed, fakeConf, fakeStatuses)
258+
fakeProcessor.ProcessReturns(changed, &graph.Graph{})
265259

266260
fakeCfg := []byte("fake")
267261
fakeGenerator.GenerateReturns(fakeCfg)
@@ -294,7 +288,7 @@ var _ = Describe("EventHandler", func() {
294288
Expect(fakeSecretStore.DeleteArgsForCall(0)).Should(Equal(secretNsName))
295289

296290
// Check that a reconfig happened
297-
expectReconfig(fakeConf, fakeCfg, fakeStatuses)
291+
expectReconfig(dataplane.Configuration{}, fakeCfg)
298292
})
299293

300294
Describe("Edge cases", func() {

internal/manager/manager.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ func Start(cfg config.Config) error {
133133
GatewayCtlrName: cfg.GatewayCtlrName,
134134
GatewayClassName: cfg.GatewayClassName,
135135
SecretMemoryManager: secretMemoryMgr,
136-
ServiceResolver: resolver.NewServiceResolverImpl(mgr.GetClient()),
137136
RelationshipCapturer: relationship.NewCapturerImpl(),
138137
Logger: cfg.Logger.WithName("changeProcessor"),
139138
Validators: validation.Validators{
@@ -160,6 +159,7 @@ func Start(cfg config.Config) error {
160159
Processor: processor,
161160
SecretStore: secretStore,
162161
SecretMemoryManager: secretMemoryMgr,
162+
ServiceResolver: resolver.NewServiceResolverImpl(mgr.GetClient()),
163163
Generator: configGenerator,
164164
Logger: cfg.Logger.WithName("eventHandler"),
165165
NginxFileMgr: nginxFileMgr,

internal/nginx/runtime/manager.go

-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ func (m *ManagerImpl) Reload(ctx context.Context) error {
5151

5252
// FIXME(pleshakov)
5353
// (1) ensure the reload actually happens.
54-
// (2) ensure that in case of an error, the error message can be seen by the admins.
5554
// https://github.com/nginxinc/nginx-kubernetes-gateway/issues/664
5655

5756
// for now, to prevent a subsequent reload starting before the in-flight reload finishes, we simply sleep.

internal/state/change_processor.go

+8-20
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package state
22

33
import (
4-
"context"
54
"fmt"
65
"sync"
76

@@ -18,10 +17,8 @@ import (
1817

1918
gwapivalidation "sigs.k8s.io/gateway-api/apis/v1beta1/validation"
2019

21-
"github.com/nginxinc/nginx-kubernetes-gateway/internal/state/dataplane"
2220
"github.com/nginxinc/nginx-kubernetes-gateway/internal/state/graph"
2321
"github.com/nginxinc/nginx-kubernetes-gateway/internal/state/relationship"
24-
"github.com/nginxinc/nginx-kubernetes-gateway/internal/state/resolver"
2522
"github.com/nginxinc/nginx-kubernetes-gateway/internal/state/secrets"
2623
"github.com/nginxinc/nginx-kubernetes-gateway/internal/state/validation"
2724
)
@@ -35,7 +32,7 @@ const (
3532

3633
type extractGVKFunc func(obj client.Object) schema.GroupVersionKind
3734

38-
// ChangeProcessor processes the changes to resources producing the internal representation
35+
// ChangeProcessor processes the changes to resources and produces a graph-like representation
3936
// of the Gateway configuration. It only supports one GatewayClass resource.
4037
type ChangeProcessor interface {
4138
// CaptureUpsertChange captures an upsert change to a resource.
@@ -46,19 +43,15 @@ type ChangeProcessor interface {
4643
// The method panics if the resource is of unsupported type or if the passed Gateway is different from the one
4744
// this ChangeProcessor was created for.
4845
CaptureDeleteChange(resourceType client.Object, nsname types.NamespacedName)
49-
// Process processes any captured changes and produces an internal representation of the Gateway configuration and
50-
// the status information about the processed resources.
51-
// If no changes were captured, the changed return argument will be false and both the configuration and statuses
52-
// will be empty.
53-
Process(ctx context.Context) (changed bool, conf dataplane.Configuration, statuses Statuses)
46+
// Process produces a graph-like representation of GatewayAPI resources.
47+
// If no changes were captured, the changed return argument will be false and graph will be empty.
48+
Process() (changed bool, graphCfg *graph.Graph)
5449
}
5550

5651
// ChangeProcessorConfig holds configuration parameters for ChangeProcessorImpl.
5752
type ChangeProcessorConfig struct {
5853
// SecretMemoryManager is the secret memory manager.
5954
SecretMemoryManager secrets.SecretDiskMemoryManager
60-
// ServiceResolver resolves Services to Endpoints.
61-
ServiceResolver resolver.ServiceResolver
6255
// RelationshipCapturer captures relationships between Kubernetes API resources and Gateway API resources.
6356
RelationshipCapturer relationship.Capturer
6457
// Validators validate resources according to data-plane specific rules.
@@ -197,26 +190,21 @@ func (c *ChangeProcessorImpl) CaptureDeleteChange(resourceType client.Object, ns
197190
c.updater.Delete(resourceType, nsname)
198191
}
199192

200-
func (c *ChangeProcessorImpl) Process(
201-
ctx context.Context,
202-
) (changed bool, conf dataplane.Configuration, statuses Statuses) {
193+
func (c *ChangeProcessorImpl) Process() (bool, *graph.Graph) {
203194
c.lock.Lock()
204195
defer c.lock.Unlock()
205196

206197
if !c.getAndResetClusterStateChanged() {
207-
return false, conf, statuses
198+
return false, nil
208199
}
209200

210-
g := graph.BuildGraph(
201+
graphCfg := graph.BuildGraph(
211202
c.clusterState,
212203
c.cfg.GatewayCtlrName,
213204
c.cfg.GatewayClassName,
214205
c.cfg.SecretMemoryManager,
215206
c.cfg.Validators,
216207
)
217208

218-
conf = dataplane.BuildConfiguration(ctx, g, c.cfg.ServiceResolver)
219-
statuses = buildStatuses(g)
220-
221-
return true, conf, statuses
209+
return true, graphCfg
222210
}

0 commit comments

Comments
 (0)