Skip to content

Commit 1df6bcd

Browse files
authored
Implement provisioner (#683)
Problem: Support provisioning NGINX data plane per Gateway, as expected by the Gateway API conformance tests. See #634 for more info Solution: - Implement provisioner command which which provisions a Deployment of NKG (static mode) for each Gateway of the provisioner GatewayClass. - Add provisioner manifests and docs Fixes #634 Additionally, introduce PrepareTimeForFakeClient helper function, which fixes an error that appeared on GitHub Action pipeline, not locally (see below). (To reproduce it locally, run `make TZ=123 unit-tests` from a commit before this one and ensure you compare Conditions in the status as in Expect(clusterGc.Status.Conditions).To(Equal(expectedConditions)) ) The timezone of the time in a resource field returned by the fake client was different from the one set in the field when updating the resource. The commit adds PrepareTimeForFakeClient() which ensures that the time is prepared correctly so that the timezone is the same. The problem is only present when comparing status Conditions using gomega like Expect(clusterGc.Status.Conditions).To(Equal(expectedConditions)) but not present if comparing using cmp like Expect(helpers.Diff(expectedGc, latestGc)).To(BeEmpty()). [FAILED] Expected <*time.Location | 0x30d0b00>: { name: "Local", zone: [ {name: "UTC", offset: 0, isDST: false}, ], tx: [ { when: -9223372036854775808, index: 0, isstd: false, isutc: false, }, ], extend: "UTC0", cacheStart: -9223372036854775808, cacheEnd: 9223372036854775807, cacheZone: {name: "UTC", offset: 0, isDST: false}, } to equal <*time.Location | 0x309f240>: {name: "UTC", zone: nil, tx: nil, extend: "", cacheStart: 0, cacheEnd: 0, cacheZone: nil}
1 parent ef84a6d commit 1df6bcd

16 files changed

+976
-99
lines changed

cmd/gateway/commands.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package main
22

33
import (
4-
"errors"
54
"fmt"
65
"os"
76

@@ -12,6 +11,7 @@ import (
1211

1312
"github.com/nginxinc/nginx-kubernetes-gateway/internal/config"
1413
"github.com/nginxinc/nginx-kubernetes-gateway/internal/manager"
14+
"github.com/nginxinc/nginx-kubernetes-gateway/internal/provisioner"
1515
)
1616

1717
const (
@@ -190,7 +190,10 @@ func createProvisionerModeCommand() *cobra.Command {
190190
"date", date,
191191
)
192192

193-
return errors.New("not implemented yet")
193+
return provisioner.StartManager(provisioner.Config{
194+
Logger: logger,
195+
GatewayClassName: gatewayClassName.value,
196+
})
194197
},
195198
}
196199
}
+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Provisioner
2+
3+
Provisioner implements data plane provisioning for NGINX Kubernetes Gateway (NKG): it creates an NKG static mode
4+
Deployment for each Gateway that belongs to the provisioner GatewayClass.
5+
6+
```
7+
Usage:
8+
gateway provisioner-mode [flags]
9+
10+
Flags:
11+
-h, --help help for provisioner-mode
12+
13+
Global Flags:
14+
--gateway-ctlr-name string The name of the Gateway controller. The controller name must be of the form: DOMAIN/PATH. The controller's domain is 'k8s-gateway.nginx.org' (default "")
15+
--gatewayclass string The name of the GatewayClass resource. Every NGINX Gateway must have a unique corresponding GatewayClass resource. (default "")
16+
```
17+
18+
Provisioner is not meant to be used in production yet (see this issue for more details
19+
https://github.com/nginxinc/nginx-kubernetes-gateway/issues/634). However, it can be used in the Gateway API conformance
20+
tests, which expect a Gateway API implementation to provision an independent data plane per Gateway.
21+
22+
How to deploy:
23+
24+
1. Follow the [installation](/docs/installation.md) instructions up until the Deploy the NGINX Kubernetes Gateway Step
25+
to deploy prerequisites for both the static mode Deployments and the provisioner.
26+
1. Deploy provisioner:
27+
```
28+
kubectl apply -f conformance/provisioner/provisioner.yaml
29+
```
30+
1. Confirm the provisioner is running in nginx-gateway namespace:
31+
```
32+
kubectl get pods -n nginx-gateway
33+
NAME READY STATUS RESTARTS AGE
34+
nginx-gateway-provisioner-6c9d9fdcb8-b2pf8 1/1 Running 0 11m
35+
```
+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
apiVersion: v1
2+
kind: ServiceAccount
3+
metadata:
4+
name: nginx-gateway-provisioner
5+
namespace: nginx-gateway
6+
---
7+
kind: ClusterRole
8+
apiVersion: rbac.authorization.k8s.io/v1
9+
metadata:
10+
name: nginx-gateway-provisioner
11+
rules:
12+
- apiGroups:
13+
- apps
14+
resources:
15+
- deployments
16+
verbs:
17+
- create
18+
- delete
19+
- apiGroups:
20+
- gateway.networking.k8s.io
21+
resources:
22+
- gatewayclasses
23+
- gateways
24+
verbs:
25+
- list
26+
- watch
27+
- apiGroups:
28+
- gateway.networking.k8s.io
29+
resources:
30+
- gatewayclasses/status
31+
verbs:
32+
- update
33+
---
34+
kind: ClusterRoleBinding
35+
apiVersion: rbac.authorization.k8s.io/v1
36+
metadata:
37+
name: nginx-gateway-provisioner
38+
subjects:
39+
- kind: ServiceAccount
40+
name: nginx-gateway-provisioner
41+
namespace: nginx-gateway
42+
roleRef:
43+
kind: ClusterRole
44+
name: nginx-gateway-provisioner
45+
apiGroup: rbac.authorization.k8s.io
46+
---
47+
apiVersion: apps/v1
48+
kind: Deployment
49+
metadata:
50+
name: nginx-gateway-provisioner
51+
namespace: nginx-gateway
52+
spec:
53+
replicas: 1
54+
selector:
55+
matchLabels:
56+
app: nginx-gateway-provisioner
57+
template:
58+
metadata:
59+
labels:
60+
app: nginx-gateway-provisioner
61+
spec:
62+
serviceAccountName: nginx-gateway-provisioner
63+
containers:
64+
- image: ghcr.io/nginxinc/nginx-kubernetes-gateway:edge
65+
imagePullPolicy: Always
66+
name: nginx-gateway-provisioner
67+
securityContext:
68+
runAsUser: 1001
69+
args:
70+
- provisioner-mode
71+
- --gateway-ctlr-name=k8s-gateway.nginx.org/nginx-gateway-controller
72+
- --gatewayclass=nginx

deploy/manifests/nginx-gateway.yaml deploy/manifests/deployment.yaml

-92
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,3 @@
1-
apiVersion: v1
2-
kind: ServiceAccount
3-
metadata:
4-
name: nginx-gateway
5-
namespace: nginx-gateway
6-
---
7-
kind: ClusterRole
8-
apiVersion: rbac.authorization.k8s.io/v1
9-
metadata:
10-
name: nginx-gateway
11-
rules:
12-
- apiGroups:
13-
- ""
14-
resources:
15-
- services
16-
- secrets
17-
verbs:
18-
- list
19-
- watch
20-
- apiGroups:
21-
- ""
22-
resources:
23-
- events
24-
verbs:
25-
- create
26-
- patch
27-
- apiGroups:
28-
- discovery.k8s.io
29-
resources:
30-
- endpointslices
31-
verbs:
32-
- list
33-
- watch
34-
- apiGroups:
35-
- gateway.networking.k8s.io
36-
resources:
37-
- gatewayclasses
38-
- gateways
39-
- httproutes
40-
verbs:
41-
- list
42-
- watch
43-
- apiGroups:
44-
- gateway.nginx.org
45-
resources:
46-
- gatewayconfigs
47-
verbs:
48-
- list
49-
- watch
50-
- apiGroups:
51-
- gateway.networking.k8s.io
52-
resources:
53-
- httproutes/status
54-
- gateways/status
55-
- gatewayclasses/status
56-
verbs:
57-
- update
58-
---
59-
kind: ClusterRoleBinding
60-
apiVersion: rbac.authorization.k8s.io/v1
61-
metadata:
62-
name: nginx-gateway
63-
subjects:
64-
- kind: ServiceAccount
65-
name: nginx-gateway
66-
namespace: nginx-gateway
67-
roleRef:
68-
kind: ClusterRole
69-
name: nginx-gateway
70-
apiGroup: rbac.authorization.k8s.io
71-
---
72-
apiVersion: v1
73-
kind: ConfigMap
74-
metadata:
75-
name: nginx-conf
76-
namespace: nginx-gateway
77-
data:
78-
nginx.conf: |
79-
load_module /usr/lib/nginx/modules/ngx_http_js_module.so;
80-
81-
events {}
82-
83-
pid /etc/nginx/nginx.pid;
84-
error_log stderr debug;
85-
86-
http {
87-
include /etc/nginx/conf.d/*.conf;
88-
js_import /usr/lib/nginx/modules/njs/httpmatches.js;
89-
server_names_hash_bucket_size 256;
90-
server_names_hash_max_size 1024;
91-
}
92-
---
931
apiVersion: apps/v1
942
kind: Deployment
953
metadata:

deploy/manifests/nginx-conf.yaml

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: nginx-conf
5+
namespace: nginx-gateway
6+
data:
7+
nginx.conf: |
8+
load_module /usr/lib/nginx/modules/ngx_http_js_module.so;
9+
10+
events {}
11+
12+
pid /etc/nginx/nginx.pid;
13+
error_log stderr debug;
14+
15+
http {
16+
include /etc/nginx/conf.d/*.conf;
17+
js_import /usr/lib/nginx/modules/njs/httpmatches.js;
18+
server_names_hash_bucket_size 256;
19+
server_names_hash_max_size 1024;
20+
}

deploy/manifests/rbac.yaml

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
apiVersion: v1
2+
kind: ServiceAccount
3+
metadata:
4+
name: nginx-gateway
5+
namespace: nginx-gateway
6+
---
7+
kind: ClusterRole
8+
apiVersion: rbac.authorization.k8s.io/v1
9+
metadata:
10+
name: nginx-gateway
11+
rules:
12+
- apiGroups:
13+
- ""
14+
resources:
15+
- services
16+
- secrets
17+
verbs:
18+
- list
19+
- watch
20+
- apiGroups:
21+
- ""
22+
resources:
23+
- events
24+
verbs:
25+
- create
26+
- patch
27+
- apiGroups:
28+
- discovery.k8s.io
29+
resources:
30+
- endpointslices
31+
verbs:
32+
- list
33+
- watch
34+
- apiGroups:
35+
- gateway.networking.k8s.io
36+
resources:
37+
- gatewayclasses
38+
- gateways
39+
- httproutes
40+
verbs:
41+
- list
42+
- watch
43+
- apiGroups:
44+
- gateway.nginx.org
45+
resources:
46+
- gatewayconfigs
47+
verbs:
48+
- list
49+
- watch
50+
- apiGroups:
51+
- gateway.networking.k8s.io
52+
resources:
53+
- httproutes/status
54+
- gateways/status
55+
- gatewayclasses/status
56+
verbs:
57+
- update
58+
---
59+
kind: ClusterRoleBinding
60+
apiVersion: rbac.authorization.k8s.io/v1
61+
metadata:
62+
name: nginx-gateway
63+
subjects:
64+
- kind: ServiceAccount
65+
name: nginx-gateway
66+
namespace: nginx-gateway
67+
roleRef:
68+
kind: ClusterRole
69+
name: nginx-gateway
70+
apiGroup: rbac.authorization.k8s.io

docs/installation.md

+13-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,18 @@ This guide walks you through how to install NGINX Kubernetes Gateway on a generi
3535
kubectl create configmap njs-modules --from-file=internal/nginx/modules/src/httpmatches.js -n nginx-gateway
3636
```
3737
38+
1. Create the ConfigMap with the main NGINX configuration file:
39+
40+
```
41+
kubectl apply -f deploy/manifests/nginx-conf.yaml
42+
```
43+
44+
1. Configure RBAC:
45+
46+
```
47+
kubectl apply -f deploy/manifests/rbac.yaml
48+
```
49+
3850
1. Create the GatewayClass resource:
3951
4052
```
@@ -44,7 +56,7 @@ This guide walks you through how to install NGINX Kubernetes Gateway on a generi
4456
1. Deploy the NGINX Kubernetes Gateway:
4557
4658
```
47-
kubectl apply -f deploy/manifests/nginx-gateway.yaml
59+
kubectl apply -f deploy/manifests/deployment.yaml
4860
```
4961
5062
1. Confirm the NGINX Kubernetes Gateway is running in `nginx-gateway` namespace:

embedded.go

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package embeddedfiles
2+
3+
import _ "embed"
4+
5+
// StaticModeDeploymentYAML contains the YAML manifest of the Deployment resource for the static mode.
6+
//
7+
// We put this in the root of the repo because goembed doesn't support relative/absolute paths and symlinks,
8+
// and we want to keep the manifests in the deploy/manifests directory.
9+
//
10+
//go:embed deploy/manifests/deployment.yaml
11+
var StaticModeDeploymentYAML []byte

internal/helpers/helpers.go

+21
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
package helpers
33

44
import (
5+
"fmt"
6+
57
"github.com/google/go-cmp/cmp"
8+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
69
"sigs.k8s.io/gateway-api/apis/v1beta1"
710
)
811

@@ -62,3 +65,21 @@ func GetBoolPointer(b bool) *bool {
6265
func GetPointer[T any](v T) *T {
6366
return &v
6467
}
68+
69+
// PrepareTimeForFakeClient processes the time similarly to the fake client
70+
// from sigs.k8s.io/controller-runtime/pkg/client/fake
71+
// making it is possible to use it in tests when comparing against values returned by the fake client.
72+
// It panics if it fails to process the time.
73+
func PrepareTimeForFakeClient(t metav1.Time) metav1.Time {
74+
bytes, err := t.Marshal()
75+
if err != nil {
76+
panic(fmt.Errorf("failed to marshal time: %w", err))
77+
}
78+
79+
err = t.Unmarshal(bytes)
80+
if err != nil {
81+
panic(fmt.Errorf("failed to unmarshal time: %w", err))
82+
}
83+
84+
return t
85+
}

0 commit comments

Comments
 (0)