|
| 1 | +# Meta |
| 2 | +[meta]: #meta |
| 3 | +- Name: Generic Per-Route Features |
| 4 | +- Start Date: 2024-01-08 |
| 5 | +- Author(s): @maxmoehl, @domdom82 |
| 6 | +- Status: Draft <!-- Acceptable values: Draft, Approved, On Hold, Superseded --> |
| 7 | +- RFC Pull Request: (fill in with PR link after you submit it) |
| 8 | + |
| 9 | +## Summary |
| 10 | + |
| 11 | +## Problem |
| 12 | + |
| 13 | +Implementing per-route features in Cloud Foundry is a tedious process because it requires changes to |
| 14 | +multiple components in multiple working groups. Most components will only need to be made aware of |
| 15 | +new fields without adding any significant logic but the overhead of making the change still exists. |
| 16 | + |
| 17 | +On the other side are the components that actually need to be aware of the change. Mostly the |
| 18 | +routing components (gorouter, maybe route-emitter / -registrar), the API (cloud controller) and |
| 19 | +UIs (CLI). |
| 20 | + |
| 21 | +## Proposal |
| 22 | + |
| 23 | +To reduce the overall overhead of implementing new per-route features we propose to add a generic |
| 24 | +key-value map to the definition of a route across all components. All components handling the map |
| 25 | +SHOULD ignore the contents of the map except gorouter, cloud controller and cf CLI which MUST handle |
| 26 | +the map and its values accordingly. Other components MAY limit the size of the map or size of keys / |
| 27 | +values for technical reasons. |
| 28 | + |
| 29 | +The following constraints apply (types are as specified in [RFC 8259](https://rfc-editor.org/rfc/rfc8259)): |
| 30 | +* The map MUST be representable as an object. |
| 31 | +* The map MUST only use strings as keys. |
| 32 | +* The map MUST only use numbers, strings and the literal values `true` and `false` as values. |
| 33 | + |
| 34 | +### Required Changes |
| 35 | + |
| 36 | +#### CLI |
| 37 | + |
| 38 | +There SHOULD be a single flag which supports generic key-value pairs for the `map-route` |
| 39 | +sub-command. The flag should accept a JSON structure like the `-c` argument of `cf create-service`. |
| 40 | + |
| 41 | +#### Cloud Controller |
| 42 | + |
| 43 | +The API MUST specify and implement a new field to carry the map inside the route object. The |
| 44 | +API MUST implement per-field validations as features of the map are specified. The route MUST be |
| 45 | +rejected with an appropriate error message if the provided map is invalid and MUST be passed on |
| 46 | +as-is otherwise. The API also MUST store the map as a generic key-value map to ensure changes to the |
| 47 | +map do not require a change to the database schema. |
| 48 | + |
| 49 | +Support for a generic map MUST be added to the manifest. The updated routes section in a manifest |
| 50 | +MAY look like this: |
| 51 | + |
| 52 | +```yml |
| 53 | +version: 1 |
| 54 | +applications: |
| 55 | +- name: test |
| 56 | + routes: |
| 57 | + - route: example.com |
| 58 | + options: |
| 59 | + loadbalancing-algorithm: round-robin |
| 60 | + connection-limit: 15 |
| 61 | + session-cookie: FOOBAR |
| 62 | + trim-path: true |
| 63 | + - route: www.example.com/foo |
| 64 | + protocol: http2 |
| 65 | + - route: tcp-example.com:1234 |
| 66 | +``` |
| 67 | +
|
| 68 | +The new fields MUST be supported by the already existing endpoints to create and update and get |
| 69 | +routes: |
| 70 | +* [`POST /v3/routes`](https://v3-apidocs.cloudfoundry.org/version/3.159.0/index.html#create-a-route) |
| 71 | +* [`GET /v3/routes/:guid`](https://v3-apidocs.cloudfoundry.org/version/3.159.0/index.html#get-a-route) |
| 72 | +* [`GET /v3/routes`](https://v3-apidocs.cloudfoundry.org/version/3.159.0/index.html#list-routes) |
| 73 | +* [`GET /v3/apps/:guid/routes`](https://v3-apidocs.cloudfoundry.org/version/3.159.0/index.html#list-routes-for-an-app) |
| 74 | +* [`PATCH /v3/routes/:guid`](https://v3-apidocs.cloudfoundry.org/version/3.159.0/index.html#update-a-route) |
| 75 | + |
| 76 | +The overall goal MUST be to implement the new fields in a way that fits in with the current |
| 77 | +behavior of the cloud controller API. It is up to the maintainers / implementers to decide on the |
| 78 | +exact behavior. |
| 79 | + |
| 80 | +Implementation pointers: |
| 81 | +* Add a new field to the [route model](https://github.com/cloudfoundry/cloud_controller_ng/blob/main/app/models/runtime/route.rb). |
| 82 | +* Add validation to the [route validator](https://github.com/cloudfoundry/cloud_controller_ng/blob/e719d017ea4625397a97c2c14352ebdee66febe9/lib/cloud_controller/route_validator.rb#L2). |
| 83 | +* Use a mechanism similar to the [droplet](https://github.com/cloudfoundry/cloud_controller_ng/blob/e719d017ea4625397a97c2c14352ebdee66febe9/app/models/runtime/droplet_model.rb#L47-L48) to store the encoded map. |
| 84 | + |
| 85 | +#### BBS, Rep, Executor, Route-Emitter |
| 86 | + |
| 87 | +The diego components MUST add support for the new field specified by the API and forward the |
| 88 | +contents as-is without modification or, if the field exceeds technical limitations, MUST raise an |
| 89 | +error. |
| 90 | + |
| 91 | +### Route-Registrar |
| 92 | + |
| 93 | +Route-registrar MUST offer the new features in its config as they are added to the other components. |
| 94 | + |
| 95 | +#### Gorouter |
| 96 | + |
| 97 | +Gorouter MUST accept the new field and parse its contents. If the map cannot be parsed it MUST be |
| 98 | +ignored. If individual key-value pairs are invalid those MUST be ignored and other key-value pairs |
| 99 | +MUST still be parsed and acted upon. For any option which could not be parsed gorouter MUST NOT |
| 100 | +fail but fall back to the value provided in the gorouter config. When encountering an invalid option |
| 101 | +gorouter MUST log the error at least once, the log SHOULD indicate what it tried to parse, why it |
| 102 | +failed and which value will come into effect. |
| 103 | + |
| 104 | +It MAY be decided to implement one feature as part of this RFC which would be implemented together |
| 105 | +with the generic logic for supporting the new map. |
| 106 | + |
| 107 | +As new features are specified they MUST be implemented in gorouter. |
| 108 | + |
| 109 | +Example `router.register` NATS message containing additional options (field names and values are |
| 110 | +not final): |
| 111 | + |
| 112 | +```json |
| 113 | +{ |
| 114 | + "host": "127.0.0.1", |
| 115 | + "port": 4567, |
| 116 | + "tls_port": 1234, |
| 117 | + "protocol": "http1", |
| 118 | + "uris": [ |
| 119 | + "my_first_url.localhost.routing.cf-app.com", |
| 120 | + "my_second_url.localhost.routing.cf-app.com/some-path" |
| 121 | + ], |
| 122 | + "tags": { |
| 123 | + "another_key": "another_value", |
| 124 | + "some_key": "some_value" |
| 125 | + }, |
| 126 | + "options": { |
| 127 | + "lb-algo": "least-conn", |
| 128 | + "conn-limit": 100, |
| 129 | + "cookie": "my-session-cookie", |
| 130 | + "trim-path": true |
| 131 | + }, |
| 132 | + "app": "some_app_guid", |
| 133 | + "stale_threshold_in_seconds": 120, |
| 134 | + "private_instance_id": "some_app_instance_id", |
| 135 | + "isolation_segment": "some_iso_seg_name", |
| 136 | + "server_cert_domain_san": "some_subject_alternative_name" |
| 137 | +} |
| 138 | +``` |
| 139 | + |
| 140 | +### Specifying Features |
| 141 | + |
| 142 | +It's the responsibility of the App Runtime Platform Working Group (ARP-WG) to specify new features |
| 143 | +based on what is specified in this RFC. When a new feature is proposed the ARP-WG uses the process |
| 144 | +it would use for any new feature to decide whether and how it should be implemented. It MAY involve |
| 145 | +other working groups in the process. |
| 146 | + |
| 147 | +### Features Which Could be Implemented |
| 148 | + |
| 149 | +So far we have collected a list of features which would benefit from a more flexible approach to |
| 150 | +per-route features: |
| 151 | + |
| 152 | +- Custom Load-Balancing Algorithm |
| 153 | +- Custom Connection Limits |
| 154 | +- Custom session cookie name (currently set on platform level, mostly to `JSESSIONID`) |
| 155 | +- Option to trim path mapping on request (i.e. an app mapped to `/some-path/` receiving a request |
| 156 | + `GET /some-path/books` will see the request as `GET /books` if the `trim-path` option is set, |
| 157 | + instead of the full path) |
| 158 | + |
| 159 | +All of these options are already (or could easily be made) configurable for the entire CF |
| 160 | +installation. Changing them will almost certainly break existing scenarios which is why we are |
| 161 | +looking for a cheap way to gradually allow for more configuration per route. |
0 commit comments