Skip to content
Merged
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
8 changes: 8 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ jobs:
matrix:
instance:
- defaults
- experimental
- pinned-old-version
- disabled
- openshift
defaults:
run:
working-directory: ${{ env.COMPONENT_NAME }}
Expand All @@ -48,6 +52,10 @@ jobs:
matrix:
instance:
- defaults
- experimental
- pinned-old-version
- disabled
- openshift
defaults:
run:
working-directory: ${{ env.COMPONENT_NAME }}
Expand Down
2 changes: 2 additions & 0 deletions .yamllint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ ignore: |
manifests/
vendor/
compiled/
10_gateway_api_crds/
20_gateway_api_policies/
2 changes: 1 addition & 1 deletion Makefile.vars.mk
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,4 @@ KUBENT_IMAGE ?= ghcr.io/doitintl/kube-no-trouble:latest
KUBENT_DOCKER ?= $(DOCKER_CMD) $(DOCKER_ARGS) $(root_volume) --entrypoint=/app/kubent $(KUBENT_IMAGE)

instance ?= defaults
test_instances = tests/defaults.yml
test_instances = tests/defaults.yml tests/experimental.yml tests/pinned-old-version.yml tests/disabled.yml tests/openshift.yml
10 changes: 9 additions & 1 deletion class/defaults.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,12 @@ parameters:
gateway_api:
=_metadata:
multi_tenant: true
namespace: syn-gateway-api

enabled: true
channel: standard
version: v1.6.1
source: https://github.com/kubernetes-sigs/gateway-api/releases/download/${gateway_api:version}/${gateway_api:channel}-install.yaml

openshift_version:
Major: '4'
Minor: '19'
5 changes: 5 additions & 0 deletions class/gateway-api.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
parameters:
kapitan:
dependencies:
- type: https
source: ${gateway_api:source}
output_path: ${_base_directory}/manifests/gateway-api/install.yaml
compile:
- input_paths:
- ${_base_directory}/component/app.jsonnet
input_type: jsonnet
output_path: .
- input_paths:
- ${_base_directory}/component/main.jsonnet
- ${_base_directory}/component/rbac.jsonnet
input_type: jsonnet
output_path: gateway-api/
10 changes: 9 additions & 1 deletion component/app.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@ local inv = kap.inventory();
local params = inv.parameters.gateway_api;
local argocd = import 'lib/argocd.libjsonnet';

local app = argocd.App('gateway-api', params.namespace);
local app = argocd.App('gateway-api', 'default') {
spec+: {
syncPolicy+: {
syncOptions+: [
'ServerSideApply=true',
],
},
},
};

local appPath =
local project = std.get(std.get(app, 'spec', {}), 'project', 'syn');
Expand Down
34 changes: 31 additions & 3 deletions component/main.jsonnet
Original file line number Diff line number Diff line change
@@ -1,10 +1,38 @@
// main template for gateway-api
local kap = import 'lib/kapitan.libjsonnet';
local kube = import 'lib/kube.libjsonnet';
local inv = kap.inventory();
// The hiera parameters for the component
local params = inv.parameters.gateway_api;

// Define outputs below
{
}

local gateway_docs =
local manifests_dir = '%s/manifests/gateway-api' % inv.parameters._base_directory;
std.flatMap(
function(file)
std.parseJson(kap.yaml_load_stream('%s/%s' % [ manifests_dir, file ])),
kap.dir_files_list(manifests_dir)
);

local gateway_crds =
std.filter(function(doc) doc.kind == 'CustomResourceDefinition', gateway_docs);

local gateway_policies =
std.filter(function(doc) doc.kind != 'CustomResourceDefinition', gateway_docs);

local is_openshift_419_or_higher =
std.member([ 'openshift4', 'oke' ], inv.parameters.facts.distribution) &&
std.parseInt(params.openshift_version.Minor) >= 19;

if params.enabled then
{
['10_gateway_api_crds/' + crd.metadata.name]: crd
for crd in gateway_crds
if !is_openshift_419_or_higher
} + {
['20_gateway_api_policies/%s_%s' % [ std.asciiLower(doc.kind), doc.metadata.name ]]: doc
for doc in gateway_policies
if !is_openshift_419_or_higher
}
else
{}
77 changes: 77 additions & 0 deletions component/rbac.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Aggregated cluster roles for the Gateway API groups
local kap = import 'lib/kapitan.libjsonnet';
local inv = kap.inventory();

local gateway_crds =
local manifests_dir = '%s/manifests/gateway-api' % inv.parameters._base_directory;
local docs = std.flatMap(
function(file)
std.parseJson(kap.yaml_load_stream('%s/%s' % [ manifests_dir, file ])),
kap.dir_files_list(manifests_dir)
);
std.filter(function(doc) doc.kind == 'CustomResourceDefinition', docs);

local rulesForScope(scope) =
local crds = std.filter(function(crd) crd.spec.scope == scope, gateway_crds);
local groups = std.set([ crd.spec.group for crd in crds ]);
[
{
apiGroups: [ group ],
resources: std.sort([
crd.spec.names.plural
for crd in crds
if crd.spec.group == group
]),
}
for group in groups
];

local withVerbs(rules, verbs) = [ rule { verbs: verbs } for rule in rules ];

local readOnlyClusterScopedRules = withVerbs(rulesForScope('Cluster'), [ 'get', 'list', 'watch' ]);
local namespacedRules = rulesForScope('Namespaced');

local aggregatedViewRole = {
apiVersion: 'rbac.authorization.k8s.io/v1',
kind: 'ClusterRole',
metadata: {
labels: {
'rbac.authorization.k8s.io/aggregate-to-cluster-reader': 'true',
'rbac.authorization.k8s.io/aggregate-to-view': 'true',
},
name: 'networking-gatewayapi-aggregated-view',
},
rules: readOnlyClusterScopedRules + withVerbs(namespacedRules, [ 'get', 'list', 'watch' ]),
};

local aggregatedEditRole = {
apiVersion: 'rbac.authorization.k8s.io/v1',
kind: 'ClusterRole',
metadata: {
labels: {
'rbac.authorization.k8s.io/aggregate-to-edit': 'true',
},
name: 'networking-gatewayapi-aggregated-edit',
},
rules: readOnlyClusterScopedRules + withVerbs(namespacedRules, [ '*' ]),
};

local aggregatedAdminRole = {
apiVersion: 'rbac.authorization.k8s.io/v1',
kind: 'ClusterRole',
metadata: {
labels: {
'rbac.authorization.k8s.io/aggregate-to-admin': 'true',
},
name: 'networking-gatewayapi-aggregated-admin',
},
rules: readOnlyClusterScopedRules + withVerbs(namespacedRules, [ '*' ]),
};

{
'30_aggregated_rbac': [
aggregatedViewRole,
aggregatedEditRole,
aggregatedAdminRole,
],
}
27 changes: 25 additions & 2 deletions docs/modules/ROOT/pages/index.adoc
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
= Gateway API
= gateway-api

gateway-api is a Commodore component to manage Gateway API.
gateway-api is a Commodore component managing the upstream https://gateway-api.sigs.k8s.io/[Kubernetes Gateway API] CRDs.

The component is the single owner of the cluster-scoped `gateway.networking.k8s.io` CRDs.
Since Gateway API v1.5, the upstream release additionally ships the `safe-upgrades` validating admission policy, which the component installs alongside the CRDs.
Gateway API implementation components (for example kgateway or Airlock Microgateway) don't have to install these CRDs themselves. Instead add this component to the cluster's applications next to the implementation.

The component also provides a xref:references/library.adoc[component library] (`lib/gateway-api.libsonnet`) with generic builder helpers (GatewayClass, Gateway, HTTPRoute, ReferenceGrant) for components which deploy Gateway API resources.

[NOTE]
====
On OpenShift 4.19+ the cluster ingress operator installs the Gateway API CRDs (standard channel).
On such clusters, keep the component in the cluster's applications (so the component library stays available) but set `enabled: false` to skip installing the CRDs and the `safe-upgrades` validating admission policy.
====

== Aggregated RBAC

The component always deploys the ClusterRoles `networking-gatewayapi-aggregated-view`, `networking-gatewayapi-aggregated-edit` and `networking-gatewayapi-aggregated-admin`, which aggregate Gateway API access to the default user-facing `view`, `edit` and `admin` cluster roles.
The view role additionally aggregates to the `cluster-reader` cluster role.

The resource rules are derived from the CRDs of the configured Gateway API version and channel.
Cluster-scoped Gateway API resources (such as `GatewayClass`) remain read-only in all three roles, including `edit` and `admin`, since namespace editors should not be able to modify a cluster-scoped resource.

NOTE: The aggregated RBAC is deployed independently of `enabled`, since the roles are also wanted on clusters where the platform ships the Gateway API CRDs.
On such clusters, set `version` and `channel` to the platform's Gateway API version so the derived rules match the installed CRDs.

See the xref:references/parameters.adoc[parameters] reference for further details.
46 changes: 46 additions & 0 deletions docs/modules/ROOT/pages/references/library.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
= Component library

The component provides a component library (`lib/gateway-api.libsonnet`) with helper functions for other components which deploy Gateway API resources.

The library is available to other components when this component is included in the cluster's applications.

== Helper functions

[horizontal]
`GatewayClass(name)`:: Creates a `GatewayClass` in API version `gateway.networking.k8s.io/v1`.
`Gateway(name)`:: Creates a `Gateway` in API version `gateway.networking.k8s.io/v1`.
`HTTPRoute(name)`:: Creates an `HTTPRoute` in API version `gateway.networking.k8s.io/v1`.
`ReferenceGrant(name)`:: Creates a `ReferenceGrant` in API version `gateway.networking.k8s.io/v1beta1`.

Each helper takes the resource name as its only argument and returns a partial resource containing `apiVersion`, `kind` and `metadata`.
Merge `metadata` and `spec` into the result as needed.
The returned resources carry the `argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true` annotation, so that ArgoCD doesn't fail a consumer's sync when the Gateway API CRDs aren't installed yet.

== Constants

[horizontal]
`gatewayApiGroup`:: The API group `gateway.networking.k8s.io`.
`gatewayApiExperimentalGroup`:: The API group `gateway.networking.x-k8s.io` used by experimental-channel-only resources.

== Example

[source,jsonnet]
----
local gw = import 'lib/gateway-api.libsonnet';

{
'10_gateway': gw.Gateway('my-gateway') {
metadata+: {
namespace: 'my-namespace',
},
spec: {
gatewayClassName: 'my-class',
listeners: [ {
name: 'http',
port: 80,
protocol: 'HTTP',
} ],
},
},
}
----
51 changes: 47 additions & 4 deletions docs/modules/ROOT/pages/references/parameters.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,61 @@

The parent key for all of the following parameters is `gateway_api`.

== `namespace`
== `enabled`

[horizontal]
type:: boolean
default:: `true`

Whether to install the upstream Gateway API CRDs and accompanying non-CRD objects from the upstream release manifest (such as the `safe-upgrades` ValidatingAdmissionPolicy shipped since v1.5).

This parameter must be set to `false` on clusters where the platform already ships the CRDs (for example OpenShift 4.19+).
It enables users to still include the component on such platforms to enable other components to unconditionally use the `gateway-api.libsonnet` provided by the component.

== `channel`

[horizontal]
type:: string
default:: `syn-gateway-api`
default:: `standard`

The namespace in which to deploy this component.
The Gateway API https://gateway-api.sigs.k8s.io/concepts/versioning/[release channel] to install.
Supported values are `standard` and `experimental`.
The experimental channel is a superset of the standard channel and additionally contains resources from the `gateway.networking.x-k8s.io` group.

NOTE: A cluster has exactly one channel and version.
If multiple Gateway API implementations with different requirements are deployed on the same cluster, pin `channel` and `version` explicitly in the cluster or tenant configuration.

== `version`

[horizontal]
type:: string
default:: `v1.6.1`

The upstream Gateway API version to install.

== `source`

[horizontal]
type:: string
default:: `https://github.com/kubernetes-sigs/gateway-api/releases/download/${gateway_api:version}/${gateway_api:channel}-install.yaml`

The URL from which to download the Gateway API CRD manifests.
Usually there's no need to override this.

== `openshift_version`

[horizontal]
type:: dictionary
default:: `{"Major": "4", "Minor": "19"}`

The OpenShift minor version of the cluster.
On OpenShift 4.19 or later (detected through this parameter together with the `openshift4` or `oke` distribution fact), the component doesn't install the Gateway API CRDs, since the cluster ingress operator ships and owns them.

== Example

[source,yaml]
----
namespace: example-namespace
gateway_api:
channel: experimental
version: v1.6.1
----
1 change: 1 addition & 0 deletions docs/modules/ROOT/partials/nav.adoc
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
* xref:index.adoc[Home]
* xref:references/parameters.adoc[Parameters]
* xref:references/library.adoc[Component library]
Loading
Loading