Repository: envoyproxy/gateway
Pull request #579 (2022-10-17 07:54:03)

Fix Secrets deletion logic in gateway reconcile method
When a Gateway resource is deleted, its Secrets in resource map which are not referenced from other Gateways should be deleted. It was in the other way around and this  PR is to fix it.


Patch:
diff --git a/.github/markdown_lint_config.json b/.github/markdown_lint_config.json
new file mode 100644
index 000000000..455051201
--- /dev/null
+++ b/.github/markdown_lint_config.json
@@ -0,0 +1,52 @@
+{
+  "MD001": true,
+  "MD002": false,
+  "MD003": false,
+  "MD004": false,
+  "MD005": false,
+  "MD006": false,
+  "MD007": false,
+  "MD008": false,
+  "MD009": false,
+  "MD010": false,
+  "MD011": false,
+  "MD012": false,
+  "MD013": false,
+  "MD014": false,
+  "MD015": false,
+  "MD016": false,
+  "MD017": false,
+  "MD018": false,
+  "MD019": false,
+  "MD020": false,
+  "MD021": false,
+  "MD022": false,
+  "MD023": false,
+  "MD024": false,
+  "MD025": false,
+  "MD026": false,
+  "MD027": false,
+  "MD028": false,
+  "MD029": false,
+  "MD030": false,
+  "MD031": true,
+  "MD032": false,
+  "MD033": false,
+  "MD034": false,
+  "MD035": false,
+  "MD036": false,
+  "MD037": true,
+  "MD038": true,
+  "MD039": false,
+  "MD040": false,
+  "MD041": false,
+  "MD042": false,
+  "MD043": false,
+  "MD044": false,
+  "MD045": false,
+  "MD046": false,
+  "MD047": false,
+  "MD048": false,
+  "MD049": false,
+  "MD050": false
+}
diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml
index f8ba90a3e..e070a532f 100644
--- a/.github/workflows/docs.yaml
+++ b/.github/workflows/docs.yaml
@@ -6,15 +6,30 @@ on:
       - "release-v*"
     paths-ignore:
       - "**/*.png"
-  # pull_request:
-  #   branches:
-  #     - "main"
-  #     - "release-v*"
-  #   paths-ignore:
-  #     - "**/*.png"
+  pull_request:
+    branches:
+      - "main"
+      - "release-v*"
+    paths-ignore:
+      - "**/*.png"
+
 jobs:
+  docs-lint:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Check out code
+        uses: actions/checkout@v3
+
+      - name: Run markdown linter
+        uses: nosborn/github-action-markdown-cli@v3.1.0
+        with:
+          files: docs/*
+          config_file: ".github/markdown_lint_config.json"
+
   docs-build:
+    if: github.event_name == 'push'
     runs-on: ubuntu-latest
+    needs: docs-lint
     steps:
       - uses: actions/checkout@v3
       - uses: ./tools/github-actions/setup-deps
@@ -33,6 +48,7 @@ jobs:
 
   # This workflow contains a single job called "build"
   docs-publish:
+    if: github.event_name == 'push'
     runs-on: ubuntu-latest
     needs: docs-build
 
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 7b1e34589..efcc8ade0 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -27,8 +27,8 @@ jobs:
         run: |
           skopeo copy --all docker://docker.io/envoyproxy/gateway-dev:${{ env.sha_short }} docker://docker.io/envoyproxy/gateway:${{ env.release_tag }}
 
-      - name: Generate Release Manifests
-        run: make generate-manifests IMAGE=envoyproxy/gateway TAG=${{ env.release_tag}} OUTPUT_DIR=release-artifacts
+      - name: Generate Release Artifacts
+        run: make generate-artifacts IMAGE=envoyproxy/gateway TAG=${{ env.release_tag}} OUTPUT_DIR=release-artifacts
 
       - name: Upload Release Manifests
         uses: softprops/action-gh-release@v1
@@ -36,3 +36,4 @@ jobs:
           files: |
             release-artifacts/install.yaml
             release-artifacts/quickstart.yaml
+            release-artifacts/release-notes.yaml
diff --git a/DEVELOPER.md b/DEVELOPER.md
index fb5639e48..c669eeedc 100644
--- a/DEVELOPER.md
+++ b/DEVELOPER.md
@@ -74,9 +74,9 @@ and runs the Gateway API conformance tests.
 
 ### Debugging the Envoy Config
 An easy way to view the envoy config that Envoy Gateway is using is to port-forward to the admin interface port (currently `19000`)
-on the Envoy Gateway deployment so that it can be accessed locally.
+on the Envoy deployment that corresponds to a Gateway so that it can be accessed locally.
 
-`kubectl port-forward deploy/envoy-default-eg -n envoy-gateway-system 19000:19000`
+`kubectl port-forward deploy/envoy-${GATEWAY_NAMESPACE}-${GATEWAY_NAME} -n envoy-gateway-system 19000:19000`
 
 Now you are able to view the running Envoy configuration by navigating to `127.0.0.1:19000/config_dump`.
 
diff --git a/docs/conf.py b/docs/conf.py
index f38797e57..6f6d6845e 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -7,6 +7,7 @@ import re
 extensions = [
     'sphinx.ext.duration',
     'sphinx.ext.autosectionlabel',
+    'myst_parser',
 ]
 
 html_theme = 'alabaster'
@@ -36,6 +37,11 @@ copyright = '2022 Envoy Gateway Project Authors | ' + fullversion
 envoyVersion = os.environ["ENVOY_VERSION"]
 gatewayAPIVersion = os.environ["GATEWAYAPI_VERSION"]
 
+source_suffix = {
+    '.rst': 'restructuredtext',
+    '.md': 'markdown',
+}
+
 variables_to_export = [
     "envoyVersion",
     "gatewayAPIVersion",
diff --git a/docs/design/ROADMAP.md b/docs/design/ROADMAP.md
index 1d5d942d8..d6ec649e4 100644
--- a/docs/design/ROADMAP.md
+++ b/docs/design/ROADMAP.md
@@ -1,4 +1,4 @@
-## Introduction
+# Roadmap
 
 This document serves as a high-level reference for Envoy Gateway users and contributors to understand the direction of
 the project.
@@ -16,7 +16,8 @@ the project.
 If you don't know where to start contributing, help is needed to reduce technical, automation, and documentation debt.
 Look for issues with the `help wanted` label to get started.
 
-## Roadmap
+## Details
+
 Roadmap features and timelines may change based on feedback, community contributions, etc. If you depend on a specific
 roadmap item, you're encouraged to attend a community meeting to discuss the details, or help us deliver the feature by
 contributing to the project.
@@ -24,6 +25,7 @@ contributing to the project.
 `Last Updated: October 2022`
 
 ### [v0.2.0][v0.2.0]: Establish a Solid Foundation
+
 - Complete the core Envoy Gateway implementation- [Issue #60][60].
 - Establish initial testing, e2e, integration, etc- [Issue #64][64].
 - Establish user and developer project documentation- [Issue #17][17].
@@ -31,17 +33,18 @@ contributing to the project.
 - Setup a CI/CD pipeline- [Issue #63][63].
 
 ### [v0.3.0][v0.3.0]: Drive Advanced Features through Extension Mechanisms
+
 - Global Rate Limiting
 - AuthN/AuthZ- [Issue #336][336].
 - Lets Encrypt Integration
 
 ### [v0.4.0][v0.4.0]: Manageability and Scale
+
 - Tooling for devs/infra admins to aid in managing/maintaining EG
 - Support advanced provisioning use cases (e.g. multi-cluster, serverless, etc.)
 - Perf testing (EG specifically)
 - Support for Chaos engineering?
 
-[eg_board]: https://github.com/orgs/envoyproxy/projects/1/views/1?layout=board
 [issue]: https://github.com/envoyproxy/gateway/issues
 [meeting]: https://docs.google.com/document/d/1leqwsHX8N-XxNEyTflYjRur462ukFxd19Rnk3Uzy55I/edit?usp=sharing
 [pr]: https://github.com/envoyproxy/gateway/compare
diff --git a/docs/design/CONFIG_API.md b/docs/design/config-api.md
similarity index 95%
rename from docs/design/CONFIG_API.md
rename to docs/design/config-api.md
index fbbfb2aa4..5a8478d8d 100644
--- a/docs/design/CONFIG_API.md
+++ b/docs/design/config-api.md
@@ -1,28 +1,19 @@
-Configuration API Design
-===================
-
-# Table of Contents
-1. [Motivation](#motivation)
-2. [Goals](#goals)
-3. [Non-Goals](#non-goals)
-4. [Control Plane API](#control_plane_api)
-   1. [Gateway Type](#gateway)
-   2. [Provider Type](#provider)
-   3. [Configuration Examples](#control_plane_configuration)
-6. [Data Plane API](#data_plane_api)
-   1. [Configuration Examples](#data_plane_configuration)
+# Configuration API Design
 
 ## Motivation
+
 [Issue 51][issue_51] specifies the need to design an API for configuring Envoy Gateway. The control plane is configured
 statically at startup and the data plane is configured dynamically through Kubernetes resources, primarily
 [Gateway API][gw_api] objects. Refer to the Envoy Gateway [design doc][design_doc] for additional details regarding
 Envoy Gateway terminology and configuration.
 
 ## Goals
+
 * Define an __initial__ API to configure Envoy Gateway at startup.
 * Define an __initial__ API for configuring the managed data plane, e.g. Envoy proxies.
 
 ## Non-Goals
+
 * Implementation of the configuration APIs.
 * Define the `status` subresource of the configuration APIs.
 * Define a __complete__ set of APIs for configuring Envoy Gateway. As stated in the [Goals](#goals), this document
@@ -32,6 +23,7 @@ Envoy Gateway terminology and configuration.
 * Specify tooling for managing the API, e.g. generate protos, CRDs, controller RBAC, etc.
 
 ## Control Plane API
+
 The `EnvoyGateway` API defines the control plane configuration, e.g. Envoy Gateway. Key points of this API are:
 
 * It will define Envoy Gateway's startup configuration file. If the file does not exist, Envoy Gateway will start up
@@ -44,6 +36,7 @@ The `EnvoyGateway` API defines the control plane configuration, e.g. Envoy Gatew
 * If data plane static configuration is required in the future, Envoy Gateway will use a separate file for this purpose.
 
 The `v1alpha1` version and `config.gateway.envoyproxy.io` API group get generated:
+
 ```go
 // gateway/api/config/v1alpha1/doc.go
 
@@ -54,6 +47,7 @@ package v1alpha1
 ```
 
 The initial `EnvoyGateway` API being proposed:
+
 ```go
 // gateway/api/config/v1alpha1/envoygateway.go
 
@@ -139,30 +133,37 @@ type FileProvider struct {
 	// TODO: Add config as use cases are better understood.
 }
 ```
+
 __Note:__ Provider-specific configuration is defined in the `{$PROVIDER_NAME}Provider` API.
 
 ### Gateway
+
 Gateway defines desired configuration of [Gateway API][gw_api] controllers that reconcile and translate Gateway API
 resources into the Intermediate Representation (IR). Refer to the Envoy Gateway [design doc][design_doc] for additional
 details.
 
 ### Provider
+
 Provider defines the desired configuration of an Envoy Gateway provider. A provider is an infrastructure component that
 Envoy Gateway calls to establish its runtime configuration. Provider is a [union type][union]. Therefore, Envoy Gateway
 can be configured with only one provider based on the `type` discriminator field. Refer to the Envoy Gateway
 [design doc][design_doc] for additional details.
 
 ### Control Plane Configuration
+
 The configuration file is defined by the EnvoyGateway API type. At startup, Envoy Gateway searches for the configuration
 at "/etc/envoy-gateway/config.yaml".
 
 Start Envoy Gateway:
+
 ```shell
 $ ./envoy-gateway
 ```
+
 Since the configuration file does not exist, Envoy Gateway will start with default configuration parameters.
 
 The Kubernetes provider can be configured explicitly using `provider.kubernetes`:
+
 ```yaml
 $ cat << EOF > /etc/envoy-gateway/config.yaml
 apiVersion: config.gateway.envoyproxy.io/v1alpha1
@@ -172,9 +173,11 @@ provider:
   kubernetes: {}
 EOF
 ```
+
 This configuration will cause Envoy Gateway to use the Kubernetes provider with default configuration parameters.
 
 The Kubernetes provider can be configured using the `provider` field. For example, the `foo` field can be set to "bar":
+
 ```yaml
 $ cat << EOF > /etc/envoy-gateway/config.yaml
 apiVersion: config.gateway.envoyproxy.io/v1alpha1
@@ -185,11 +188,13 @@ provider:
     foo: bar
 EOF
 ```
+
 __Note:__ The Provider API from the Kubernetes package is currently undefined and `foo: bar` is provided for
 illustration purposes only.
 
 The same API structure is followed for each supported provider. The following example causes Envoy Gateway to use the
 File provider:
+
 ```yaml
 $ cat << EOF > /etc/envoy-gateway/config.yaml
 apiVersion: config.gateway.envoyproxy.io/v1alpha1
@@ -200,12 +205,14 @@ provider:
     foo: bar
 EOF
 ```
+
 __Note:__ The Provider API from the File package is currently undefined and `foo: bar` is provided for illustration
 purposes only.
 
 Gateway API-related configuration is expressed through the `gateway` field. If unspecified, Envoy Gateway will use
 default configuration parameters for `gateway`. The following example causes the [GatewayClass][gc] controller to
 manage GatewayClasses with controllerName `foo` instead of the default `gateway.envoyproxy.io/gatewayclass-controller`:
+
 ```yaml
 $ cat << EOF > /etc/envoy-gateway/config.yaml
 apiVersion: config.gateway.envoyproxy.io/v1alpha1
@@ -215,11 +222,13 @@ gateway:
 ```
 
 With any of the above configuration examples, Envoy Gateway can be started without any additional arguments:
+
 ```shell
 $ ./envoy-gateway
 ```
 
 ## Data Plane API
+
 The data plane is configured dynamically through Kubernetes resources, primarily [Gateway API][gw_api] objects.
 Optionally, the data plane infrastructure can be configured by referencing a [custom resource (CR)][cr] through
 `spec.parametersRef` of the managed GatewayClass. The `envoyproxies` API defines the data plane infrastructure
@@ -233,6 +242,7 @@ configuration and is represented as the CR referenced by the managed GatewayClas
   > not propagated down to existing Gateways.
 
 The initial `envoyproxies` API being proposed:
+
 ```go
 // gateway/api/config/v1alpha1/envoyproxy.go
 
@@ -262,12 +272,15 @@ type EnvoyProxyStatus struct {
 	// Undefined by this design spec.
 }
 ```
+
 The EnvoyProxySpec and EnvoyProxyStatus fields will be defined in the future as proxy infrastructure configuration use
 cases are better understood.
 
 ### Data Plane Configuration
+
 GatewayClass and Gateway resources define the data plane infrastructure. Note that all examples assume Envoy Gateway is
 running with the Kubernetes provider.
+
 ```yaml
 apiVersion: gateway.networking.k8s.io/v1beta1
 kind: GatewayClass
@@ -287,12 +300,14 @@ spec:
     protocol: HTTP
     port: 80
 ```
+
 Since the GatewayClass does not define `spec.parametersRef`, the data plane is provisioned using default configuration
 parameters. All Envoy proxies will be configured with a http listener and a Kubernetes LoadBalancer service listening
 on port 80.
 
 The following example will configure the data plane to use a ClusterIP service instead of the default LoadBalancer
 service:
+
 ```yaml
 apiVersion: gateway.networking.k8s.io/v1beta1
 kind: GatewayClass
@@ -324,13 +339,12 @@ spec:
   networkPublishing:
     type: ClusterIPService
 ```
+
 __Note:__ The NetworkPublishing API is currently undefined and is provided here for illustration purposes only.
 
 [issue_51]: https://github.com/envoyproxy/gateway/issues/51
 [design_doc]: https://github.com/envoyproxy/gateway/blob/main/docs/design/SYSTEM_DESIGN.md
-[xds]: https://github.com/cncf/xds
 [gw_api]: https://gateway-api.sigs.k8s.io/
-[config_guide]: https://github.com/envoyproxy/gateway/blob/main/docs/CONFIG.md
 [gc]: https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.GatewayClass
 [cr]: https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/
 [union]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#unions
diff --git a/docs/design/gatewayapi-translator.md b/docs/design/gatewayapi-translator.md
index b79abc314..5f2d4abcf 100644
--- a/docs/design/gatewayapi-translator.md
+++ b/docs/design/gatewayapi-translator.md
@@ -1,22 +1,27 @@
 # Gateway API Translator Design
 
 ## Assumptions
+
 - initially target core conformance features only, to be followed by extended conformance features
 
 ## Inputs and Outputs
 
 The main inputs to the Gateway API translator are:
+
 - the GatewayClass to process
 - Gateways, HTTPRoutes, Services, Secrets
 
 The outputs of the Gateway API translator are:
+
 - IR
 - status updates for GatewayClass, Gateways, HTTPRoutes
 
 ## Listener Compatibility
+
 Since Envoy Gateway handles all Gateways for a given GatewayClass, we need to determine the compatibility of _all_ Listeners across _all_ of those Gateways.
 
 The rules are:
+
 - for a given port number, every Listener using that port number must have a compatible protocol (either all HTTP, or all HTTPS/TLS).
 - for a given port number, every Listener using that port number must have a distinct hostname (at most one Listener per port can have no hostname).
 
@@ -60,7 +65,7 @@ spec:
       hostname: whales.envoygateway.io
 ```
 
-####  Example 2: Gateways with compatible Listeners (same port & protocol, one hostname specified, one not)
+#### Example 2: Gateways with compatible Listeners (same port & protocol, one hostname specified, one not)
 
 ```yaml
 kind: Gateway
@@ -171,6 +176,7 @@ Gateway API specifies a rich set of status fields & conditions for each resource
 To be conformant, Envoy Gateway needs to compute the appropriate status fields and conditions as it's processing resources.
 
 Status needs to be computed and set for:
+
 - the GatewayClass (gatewayclass.status.conditions)
 - each Listener for each Gateway (gateway.status.listeners)
 - each Gateway, based on its Listeners' statuses (gateway.status.conditions)
@@ -183,7 +189,6 @@ The Gateway API translator will take the approach of populating status on the re
 The following roughly outlines the translation process.
 Each step may produce (1) IR; and (2) status updates on Gateway API resources.
 
-```
 1. Process Gateway Listeners
     - validate unique hostnames/ports/protcols
     - validate/compute supported kinds
@@ -208,7 +213,6 @@ Each step may produce (1) IR; and (2) status updates on Gateway API resources.
         - foreach matching listener:
             - foreach hostname intersection with route:
                 - add each computed route rule to host
-```
 
 ## Context Structs
 
diff --git a/docs/design/SYSTEM_DESIGN.md b/docs/design/system-design.md
similarity index 95%
rename from docs/design/SYSTEM_DESIGN.md
rename to docs/design/system-design.md
index 2b741bc11..1f30dc720 100644
--- a/docs/design/SYSTEM_DESIGN.md
+++ b/docs/design/system-design.md
@@ -1,33 +1,41 @@
-## System Design
+# System Design
+
+## Goals
 
-### Goals
 * Define the system components needed to satisfy the requirements of Envoy Gateway.
 
-### Non-Goals
+## Non-Goals
+
 * Create a detailed design and interface specification for each system component.
 
-### Terminology
+## Terminology
+
 * Control Plane- A collection of inter-related software components for providing application gateway and routing
   functionality. The control plane is implemented by Envoy Gateway and provides services for managing the data plane.
   These services are detailed in the [components](#components) section.
 * Data Plane- Provides intelligent application-level traffic routing and is implemented as one or more Envoy proxies.
 
-### Architecture
+## Architecture
+
 ![Architecture](../images/architecture.png)
 
-### Configuration
+## Configuration
+
 Envoy Gateway is configured statically at startup and the managed data plane is configured dynamically through
 Kubernetes resources, primarily [Gateway API][gw_api] objects.
 
-#### Static Configuration
+### Static Configuration
+
 Static configuration is used to configure Envoy Gateway at startup, i.e. change the GatewayClass controllerName,
 configure a Provider, etc. Currently, Envoy Gateway only supports configuration through a configuration file. If the
 configuration file is not provided, Envoy Gateway will start up with default configuration parameters.
 
-#### Dynamic Configuration
+### Dynamic Configuration
+
 Dynamic configuration is based on the concept of a declaring the desired state of the data plane and using
 reconciliation loops to drive the actual state toward the desired state. The desired state of the data plane is
 defined as Kubernetes resources that provide the following services:
+
 * Infrastructure Management- Manage the data plane infrastructure, i.e. deploy, upgrade, etc. This configuration is
   expressed through [GatewayClass][gc] and [Gateway][gw] resources. A TBD [Custom Resource][cr] can be referenced by
   `gatewayclass.spec.parametersRef` to modify data plane infrastructure default parameters,
@@ -38,58 +46,68 @@ defined as Kubernetes resources that provide the following services:
   Although a backend can be any valid Kubernetes Group/Kind resource, Envoy Gateway only supports a [Service][svc]
   reference.
 
-### Components
+## Components
 
 Envoy Gateway is made up of several components that communicate in-process; how this communication happens is described
 in [watching.md][].
 
-#### Provider
+### Provider
+
 A Provider is an infrastructure component that Envoy Gateway calls to establish its runtime configuration, resolve
 services, persist data, etc. Kubernetes and File are the only supported providers. However, other providers can be added
 in the future as Envoy Gateway use cases are better understood. A provider is configured at start up through Envoy
 Gateway's [static configuration](#static-configuration).
 
-##### Kubernetes Provider
+#### Kubernetes Provider
+
 * Uses Kubernetes-style controllers to reconcile Kubernetes resources that comprise the
   [dynamic configuration](#dynamic-configuration).
 * Manages the data plane through Kubernetes API CRUD operations.
 * Uses Kubernetes for Service discovery.
 * Uses etcd (via Kubernetes API) to persist data.
 
-##### File Provider
+#### File Provider
 
 * Uses a file watcher to watch files in a directory that define the data plane configuration.
 * Manages the data plane by calling internal APIs, e.g. `CreateDataPlane()`.
 * Uses the host's DNS for Service discovery.
 * If needed, the local filesystem is used to persist data.
 
-#### Resource Watcher
+### Resource Watcher
+
 The Resource Watcher watches resources used to establish and maintain Envoy Gateway's dynamic configuration. The
 mechanics for watching resources is provider-specific, e.g. informers, caches, etc. are used for the Kubernetes
 provider. The Resource Watcher uses the configured provider for input and provides resources to the Resource Translator
 as output.
 
-#### Resource Translator
+### Resource Translator
+
 The Resource Translator translates external resources, e.g. GatewayClass, from the Resource Watcher to the Intermediate
 Representation (IR). It is responsible for:
+
 * Translating infrastructure-specific resources/fields from the Resource Watcher to the Infra IR.
 * Translating proxy configuration resources/fields from the Resource Watcher to the xDS IR.
 
-#### Intermediate Representation (IR)
+### Intermediate Representation (IR)
+
 The Intermediate Representation defines internal data models that external resources are translated into. This allows
 Envoy Gateway to be decoupled from the external resources used for dynamic configuration. The IR consists of an Infra IR
 used as input for the Infra Manager and an xDS IR used as input for the xDS Translator.
+
 * Infra IR- Used as the internal definition of the managed data plane infrastructure.
 * xDS IR- Used as the internal definition of the managed data plane xDS configuration.
 
-#### xDS Translator
+### xDS Translator
+
 The xDS Translator translates the xDS IR into xDS Resources that are consumed by the xDS server.
 
-#### xDS Server
+### xDS Server
+
 The xDS Server is a xDS gRPC Server based on [Go Control Plane][go_cp]. Go Control Plane implements the xDS Server
 Protocol and is responsible for using xDS to configure the data plane.
 
-#### Infra Manager
+### Infra Manager
+
 The Infra Manager is a provider-specific component responsible for managing the following infrastructure:
 
 * Data Plane - Manages all the infrastructure required to run the managed Envoy proxies. For example, CRUD Deployment,
@@ -101,7 +119,8 @@ The Infra Manager is a provider-specific component responsible for managing the
 
 The Infra Manager consumes the Infra IR as input to manage the data plane infrastructure.
 
-### Design Decisions
+## Design Decisions
+
 * Envoy Gateway will consume one [GatewayClass][gc] by comparing its configured controller name with
   `spec.controllerName` of a GatewayClass. If multiple GatewayClasses exist with the same `spec.controllerName`, Envoy
   Gateway will follow Gateway API [guidelines][gwapi_conflicts] to resolve the conflict.
@@ -119,7 +138,8 @@ The Infra Manager consumes the Infra IR as input to manage the data plane infras
 
 The draft for this document is [here][draft_design].
 
-### Caveats
+## Caveats
+
 * The custom resource used to configure the data plane infrastructure is TBD. Track [issue 95][issue_95] for the latest
   updates.
 * Envoy Gateway's static configuration spec is currently undefined. Track [issue 95][issue_95] for the latest updates.
diff --git a/docs/design/watching.md b/docs/design/watching.md
index e505b6280..35b45abe6 100644
--- a/docs/design/watching.md
+++ b/docs/design/watching.md
@@ -1,4 +1,4 @@
-## Watching the results of other components
+# Watching Components Design
 
 Envoy Gateway is made up of several components that communicate in-process.  Some of them (namely providers) watch
 external resources, and "publish" what they see for other components to consume; others watch what another publishes and
@@ -9,7 +9,7 @@ To facilitate this communication use the [watchable][] library.  The `watchable.
 standard library's `sync.Map` type, but supports a `.Subscribe` (and `.SubscribeSubset`) method that promotes a pub/sub
 pattern.
 
-### pub
+## Pub
 
 Many of the things we communicate around are naturally named, either by a bare "name" string or by a "name"/"namespace"
 tuple.  And because `watchable.Map` is typed, it makes sense to have one map for each type of thing (very similar to if
@@ -34,7 +34,7 @@ method) the current value is a no-op; it won't trigger an event for subscribers.
 doesn't have as much state to keep track of; it doesn't need to know "did I already publish this thing", it can just
 `.Store` its data and `watchable` will do the right thing.
 
-### sub
+## Sub
 
 Meanwhile, the translator and other interested components subscribe to it with `table.Thing.Subscribe` (or
 `table.Thing.SubscribeSubset` if they only care about a few "Thing"s).  So the translator goroutine might look like:
@@ -102,7 +102,7 @@ entries for those pre-existing items; if you are working with `snapshot.Update`
 must add special handling for your first read.  We have a utility function `./internal/message.HandleSubscription` to
 help with this.
 
-### other notes
+## Other Notes
 
 The common pattern will likely be that the entrypoint that launches the goroutines for each component instantiates the
 map, and passes them to the appropriate publishers and subscribers; same as if they were communicating via a dumb
diff --git a/docs/dev/RELEASE.md b/docs/dev/RELEASE.md
new file mode 100644
index 000000000..a94e38449
--- /dev/null
+++ b/docs/dev/RELEASE.md
@@ -0,0 +1,107 @@
+# Release Process
+
+This document guides maintainers through the process of creating an Envoy Gateway release.
+
+## Prerequisites
+
+- Permissions to push to the Envoy Gateway repository.
+
+## Creating a Minor Release
+
+1. Clone the repo, checkout the `main` branch, ensure it’s up-to-date, and your local branch is clean.
+2. Create the release notes corresponding to the release number. Reference previous [release notes][]
+   for additional details.
+3. Submit a [Pull Request][] to merge the release notes into the main branch. This should be the last commit to main
+   before cutting the release.
+4. Create a new release branch from `main`. The release branch should be named
+   `release/v${MAJOR_VERSION}.${MINOR_VERSION}.0`, e.g. `release/v0.3.0`.
+
+   ```shell
+   git checkout -b release/v0.3.0
+   ```
+
+5. Push the branch to the Envoy Gateway repo.
+6. Create a topic branch and update the release tag references in the [Quickstart Guide][]. See [PR 481][] as
+   a reference for the required changes.
+7. Sign, commit, and push your changes to your fork. Send a PR to get your changes merged into the release branch.
+   Do not proceed until your PR is merged.
+8. Confirm that the [release workflow][] for your PR completed successfully.
+9. Tag the head of your release branch with the release tag. For example:
+
+   ```shell
+   git tag -a v0.3.0 -m 'Envoy Gateway v0.3.0 Release'
+   ```
+
+10. Push the tag to the Envoy Gateway repository.
+
+    ```shell
+    git push --tags
+    ```
+
+11. This will trigger the [release GitHub action][] that generates the release, release artifacts, etc.
+12. Confirm that the [release workflow][] completed successfully.
+13. Confirm that the Envoy Gateway [image][] with the correct release tag was published to Docker Hub.
+14. Confirm that the [release][] was created.
+15. Confirm that the steps in the [Quickstart Guide][] work as expected.
+16. [Generate][] the GitHub changelog.
+17. Submit a PR to merge the Quickstart Guide changes from the release branch into the main branch.
+18. If you find any bugs in this process, please create an issue.
+
+## Creating a Release Candidate
+
+1. Clone the repo, checkout the `main` branch, ensure it’s up-to-date, and your local branch is clean.
+2. Create the release notes corresponding to the release candidate that summarizes the changes included in the
+   release candidate. Reference previous [release notes][] for additional details.
+3. Submit a [Pull Request][] to merge the changelog into the main branch. This should be the last commit to main
+   before cutting the release candidate.
+4. Tag the head of the main branch with the release candidate number. The tag should be named
+   `v${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER}`. For example:
+
+   ```shell
+   git tag -a v0.3.0-rc.1 -m 'Envoy Gateway v0.3.0-rc.1 Release Candidate'
+   ```
+
+5. Push the tag to the Envoy Gateway repository.
+
+   ```shell
+   git push --tags
+   ```
+
+6. This will trigger the [release GitHub action][] that generates the release, release artifacts, etc.
+7. Confirm that the [release workflow][] completed successfully.
+8. Confirm that the Envoy Gateway [image][] with the correct release tag was published to Docker Hub.
+9. Confirm that the [release][] was created.
+10. Note that the [Quickstart Guide][] references are __not__ updated for release candidates. However, test
+    the quickstart steps using the release candidate by manually updating the links.
+11. [Generate][] the GitHub changelog.
+12. Ensure you check the "This is a pre-release" checkbox when editing the GitHub release.
+13. If you find any bugs in this process, please create an issue.
+
+## Announcing the Release
+
+It's important that the world knows about the release. Follow the steps to announce the release.
+
+1. Set the release information in the Envoy Gateway Slack channel. For example:
+
+   ```shell
+   Envoy Gateway v0.3.0 has been released: https://github.com/envoyproxy/gateway/releases/tag/v0.3.0
+   ```
+
+2. Send a message to the Envoy Gateway Slack channel. For example:
+
+   ```shell
+   I am pleased to announce the release of Envoy Gateway v${MAJOR_VERSION}.${MINOR_VERSION}.0. The release would not be
+   possible without all the support from the Envoy Gateway community...
+   ```
+
+   Include a sentence or two that highlights key aspects of the release.
+
+[release notes]: https://github.com/envoyproxy/gateway/tree/main/release-notes
+[PR 481]: https://github.com/envoyproxy/gateway/pull/481
+[Pull Request]: https://github.com/envoyproxy/gateway/pulls
+[Quickstart Guide]: https://github.com/envoyproxy/gateway/blob/main/docs/user/QUICKSTART.md
+[release GitHub action]: https://github.com/envoyproxy/gateway/blob/main/.github/workflows/release.yaml
+[release workflow]: https://github.com/envoyproxy/gateway/actions/workflows/release.yaml
+[image]: https://hub.docker.com/r/envoyproxy/gateway/tags
+[release]: https://github.com/envoyproxy/gateway/releases
+[Generate]: https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes
diff --git a/docs/index.rst b/docs/index.rst
index 4304b533b..ec7467ae4 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -20,5 +20,11 @@ standalone or Kubernetes-based application gateway.
 
   intro/index
   intro/compatibility
+  user/QUICKSTART
+  design/system-design
+  design/ROADMAP
+  design/gatewayapi-translator
+  design/watching
+  design/config-api
   about_docs
   get_involved
diff --git a/docs/user/HTTP_ROUTING.md b/docs/user/HTTP_ROUTING.md
new file mode 100644
index 000000000..3d19062ab
--- /dev/null
+++ b/docs/user/HTTP_ROUTING.md
@@ -0,0 +1,115 @@
+# HTTP Routing
+
+The [HTTPRoute][] resource allows users to configure HTTP routing by matching HTTP traffic and forwarding it to
+Kubernetes backends. Currently, the only supported backend supported by Envoy Gateway is a Service resource. This guide
+shows how to route traffic based on host, header, and path fields and forward the traffic to different Kubernetes
+Services. To learn more about HTTP routing, refer to the [Gateway API documentation][].
+
+Follow the steps from the [Quickstart Guide](QUICKSTART.md) to install Envoy Gateway and then install the example
+resources used for this guide.
+
+```shell
+kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/v0.2.0/examples/kubernetes/http-routing.yaml
+```
+
+The manifest installs a [GatewayClass][], [Gateway][], four Deployments, four Services, and three HTTPRoute resources.
+
+The GatewayClass is a cluster-scoped resource that represents a class of Gateways that can be instantiated. Envoy
+Gateway is configured by default to manage GatewayClasses with
+`controllerName: gateway.envoyproxy.io/gatewayclass-controller`.
+
+Check the status of the GatewayClass:
+
+```shell
+kubectl get gc --selector=example=http-routing
+```
+
+The status should reflect "Accepted=True", indicating Envoy Gateway is managing the GatewayClass.
+
+A Gateway represents configuration of infrastructure. When a Gateway is created, [Envoy proxy][] infrastructure is
+provisioned or configured by Envoy Gateway. The `gatewayClassName` defines the name of a GatewayClass used by this
+Gateway. Check the status of the Gateway:
+
+```shell
+kubectl get gateways --selector=example=http-routing
+```
+
+The status should reflect "Ready=True", indicating the Envoy proxy infrastructure has been provisioned. The status also
+provides the address of the Gateway. This address is used later in the guide to test connectivity to proxied backend
+services.
+
+The three HTTPRoute resources create routing rules on the Gateway. In order to receive traffic from a Gateway,
+an HTTPRoute must be configured with `parentRefs` which reference the parent Gateway(s) that it should be attached to.
+An HTTPRoute can match against a [single set of hostnames][spec]. These hostnames are matched before any other matching
+within the HTTPRoute takes place. Since `example.com`, `foo.example.com`, and `bar.example.com` are separate hosts with
+different routing requirements, each is deployed as its own HTTPRoute - `example-route, ``foo-route`, and `bar-route`.
+
+Check the status of the HTTPRoutes:
+
+```shell
+kubectl get httproutes --selector=example=http-routing -o yaml
+```
+
+The status for each HTTPRoute should surface "Accepted=True" and a `parentRef` that references the example Gateway.
+The `example-route` matches any traffic for "example.com" and forwards it to the "example-svc" Service. Before testing
+HTTP routing to the `example-svc` backend, get the Gateway's address.
+
+```shell
+export GATEWAY_HOST=$(kubectl get gateway/example-gateway -o jsonpath='{.status.addresses[0].value}')
+```
+
+Test HTTP routing to the `example-svc` backend.
+
+```shell
+curl -vvv --header "Host: example.com" "http://${GATEWAY_HOST}/"
+```
+
+A `200` status code should be returned and the body should include `"pod": "example-backend-*"` indicating the traffic
+was routed to the example backend service. If you change the hostname to a hostname not represented in any of the
+HTTPRoutes, e.g. "www.example.com", the HTTP traffic will not be routed and a `404` should be returned.
+
+The `foo-route` matches any traffic for `foo.example.com` and applies its routing rules to forward the traffic to the
+"foo-svc" Service. Since there is only one path prefix match for `/login`, only `foo.example.com/login/*` traffic will
+be forwarded. Test HTTP routing to the `foo-svc` backend.
+
+```shell
+curl -vvv --header "Host: foo.example.com" "http://${GATEWAY_HOST}/login"
+```
+
+A `200` status code should be returned and the body should include `"pod": "foo-backend-*"` indicating the traffic
+was routed to the foo backend service. Traffic to any other paths that do not begin with `/login` will not be matched by
+this HTTPRoute. Test this by removing `/login` from the request.
+
+```shell
+curl -vvv --header "Host: foo.example.com" "http://${GATEWAY_HOST}/"
+```
+
+The HTTP traffic will not be routed and a `404` should be returned.
+
+Similarly, the `bar-route` HTTPRoute matches traffic for `bar.example.com`. All traffic for this hostname will be
+evaluated against the routing rules. The most specific match will take precedence which means that any traffic with the
+`env:canary` header will be forwarded to `bar-svc-canary` and if the header is missing or not `canary` then it'll be
+forwarded to `bar-svc`. Test HTTP routing to the `bar-svc` backend.
+
+```shell
+curl -vvv --header "Host: bar.example.com" "http://${GATEWAY_HOST}/"
+```
+
+A `200` status code should be returned and the body should include `"pod": "bar-backend-*"` indicating the traffic
+was routed to the foo backend service.
+
+Test HTTP routing to the `bar-canary-svc` backend by adding the `env: canary` header to the request.
+
+```shell
+curl -vvv --header "Host: bar.example.com" --header "env: canary" "http://${GATEWAY_HOST}/"
+```
+
+A `200` status code should be returned and the body should include `"pod": "bar-canary-backend-*"` indicating the
+traffic was routed to the foo backend service.
+
+[HTTPRoute]: https://gateway-api.sigs.k8s.io/api-types/httproute/
+[Gateway API documentation]: https://gateway-api.sigs.k8s.io/
+[GatewayClass]: https://gateway-api.sigs.k8s.io/api-types/gatewayclass/
+[Gateway]: https://gateway-api.sigs.k8s.io/api-types/gateway/
+[Envoy proxy]: https://www.envoyproxy.io/
+[spec]: /references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteSpec
diff --git a/docs/user/QUICKSTART.md b/docs/user/QUICKSTART.md
index 980d1e371..931ca88bd 100644
--- a/docs/user/QUICKSTART.md
+++ b/docs/user/QUICKSTART.md
@@ -1,57 +1,72 @@
-## Introduction
+# Quickstart
+
 This guide will help you get started with Envoy Gateway in a few simple steps.
 
 ## Prerequisites
+
 A Kubernetes cluster.
 
 __Note:__ Envoy Gateway is tested against Kubernetes v1.24.0.
 
 ## Installation
+
 Install the Gateway API CRDs:
+
 ```shell
 kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/v0.2.0-rc2/gatewayapi-crds.yaml
 ```
 
 Run Envoy Gateway:
+
 ```shell
 kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/v0.2.0-rc2/install.yaml
 ```
 
 Run the example app:
+
 ```shell
 kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/v0.2.0-rc2/examples/kubernetes/httpbin.yaml
 ```
 
 The Gateway API resources must be created in the following order. First, create the GatewayClass:
+
 ```shell
 kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/v0.2.0-rc2/examples/kubernetes/gatewayclass.yaml
 ```
 
 Create the Gateway:
+
 ```shell
 kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/v0.2.0-rc2/examples/kubernetes/gateway.yaml
 ```
 
 Create the HTTPRoute to route traffic through Envoy proxy to the example app:
+
 ```shell
 kubectl apply -f https://raw.githubusercontent.com/envoyproxy/gateway/v0.2.0-rc2/examples/kubernetes/httproute.yaml
 ```
 
 ### Testing the configuration
+
 Port forward to the Envoy service:
+
 ```shell
 kubectl -n envoy-gateway-system port-forward service/envoy-default-eg 8888:8080 &
 ```
 
 Curl the example app through Envoy proxy:
+
 ```shell
 curl --verbose --header "Host: www.example.com" http://localhost:8888/get
 ```
+
 You can replace `get` with any of the supported [httpbin methods][httpbin_methods].
 
 ### For clusters with External Loadbalancer support
+
 You can also test the same functionality by sending traffic to the External IP. To get the external IP of the
 Envoy service, run:
+
 ```shell
 export GATEWAY_HOST=$(kubectl get svc/envoy-default-eg -n envoy-gateway-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
 ```
@@ -60,46 +75,55 @@ In certain environments, the load balancer may be exposed using a hostname, inst
 `ip` in the above command with `hostname`.
 
 Curl the example app through Envoy proxy:
+
 ```shell
 curl --verbose --header "Host: www.example.com" http://$GATEWAY_HOST:8080/get
 ```
+
 You can replace `get` with any of the supported [httpbin methods][httpbin_methods].
 
 ## Clean-Up
+
 Use the steps in this section to uninstall everything from the quickstart guide.
 
 Delete the HTTPRoute:
+
 ```shell
 kubectl delete httproute/httpbin
 ```
 
 Delete the Gateway:
+
 ```shell
 kubectl delete gateway/eg
 ```
 
 Delete the GatewayClass:
+
 ```shell
 kubectl delete gc/eg
 ```
 
 Uninstall the example app:
+
 ```shell
 kubectl delete -f https://raw.githubusercontent.com/envoyproxy/gateway/v0.2.0-rc2/examples/kubernetes/httpbin.yaml
 ```
 
 Uninstall Envoy Gateway:
+
 ```shell
 kubectl delete -f https://github.com/envoyproxy/gateway/releases/download/v0.2.0-rc2/install.yaml
 ```
 
 Uninstall Gateway API CRDs:
+
 ```shell
 kubectl delete -f https://github.com/envoyproxy/gateway/releases/download/v0.2.0-rc2/gatewayapi-crds.yaml
 ```
 
 ## Next Steps
+
 Checkout the [Developer Guide](../../DEVELOPER.md) to get involved in the project.
 
-[kind]: https://kind.sigs.k8s.io/
 [httpbin_methods]: https://httpbin.org/#/HTTP_Methods
diff --git a/examples/kubernetes/http-routing.yaml b/examples/kubernetes/http-routing.yaml
new file mode 100644
index 000000000..2dc774b71
--- /dev/null
+++ b/examples/kubernetes/http-routing.yaml
@@ -0,0 +1,272 @@
+# This file contains the base resources that the docs/user/HTTP_ROUTING.md guide relies on.
+# This includes a GatewayClass, Gateway, Services and Deployments that are used as backends
+# for routing traffic.
+kind: GatewayClass
+apiVersion: gateway.networking.k8s.io/v1beta1
+metadata:
+  name: example-gateway-class
+  labels:
+    example: http-routing
+spec:
+  controllerName: gateway.envoyproxy.io/gatewayclass-controller
+---
+apiVersion: gateway.networking.k8s.io/v1beta1
+kind: Gateway
+metadata:
+  name: example-gateway
+  labels:
+    example: http-routing
+spec:
+  gatewayClassName: example-gateway-class
+  listeners:
+    - name: http
+      protocol: HTTP
+      port: 80
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: example-svc
+  labels:
+    example: http-routing
+spec:
+  ports:
+    - name: http
+      port: 8080
+      targetPort: 3000
+  selector:
+    app: example-backend
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: example-backend
+  labels:
+    app: example-backend
+    example: http-routing
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: example-backend
+  template:
+    metadata:
+      labels:
+        app: example-backend
+    spec:
+      containers:
+        - name: example-backend
+          image: gcr.io/k8s-staging-ingressconformance/echoserver:v20220815-e21d1a4
+          env:
+            - name: POD_NAME
+              valueFrom:
+                fieldRef:
+                  fieldPath: metadata.name
+            - name: NAMESPACE
+              valueFrom:
+                fieldRef:
+                  fieldPath: metadata.namespace
+          resources:
+            requests:
+              cpu: 10m
+---
+apiVersion: gateway.networking.k8s.io/v1beta1
+kind: HTTPRoute
+metadata:
+  name: example-route
+  labels:
+    example: http-routing
+spec:
+  parentRefs:
+    - name: example-gateway
+  hostnames:
+    - "example.com"
+  rules:
+    - backendRefs:
+        - name: example-svc
+          port: 8080
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: foo-svc
+  labels:
+    example: http-routing
+spec:
+  ports:
+    - name: http
+      port: 8080
+      targetPort: 3000
+  selector:
+    app: foo-backend
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: foo-backend
+  labels:
+    app: foo-backend
+    example: http-routing
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: foo-backend
+  template:
+    metadata:
+      labels:
+        app: foo-backend
+    spec:
+      containers:
+        - name: foo-backend
+          image: gcr.io/k8s-staging-ingressconformance/echoserver:v20220815-e21d1a4
+          env:
+            - name: POD_NAME
+              valueFrom:
+                fieldRef:
+                  fieldPath: metadata.name
+            - name: NAMESPACE
+              valueFrom:
+                fieldRef:
+                  fieldPath: metadata.namespace
+          resources:
+            requests:
+              cpu: 10m
+---
+apiVersion: gateway.networking.k8s.io/v1beta1
+kind: HTTPRoute
+metadata:
+  name: foo-route
+  labels:
+    example: http-routing
+spec:
+  parentRefs:
+    - name: example-gateway
+  hostnames:
+    - "foo.example.com"
+  rules:
+    - matches:
+        - path:
+            type: PathPrefix
+            value: /login
+      backendRefs:
+        - name: foo-svc
+          port: 8080
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: bar-svc
+  labels:
+    example: http-routing
+spec:
+  ports:
+    - name: http
+      port: 8080
+      targetPort: 3000
+  selector:
+    app: bar-backend
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: bar-backend
+  labels:
+    app: bar-backend
+    example: http-routing
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: bar-backend
+  template:
+    metadata:
+      labels:
+        app: bar-backend
+    spec:
+      containers:
+        - name: bar-backend
+          image: gcr.io/k8s-staging-ingressconformance/echoserver:v20220815-e21d1a4
+          env:
+            - name: POD_NAME
+              valueFrom:
+                fieldRef:
+                  fieldPath: metadata.name
+            - name: NAMESPACE
+              valueFrom:
+                fieldRef:
+                  fieldPath: metadata.namespace
+          resources:
+            requests:
+              cpu: 10m
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: bar-canary-svc
+  labels:
+    example: http-routing
+spec:
+  ports:
+    - name: http
+      port: 8080
+      targetPort: 3000
+  selector:
+    app: bar-canary-backend
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: bar-canary-backend
+  labels:
+    app: bar-canary-backend
+    example: http-routing
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: bar-canary-backend
+  template:
+    metadata:
+      labels:
+        app: bar-canary-backend
+    spec:
+      containers:
+        - name: bar-canary-backend
+          image: gcr.io/k8s-staging-ingressconformance/echoserver:v20220815-e21d1a4
+          env:
+            - name: POD_NAME
+              valueFrom:
+                fieldRef:
+                  fieldPath: metadata.name
+            - name: NAMESPACE
+              valueFrom:
+                fieldRef:
+                  fieldPath: metadata.namespace
+          resources:
+            requests:
+              cpu: 10m
+---
+apiVersion: gateway.networking.k8s.io/v1beta1
+kind: HTTPRoute
+metadata:
+  name: bar-route
+  labels:
+    example: http-routing
+spec:
+  parentRefs:
+    - name: example-gateway
+  hostnames:
+    - "bar.example.com"
+  rules:
+    - matches:
+        - headers:
+            - type: Exact
+              name: env
+              value: canary
+      backendRefs:
+        - name: bar-canary-svc
+          port: 8080
+    - backendRefs:
+        - name: bar-svc
+          port: 8080
diff --git a/internal/gatewayapi/translator.go b/internal/gatewayapi/translator.go
index 0d4db0be7..6212fd915 100644
--- a/internal/gatewayapi/translator.go
+++ b/internal/gatewayapi/translator.go
@@ -431,7 +431,7 @@ func (t *Translator) ProcessListeners(gateways []*GatewayContext, xdsIR XdsIRMap
 						listener.SetCondition(
 							v1beta1.ListenerConditionResolvedRefs,
 							metav1.ConditionFalse,
-							v1beta1.ListenerReasonInvalidCertificateRef,
+							v1beta1.ListenerReasonRefNotPermitted,
 							fmt.Sprintf("Certificate ref to secret %s/%s not permitted by any ReferenceGrant", *certificateRef.Namespace, certificateRef.Name),
 						)
 						break
@@ -1025,7 +1025,7 @@ func (t *Translator) ProcessHTTPRoutes(httpRoutes []*v1beta1.HTTPRoute, gateways
 								"RequestHeaderModifier Filter did not provide valid configuration to add/set/remove any headers",
 							)
 						}
-					default:
+					case v1beta1.HTTPRouteFilterExtensionRef:
 						// "If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped.
 						// Instead, requests that would have been processed by that filter MUST receive a HTTP error response."
 						errMsg := fmt.Sprintf("Unknown custom filter type: %s", filter.Type)
@@ -1039,6 +1039,19 @@ func (t *Translator) ProcessHTTPRoutes(httpRoutes []*v1beta1.HTTPRoute, gateways
 							Body:       &errMsg,
 							StatusCode: 500,
 						}
+					default:
+						// Unsupported filters.
+						errMsg := fmt.Sprintf("Unsupported filter type: %s", filter.Type)
+						parentRef.SetCondition(httpRoute,
+							v1beta1.RouteConditionAccepted,
+							metav1.ConditionFalse,
+							v1beta1.RouteReasonUnsupportedValue,
+							errMsg,
+						)
+						directResponse = &ir.DirectResponse{
+							Body:       &errMsg,
+							StatusCode: 500,
+						}
 					}
 				}
 
@@ -1181,7 +1194,11 @@ func (t *Translator) ProcessHTTPRoutes(httpRoutes []*v1beta1.HTTPRoute, gateways
 					v1beta1.RouteReasonNoMatchingListenerHostname,
 					"There were no hostname intersections between the HTTPRoute and this parent ref's Listener(s).",
 				)
-			} else {
+			}
+
+			// If no negative conditions have been set, the route is considered "Accepted=True".
+			if parentRef.httpRoute != nil &&
+				len(parentRef.httpRoute.Status.Parents[parentRef.routeParentStatusIdx].Conditions) == 0 {
 				parentRef.SetCondition(httpRoute,
 					v1beta1.RouteConditionAccepted,
 					metav1.ConditionTrue,
@@ -1361,7 +1378,11 @@ func (t *Translator) ProcessTLSRoutes(tlsRoutes []*v1alpha2.TLSRoute, gateways [
 					v1beta1.RouteReasonNoMatchingListenerHostname,
 					"There were no hostname intersections between the HTTPRoute and this parent ref's Listener(s).",
 				)
-			} else {
+			}
+
+			// If no negative conditions have been set, the route is considered "Accepted=True".
+			if parentRef.tlsRoute != nil &&
+				len(parentRef.tlsRoute.Status.Parents[parentRef.routeParentStatusIdx].Conditions) == 0 {
 				parentRef.SetCondition(tlsRoute,
 					v1beta1.RouteConditionAccepted,
 					metav1.ConditionTrue,
diff --git a/internal/provider/kubernetes/gateway.go b/internal/provider/kubernetes/gateway.go
index e740202bd..6e19957b6 100644
--- a/internal/provider/kubernetes/gateway.go
+++ b/internal/provider/kubernetes/gateway.go
@@ -385,13 +385,13 @@ func (r *gatewayReconciler) Reconcile(ctx context.Context, request reconcile.Req
 			switch {
 			case err != nil:
 				r.log.Error(err, "failed to verify if other gateways reference secret")
-			case referenced:
+			case !referenced:
 				r.log.Info("no other gateways reference secret; deleting from resource map",
 					"namespace", secret.Namespace, "name", secret.Name)
 				key := utils.NamespacedName(&secret)
 				r.resources.Secrets.Delete(key)
 			default:
-				r.log.Info("no other gateways reference secret; deleting from resource map",
+				r.log.Info("other gateways reference secret; keeping the secret in the resource map",
 					"namespace", secret.Namespace, "name", secret.Name)
 			}
 		}
diff --git a/tools/make/kube.mk b/tools/make/kube.mk
index e0c8d3dd4..4025dfbfe 100644
--- a/tools/make/kube.mk
+++ b/tools/make/kube.mk
@@ -106,3 +106,8 @@ generate-manifests: $(tools/kustomize) ## Generate Kubernetes release manifests.
 	@echo "\033[36m===========> Added: $(OUTPUT_DIR)/install.yaml\033[0m"
 	cp examples/kubernetes/quickstart.yaml $(OUTPUT_DIR)/quickstart.yaml
 	@echo "\033[36m===========> Added: $(OUTPUT_DIR)/quickstart.yaml\033[0m"
+
+.PHONY: generate-artifacts
+generate-artifacts: generate-manifests ## Generate release artifacts.
+	cp -r $(ROOT_DIR)/release-notes/$(TAG).yaml $(OUTPUT_DIR)/release-notes.yaml
+	@echo "\033[36m===========> Added: $(OUTPUT_DIR)/release-notes.yaml\033[0m"
diff --git a/tools/src/sphinx-build/requirements.txt b/tools/src/sphinx-build/requirements.txt
index baa05d40d..de4d2c42f 100644
--- a/tools/src/sphinx-build/requirements.txt
+++ b/tools/src/sphinx-build/requirements.txt
@@ -1,1 +1,2 @@
 Sphinx==5.1.1
+myst-parser==0.18.1
\ No newline at end of file


Test patch:
diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-in-other-namespace.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-in-other-namespace.out.yaml
index 4dd99085f..757e4c695 100644
--- a/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-in-other-namespace.out.yaml
+++ b/internal/gatewayapi/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-in-other-namespace.out.yaml
@@ -28,7 +28,7 @@ gateways:
           conditions:
             - type: ResolvedRefs
               status: "False"
-              reason: InvalidCertificateRef
+              reason: RefNotPermitted
               message: Certificate ref to secret default/tls-secret-1 not permitted by any ReferenceGrant
             - type: Ready
               status: "False"
diff --git a/internal/gatewayapi/testdata/httproute-with-header-filter-empty-headers.out.yaml b/internal/gatewayapi/testdata/httproute-with-header-filter-empty-headers.out.yaml
index ae0cd811c..cdf6c1f2f 100644
--- a/internal/gatewayapi/testdata/httproute-with-header-filter-empty-headers.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-with-header-filter-empty-headers.out.yaml
@@ -66,9 +66,9 @@ httpRoutes:
       controllerName: gateway.envoyproxy.io/gatewayclass-controller
       conditions:
       - type: Accepted
-        status: "True"
-        reason: Accepted
-        message: Route is accepted
+        status: "False"
+        reason: UnsupportedValue
+        message: RequestHeaderModifier Filter cannot set a header with an empty name
 xdsIR:
   envoy-gateway-gateway-1:
     http:
diff --git a/internal/gatewayapi/testdata/httproute-with-header-filter-invalid-headers.out.yaml b/internal/gatewayapi/testdata/httproute-with-header-filter-invalid-headers.out.yaml
index 29a054590..2aec958ed 100644
--- a/internal/gatewayapi/testdata/httproute-with-header-filter-invalid-headers.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-with-header-filter-invalid-headers.out.yaml
@@ -66,9 +66,9 @@ httpRoutes:
       controllerName: gateway.envoyproxy.io/gatewayclass-controller
       conditions:
       - type: Accepted
-        status: "True"
-        reason: Accepted
-        message: Route is accepted
+        status: "False"
+        reason: UnsupportedValue
+        message: "RequestHeaderModifier Filter cannot set headers with a '/' or ':' character in them. Header: 'example:1'"
 xdsIR:
   envoy-gateway-gateway-1:
     http:
diff --git a/internal/gatewayapi/testdata/httproute-with-header-filter-no-valid-headers.out.yaml b/internal/gatewayapi/testdata/httproute-with-header-filter-no-valid-headers.out.yaml
index 3892e0bc6..399ca6013 100644
--- a/internal/gatewayapi/testdata/httproute-with-header-filter-no-valid-headers.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-with-header-filter-no-valid-headers.out.yaml
@@ -61,9 +61,9 @@ httpRoutes:
       controllerName: gateway.envoyproxy.io/gatewayclass-controller
       conditions:
       - type: Accepted
-        status: "True"
-        reason: Accepted
-        message: Route is accepted
+        status: "False"
+        reason: UnsupportedValue
+        message: "RequestHeaderModifier Filter did not provide valid configuration to add/set/remove any headers"
 xdsIR:
   envoy-gateway-gateway-1:
     http:
diff --git a/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-filter-type.in.yaml b/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-filter-type.in.yaml
index 32d8717c5..e40076f5b 100644
--- a/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-filter-type.in.yaml
+++ b/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-filter-type.in.yaml
@@ -35,8 +35,9 @@ httpRoutes:
       - name: service-1
         port: 8080
       filters:
-      - type: UnsupportedType
-        requestRedirect:
-          scheme: https
-          statusCode: 301
+      - type: ExtensionRef
+        extensionRef:
+          group: unsupported.group.io
+          kind: UnsupportedKind
+          name: unsupported
 
diff --git a/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-filter-type.out.yaml b/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-filter-type.out.yaml
index 3127021f0..0217bbf57 100644
--- a/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-filter-type.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-filter-type.out.yaml
@@ -47,10 +47,11 @@ httpRoutes:
       - name: service-1
         port: 8080
       filters:
-      - type: UnsupportedType
-        requestRedirect:
-          scheme: https
-          statusCode: 301
+      - type: ExtensionRef
+        extensionRef:
+          group: unsupported.group.io
+          kind: UnsupportedKind
+          name: unsupported
   status:
     parents:
     - parentRef:
@@ -60,9 +61,9 @@ httpRoutes:
       controllerName: gateway.envoyproxy.io/gatewayclass-controller
       conditions:
       - type: Accepted
-        status: "True"
-        reason: Accepted
-        message: Route is accepted
+        status: "False"
+        reason: UnsupportedValue
+        message: "Unknown custom filter type: ExtensionRef"
 xdsIR:
   envoy-gateway-gateway-1:
     http:
@@ -81,7 +82,7 @@ xdsIR:
         # I believe the correct way to handle an invalid filter should be to allow the HTTPRoute to function
         # normally but leave out the filter config and set the status, but this behaviour can be changed.
         directResponse:
-          body: "Unknown custom filter type: UnsupportedType"
+          body: "Unknown custom filter type: ExtensionRef"
           statusCode: 500
 infraIR:
   envoy-gateway-gateway-1:
diff --git a/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-scheme.out.yaml b/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-scheme.out.yaml
index 630f9fb75..c211bfb8e 100644
--- a/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-scheme.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-scheme.out.yaml
@@ -60,9 +60,9 @@ httpRoutes:
       controllerName: gateway.envoyproxy.io/gatewayclass-controller
       conditions:
       - type: Accepted
-        status: "True"
-        reason: Accepted
-        message: Route is accepted
+        status: "False"
+        reason: UnsupportedValue
+        message: "Scheme: unknown is unsupported, only 'https' and 'http' are supported"
 xdsIR:
   envoy-gateway-gateway-1:
     http:
diff --git a/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-status.out.yaml b/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-status.out.yaml
index dbb2df4af..007254e44 100644
--- a/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-status.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-with-redirect-filter-invalid-status.out.yaml
@@ -60,9 +60,9 @@ httpRoutes:
       controllerName: gateway.envoyproxy.io/gatewayclass-controller
       conditions:
       - type: Accepted
-        status: "True"
-        reason: Accepted
-        message: Route is accepted
+        status: "False"
+        reason: UnsupportedValue
+        message: "Status code 666 is invalid, only 302 and 301 are supported"
 xdsIR:
   envoy-gateway-gateway-1:
     http:
diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-filter-type.in.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-filter-type.in.yaml
new file mode 100644
index 000000000..17a0293da
--- /dev/null
+++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-filter-type.in.yaml
@@ -0,0 +1,41 @@
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1beta1
+  kind: Gateway
+  metadata:
+    namespace: envoy-gateway
+    name: gateway-1
+  spec:
+    gatewayClassName: envoy-gateway-class
+    listeners:
+    - name: http
+      protocol: HTTP
+      port: 80
+      hostname: "*.envoyproxy.io"
+      allowedRoutes:
+        namespaces:
+          from: All
+httpRoutes:
+- apiVersion: gateway.networking.k8s.io/v1beta1
+  kind: HTTPRoute
+  metadata:
+    namespace: default
+    name: httproute-1
+  spec:
+    hostnames:
+    - gateway.envoyproxy.io
+    parentRefs:
+    - namespace: envoy-gateway
+      name: gateway-1
+      sectionName: http
+    rules:
+    - matches:
+      - path:
+          value: "/"
+      backendRefs:
+      - name: service-1
+        port: 8080
+      filters:
+      - type: URLRewrite
+        urlRewrite:
+          hostname: urlrewrite.envoyproxy.io
+
diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-filter-type.out.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-filter-type.out.yaml
new file mode 100644
index 000000000..8ff359b48
--- /dev/null
+++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-filter-type.out.yaml
@@ -0,0 +1,100 @@
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1beta1
+  kind: Gateway
+  metadata:
+    namespace: envoy-gateway
+    name: gateway-1
+  spec:
+    gatewayClassName: envoy-gateway-class
+    listeners:
+    - name: http
+      protocol: HTTP
+      port: 80
+      hostname: "*.envoyproxy.io"
+      allowedRoutes:
+        namespaces:
+          from: All
+  status:
+    listeners:
+    - name: http
+      supportedKinds:
+      - group: gateway.networking.k8s.io
+        kind: HTTPRoute
+      attachedRoutes: 1
+      conditions:
+      - type: Ready
+        status: "True"
+        reason: Ready
+        message: Listener is ready
+httpRoutes:
+- apiVersion: gateway.networking.k8s.io/v1beta1
+  kind: HTTPRoute
+  metadata:
+    namespace: default
+    name: httproute-1
+  spec:
+    hostnames:
+    - gateway.envoyproxy.io
+    parentRefs:
+    - namespace: envoy-gateway
+      name: gateway-1
+      sectionName: http
+    rules:
+    - matches:
+      - path:
+          value: "/"
+      backendRefs:
+      - name: service-1
+        port: 8080
+      filters:
+      - type: URLRewrite
+        urlRewrite:
+          hostname: urlrewrite.envoyproxy.io
+  status:
+    parents:
+    - parentRef:
+        namespace: envoy-gateway
+        name: gateway-1
+        sectionName: http
+      controllerName: gateway.envoyproxy.io/gatewayclass-controller
+      conditions:
+      - type: Accepted
+        status: "False"
+        reason: UnsupportedValue
+        message: "Unsupported filter type: URLRewrite"
+xdsIR:
+  envoy-gateway-gateway-1:
+    http:
+    - name: envoy-gateway-gateway-1-http
+      address: 0.0.0.0
+      port: 10080
+      hostnames:
+      - "*.envoyproxy.io"
+      routes:
+      - name: default-httproute-1-rule-0-match-0-gateway.envoyproxy.io
+        pathMatch:
+          prefix: "/"
+        headerMatches:
+        - name: ":authority"
+          exact: gateway.envoyproxy.io
+        # I believe the correct way to handle an invalid filter should be to allow the HTTPRoute to function
+        # normally but leave out the filter config and set the status, but this behaviour can be changed.
+        directResponse:
+          body: "Unsupported filter type: URLRewrite"
+          statusCode: 500
+infraIR:
+  envoy-gateway-gateway-1:
+    proxy:
+      metadata:
+        labels:
+          gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway
+          gateway.envoyproxy.io/owning-gateway-name: gateway-1
+      name: envoy-gateway-gateway-1
+      image: envoyproxy/envoy:v1.23-latest
+      listeners:
+      - address: ""
+        ports:
+        - name: http
+          protocol: "HTTP"
+          containerPort: 10080
+          servicePort: 80
diff --git a/changelogs/0.1.0.yaml b/release-notes/v0.1.0.yaml
similarity index 100%
rename from changelogs/0.1.0.yaml
rename to release-notes/v0.1.0.yaml
diff --git a/changelogs/0.2.0-rc1.yaml b/release-notes/v0.2.0-rc1.yaml
similarity index 100%
rename from changelogs/0.2.0-rc1.yaml
rename to release-notes/v0.2.0-rc1.yaml
diff --git a/changelogs/0.2.0-rc2.yaml b/release-notes/v0.2.0-rc2.yaml
similarity index 100%
rename from changelogs/0.2.0-rc2.yaml
rename to release-notes/v0.2.0-rc2.yaml
diff --git a/test/conformance/conformance_test.go b/test/conformance/conformance_test.go
index 9490e96bf..b50613936 100644
--- a/test/conformance/conformance_test.go
+++ b/test/conformance/conformance_test.go
@@ -46,7 +46,7 @@ func TestGatewayAPIConformance(t *testing.T) {
 		Debug:                    *flags.ShowDebug,
 		CleanupBaseResources:     *flags.CleanupBaseResources,
 		ValidUniqueListenerPorts: validUniqueListenerPorts,
-		SupportedFeatures: []suite.SupportedFeature{suite.SupportReferenceGrant},
+		SupportedFeatures:        []suite.SupportedFeature{suite.SupportReferenceGrant},
 	})
 	cSuite.Setup(t)
 	egTests := []suite.ConformanceTest{
@@ -63,7 +63,7 @@ func TestGatewayAPIConformance(t *testing.T) {
 		tests.HTTPRouteInvalidCrossNamespaceBackendRef,
 		tests.GatewaySecretReferenceGrantAllInNamespace,
 		tests.GatewaySecretReferenceGrantSpecific,
-		// Uncomment when https://github.com/envoyproxy/gateway/issues/538 is fixed.
+		// Uncomment when https://github.com/envoyproxy/gateway/issues/539 is fixed.
 		/*tests.GatewaySecretMissingReferenceGrant,
 		tests.GatewaySecretInvalidReferenceGrant,*/
 	}