From 5e5cb65ce2cdcf70db87317fd19a9e2bd2578303 Mon Sep 17 00:00:00 2001 From: bryn Date: Wed, 16 Nov 2022 09:53:50 +0000 Subject: [PATCH 1/8] Fix naming inconsistency of telemetry.metrics.common.attributes.router Mirroring the rest of the config `router` should be `supergraph` ```yaml telemetry: metrics: common: attributes: router: # old ``` becomes ```yaml telemetry: metrics: common: attributes: supergraph: # new ``` Fixes #2076 --- NEXT_CHANGELOG.md | 121 +- ...nfiguration__tests__schema_generation.snap | 502 +++---- .../src/plugins/telemetry/metrics/mod.rs | 2 +- apollo-router/src/plugins/telemetry/mod.rs | 10 +- docs/source/configuration/metrics.mdx | 2 +- licenses.html | 1208 +++++++++++------ 6 files changed, 1079 insertions(+), 766 deletions(-) diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md index e3e25dde63..655a06b84b 100644 --- a/NEXT_CHANGELOG.md +++ b/NEXT_CHANGELOG.md @@ -24,117 +24,30 @@ By [@USERNAME](https://github.com/USERNAME) in https://github.com/apollographql/ --> # [x.x.x] (unreleased) - 2022-mm-dd - ## ❗ BREAKING ❗ -## 🚀 Features +### Fix naming inconsistency of telemetry.metrics.common.attributes.router ([Issue #2076](https://github.com/apollographql/router/issues/2076)) -### Add support for returning different HTTP status codes to rhai engine ([Issue #2023](https://github.com/apollographql/router/issues/2023)) +Mirroring the rest of the config `router` should be `supergraph` -This feature now makes it possible to return different HTTP status codes when raising an exception in Rhai. You do this by providing an objectmap with two keys: status and message. - -``` - throw #{ - status: 403, - message: "I have raised a 403" - }; +```yaml +telemetry: + metrics: + common: + attributes: + router: # old ``` - -This would short-circuit request/response processing and set an HTTP status code of 403 in the client response and also set the error message. - -It is still possible to return errors as per the current method: - +becomes +```yaml +telemetry: + metrics: + common: + attributes: + supergraph: # new ``` - throw "I have raised an error"; -``` -This will have a 500 HTTP status code with the specified message. - -It is not currently possible to return a 200 "error". If you try, it will be implicitly converted into a 500 error. - -By [@garypen](https://github.com/garypen) in https://github.com/apollographql/router/pull/2097 - -### Add support for urlencode/decode to rhai engine ([Issue #2052](https://github.com/apollographql/router/issues/2052)) - -Two new functions, `urlencode()` and `urldecode()` may now be used to urlencode/decode strings. - -By [@garypen](https://github.com/garypen) in https://github.com/apollographql/router/pull/2053 -### **Experimental** 🥼 External cache storage in Redis ([PR #2024](https://github.com/apollographql/router/pull/2024)) - -implement caching in external storage for query plans, introspection and APQ. This is done as a multi level cache, first in -memory with LRU then with a redis cluster backend. Since it is still experimental, it is opt-in through a Cargo feature. - -By [@garypen](https://github.com/garypen) and [@Geal](https://github.com/Geal) in https://github.com/apollographql/router/pull/2024 - -### Add Query Plan access to ExecutionRequest ([PR #2081](https://github.com/apollographql/router/pull/2081)) - -You can now access the query plan from an execution request: - -``` -request.query_plan -``` - -`request.context` also now supports the rhai `in` keyword. - -By [@garypen](https://github.com/garypen) in https://github.com/apollographql/router/pull/2081 +By [@bryncooke](https://github.com/bryncooke) in https://github.com/apollographql/router/pull/2116 +## 🚀 Features ## 🐛 Fixes - -### Move the nullifying error messages to extension ([Issue #2071](https://github.com/apollographql/router/issues/2071)) - -The Router was generating error messages when triggering nullability rules (when a non nullable field is null, -it will nullify the parent object). Adding those messages in the list of errors was potentially redundant -(subgraph can already add an error message indicating why a field is null) and could be treated as a failure by -clients, while nullifying fields is a part of normal operation. We still add the messages in extensions so -clients can easily debug why parts of the response were removed - -By [@Geal](https://github.com/Geal) in https://github.com/apollographql/router/pull/2077 - -### Fix `Float` input-type coercion for default values with values larger than 32-bits ([Issue #2087](https://github.com/apollographql/router/issues/2087)) - -A regression has been fixed which caused the Router to reject integers larger than 32-bits used as the default values on `Float` fields in input types. - -In other words, the following will once again work as expected: - -```graphql -input MyInputType { - a_float_input: Float = 9876543210 -} -``` - -By [@o0Ignition0o](https://github.com/o0Ignition0o) in https://github.com/apollographql/router/pull/2090 - -### Assume `Accept: application/json` when no `Accept` header is present [Issue #1990](https://github.com/apollographql/router/issues/1990)) - -the `Accept` header means `*/*` when it is absent. - -By [@bnjjj](https://github.com/bnjjj) in https://github.com/apollographql/router/pull/2078 - -### Missing `@skip` and `@include` implementation for root operations ([Issue #2072](https://github.com/apollographql/router/issues/2072)) - -`@skip` and `@include` were not implemented for inline fragments and fragment spreads on top level operations. - -By [@Geal](https://github.com/Geal) in https://github.com/apollographql/router/pull/2096 - ## 🛠 Maintenance - -### Use `debian:bullseye-slim` as our base Docker image ([PR #2085](https://github.com/apollographql/router/pull/2085)) - -A while ago, when we added compression support to the router, we discovered that the Distroless base-images we were using didn't ship with a copy of `libz.so.1`. We addressed that problem by copying in a version of the library from the Distroless image (Java) which does ship it. While that worked, we found challenges in adding support for both `aarch64` and `amd64` Docker images that would make it less than ideal to continue using those Distroless images. - -Rather than persist with this complexity, we've concluded that it would be better to just use a base image which ships with `libz.so.1`, hence the change to `debian:bullseye-slim`. Those images are still quite minimal and the resulting images are similar in size. - -By [@garypen](https://github.com/garypen) in https://github.com/apollographql/router/pull/2085 - -### Update `apollo-parser` to `v0.3.2` ([PR #2103](https://github.com/apollographql/router/pull/2103)) - -This updates our dependency on our `apollo-parser` package which brings a few improvements, including more defensive parsing of some operations. See its CHANGELOG in [the `apollo-rs` repository](https://github.com/apollographql/apollo-rs/blob/main/crates/apollo-parser/CHANGELOG.md#032---2022-11-15) for more details. - -By [@abernix](https://github.com/abernix) in https://github.com/apollographql/router/pull/2103 - ## 📚 Documentation - -### Fix example `helm show values` command ([PR #2088](https://github.com/apollographql/router/pull/2088)) - -The `helm show vaues` command needs to use the correct Helm chart reference `oci://ghcr.io/apollographql/helm-charts/router`. - -By [@col](https://github.com/col) in https://github.com/apollographql/router/pull/2088 diff --git a/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap b/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap index 8a249cfcc5..971e0c6891 100644 --- a/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap +++ b/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap @@ -797,257 +797,6 @@ expression: "&schema" "description": "Configuration to add custom labels/attributes to metrics", "type": "object", "properties": { - "router": { - "description": "Configuration to forward header values or body values from router request/response in metric attributes/labels", - "type": "object", - "properties": { - "context": { - "description": "Configuration to forward values from the context to custom attributes/labels in metrics", - "type": "array", - "items": { - "description": "Configuration to forward context values in metric attributes/labels", - "type": "object", - "required": [ - "named" - ], - "properties": { - "default": { - "type": "string", - "nullable": true - }, - "named": { - "type": "string" - }, - "rename": { - "type": "string", - "nullable": true - } - }, - "additionalProperties": false - }, - "nullable": true - }, - "errors": { - "description": "Configuration to forward values from the error to custom attributes/labels in metrics", - "type": "object", - "properties": { - "extensions": { - "description": "Forward extensions values as custom attributes/labels in metrics", - "type": "array", - "items": { - "description": "Configuration to forward body values in metric attributes/labels", - "type": "object", - "required": [ - "name", - "path" - ], - "properties": { - "default": { - "type": "string", - "nullable": true - }, - "name": { - "type": "string" - }, - "path": { - "type": "string" - } - }, - "additionalProperties": false - }, - "nullable": true - }, - "include_messages": { - "description": "Will include the error message in a \"message\" attribute", - "default": false, - "type": "boolean" - } - }, - "additionalProperties": false, - "nullable": true - }, - "request": { - "description": "Configuration to forward headers or body values from the request to custom attributes/labels in metrics", - "type": "object", - "properties": { - "body": { - "description": "Forward body values as custom attributes/labels in metrics", - "type": "array", - "items": { - "description": "Configuration to forward body values in metric attributes/labels", - "type": "object", - "required": [ - "name", - "path" - ], - "properties": { - "default": { - "type": "string", - "nullable": true - }, - "name": { - "type": "string" - }, - "path": { - "type": "string" - } - }, - "additionalProperties": false - }, - "nullable": true - }, - "header": { - "description": "Forward header values as custom attributes/labels in metrics", - "type": "array", - "items": { - "description": "Configuration to forward header values in metric labels", - "anyOf": [ - { - "description": "Using a named header", - "type": "object", - "required": [ - "named" - ], - "properties": { - "default": { - "type": "string", - "nullable": true - }, - "named": { - "type": "string" - }, - "rename": { - "type": "string", - "nullable": true - } - }, - "additionalProperties": false - }, - { - "description": "Using a regex on the header name", - "type": "object", - "required": [ - "matching" - ], - "properties": { - "matching": { - "type": "string" - } - }, - "additionalProperties": false - } - ] - }, - "nullable": true - } - }, - "additionalProperties": false, - "nullable": true - }, - "response": { - "description": "Configuration to forward headers or body values from the response to custom attributes/labels in metrics", - "type": "object", - "properties": { - "body": { - "description": "Forward body values as custom attributes/labels in metrics", - "type": "array", - "items": { - "description": "Configuration to forward body values in metric attributes/labels", - "type": "object", - "required": [ - "name", - "path" - ], - "properties": { - "default": { - "type": "string", - "nullable": true - }, - "name": { - "type": "string" - }, - "path": { - "type": "string" - } - }, - "additionalProperties": false - }, - "nullable": true - }, - "header": { - "description": "Forward header values as custom attributes/labels in metrics", - "type": "array", - "items": { - "description": "Configuration to forward header values in metric labels", - "anyOf": [ - { - "description": "Using a named header", - "type": "object", - "required": [ - "named" - ], - "properties": { - "default": { - "type": "string", - "nullable": true - }, - "named": { - "type": "string" - }, - "rename": { - "type": "string", - "nullable": true - } - }, - "additionalProperties": false - }, - { - "description": "Using a regex on the header name", - "type": "object", - "required": [ - "matching" - ], - "properties": { - "matching": { - "type": "string" - } - }, - "additionalProperties": false - } - ] - }, - "nullable": true - } - }, - "additionalProperties": false, - "nullable": true - }, - "static": { - "description": "Configuration to insert custom attributes/labels in metrics", - "type": "array", - "items": { - "description": "Configuration to insert custom attributes/labels in metrics", - "type": "object", - "required": [ - "name", - "value" - ], - "properties": { - "name": { - "type": "string" - }, - "value": { - "type": "string" - } - }, - "additionalProperties": false - }, - "nullable": true - } - }, - "additionalProperties": false, - "nullable": true - }, "subgraph": { "description": "Configuration to forward header values or body values from subgraph request/response in metric attributes/labels", "type": "object", @@ -1558,6 +1307,257 @@ expression: "&schema" }, "additionalProperties": false, "nullable": true + }, + "supergraph": { + "description": "Configuration to forward header values or body values from router request/response in metric attributes/labels", + "type": "object", + "properties": { + "context": { + "description": "Configuration to forward values from the context to custom attributes/labels in metrics", + "type": "array", + "items": { + "description": "Configuration to forward context values in metric attributes/labels", + "type": "object", + "required": [ + "named" + ], + "properties": { + "default": { + "type": "string", + "nullable": true + }, + "named": { + "type": "string" + }, + "rename": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "nullable": true + }, + "errors": { + "description": "Configuration to forward values from the error to custom attributes/labels in metrics", + "type": "object", + "properties": { + "extensions": { + "description": "Forward extensions values as custom attributes/labels in metrics", + "type": "array", + "items": { + "description": "Configuration to forward body values in metric attributes/labels", + "type": "object", + "required": [ + "name", + "path" + ], + "properties": { + "default": { + "type": "string", + "nullable": true + }, + "name": { + "type": "string" + }, + "path": { + "type": "string" + } + }, + "additionalProperties": false + }, + "nullable": true + }, + "include_messages": { + "description": "Will include the error message in a \"message\" attribute", + "default": false, + "type": "boolean" + } + }, + "additionalProperties": false, + "nullable": true + }, + "request": { + "description": "Configuration to forward headers or body values from the request to custom attributes/labels in metrics", + "type": "object", + "properties": { + "body": { + "description": "Forward body values as custom attributes/labels in metrics", + "type": "array", + "items": { + "description": "Configuration to forward body values in metric attributes/labels", + "type": "object", + "required": [ + "name", + "path" + ], + "properties": { + "default": { + "type": "string", + "nullable": true + }, + "name": { + "type": "string" + }, + "path": { + "type": "string" + } + }, + "additionalProperties": false + }, + "nullable": true + }, + "header": { + "description": "Forward header values as custom attributes/labels in metrics", + "type": "array", + "items": { + "description": "Configuration to forward header values in metric labels", + "anyOf": [ + { + "description": "Using a named header", + "type": "object", + "required": [ + "named" + ], + "properties": { + "default": { + "type": "string", + "nullable": true + }, + "named": { + "type": "string" + }, + "rename": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + { + "description": "Using a regex on the header name", + "type": "object", + "required": [ + "matching" + ], + "properties": { + "matching": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "nullable": true + } + }, + "additionalProperties": false, + "nullable": true + }, + "response": { + "description": "Configuration to forward headers or body values from the response to custom attributes/labels in metrics", + "type": "object", + "properties": { + "body": { + "description": "Forward body values as custom attributes/labels in metrics", + "type": "array", + "items": { + "description": "Configuration to forward body values in metric attributes/labels", + "type": "object", + "required": [ + "name", + "path" + ], + "properties": { + "default": { + "type": "string", + "nullable": true + }, + "name": { + "type": "string" + }, + "path": { + "type": "string" + } + }, + "additionalProperties": false + }, + "nullable": true + }, + "header": { + "description": "Forward header values as custom attributes/labels in metrics", + "type": "array", + "items": { + "description": "Configuration to forward header values in metric labels", + "anyOf": [ + { + "description": "Using a named header", + "type": "object", + "required": [ + "named" + ], + "properties": { + "default": { + "type": "string", + "nullable": true + }, + "named": { + "type": "string" + }, + "rename": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + { + "description": "Using a regex on the header name", + "type": "object", + "required": [ + "matching" + ], + "properties": { + "matching": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "nullable": true + } + }, + "additionalProperties": false, + "nullable": true + }, + "static": { + "description": "Configuration to insert custom attributes/labels in metrics", + "type": "array", + "items": { + "description": "Configuration to insert custom attributes/labels in metrics", + "type": "object", + "required": [ + "name", + "value" + ], + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": false + }, + "nullable": true + } + }, + "additionalProperties": false, + "nullable": true } }, "additionalProperties": false, diff --git a/apollo-router/src/plugins/telemetry/metrics/mod.rs b/apollo-router/src/plugins/telemetry/metrics/mod.rs index 01a9bccb59..97fa39ca13 100644 --- a/apollo-router/src/plugins/telemetry/metrics/mod.rs +++ b/apollo-router/src/plugins/telemetry/metrics/mod.rs @@ -43,7 +43,7 @@ pub(crate) type MetricsExporterHandle = Box; /// Configuration to add custom attributes/labels on metrics pub(crate) struct MetricsAttributesConf { /// Configuration to forward header values or body values from router request/response in metric attributes/labels - pub(crate) router: Option, + pub(crate) supergraph: Option, /// Configuration to forward header values or body values from subgraph request/response in metric attributes/labels pub(crate) subgraph: Option, } diff --git a/apollo-router/src/plugins/telemetry/mod.rs b/apollo-router/src/plugins/telemetry/mod.rs index aa0d921c9a..d9d7566520 100644 --- a/apollo-router/src/plugins/telemetry/mod.rs +++ b/apollo-router/src/plugins/telemetry/mod.rs @@ -761,7 +761,7 @@ impl Telemetry { if let Some(MetricsCommon { attributes: Some(MetricsAttributesConf { - router: Some(forward_attributes), + supergraph: Some(forward_attributes), .. }), .. @@ -840,7 +840,7 @@ impl Telemetry { .common .as_ref() .and_then(|c| c.attributes.as_ref()) - .and_then(|a| a.router.as_ref()) + .and_then(|a| a.supergraph.as_ref()) { attributes.extend( router_attributes_conf @@ -1041,7 +1041,7 @@ impl Telemetry { .as_ref() .and_then(|m| m.common.as_ref()) .and_then(|c| c.attributes.as_ref()) - .and_then(|c| c.router.as_ref()) + .and_then(|c| c.supergraph.as_ref()) { metric_attrs.extend( subgraph_attributes_conf @@ -1312,7 +1312,7 @@ mod tests { "metrics": { "common": { "attributes": { - "router": { + "supergraph": { "static": [ { "name": "myname", @@ -1513,7 +1513,7 @@ mod tests { "common": { "service_name": "apollo-router", "attributes": { - "router": { + "supergraph": { "static": [ { "name": "myname", diff --git a/docs/source/configuration/metrics.mdx b/docs/source/configuration/metrics.mdx index dc70a6d79f..3d6517633f 100644 --- a/docs/source/configuration/metrics.mdx +++ b/docs/source/configuration/metrics.mdx @@ -92,7 +92,7 @@ telemetry: metrics: common: attributes: - router: # Attribute configuration for requests to/responses from the router + supergraph: # Attribute configuration for requests to/responses from the router static: - name: "version" value: "v1.0.0" diff --git a/licenses.html b/licenses.html index a77f255df5..a670e5304b 100644 --- a/licenses.html +++ b/licenses.html @@ -44,13 +44,14 @@

Third Party Licenses

Overview of licenses:

All license text:

@@ -242,216 +243,6 @@

Used by:

of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS - - -
  • -

    Apache License 2.0

    -

    Used by:

    - -
    -                                 Apache License
    -                           Version 2.0, January 2004
    -                        http://www.apache.org/licenses/
    -
    -   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
    -
    -   1. Definitions.
    -
    -      "License" shall mean the terms and conditions for use, reproduction,
    -      and distribution as defined by Sections 1 through 9 of this document.
    -
    -      "Licensor" shall mean the copyright owner or entity authorized by
    -      the copyright owner that is granting the License.
    -
    -      "Legal Entity" shall mean the union of the acting entity and all
    -      other entities that control, are controlled by, or are under common
    -      control with that entity. For the purposes of this definition,
    -      "control" means (i) the power, direct or indirect, to cause the
    -      direction or management of such entity, whether by contract or
    -      otherwise, or (ii) ownership of fifty percent (50%) or more of the
    -      outstanding shares, or (iii) beneficial ownership of such entity.
    -
    -      "You" (or "Your") shall mean an individual or Legal Entity
    -      exercising permissions granted by this License.
    -
    -      "Source" form shall mean the preferred form for making modifications,
    -      including but not limited to software source code, documentation
    -      source, and configuration files.
    -
    -      "Object" form shall mean any form resulting from mechanical
    -      transformation or translation of a Source form, including but
    -      not limited to compiled object code, generated documentation,
    -      and conversions to other media types.
    -
    -      "Work" shall mean the work of authorship, whether in Source or
    -      Object form, made available under the License, as indicated by a
    -      copyright notice that is included in or attached to the work
    -      (an example is provided in the Appendix below).
    -
    -      "Derivative Works" shall mean any work, whether in Source or Object
    -      form, that is based on (or derived from) the Work and for which the
    -      editorial revisions, annotations, elaborations, or other modifications
    -      represent, as a whole, an original work of authorship. For the purposes
    -      of this License, Derivative Works shall not include works that remain
    -      separable from, or merely link (or bind by name) to the interfaces of,
    -      the Work and Derivative Works thereof.
    -
    -      "Contribution" shall mean any work of authorship, including
    -      the original version of the Work and any modifications or additions
    -      to that Work or Derivative Works thereof, that is intentionally
    -      submitted to Licensor for inclusion in the Work by the copyright owner
    -      or by an individual or Legal Entity authorized to submit on behalf of
    -      the copyright owner. For the purposes of this definition, "submitted"
    -      means any form of electronic, verbal, or written communication sent
    -      to the Licensor or its representatives, including but not limited to
    -      communication on electronic mailing lists, source code control systems,
    -      and issue tracking systems that are managed by, or on behalf of, the
    -      Licensor for the purpose of discussing and improving the Work, but
    -      excluding communication that is conspicuously marked or otherwise
    -      designated in writing by the copyright owner as "Not a Contribution."
    -
    -      "Contributor" shall mean Licensor and any individual or Legal Entity
    -      on behalf of whom a Contribution has been received by Licensor and
    -      subsequently incorporated within the Work.
    -
    -   2. Grant of Copyright License. Subject to the terms and conditions of
    -      this License, each Contributor hereby grants to You a perpetual,
    -      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
    -      copyright license to reproduce, prepare Derivative Works of,
    -      publicly display, publicly perform, sublicense, and distribute the
    -      Work and such Derivative Works in Source or Object form.
    -
    -   3. Grant of Patent License. Subject to the terms and conditions of
    -      this License, each Contributor hereby grants to You a perpetual,
    -      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
    -      (except as stated in this section) patent license to make, have made,
    -      use, offer to sell, sell, import, and otherwise transfer the Work,
    -      where such license applies only to those patent claims licensable
    -      by such Contributor that are necessarily infringed by their
    -      Contribution(s) alone or by combination of their Contribution(s)
    -      with the Work to which such Contribution(s) was submitted. If You
    -      institute patent litigation against any entity (including a
    -      cross-claim or counterclaim in a lawsuit) alleging that the Work
    -      or a Contribution incorporated within the Work constitutes direct
    -      or contributory patent infringement, then any patent licenses
    -      granted to You under this License for that Work shall terminate
    -      as of the date such litigation is filed.
    -
    -   4. Redistribution. You may reproduce and distribute copies of the
    -      Work or Derivative Works thereof in any medium, with or without
    -      modifications, and in Source or Object form, provided that You
    -      meet the following conditions:
    -
    -      (a) You must give any other recipients of the Work or
    -          Derivative Works a copy of this License; and
    -
    -      (b) You must cause any modified files to carry prominent notices
    -          stating that You changed the files; and
    -
    -      (c) You must retain, in the Source form of any Derivative Works
    -          that You distribute, all copyright, patent, trademark, and
    -          attribution notices from the Source form of the Work,
    -          excluding those notices that do not pertain to any part of
    -          the Derivative Works; and
    -
    -      (d) If the Work includes a "NOTICE" text file as part of its
    -          distribution, then any Derivative Works that You distribute must
    -          include a readable copy of the attribution notices contained
    -          within such NOTICE file, excluding those notices that do not
    -          pertain to any part of the Derivative Works, in at least one
    -          of the following places: within a NOTICE text file distributed
    -          as part of the Derivative Works; within the Source form or
    -          documentation, if provided along with the Derivative Works; or,
    -          within a display generated by the Derivative Works, if and
    -          wherever such third-party notices normally appear. The contents
    -          of the NOTICE file are for informational purposes only and
    -          do not modify the License. You may add Your own attribution
    -          notices within Derivative Works that You distribute, alongside
    -          or as an addendum to the NOTICE text from the Work, provided
    -          that such additional attribution notices cannot be construed
    -          as modifying the License.
    -
    -      You may add Your own copyright statement to Your modifications and
    -      may provide additional or different license terms and conditions
    -      for use, reproduction, or distribution of Your modifications, or
    -      for any such Derivative Works as a whole, provided Your use,
    -      reproduction, and distribution of the Work otherwise complies with
    -      the conditions stated in this License.
    -
    -   5. Submission of Contributions. Unless You explicitly state otherwise,
    -      any Contribution intentionally submitted for inclusion in the Work
    -      by You to the Licensor shall be under the terms and conditions of
    -      this License, without any additional terms or conditions.
    -      Notwithstanding the above, nothing herein shall supersede or modify
    -      the terms of any separate license agreement you may have executed
    -      with Licensor regarding such Contributions.
    -
    -   6. Trademarks. This License does not grant permission to use the trade
    -      names, trademarks, service marks, or product names of the Licensor,
    -      except as required for reasonable and customary use in describing the
    -      origin of the Work and reproducing the content of the NOTICE file.
    -
    -   7. Disclaimer of Warranty. Unless required by applicable law or
    -      agreed to in writing, Licensor provides the Work (and each
    -      Contributor provides its Contributions) on an "AS IS" BASIS,
    -      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    -      implied, including, without limitation, any warranties or conditions
    -      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
    -      PARTICULAR PURPOSE. You are solely responsible for determining the
    -      appropriateness of using or redistributing the Work and assume any
    -      risks associated with Your exercise of permissions under this License.
    -
    -   8. Limitation of Liability. In no event and under no legal theory,
    -      whether in tort (including negligence), contract, or otherwise,
    -      unless required by applicable law (such as deliberate and grossly
    -      negligent acts) or agreed to in writing, shall any Contributor be
    -      liable to You for damages, including any direct, indirect, special,
    -      incidental, or consequential damages of any character arising as a
    -      result of this License or out of the use or inability to use the
    -      Work (including but not limited to damages for loss of goodwill,
    -      work stoppage, computer failure or malfunction, or any and all
    -      other commercial damages or losses), even if such Contributor
    -      has been advised of the possibility of such damages.
    -
    -   9. Accepting Warranty or Additional Liability. While redistributing
    -      the Work or Derivative Works thereof, You may choose to offer,
    -      and charge a fee for, acceptance of support, warranty, indemnity,
    -      or other liability obligations and/or rights consistent with this
    -      License. However, in accepting such obligations, You may act only
    -      on Your own behalf and on Your sole responsibility, not on behalf
    -      of any other Contributor, and only if You agree to indemnify,
    -      defend, and hold each Contributor harmless for any liability
    -      incurred by, or claims asserted against, such Contributor by reason
    -      of your accepting any such warranty or additional liability.
    -
    -   END OF TERMS AND CONDITIONS
    -
    -   APPENDIX: How to apply the Apache License to your work.
    -
    -      To apply the Apache License to your work, attach the following
    -      boilerplate notice, with the fields enclosed by brackets "[]"
    -      replaced with your own identifying information. (Don't include
    -      the brackets!)  The text should be enclosed in the appropriate
    -      comment syntax for the file format. We also recommend that a
    -      file or class name and description of purpose be included on the
    -      same "printed page" as the copyright notice for easier
    -      identification within third-party archives.
    -
    -   Copyright 2021 Jacob Pratt
    -
    -   Licensed under the Apache License, Version 2.0 (the "License");
    -   you may not use this file except in compliance with the License.
    -   You may obtain a copy of the License at
    -
    -       http://www.apache.org/licenses/LICENSE-2.0
    -
    -   Unless required by applicable law or agreed to in writing, software
    -   distributed under the License is distributed on an "AS IS" BASIS,
    -   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    -   See the License for the specific language governing permissions and
    -   limitations under the License.
     
  • @@ -459,6 +250,7 @@

    Apache License 2.0

    Used by:

    @@ -674,7 +466,6 @@ 

    Used by:

  • fragile
  • static_assertions
  • zeroize
  • -
  • zeroize_derive
  •                                   Apache License
    @@ -1281,6 +1072,7 @@ 

    Apache License 2.0

    Used by:

                                     Apache License
                                Version 2.0, January 2004
    @@ -2561,6 +2353,7 @@ 

    Used by:

  • hex
  • humantime
  • native-tls
  • +
  • openssl-macros
  • terminal_size
  • tokio-io-timeout
  • unreachable
  • @@ -3350,7 +3143,6 @@

    Used by:

    Apache License 2.0

    Used by:

    @@ -5010,7 +4802,8 @@

    Used by:

    Apache License 2.0

    Used by:

                                  Apache License
                             Version 2.0, January 2004
    @@ -5200,13 +4993,13 @@ 

    Used by:

    same "printed page" as the copyright notice for easier identification within third-party archives. -Copyright 2019 The CryptoCorrosion Contributors +Copyright 2018-2022 RustCrypto Developers Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -5219,7 +5012,7 @@

    Used by:

    Apache License 2.0

    Used by:

                                  Apache License
                             Version 2.0, January 2004
    @@ -5409,13 +5202,13 @@ 

    Used by:

    same "printed page" as the copyright notice for easier identification within third-party archives. -Copyright 2019-2020 CreepySkeleton <creepy-skeleton@yandex.ru> +Copyright 2019 The CryptoCorrosion Contributors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -5428,7 +5221,7 @@

    Used by:

    Apache License 2.0

    Used by:

                                  Apache License
                             Version 2.0, January 2004
    @@ -5618,27 +5411,26 @@ 

    Used by:

    same "printed page" as the copyright notice for easier identification within third-party archives. -Copyright 2020 John Foley +Copyright 2019-2020 CreepySkeleton <creepy-skeleton@yandex.ru> Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -
  • Apache License 2.0

    Used by:

                                  Apache License
                             Version 2.0, January 2004
    @@ -5828,7 +5620,7 @@ 

    Used by:

    same "printed page" as the copyright notice for easier identification within third-party archives. -Copyright [2016] [rust-uname Developers] +Copyright 2020 John Foley Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -5841,13 +5633,14 @@

    Used by:

    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +
  • Apache License 2.0

    Used by:

                                  Apache License
                             Version 2.0, January 2004
    @@ -6037,7 +5830,7 @@ 

    Used by:

    same "printed page" as the copyright notice for easier identification within third-party archives. -Copyright [yyyy] [name of copyright owner] +Copyright [2016] [rust-uname Developers] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -6049,33 +5842,243 @@

    Used by:

    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and -limitations under the License.
    +limitations under the License. +
  • Apache License 2.0

    Used by:

    +
                                  Apache License
    +                        Version 2.0, January 2004
    +                     http://www.apache.org/licenses/
    +
    +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
    +
    +1. Definitions.
    +
    +   "License" shall mean the terms and conditions for use, reproduction,
    +   and distribution as defined by Sections 1 through 9 of this document.
    +
    +   "Licensor" shall mean the copyright owner or entity authorized by
    +   the copyright owner that is granting the License.
    +
    +   "Legal Entity" shall mean the union of the acting entity and all
    +   other entities that control, are controlled by, or are under common
    +   control with that entity. For the purposes of this definition,
    +   "control" means (i) the power, direct or indirect, to cause the
    +   direction or management of such entity, whether by contract or
    +   otherwise, or (ii) ownership of fifty percent (50%) or more of the
    +   outstanding shares, or (iii) beneficial ownership of such entity.
    +
    +   "You" (or "Your") shall mean an individual or Legal Entity
    +   exercising permissions granted by this License.
    +
    +   "Source" form shall mean the preferred form for making modifications,
    +   including but not limited to software source code, documentation
    +   source, and configuration files.
    +
    +   "Object" form shall mean any form resulting from mechanical
    +   transformation or translation of a Source form, including but
    +   not limited to compiled object code, generated documentation,
    +   and conversions to other media types.
    +
    +   "Work" shall mean the work of authorship, whether in Source or
    +   Object form, made available under the License, as indicated by a
    +   copyright notice that is included in or attached to the work
    +   (an example is provided in the Appendix below).
    +
    +   "Derivative Works" shall mean any work, whether in Source or Object
    +   form, that is based on (or derived from) the Work and for which the
    +   editorial revisions, annotations, elaborations, or other modifications
    +   represent, as a whole, an original work of authorship. For the purposes
    +   of this License, Derivative Works shall not include works that remain
    +   separable from, or merely link (or bind by name) to the interfaces of,
    +   the Work and Derivative Works thereof.
    +
    +   "Contribution" shall mean any work of authorship, including
    +   the original version of the Work and any modifications or additions
    +   to that Work or Derivative Works thereof, that is intentionally
    +   submitted to Licensor for inclusion in the Work by the copyright owner
    +   or by an individual or Legal Entity authorized to submit on behalf of
    +   the copyright owner. For the purposes of this definition, "submitted"
    +   means any form of electronic, verbal, or written communication sent
    +   to the Licensor or its representatives, including but not limited to
    +   communication on electronic mailing lists, source code control systems,
    +   and issue tracking systems that are managed by, or on behalf of, the
    +   Licensor for the purpose of discussing and improving the Work, but
    +   excluding communication that is conspicuously marked or otherwise
    +   designated in writing by the copyright owner as "Not a Contribution."
    +
    +   "Contributor" shall mean Licensor and any individual or Legal Entity
    +   on behalf of whom a Contribution has been received by Licensor and
    +   subsequently incorporated within the Work.
    +
    +2. Grant of Copyright License. Subject to the terms and conditions of
    +   this License, each Contributor hereby grants to You a perpetual,
    +   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
    +   copyright license to reproduce, prepare Derivative Works of,
    +   publicly display, publicly perform, sublicense, and distribute the
    +   Work and such Derivative Works in Source or Object form.
    +
    +3. Grant of Patent License. Subject to the terms and conditions of
    +   this License, each Contributor hereby grants to You a perpetual,
    +   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
    +   (except as stated in this section) patent license to make, have made,
    +   use, offer to sell, sell, import, and otherwise transfer the Work,
    +   where such license applies only to those patent claims licensable
    +   by such Contributor that are necessarily infringed by their
    +   Contribution(s) alone or by combination of their Contribution(s)
    +   with the Work to which such Contribution(s) was submitted. If You
    +   institute patent litigation against any entity (including a
    +   cross-claim or counterclaim in a lawsuit) alleging that the Work
    +   or a Contribution incorporated within the Work constitutes direct
    +   or contributory patent infringement, then any patent licenses
    +   granted to You under this License for that Work shall terminate
    +   as of the date such litigation is filed.
    +
    +4. Redistribution. You may reproduce and distribute copies of the
    +   Work or Derivative Works thereof in any medium, with or without
    +   modifications, and in Source or Object form, provided that You
    +   meet the following conditions:
    +
    +   (a) You must give any other recipients of the Work or
    +       Derivative Works a copy of this License; and
    +
    +   (b) You must cause any modified files to carry prominent notices
    +       stating that You changed the files; and
    +
    +   (c) You must retain, in the Source form of any Derivative Works
    +       that You distribute, all copyright, patent, trademark, and
    +       attribution notices from the Source form of the Work,
    +       excluding those notices that do not pertain to any part of
    +       the Derivative Works; and
    +
    +   (d) If the Work includes a "NOTICE" text file as part of its
    +       distribution, then any Derivative Works that You distribute must
    +       include a readable copy of the attribution notices contained
    +       within such NOTICE file, excluding those notices that do not
    +       pertain to any part of the Derivative Works, in at least one
    +       of the following places: within a NOTICE text file distributed
    +       as part of the Derivative Works; within the Source form or
    +       documentation, if provided along with the Derivative Works; or,
    +       within a display generated by the Derivative Works, if and
    +       wherever such third-party notices normally appear. The contents
    +       of the NOTICE file are for informational purposes only and
    +       do not modify the License. You may add Your own attribution
    +       notices within Derivative Works that You distribute, alongside
    +       or as an addendum to the NOTICE text from the Work, provided
    +       that such additional attribution notices cannot be construed
    +       as modifying the License.
    +
    +   You may add Your own copyright statement to Your modifications and
    +   may provide additional or different license terms and conditions
    +   for use, reproduction, or distribution of Your modifications, or
    +   for any such Derivative Works as a whole, provided Your use,
    +   reproduction, and distribution of the Work otherwise complies with
    +   the conditions stated in this License.
    +
    +5. Submission of Contributions. Unless You explicitly state otherwise,
    +   any Contribution intentionally submitted for inclusion in the Work
    +   by You to the Licensor shall be under the terms and conditions of
    +   this License, without any additional terms or conditions.
    +   Notwithstanding the above, nothing herein shall supersede or modify
    +   the terms of any separate license agreement you may have executed
    +   with Licensor regarding such Contributions.
    +
    +6. Trademarks. This License does not grant permission to use the trade
    +   names, trademarks, service marks, or product names of the Licensor,
    +   except as required for reasonable and customary use in describing the
    +   origin of the Work and reproducing the content of the NOTICE file.
    +
    +7. Disclaimer of Warranty. Unless required by applicable law or
    +   agreed to in writing, Licensor provides the Work (and each
    +   Contributor provides its Contributions) on an "AS IS" BASIS,
    +   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    +   implied, including, without limitation, any warranties or conditions
    +   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
    +   PARTICULAR PURPOSE. You are solely responsible for determining the
    +   appropriateness of using or redistributing the Work and assume any
    +   risks associated with Your exercise of permissions under this License.
    +
    +8. Limitation of Liability. In no event and under no legal theory,
    +   whether in tort (including negligence), contract, or otherwise,
    +   unless required by applicable law (such as deliberate and grossly
    +   negligent acts) or agreed to in writing, shall any Contributor be
    +   liable to You for damages, including any direct, indirect, special,
    +   incidental, or consequential damages of any character arising as a
    +   result of this License or out of the use or inability to use the
    +   Work (including but not limited to damages for loss of goodwill,
    +   work stoppage, computer failure or malfunction, or any and all
    +   other commercial damages or losses), even if such Contributor
    +   has been advised of the possibility of such damages.
    +
    +9. Accepting Warranty or Additional Liability. While redistributing
    +   the Work or Derivative Works thereof, You may choose to offer,
    +   and charge a fee for, acceptance of support, warranty, indemnity,
    +   or other liability obligations and/or rights consistent with this
    +   License. However, in accepting such obligations, You may act only
    +   on Your own behalf and on Your sole responsibility, not on behalf
    +   of any other Contributor, and only if You agree to indemnify,
    +   defend, and hold each Contributor harmless for any liability
    +   incurred by, or claims asserted against, such Contributor by reason
    +   of your accepting any such warranty or additional liability.
    +
    +END OF TERMS AND CONDITIONS
    +
    +APPENDIX: How to apply the Apache License to your work.
    +
    +   To apply the Apache License to your work, attach the following
    +   boilerplate notice, with the fields enclosed by brackets "[]"
    +   replaced with your own identifying information. (Don't include
    +   the brackets!)  The text should be enclosed in the appropriate
    +   comment syntax for the file format. We also recommend that a
    +   file or class name and description of purpose be included on the
    +   same "printed page" as the copyright notice for easier
    +   identification within third-party archives.
    +
    +Copyright [yyyy] [name of copyright owner]
    +
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
    +
    +	http://www.apache.org/licenses/LICENSE-2.0
    +
    +Unless required by applicable law or agreed to in writing, software
    +distributed under the License is distributed on an "AS IS" BASIS,
    +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +See the License for the specific language governing permissions and
    +limitations under the License.
    +
  • +
  • +

    Apache License 2.0

    +

    Used by:

    +
      +
    • addr2line
    • +
    • ahash
    • +
    • ahash
    • +
    • anyhow
    • +
    • arbitrary
    • +
    • async-channel
    • +
    • async-compression
    • +
    • async-trait
    • +
    • autocfg
    • +
    • backtrace
    • +
    • base64
    • +
    • base64
    • +
    • bitflags
    • +
    • bstr
    • +
    • bumpalo
    • cache-padded
    • camino
    • -
    • cc
    • +
    • cc
    • cfg-if
    • -
    • concurrent-queue
    • +
    • concurrent-queue
    • +
    • const-random
    • +
    • const-random-macro
    • core-foundation
    • core-foundation-sys
    • countme
    • @@ -6122,7 +6125,6 @@

      Used by:

    • libm
    • libz-sys
    • lock_api
    • -
    • maplit
    • mime
    • mockall
    • mockall_derive
    • @@ -6151,6 +6153,7 @@

      Used by:

    • pest_meta
    • petgraph
    • pkg-config
    • +
    • proc-macro-hack
    • proc-macro2
    • prost
    • quote
    • @@ -6192,7 +6195,6 @@

      Used by:

    • text-size
    • thiserror
    • thiserror-impl
    • -
    • thousands
    • thread_local
    • threadpool
    • toml
    • @@ -6205,12 +6207,10 @@

      Used by:

    • unicode-normalization
    • unicode-segmentation
    • unicode-width
    • -
    • unicode-xid
    • url
    • uuid
    • version_check
    • wasi
    • -
    • wasi
    • wasm-bindgen
    • wasm-bindgen-backend
    • wasm-bindgen-futures
    • @@ -6838,7 +6838,445 @@

      Used by:

      you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +
    • +

      Apache License 2.0

      +

      Used by:

      + +
                                    Apache License
      +                        Version 2.0, January 2004
      +                     http://www.apache.org/licenses/
      +
      +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
      +
      +1. Definitions.
      +
      +   "License" shall mean the terms and conditions for use, reproduction,
      +   and distribution as defined by Sections 1 through 9 of this document.
      +
      +   "Licensor" shall mean the copyright owner or entity authorized by
      +   the copyright owner that is granting the License.
      +
      +   "Legal Entity" shall mean the union of the acting entity and all
      +   other entities that control, are controlled by, or are under common
      +   control with that entity. For the purposes of this definition,
      +   "control" means (i) the power, direct or indirect, to cause the
      +   direction or management of such entity, whether by contract or
      +   otherwise, or (ii) ownership of fifty percent (50%) or more of the
      +   outstanding shares, or (iii) beneficial ownership of such entity.
      +
      +   "You" (or "Your") shall mean an individual or Legal Entity
      +   exercising permissions granted by this License.
      +
      +   "Source" form shall mean the preferred form for making modifications,
      +   including but not limited to software source code, documentation
      +   source, and configuration files.
      +
      +   "Object" form shall mean any form resulting from mechanical
      +   transformation or translation of a Source form, including but
      +   not limited to compiled object code, generated documentation,
      +   and conversions to other media types.
      +
      +   "Work" shall mean the work of authorship, whether in Source or
      +   Object form, made available under the License, as indicated by a
      +   copyright notice that is included in or attached to the work
      +   (an example is provided in the Appendix below).
      +
      +   "Derivative Works" shall mean any work, whether in Source or Object
      +   form, that is based on (or derived from) the Work and for which the
      +   editorial revisions, annotations, elaborations, or other modifications
      +   represent, as a whole, an original work of authorship. For the purposes
      +   of this License, Derivative Works shall not include works that remain
      +   separable from, or merely link (or bind by name) to the interfaces of,
      +   the Work and Derivative Works thereof.
      +
      +   "Contribution" shall mean any work of authorship, including
      +   the original version of the Work and any modifications or additions
      +   to that Work or Derivative Works thereof, that is intentionally
      +   submitted to Licensor for inclusion in the Work by the copyright owner
      +   or by an individual or Legal Entity authorized to submit on behalf of
      +   the copyright owner. For the purposes of this definition, "submitted"
      +   means any form of electronic, verbal, or written communication sent
      +   to the Licensor or its representatives, including but not limited to
      +   communication on electronic mailing lists, source code control systems,
      +   and issue tracking systems that are managed by, or on behalf of, the
      +   Licensor for the purpose of discussing and improving the Work, but
      +   excluding communication that is conspicuously marked or otherwise
      +   designated in writing by the copyright owner as "Not a Contribution."
      +
      +   "Contributor" shall mean Licensor and any individual or Legal Entity
      +   on behalf of whom a Contribution has been received by Licensor and
      +   subsequently incorporated within the Work.
      +
      +2. Grant of Copyright License. Subject to the terms and conditions of
      +   this License, each Contributor hereby grants to You a perpetual,
      +   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      +   copyright license to reproduce, prepare Derivative Works of,
      +   publicly display, publicly perform, sublicense, and distribute the
      +   Work and such Derivative Works in Source or Object form.
      +
      +3. Grant of Patent License. Subject to the terms and conditions of
      +   this License, each Contributor hereby grants to You a perpetual,
      +   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      +   (except as stated in this section) patent license to make, have made,
      +   use, offer to sell, sell, import, and otherwise transfer the Work,
      +   where such license applies only to those patent claims licensable
      +   by such Contributor that are necessarily infringed by their
      +   Contribution(s) alone or by combination of their Contribution(s)
      +   with the Work to which such Contribution(s) was submitted. If You
      +   institute patent litigation against any entity (including a
      +   cross-claim or counterclaim in a lawsuit) alleging that the Work
      +   or a Contribution incorporated within the Work constitutes direct
      +   or contributory patent infringement, then any patent licenses
      +   granted to You under this License for that Work shall terminate
      +   as of the date such litigation is filed.
      +
      +4. Redistribution. You may reproduce and distribute copies of the
      +   Work or Derivative Works thereof in any medium, with or without
      +   modifications, and in Source or Object form, provided that You
      +   meet the following conditions:
      +
      +   (a) You must give any other recipients of the Work or
      +       Derivative Works a copy of this License; and
      +
      +   (b) You must cause any modified files to carry prominent notices
      +       stating that You changed the files; and
      +
      +   (c) You must retain, in the Source form of any Derivative Works
      +       that You distribute, all copyright, patent, trademark, and
      +       attribution notices from the Source form of the Work,
      +       excluding those notices that do not pertain to any part of
      +       the Derivative Works; and
      +
      +   (d) If the Work includes a "NOTICE" text file as part of its
      +       distribution, then any Derivative Works that You distribute must
      +       include a readable copy of the attribution notices contained
      +       within such NOTICE file, excluding those notices that do not
      +       pertain to any part of the Derivative Works, in at least one
      +       of the following places: within a NOTICE text file distributed
      +       as part of the Derivative Works; within the Source form or
      +       documentation, if provided along with the Derivative Works; or,
      +       within a display generated by the Derivative Works, if and
      +       wherever such third-party notices normally appear. The contents
      +       of the NOTICE file are for informational purposes only and
      +       do not modify the License. You may add Your own attribution
      +       notices within Derivative Works that You distribute, alongside
      +       or as an addendum to the NOTICE text from the Work, provided
      +       that such additional attribution notices cannot be construed
      +       as modifying the License.
      +
      +   You may add Your own copyright statement to Your modifications and
      +   may provide additional or different license terms and conditions
      +   for use, reproduction, or distribution of Your modifications, or
      +   for any such Derivative Works as a whole, provided Your use,
      +   reproduction, and distribution of the Work otherwise complies with
      +   the conditions stated in this License.
      +
      +5. Submission of Contributions. Unless You explicitly state otherwise,
      +   any Contribution intentionally submitted for inclusion in the Work
      +   by You to the Licensor shall be under the terms and conditions of
      +   this License, without any additional terms or conditions.
      +   Notwithstanding the above, nothing herein shall supersede or modify
      +   the terms of any separate license agreement you may have executed
      +   with Licensor regarding such Contributions.
      +
      +6. Trademarks. This License does not grant permission to use the trade
      +   names, trademarks, service marks, or product names of the Licensor,
      +   except as required for reasonable and customary use in describing the
      +   origin of the Work and reproducing the content of the NOTICE file.
      +
      +7. Disclaimer of Warranty. Unless required by applicable law or
      +   agreed to in writing, Licensor provides the Work (and each
      +   Contributor provides its Contributions) on an "AS IS" BASIS,
      +   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      +   implied, including, without limitation, any warranties or conditions
      +   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      +   PARTICULAR PURPOSE. You are solely responsible for determining the
      +   appropriateness of using or redistributing the Work and assume any
      +   risks associated with Your exercise of permissions under this License.
      +
      +8. Limitation of Liability. In no event and under no legal theory,
      +   whether in tort (including negligence), contract, or otherwise,
      +   unless required by applicable law (such as deliberate and grossly
      +   negligent acts) or agreed to in writing, shall any Contributor be
      +   liable to You for damages, including any direct, indirect, special,
      +   incidental, or consequential damages of any character arising as a
      +   result of this License or out of the use or inability to use the
      +   Work (including but not limited to damages for loss of goodwill,
      +   work stoppage, computer failure or malfunction, or any and all
      +   other commercial damages or losses), even if such Contributor
      +   has been advised of the possibility of such damages.
      +
      +9. Accepting Warranty or Additional Liability. While redistributing
      +   the Work or Derivative Works thereof, You may choose to offer,
      +   and charge a fee for, acceptance of support, warranty, indemnity,
      +   or other liability obligations and/or rights consistent with this
      +   License. However, in accepting such obligations, You may act only
      +   on Your own behalf and on Your sole responsibility, not on behalf
      +   of any other Contributor, and only if You agree to indemnify,
      +   defend, and hold each Contributor harmless for any liability
      +   incurred by, or claims asserted against, such Contributor by reason
      +   of your accepting any such warranty or additional liability.
      +
      +END OF TERMS AND CONDITIONS
      +
      +APPENDIX: How to apply the Apache License to your work.
      +
      +   To apply the Apache License to your work, attach the following
      +   boilerplate notice, with the fields enclosed by brackets "[]"
      +   replaced with your own identifying information. (Don't include
      +   the brackets!)  The text should be enclosed in the appropriate
      +   comment syntax for the file format. We also recommend that a
      +   file or class name and description of purpose be included on the
      +   same "printed page" as the copyright notice for easier
      +   identification within third-party archives.
      +
      +Copyright [yyyy] [name of copyright owner]
      +
      +Licensed under the Apache License, Version 2.0 (the "License");
      +you may not use this file except in compliance with the License.
      +You may obtain a copy of the License at
      +
      +   http://www.apache.org/licenses/LICENSE-2.0
      +
      +Unless required by applicable law or agreed to in writing, software
      +distributed under the License is distributed on an "AS IS" BASIS,
      +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      +See the License for the specific language governing permissions and
      +limitations under the License.
      +
    • +
    • +

      Apache License 2.0

      +

      Used by:

      + +
                                    Apache License
      +                        Version 2.0, January 2004
      +                     http://www.apache.org/licenses/
      +
      +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
      +
      +1. Definitions.
      +
      +   "License" shall mean the terms and conditions for use, reproduction,
      +   and distribution as defined by Sections 1 through 9 of this document.
      +
      +   "Licensor" shall mean the copyright owner or entity authorized by
      +   the copyright owner that is granting the License.
      +
      +   "Legal Entity" shall mean the union of the acting entity and all
      +   other entities that control, are controlled by, or are under common
      +   control with that entity. For the purposes of this definition,
      +   "control" means (i) the power, direct or indirect, to cause the
      +   direction or management of such entity, whether by contract or
      +   otherwise, or (ii) ownership of fifty percent (50%) or more of the
      +   outstanding shares, or (iii) beneficial ownership of such entity.
      +
      +   "You" (or "Your") shall mean an individual or Legal Entity
      +   exercising permissions granted by this License.
      +
      +   "Source" form shall mean the preferred form for making modifications,
      +   including but not limited to software source code, documentation
      +   source, and configuration files.
      +
      +   "Object" form shall mean any form resulting from mechanical
      +   transformation or translation of a Source form, including but
      +   not limited to compiled object code, generated documentation,
      +   and conversions to other media types.
      +
      +   "Work" shall mean the work of authorship, whether in Source or
      +   Object form, made available under the License, as indicated by a
      +   copyright notice that is included in or attached to the work
      +   (an example is provided in the Appendix below).
      +
      +   "Derivative Works" shall mean any work, whether in Source or Object
      +   form, that is based on (or derived from) the Work and for which the
      +   editorial revisions, annotations, elaborations, or other modifications
      +   represent, as a whole, an original work of authorship. For the purposes
      +   of this License, Derivative Works shall not include works that remain
      +   separable from, or merely link (or bind by name) to the interfaces of,
      +   the Work and Derivative Works thereof.
      +
      +   "Contribution" shall mean any work of authorship, including
      +   the original version of the Work and any modifications or additions
      +   to that Work or Derivative Works thereof, that is intentionally
      +   submitted to Licensor for inclusion in the Work by the copyright owner
      +   or by an individual or Legal Entity authorized to submit on behalf of
      +   the copyright owner. For the purposes of this definition, "submitted"
      +   means any form of electronic, verbal, or written communication sent
      +   to the Licensor or its representatives, including but not limited to
      +   communication on electronic mailing lists, source code control systems,
      +   and issue tracking systems that are managed by, or on behalf of, the
      +   Licensor for the purpose of discussing and improving the Work, but
      +   excluding communication that is conspicuously marked or otherwise
      +   designated in writing by the copyright owner as "Not a Contribution."
      +
      +   "Contributor" shall mean Licensor and any individual or Legal Entity
      +   on behalf of whom a Contribution has been received by Licensor and
      +   subsequently incorporated within the Work.
      +
      +2. Grant of Copyright License. Subject to the terms and conditions of
      +   this License, each Contributor hereby grants to You a perpetual,
      +   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      +   copyright license to reproduce, prepare Derivative Works of,
      +   publicly display, publicly perform, sublicense, and distribute the
      +   Work and such Derivative Works in Source or Object form.
      +
      +3. Grant of Patent License. Subject to the terms and conditions of
      +   this License, each Contributor hereby grants to You a perpetual,
      +   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      +   (except as stated in this section) patent license to make, have made,
      +   use, offer to sell, sell, import, and otherwise transfer the Work,
      +   where such license applies only to those patent claims licensable
      +   by such Contributor that are necessarily infringed by their
      +   Contribution(s) alone or by combination of their Contribution(s)
      +   with the Work to which such Contribution(s) was submitted. If You
      +   institute patent litigation against any entity (including a
      +   cross-claim or counterclaim in a lawsuit) alleging that the Work
      +   or a Contribution incorporated within the Work constitutes direct
      +   or contributory patent infringement, then any patent licenses
      +   granted to You under this License for that Work shall terminate
      +   as of the date such litigation is filed.
      +
      +4. Redistribution. You may reproduce and distribute copies of the
      +   Work or Derivative Works thereof in any medium, with or without
      +   modifications, and in Source or Object form, provided that You
      +   meet the following conditions:
      +
      +   (a) You must give any other recipients of the Work or
      +       Derivative Works a copy of this License; and
      +
      +   (b) You must cause any modified files to carry prominent notices
      +       stating that You changed the files; and
      +
      +   (c) You must retain, in the Source form of any Derivative Works
      +       that You distribute, all copyright, patent, trademark, and
      +       attribution notices from the Source form of the Work,
      +       excluding those notices that do not pertain to any part of
      +       the Derivative Works; and
      +
      +   (d) If the Work includes a "NOTICE" text file as part of its
      +       distribution, then any Derivative Works that You distribute must
      +       include a readable copy of the attribution notices contained
      +       within such NOTICE file, excluding those notices that do not
      +       pertain to any part of the Derivative Works, in at least one
      +       of the following places: within a NOTICE text file distributed
      +       as part of the Derivative Works; within the Source form or
      +       documentation, if provided along with the Derivative Works; or,
      +       within a display generated by the Derivative Works, if and
      +       wherever such third-party notices normally appear. The contents
      +       of the NOTICE file are for informational purposes only and
      +       do not modify the License. You may add Your own attribution
      +       notices within Derivative Works that You distribute, alongside
      +       or as an addendum to the NOTICE text from the Work, provided
      +       that such additional attribution notices cannot be construed
      +       as modifying the License.
      +
      +   You may add Your own copyright statement to Your modifications and
      +   may provide additional or different license terms and conditions
      +   for use, reproduction, or distribution of Your modifications, or
      +   for any such Derivative Works as a whole, provided Your use,
      +   reproduction, and distribution of the Work otherwise complies with
      +   the conditions stated in this License.
      +
      +5. Submission of Contributions. Unless You explicitly state otherwise,
      +   any Contribution intentionally submitted for inclusion in the Work
      +   by You to the Licensor shall be under the terms and conditions of
      +   this License, without any additional terms or conditions.
      +   Notwithstanding the above, nothing herein shall supersede or modify
      +   the terms of any separate license agreement you may have executed
      +   with Licensor regarding such Contributions.
      +
      +6. Trademarks. This License does not grant permission to use the trade
      +   names, trademarks, service marks, or product names of the Licensor,
      +   except as required for reasonable and customary use in describing the
      +   origin of the Work and reproducing the content of the NOTICE file.
      +
      +7. Disclaimer of Warranty. Unless required by applicable law or
      +   agreed to in writing, Licensor provides the Work (and each
      +   Contributor provides its Contributions) on an "AS IS" BASIS,
      +   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      +   implied, including, without limitation, any warranties or conditions
      +   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      +   PARTICULAR PURPOSE. You are solely responsible for determining the
      +   appropriateness of using or redistributing the Work and assume any
      +   risks associated with Your exercise of permissions under this License.
      +
      +8. Limitation of Liability. In no event and under no legal theory,
      +   whether in tort (including negligence), contract, or otherwise,
      +   unless required by applicable law (such as deliberate and grossly
      +   negligent acts) or agreed to in writing, shall any Contributor be
      +   liable to You for damages, including any direct, indirect, special,
      +   incidental, or consequential damages of any character arising as a
      +   result of this License or out of the use or inability to use the
      +   Work (including but not limited to damages for loss of goodwill,
      +   work stoppage, computer failure or malfunction, or any and all
      +   other commercial damages or losses), even if such Contributor
      +   has been advised of the possibility of such damages.
      +
      +9. Accepting Warranty or Additional Liability. While redistributing
      +   the Work or Derivative Works thereof, You may choose to offer,
      +   and charge a fee for, acceptance of support, warranty, indemnity,
      +   or other liability obligations and/or rights consistent with this
      +   License. However, in accepting such obligations, You may act only
      +   on Your own behalf and on Your sole responsibility, not on behalf
      +   of any other Contributor, and only if You agree to indemnify,
      +   defend, and hold each Contributor harmless for any liability
      +   incurred by, or claims asserted against, such Contributor by reason
      +   of your accepting any such warranty or additional liability.
      +
      +END OF TERMS AND CONDITIONS
      +
      +APPENDIX: How to apply the Apache License to your work.
      +
      +   To apply the Apache License to your work, attach the following
      +   boilerplate notice, with the fields enclosed by brackets "[]"
      +   replaced with your own identifying information. (Don't include
      +   the brackets!)  The text should be enclosed in the appropriate
      +   comment syntax for the file format. We also recommend that a
      +   file or class name and description of purpose be included on the
      +   same "printed page" as the copyright notice for easier
      +   identification within third-party archives.
      +
      +Copyright [yyyy] [name of copyright owner]
      +
      +Licensed under the Apache License, Version 2.0 (the "License");
      +you may not use this file except in compliance with the License.
      +You may obtain a copy of the License at
      +
      +   http://www.apache.org/licenses/LICENSE-2.0
       
       Unless required by applicable law or agreed to in writing, software
       distributed under the License is distributed on an "AS IS" BASIS,
      @@ -6851,49 +7289,11 @@ 

      Used by:

      Apache License 2.0

      Used by:

                                    Apache License
                               Version 2.0, January 2004
      -                     http://www.apache.org/licenses/
      +                     https://www.apache.org/licenses/
       
       TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
       
      @@ -7067,38 +7467,13 @@ 

      Used by:

      of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License.
    • Apache License 2.0

      Used by:

                                    Apache License
                               Version 2.0, January 2004
      @@ -7276,6 +7651,17 @@ 

      Used by:

      of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives.
    • @@ -7284,7 +7670,6 @@

      Used by:

                                    Apache License
                               Version 2.0, January 2004
      @@ -8122,7 +8507,6 @@ 

      Apache License 2.0

      Used by:

                                    Apache License
                               Version 2.0, January 2004
      @@ -8945,17 +9329,6 @@ 

      Used by:

      limitations under the License.
    • -
    • -

      Apache License 2.0

      -

      Used by:

      - -
      ../../LICENSE-APACHE
      -
    • Apache License 2.0

      Used by:

      @@ -9391,9 +9764,13 @@

      Used by:

      Apache License 2.0

      Used by:

      Apache License
      @@ -9763,7 +10146,6 @@ 

      Apache License 2.0

      Used by:

      Copyright [2022] [Bryn Cooke]
       
      @@ -9787,7 +10169,6 @@ 

      Used by:

    • unicode-normalization
    • unicode-segmentation
    • unicode-width
    • -
    • unicode-xid
    Licensed under the Apache License, Version 2.0
     <LICENSE-APACHE or
    @@ -10040,6 +10421,7 @@ 

    Creative Commons Zero v1.0 Universal

    Used by:

    Creative Commons Legal Code
     
    @@ -10328,7 +10710,6 @@ 

    ISC License

    Used by:

    ISC License:
    @@ -11376,9 +11757,11 @@ 

    Used by:

    MIT License

    Used by:

    -
    Copyright (c) 2021 Tokio Contributors
    +                
    Copyright (c) 2022 Tokio Contributors
     
     Permission is hereby granted, free of charge, to any
     person obtaining a copy of this software and associated
    @@ -11403,37 +11786,13 @@ 

    Used by:

    OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -The MIT License (MIT) - -Copyright (c) 2019 Yoshua Wuyts - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE.
  • MIT License

    Used by:

    Copyright (c) 2022 Tokio Contributors
     
    @@ -11460,21 +11819,28 @@ 

    Used by:

    OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -
    -
  • -
  • -

    MIT License

    -

    Used by:

    - -
    Copyright 2016 Nika Layzell
     
    -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
    +The MIT License (MIT)
     
    -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
    +Copyright (c) 2019 Yoshua Wuyts
     
    -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    +Permission is hereby granted, free of charge, to any person obtaining a copy
    +of this software and associated documentation files (the "Software"), to deal
    +in the Software without restriction, including without limitation the rights
    +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    +copies of the Software, and to permit persons to whom the Software is
    +furnished to do so, subject to the following conditions:
    +
    +The above copyright notice and this permission notice shall be included in all
    +copies or substantial portions of the Software.
    +
    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    +SOFTWARE.
     
  • @@ -11748,6 +12114,35 @@

    Used by:

    The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +
  • +
  • +

    MIT License

    +

    Used by:

    + +
    MIT License
    +
    +Copyright (c) 2020 - present The owo-colors Developers
    +
    +Permission is hereby granted, free of charge, to any person obtaining a copy
    +of this software and associated documentation files (the "Software"), to deal
    +in the Software without restriction, including without limitation the rights
    +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    +copies of the Software, and to permit persons to whom the Software is
    +furnished to do so, subject to the following conditions:
    +
    +The above copyright notice and this permission notice shall be included in all
    +copies or substantial portions of the Software.
    +
     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    @@ -11762,6 +12157,7 @@ 

    MIT License

    Used by:

    MIT License
     
    @@ -11819,13 +12215,14 @@ 

    Used by:

    MIT License

    Used by:

    -
  • -
  • -

    MIT License

    -

    Used by:

    - -
    The MIT License (MIT)
    -
    -Copyright (c) 2015 Bartłomiej Kamiński
    -
    -Permission is hereby granted, free of charge, to any person obtaining a copy
    -of this software and associated documentation files (the "Software"), to deal
    -in the Software without restriction, including without limitation the rights
    -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -copies of the Software, and to permit persons to whom the Software is
    -furnished to do so, subject to the following conditions:
    -
    -The above copyright notice and this permission notice shall be included in all
    -copies or substantial portions of the Software.
    -
    -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    -SOFTWARE.
  • MIT License

    @@ -12379,6 +12748,7 @@

    Used by:

    @@ -12987,6 +13357,36 @@

    Used by:

    ***** END LICENSE BLOCK ***** @(#) $RCSfile: certdata.txt,v $ $Revision: 1.80 $ $Date: 2011/11/03 15:11:58 $ + +
  • +
  • +

    Unicode License Agreement - Data Files and Software (2016)

    +

    Used by:

    + +
    UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE
    +
    +Unicode Data Files include all data files under the directories http://www.unicode.org/Public/, http://www.unicode.org/reports/, http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/, and http://www.unicode.org/utility/trac/browser/.
    +
    +Unicode Data Files do not include PDF online code charts under the directory http://www.unicode.org/Public/.
    +
    +Software includes any source code published in the Unicode Standard or under the directories http://www.unicode.org/Public/, http://www.unicode.org/reports/, http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/, and http://www.unicode.org/utility/trac/browser/.
    +
    +NOTICE TO USER: Carefully read the following legal agreement. BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.
    +
    +COPYRIGHT AND PERMISSION NOTICE
    +
    +Copyright © 1991-2016 Unicode, Inc. All rights reserved. Distributed under the Terms of Use in http://www.unicode.org/copyright.html.
    +
    +Permission is hereby granted, free of charge, to any person obtaining a copy of the Unicode data files and any associated documentation (the "Data Files") or Unicode software and any associated documentation (the "Software") to deal in the Data Files or Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, and/or sell copies of the Data Files or Software, and to permit persons to whom the Data Files or Software are furnished to do so, provided that either
    +
    +     (a) this copyright and permission notice appear with all copies of the Data Files or Software, or
    +     (b) this copyright and permission notice appear in associated Documentation.
    +
    +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA FILES OR SOFTWARE.
    +
    +Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in these Data Files or Software without prior written authorization of the copyright holder.
     
  • From 24e609888dfc764dafc8a878d8841c76310b1a25 Mon Sep 17 00:00:00 2001 From: bryn Date: Mon, 21 Nov 2022 17:33:18 +0000 Subject: [PATCH 2/8] Added configuration upgrade migrations. Fixes #2123 --- Cargo.lock | 95 ++++++++ apollo-router/Cargo.toml | 5 +- .../0001-telemetry_router_to_supergraph.yaml | 6 + .../src/configuration/migrations/README.md | 52 ++++ apollo-router/src/configuration/mod.rs | 8 +- apollo-router/src/configuration/schema.rs | 19 +- ...etry_router_to_supergraph.router.yaml.snap | 14 ++ ...on__upgrade__test__copy_array_element.snap | 19 ++ ...figuration__upgrade__test__copy_field.snap | 19 ++ ...__upgrade__test__delete_array_element.snap | 13 + ...guration__upgrade__test__delete_field.snap | 13 + ...on__upgrade__test__move_array_element.snap | 18 ++ ...figuration__upgrade__test__move_field.snap | 18 ++ .../testdata/migrations/.skipconfigvalidation | 0 ...telemetry_router_to_supergraph.router.yaml | 8 + apollo-router/src/configuration/tests.rs | 65 ++++- apollo-router/src/configuration/upgrade.rs | 224 ++++++++++++++++++ apollo-router/src/executable.rs | 93 ++++++-- 18 files changed, 664 insertions(+), 25 deletions(-) create mode 100644 apollo-router/src/configuration/migrations/0001-telemetry_router_to_supergraph.yaml create mode 100644 apollo-router/src/configuration/migrations/README.md create mode 100644 apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__update_old_configuration@telemetry_router_to_supergraph.router.yaml.snap create mode 100644 apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__copy_array_element.snap create mode 100644 apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__copy_field.snap create mode 100644 apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__delete_array_element.snap create mode 100644 apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__delete_field.snap create mode 100644 apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__move_array_element.snap create mode 100644 apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__move_field.snap create mode 100644 apollo-router/src/configuration/testdata/migrations/.skipconfigvalidation create mode 100644 apollo-router/src/configuration/testdata/migrations/telemetry_router_to_supergraph.router.yaml create mode 100644 apollo-router/src/configuration/upgrade.rs diff --git a/Cargo.lock b/Cargo.lock index 121633f063..ae2c2c0b91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -208,6 +208,7 @@ dependencies = [ "prometheus", "prost 0.9.0", "prost-types 0.9.0", + "proteus", "rand", "redis", "redis_cluster_async", @@ -215,6 +216,7 @@ dependencies = [ "reqwest", "rhai", "router-bridge", + "rust-embed", "schemars", "serde", "serde_json", @@ -1803,6 +1805,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "ghost" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb19fe8de3ea0920d282f7b77dd4227aea6b8b999b42cdf0ca41b2472b14443a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "gimli" version = "0.26.2" @@ -2315,6 +2328,16 @@ dependencies = [ "tracing", ] +[[package]] +name = "inventory" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84344c6e0b90a9e2b6f3f9abe5cc74402684e348df7b32adca28747e0cef091a" +dependencies = [ + "ctor", + "ghost", +] + [[package]] name = "ipnet" version = "2.5.1" @@ -3657,6 +3680,20 @@ dependencies = [ "prost 0.11.2", ] +[[package]] +name = "proteus" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "279396105537894fdecabfba63493bc93192c94a97951bef640d2feac3cfc362" +dependencies = [ + "once_cell", + "regex", + "serde", + "serde_json", + "thiserror", + "typetag", +] + [[package]] name = "protobuf" version = "2.28.0" @@ -4063,6 +4100,40 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rust-embed" +version = "6.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "283ffe2f866869428c92e0d61c2f35dfb4355293cdfdc48f49e895c15f1333d1" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir 2.3.2", +] + +[[package]] +name = "rust-embed-impl" +version = "6.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ab23d42d71fb9be1b643fe6765d292c5e14d46912d13f3ae2815ca048ea04d" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn", + "walkdir 2.3.2", +] + +[[package]] +name = "rust-embed-utils" +version = "7.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1669d81dfabd1b5f8e2856b8bbe146c6192b0ba22162edc738ac0a5de18f054" +dependencies = [ + "sha2", + "walkdir 2.3.2", +] + [[package]] name = "rustc-demangle" version = "0.1.21" @@ -5449,6 +5520,30 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +[[package]] +name = "typetag" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4080564c5b2241b5bff53ab610082234e0c57b0417f4bd10596f183001505b8a" +dependencies = [ + "erased-serde", + "inventory", + "once_cell", + "serde", + "typetag-impl", +] + +[[package]] +name = "typetag-impl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e60147782cc30833c05fba3bab1d9b5771b2685a2557672ac96fa5d154099c0e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "ucd-trie" version = "0.1.5" diff --git a/apollo-router/Cargo.toml b/apollo-router/Cargo.toml index 389bc89d6e..f1ab09ba91 100644 --- a/apollo-router/Cargo.toml +++ b/apollo-router/Cargo.toml @@ -84,6 +84,7 @@ hyper = { version = "0.14.23", features = ["server", "client"] } hyper-rustls = { version = "0.23.1", features = ["http1", "http2"] } indexmap = { version = "1.9.1", features = ["serde-1"] } itertools = "0.10.5" +jsonpath_lib = "0.3.0" jsonschema = { version = "0.16.1", default-features = false } lazy_static = "1.4.0" libc = "0.2.137" @@ -140,6 +141,7 @@ pin-project-lite = "0.2.9" prometheus = "0.13" prost = "0.9.0" prost-types = "0.9.0" +proteus = "0.5.0" rand = "0.8.5" rhai = { version = "1.11.0", features = ["sync", "serde", "internals"] } redis = { version = "0.21.6", optional = true, features = ["cluster", "tokio-comp"] } @@ -151,6 +153,7 @@ reqwest = { version = "0.11.12", default-features = false, features = [ "stream", ] } router-bridge = "0.1.11" +rust-embed="6.4.2" schemars = { version = "0.8.11", features = ["url"] } shellexpand = "2.1.2" sha2 = "0.10.6" @@ -193,7 +196,6 @@ tracing-core = "=0.1.26" tracing-futures = { version = "0.2.5", features = ["futures-03"] } tracing-opentelemetry = "0.17.4" tracing-subscriber = { version = "0.3.11", features = ["env-filter", "json"] } - url = { version = "2.3.1", features = ["serde"] } urlencoding = "2.1.2" uuid = { version = "1.2.2", features = ["serde", "v4"] } @@ -209,7 +211,6 @@ uname = "0.1.1" [dev-dependencies] insta = { version = "1.21.1", features = ["json", "redactions"] } introspector-gadget = "0.1.0" -jsonpath_lib = "0.3.0" maplit = "1.0.2" memchr = { version = "2.5.0", default-features = false } mockall = "0.11.3" diff --git a/apollo-router/src/configuration/migrations/0001-telemetry_router_to_supergraph.yaml b/apollo-router/src/configuration/migrations/0001-telemetry_router_to_supergraph.yaml new file mode 100644 index 0000000000..7061a07d2a --- /dev/null +++ b/apollo-router/src/configuration/migrations/0001-telemetry_router_to_supergraph.yaml @@ -0,0 +1,6 @@ +description: telemetry.tracing.trace_config.attributes.router has been renamed to 'supergraph' for consistency +actions: + - type: move + from: telemetry.metrics.common.attributes.router + to: telemetry.metrics.common.attributes.supergraph + diff --git a/apollo-router/src/configuration/migrations/README.md b/apollo-router/src/configuration/migrations/README.md new file mode 100644 index 0000000000..d6367087c5 --- /dev/null +++ b/apollo-router/src/configuration/migrations/README.md @@ -0,0 +1,52 @@ +# Configuration migrations +This directory contains configuration migrations that can be applied to a router to a router config to bring it up to date with current config format. + +It uses [proteus](https://github.com/rust-playground/proteus) under the hood, which handles the complexities of merging Json. + +A migration has the following format: + +The filename should begin with a 5 digit numerical prefix. This allows us to apply migrations in a deterministic order. +`Filename: 00001-name.yaml` + +The yaml consists of a description and a number of actions: +```yaml +description: telemetry.tracing.trace_config.attributes.router has been renamed to 'supergraph' for consistency +actions: + - type: move + from: some.source + to: some.destination + - type: copy + from: some.source + to: some.destination + - type: delete + path: some.destination +``` + +Each action is applied in order. Use the following formats for from, to and path. + +## Getter (from) +| syntax | description | +---------|-------------| +| | this will grab the top-level value which could be any valid type: Object, array, ... | +| id | Gets a JSON Object's name. eg. key in HashMap | +| [0] | Gets a JSON Arrays index at the specified index. | +| profile.first_name | Combine Object names with dot notation. | +| profile.address[0].street | Combinations using dot notation and indexes is also supported. | + +## Setter (to, path) +| syntax | description | +---------|-------------| +| | this will set the top-level value in the destination | +| id | By itself any text is considered to be a JSON Object's name. | +| [] | This appends the source **data** to an array, creating it if it doesn't exist and is only valid at the end of set syntax eg. profile.address[] | +| [\+] | The source Array should append all of it's values into the destination Array and is only valid at the end of set syntax eg. profile.address[] | +| [\-] | The source Array values should replace the destination Array's values at the overlapping indexes and is only valid at the end of set syntax eg. profile.address[] | +| {} | This merges the supplied Object overtop of the existing and is only valid at the end of set syntax eg. profile{} | +| profile.first_name | Combine Object names with dot notation. | +| profile.address[0].street | Combinations using dot notation and indexes is also supported. | + +See [proteus](https://github.com/rust-playground/proteus) for more options. + +If a migration is deemed to have changed the configuration then the description of the migration will be output to the user as a warning. + +In future we will be able to use these files to support offline migrations. diff --git a/apollo-router/src/configuration/mod.rs b/apollo-router/src/configuration/mod.rs index 22e5d6ebfe..0423360710 100644 --- a/apollo-router/src/configuration/mod.rs +++ b/apollo-router/src/configuration/mod.rs @@ -5,6 +5,7 @@ mod expansion; mod schema; #[cfg(test)] mod tests; +mod upgrade; mod yaml; use std::fmt; @@ -12,6 +13,7 @@ use std::net::IpAddr; use std::net::SocketAddr; use std::str::FromStr; +use crate::configuration::schema::Mode; use askama::Template; use bytes::Bytes; use cors::*; @@ -20,6 +22,7 @@ use displaydoc::Display; use expansion::*; use itertools::Itertools; pub(crate) use schema::generate_config_schema; +pub(crate) use schema::upgrade_configuration; use schemars::gen::SchemaGenerator; use schemars::schema::ObjectValidation; use schemars::schema::Schema; @@ -60,6 +63,9 @@ pub enum ConfigurationError { /// APOLLO_ROUTER_CONFIG_SUPPORTED_MODES must be of the format env,file,... Possible modes are 'env' and 'file'. InvalidExpansionModeConfig, + + /// could not migrate configuration: {error}. + MigrationFailure { error: String }, } /// The configuration for the router. @@ -353,7 +359,7 @@ impl FromStr for Configuration { type Err = ConfigurationError; fn from_str(s: &str) -> Result { - schema::validate_yaml_configuration(s, Expansion::default()?)?.validate() + schema::validate_yaml_configuration(s, Expansion::default()?, Mode::Upgrade)?.validate() } } diff --git a/apollo-router/src/configuration/schema.rs b/apollo-router/src/configuration/schema.rs index 258f3fdb4c..67200a16c8 100644 --- a/apollo-router/src/configuration/schema.rs +++ b/apollo-router/src/configuration/schema.rs @@ -18,6 +18,7 @@ use super::yaml; use super::Configuration; use super::ConfigurationError; use super::APOLLO_PLUGIN_PREFIX; +pub(crate) use crate::configuration::upgrade::upgrade_configuration; /// Generate a JSON schema for the configuration. pub(crate) fn generate_config_schema() -> RootSchema { @@ -37,6 +38,15 @@ pub(crate) fn generate_config_schema() -> RootSchema { schema } +#[derive(Eq, PartialEq)] +pub(crate) enum Mode { + Upgrade, + + // This is used only in testing to ensure that we don't allow old config in our tests. + #[cfg(test)] + NoUpgrade, +} + /// Validate config yaml against the generated json schema. /// This is a tricky problem, and the solution here is by no means complete. /// In the case that validation cannot be performed then it will let serde validate as normal. The @@ -54,6 +64,7 @@ pub(crate) fn generate_config_schema() -> RootSchema { pub(crate) fn validate_yaml_configuration( raw_yaml: &str, expansion: Expansion, + migration: Mode, ) -> Result { let defaulted_yaml = if raw_yaml.trim().is_empty() { "plugins:".to_string() @@ -61,14 +72,16 @@ pub(crate) fn validate_yaml_configuration( raw_yaml.to_string() }; - let yaml = &serde_yaml::from_str(&defaulted_yaml).map_err(|e| { + let mut yaml = serde_yaml::from_str(&defaulted_yaml).map_err(|e| { ConfigurationError::InvalidConfiguration { message: "failed to parse yaml", error: e.to_string(), } })?; - - let expanded_yaml = expand_env_variables(yaml, expansion)?; + if migration == Mode::Upgrade { + yaml = upgrade_configuration(yaml, true)?; + } + let expanded_yaml = expand_env_variables(&yaml, expansion)?; let schema = serde_json::to_value(generate_config_schema()).map_err(|e| { ConfigurationError::InvalidConfiguration { message: "failed to parse schema", diff --git a/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__update_old_configuration@telemetry_router_to_supergraph.router.yaml.snap b/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__update_old_configuration@telemetry_router_to_supergraph.router.yaml.snap new file mode 100644 index 0000000000..1db53b0e56 --- /dev/null +++ b/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__update_old_configuration@telemetry_router_to_supergraph.router.yaml.snap @@ -0,0 +1,14 @@ +--- +source: apollo-router/src/configuration/tests.rs +expression: new_config +--- +--- +telemetry: + metrics: + common: + attributes: + supergraph: + request: + header: + - named: fd + diff --git a/apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__copy_array_element.snap b/apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__copy_array_element.snap new file mode 100644 index 0000000000..7455fbbda0 --- /dev/null +++ b/apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__copy_array_element.snap @@ -0,0 +1,19 @@ +--- +source: apollo-router/src/configuration/upgrade.rs +expression: "apply_migration(&source_doc(),\n &Migration::builder().action(Action::Copy {\n from: \"arr[0]\".to_string(),\n to: \"new.arr[0]\".to_string(),\n }).description(\"copy arr[0]\").build()).expect(\"expected successful migration\")" +--- +{ + "obj": { + "field1": 1, + "field2": 2 + }, + "arr": [ + "v1", + "v2" + ], + "new": { + "arr": [ + "v1" + ] + } +} diff --git a/apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__copy_field.snap b/apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__copy_field.snap new file mode 100644 index 0000000000..029e4e814a --- /dev/null +++ b/apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__copy_field.snap @@ -0,0 +1,19 @@ +--- +source: apollo-router/src/configuration/upgrade.rs +expression: "apply_migration(&source_doc(),\n &Migration::builder().action(Action::Copy {\n from: \"obj.field1\".to_string(),\n to: \"new.obj.field1\".to_string(),\n }).description(\"copy field1\").build()).expect(\"expected successful migration\")" +--- +{ + "obj": { + "field1": 1, + "field2": 2 + }, + "arr": [ + "v1", + "v2" + ], + "new": { + "obj": { + "field1": 1 + } + } +} diff --git a/apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__delete_array_element.snap b/apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__delete_array_element.snap new file mode 100644 index 0000000000..850c1bb543 --- /dev/null +++ b/apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__delete_array_element.snap @@ -0,0 +1,13 @@ +--- +source: apollo-router/src/configuration/upgrade.rs +expression: "apply_migration(&source_doc(),\n &Migration::builder().action(Action::Delete {\n path: \"arr[0]\".to_string(),\n }).description(\"delete arr[0]\").build()).expect(\"expected successful migration\")" +--- +{ + "obj": { + "field1": 1, + "field2": 2 + }, + "arr": [ + "v2" + ] +} diff --git a/apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__delete_field.snap b/apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__delete_field.snap new file mode 100644 index 0000000000..d3e1045312 --- /dev/null +++ b/apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__delete_field.snap @@ -0,0 +1,13 @@ +--- +source: apollo-router/src/configuration/upgrade.rs +expression: "apply_migration(&source_doc(),\n &Migration::builder().action(Action::Delete {\n path: \"obj.field1\".to_string(),\n }).description(\"delete field1\").build()).expect(\"expected successful migration\")" +--- +{ + "obj": { + "field2": 2 + }, + "arr": [ + "v1", + "v2" + ] +} diff --git a/apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__move_array_element.snap b/apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__move_array_element.snap new file mode 100644 index 0000000000..f8e479e750 --- /dev/null +++ b/apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__move_array_element.snap @@ -0,0 +1,18 @@ +--- +source: apollo-router/src/configuration/upgrade.rs +expression: "apply_migration(&source_doc(),\n &Migration::builder().action(Action::Move {\n from: \"arr[0]\".to_string(),\n to: \"new.arr[0]\".to_string(),\n }).description(\"move arr[0]\").build()).expect(\"expected successful migration\")" +--- +{ + "obj": { + "field1": 1, + "field2": 2 + }, + "arr": [ + "v2" + ], + "new": { + "arr": [ + "v1" + ] + } +} diff --git a/apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__move_field.snap b/apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__move_field.snap new file mode 100644 index 0000000000..e8136fb407 --- /dev/null +++ b/apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__move_field.snap @@ -0,0 +1,18 @@ +--- +source: apollo-router/src/configuration/upgrade.rs +expression: "apply_migration(&source_doc(),\n &Migration::builder().action(Action::Move {\n from: \"obj.field1\".to_string(),\n to: \"new.obj.field1\".to_string(),\n }).description(\"move field1\").build()).expect(\"expected successful migration\")" +--- +{ + "obj": { + "field2": 2 + }, + "arr": [ + "v1", + "v2" + ], + "new": { + "obj": { + "field1": 1 + } + } +} diff --git a/apollo-router/src/configuration/testdata/migrations/.skipconfigvalidation b/apollo-router/src/configuration/testdata/migrations/.skipconfigvalidation new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apollo-router/src/configuration/testdata/migrations/telemetry_router_to_supergraph.router.yaml b/apollo-router/src/configuration/testdata/migrations/telemetry_router_to_supergraph.router.yaml new file mode 100644 index 0000000000..0e44d606a6 --- /dev/null +++ b/apollo-router/src/configuration/testdata/migrations/telemetry_router_to_supergraph.router.yaml @@ -0,0 +1,8 @@ +telemetry: + metrics: + common: + attributes: + router: + request: + header: + - named: "fd" diff --git a/apollo-router/src/configuration/tests.rs b/apollo-router/src/configuration/tests.rs index c05a8e6434..a4515afa82 100644 --- a/apollo-router/src/configuration/tests.rs +++ b/apollo-router/src/configuration/tests.rs @@ -6,6 +6,7 @@ use http::Uri; #[cfg(unix)] use insta::assert_json_snapshot; use regex::Regex; +use rust_embed::RustEmbed; #[cfg(unix)] use schemars::gen::SchemaSettings; use walkdir::DirEntry; @@ -166,6 +167,7 @@ subgraphs: account: true "#, Expansion::default().unwrap(), + Mode::NoUpgrade, ) .expect_err("should have resulted in an error"); assert_eq!(error.to_string(), String::from("unknown fields: additional properties are not allowed ('subgraphs' was/were unexpected)")); @@ -179,6 +181,7 @@ unknown: foo: true "#, Expansion::default().unwrap(), + Mode::NoUpgrade, ) .expect_err("should have resulted in an error"); assert_eq!( @@ -195,6 +198,7 @@ fn empty_config() { r#" "#, Expansion::default().unwrap(), + Mode::NoUpgrade, ) .expect("should have been ok with an empty config"); } @@ -221,6 +225,7 @@ telemetry: another_non_existant: 3 "#, Expansion::default().unwrap(), + Mode::NoUpgrade, ) .expect_err("should have resulted in an error"); insta::assert_snapshot!(error.to_string()); @@ -238,6 +243,7 @@ supergraph: another_one: true "#, Expansion::default().unwrap(), + Mode::NoUpgrade, ) .expect_err("should have resulted in an error"); insta::assert_snapshot!(error.to_string()); @@ -253,6 +259,7 @@ supergraph: listen: true "#, Expansion::default().unwrap(), + Mode::NoUpgrade, ) .expect_err("should have resulted in an error"); insta::assert_snapshot!(error.to_string()); @@ -270,6 +277,7 @@ cors: allow_headers: [ Content-Type, 5 ] "#, Expansion::default().unwrap(), + Mode::NoUpgrade, ) .expect_err("should have resulted in an error"); insta::assert_snapshot!(error.to_string()); @@ -289,6 +297,7 @@ cors: - 5 "#, Expansion::default().unwrap(), + Mode::NoUpgrade, ) .expect_err("should have resulted in an error"); insta::assert_snapshot!(error.to_string()); @@ -303,6 +312,7 @@ cors: allow_headers: [ "*" ] "#, Expansion::default().unwrap(), + Mode::NoUpgrade, ) .expect("should not have resulted in an error"); let error = cfg @@ -321,6 +331,7 @@ cors: methods: [ GET, "*" ] "#, Expansion::default().unwrap(), + Mode::NoUpgrade, ) .expect("should not have resulted in an error"); let error = cfg @@ -339,6 +350,7 @@ cors: allow_any_origin: true "#, Expansion::default().unwrap(), + Mode::NoUpgrade, ) .expect("should not have resulted in an error"); let error = cfg @@ -401,7 +413,11 @@ fn validate_project_config_files() { }; for yaml in yamls { - if let Err(e) = validate_yaml_configuration(&yaml, Expansion::default().unwrap()) { + if let Err(e) = validate_yaml_configuration( + &yaml, + Expansion::default().unwrap(), + Mode::NoUpgrade, + ) { panic!( "{} configuration error: \n{}", entry.path().to_string_lossy(), @@ -422,6 +438,7 @@ supergraph: introspection: ${env.TEST_CONFIG_NUMERIC_ENV_UNIQUE:-true} "#, Expansion::default().unwrap(), + Mode::NoUpgrade, ) .expect_err("Must have an error because we expect a boolean"); insta::assert_snapshot!(error.to_string()); @@ -440,6 +457,7 @@ cors: allow_headers: [ Content-Type, "${env.TEST_CONFIG_NUMERIC_ENV_UNIQUE}" ] "#, Expansion::default().unwrap(), + Mode::NoUpgrade, ) .expect_err("should have resulted in an error"); insta::assert_snapshot!(error.to_string()); @@ -461,6 +479,7 @@ cors: - "${env.TEST_CONFIG_NUMERIC_ENV_UNIQUE:-true}" "#, Expansion::default().unwrap(), + Mode::NoUpgrade, ) .expect_err("should have resulted in an error"); insta::assert_snapshot!(error.to_string()); @@ -478,6 +497,7 @@ supergraph: another_one: foo "#, Expansion::default().unwrap(), + Mode::NoUpgrade, ) .expect_err("should have resulted in an error"); insta::assert_snapshot!(error.to_string()); @@ -491,6 +511,7 @@ supergraph: introspection: ${env.TEST_CONFIG_UNKNOWN_WITH_NO_DEFAULT} "#, Expansion::default().unwrap(), + Mode::NoUpgrade, ) .expect_err("must have an error because the env variable is unknown"); insta::assert_snapshot!(error.to_string()); @@ -507,6 +528,7 @@ supergraph: .prefix("TEST_CONFIG") .supported_mode("env") .build(), + Mode::NoUpgrade, ) .expect_err("must have an error because the mode is unknown"); insta::assert_snapshot!(error.to_string()); @@ -524,6 +546,7 @@ supergraph: .prefix("TEST_CONFIG") .supported_mode("env") .build(), + Mode::NoUpgrade, ) .expect("must have expanded successfully"); } @@ -544,8 +567,48 @@ supergraph: path.to_string_lossy() ), Expansion::builder().supported_mode("file").build(), + Mode::NoUpgrade, ) .expect("must have expanded successfully"); assert!(config.supergraph.introspection); } + +#[derive(RustEmbed)] +#[folder = "src/configuration/testdata/migrations"] +struct Asset; + +#[test] +fn upgrade_old_configuration() { + for file_name in Asset::iter() { + if file_name.ends_with(".yaml") { + let source = Asset::get(&file_name).expect("test file must exist"); + let input = std::str::from_utf8(&source.data) + .expect("expected utf8") + .to_string(); + let new_config = crate::configuration::upgrade::upgrade_configuration( + serde_yaml::from_str(&input).expect("config must be valid yaml"), + true, + ) + .expect("configuration could not be updated"); + let result = validate_yaml_configuration( + &new_config.to_string(), + Expansion::builder().build(), + Mode::NoUpgrade, + ); + + let new_config = + serde_yaml::to_string(&new_config).expect("must be able to serialize config"); + match result { + Ok(_) => { + insta::with_settings!({snapshot_suffix => file_name}, { + insta::assert_snapshot!(new_config) + }); + } + Err(e) => { + panic!("migrated configuration had validation errors:\n{}\n\noriginal configuration:\n{}\n\nmigrated configuration:\n{}", e, input, new_config) + } + } + } + } +} diff --git a/apollo-router/src/configuration/upgrade.rs b/apollo-router/src/configuration/upgrade.rs new file mode 100644 index 0000000000..8636c6e81c --- /dev/null +++ b/apollo-router/src/configuration/upgrade.rs @@ -0,0 +1,224 @@ +use crate::error::ConfigurationError; +use itertools::Itertools; +use proteus::{Parser, TransformBuilder}; +use rust_embed::RustEmbed; +use serde::Deserialize; +use serde_json::Value; + +#[derive(RustEmbed)] +#[folder = "src/configuration/migrations"] +struct Asset; + +#[derive(Deserialize, buildstructor::Builder)] +struct Migration { + description: String, + actions: Vec, +} + +#[derive(Deserialize)] +#[serde(tag = "type", rename_all = "snake_case")] +enum Action { + Delete { path: String }, + Copy { from: String, to: String }, + Move { from: String, to: String }, +} + +const REMOVAL_VALUE: &str = "__PLEASE_DELETE_ME"; +const REMOVAL_EXPRESSION: &str = r#"const("__PLEASE_DELETE_ME")"#; + +pub(crate) fn upgrade_configuration( + mut config: serde_json::Value, + log_warnings: bool, +) -> Result { + // Transformers are loaded from a file and applied in order + let migrations: Vec = Asset::iter() + .sorted() + .filter(|filename| filename.ends_with(".yaml")) + .map(|filename| Asset::get(&filename).expect("migration must exist").data) + .map(|data| serde_yaml::from_slice(&data).expect("migration must be valid")) + .collect(); + + for migration in migrations { + let new_config = apply_migration(&config, &migration)?; + + // If the config has been modified by the migration then let the user know + if log_warnings && new_config != config { + tracing::warn!("router configuration contains deprecated options: {}. This will become an error in the future", migration.description); + } + + // Get ready for the next migration + config = new_config; + } + Ok(config) +} + +fn apply_migration(config: &Value, migration: &Migration) -> Result { + let mut transformer_builder = TransformBuilder::default(); + //We always copy the entire doc to the destination first + transformer_builder = + transformer_builder.add_action(Parser::parse("", "").expect("migration must be valid")); + for action in &migration.actions { + match action { + Action::Delete { path } => { + // Deleting isn't actually supported by protus so we add a magic value to delete later + transformer_builder = transformer_builder.add_action( + Parser::parse(REMOVAL_EXPRESSION, &path).expect("migration must be valid"), + ); + } + Action::Copy { from, to } => { + transformer_builder = transformer_builder + .add_action(Parser::parse(&from, &to).expect("migration must be valid")); + } + Action::Move { from, to } => { + transformer_builder = transformer_builder + .add_action(Parser::parse(&from, &to).expect("migration must be valid")); + // Deleting isn't actually supported by protus so we add a magic value to delete later + transformer_builder = transformer_builder.add_action( + Parser::parse(REMOVAL_EXPRESSION, &from).expect("migration must be valid"), + ); + } + } + } + let transformer = transformer_builder + .build() + .expect("transformer for migration must be valid"); + let mut new_config = + transformer + .apply(&config) + .map_err(|e| ConfigurationError::MigrationFailure { + error: e.to_string(), + })?; + + // Now we need to clean up elements that should be deleted. + cleanup(&mut new_config); + + Ok(new_config) +} + +fn cleanup(value: &mut Value) { + match value { + Value::Null => {} + Value::Bool(_) => {} + Value::Number(_) => {} + Value::String(_) => {} + Value::Array(a) => { + a.retain(|v| &Value::String(REMOVAL_VALUE.to_string()) != v); + for value in a { + cleanup(value); + } + } + Value::Object(o) => { + o.retain(|_, v| &Value::String(REMOVAL_VALUE.to_string()) != v); + for value in o.values_mut() { + cleanup(value); + } + } + } +} + +#[cfg(test)] +mod test { + use crate::configuration::upgrade::{apply_migration, Action, Migration}; + use serde_json::{json, Value}; + + fn source_doc() -> Value { + json!( { + "obj": { + "field1": 1, + "field2": 2 + }, + "arr": [ + "v1", + "v2" + ] + }) + } + + #[test] + fn delete_field() { + insta::assert_json_snapshot!(apply_migration( + &source_doc(), + &Migration::builder() + .action(Action::Delete { + path: "obj.field1".to_string() + }) + .description("delete field1") + .build(), + ) + .expect("expected successful migration")); + } + + #[test] + fn delete_array_element() { + insta::assert_json_snapshot!(apply_migration( + &source_doc(), + &Migration::builder() + .action(Action::Delete { + path: "arr[0]".to_string() + }) + .description("delete arr[0]") + .build(), + ) + .expect("expected successful migration")); + } + + #[test] + fn move_field() { + insta::assert_json_snapshot!(apply_migration( + &source_doc(), + &Migration::builder() + .action(Action::Move { + from: "obj.field1".to_string(), + to: "new.obj.field1".to_string() + }) + .description("move field1") + .build(), + ) + .expect("expected successful migration")); + } + + #[test] + fn move_array_element() { + insta::assert_json_snapshot!(apply_migration( + &source_doc(), + &Migration::builder() + .action(Action::Move { + from: "arr[0]".to_string(), + to: "new.arr[0]".to_string() + }) + .description("move arr[0]") + .build(), + ) + .expect("expected successful migration")); + } + + #[test] + fn copy_field() { + insta::assert_json_snapshot!(apply_migration( + &source_doc(), + &Migration::builder() + .action(Action::Copy { + from: "obj.field1".to_string(), + to: "new.obj.field1".to_string() + }) + .description("copy field1") + .build(), + ) + .expect("expected successful migration")); + } + + #[test] + fn copy_array_element() { + insta::assert_json_snapshot!(apply_migration( + &source_doc(), + &Migration::builder() + .action(Action::Copy { + from: "arr[0]".to_string(), + to: "new.arr[0]".to_string() + }) + .description("copy arr[0]") + .build(), + ) + .expect("expected successful migration")); + } +} diff --git a/apollo-router/src/executable.rs b/apollo-router/src/executable.rs index cef3b3bf61..2c754a1224 100644 --- a/apollo-router/src/executable.rs +++ b/apollo-router/src/executable.rs @@ -10,10 +10,10 @@ use std::time::Duration; use anyhow::anyhow; use anyhow::Context; use anyhow::Result; -use clap::AppSettings; -use clap::ArgAction; use clap::CommandFactory; use clap::Parser; +use clap::{AppSettings, Subcommand}; +use clap::{ArgAction, Args}; use directories::ProjectDirs; use once_cell::sync::OnceCell; use tracing::dispatcher::with_default; @@ -24,6 +24,7 @@ use url::ParseError; use url::Url; use crate::configuration::generate_config_schema; +use crate::configuration::upgrade_configuration; use crate::configuration::Configuration; use crate::configuration::ConfigurationError; use crate::router::ConfigurationSource; @@ -97,6 +98,43 @@ extern "C" fn drop_ad_hoc_profiler() { } } +/// Subcommands +#[derive(Subcommand, Debug)] +enum Commands { + /// Runs the router (default). + Run, + + /// Configuration subcommands. + Config(ConfigSubcommandArgs), + + /// Print the configuration json schema (deprecated). + Schema, +} + +#[derive(Args, Debug)] +struct ConfigSubcommandArgs { + /// Subcommands + #[clap(subcommand)] + command: ConfigSubcommand, +} + +#[derive(Subcommand, Debug)] +enum ConfigSubcommand { + /// Print the json configuration schema. + Schema, + + /// Print upgraded configuration. + Upgrade { + #[clap( + short, + long = "config", + parse(from_os_str), + env = "APOLLO_ROUTER_CONFIG_PATH" + )] + config: PathBuf, + }, +} + /// Options for the router #[derive(Parser, Debug)] #[clap( @@ -150,9 +188,9 @@ pub(crate) struct Opt { )] supergraph_path: Option, - /// Prints the configuration schema. - #[clap(long, action(ArgAction::SetTrue))] - schema: bool, + /// Subcommands + #[clap(subcommand)] + command: Option, /// Your Apollo key. #[clap(skip = std::env::var("APOLLO_KEY").ok())] @@ -294,12 +332,6 @@ impl Executable { copy_args_to_env(); - if opt.schema { - let schema = generate_config_schema(); - println!("{}", serde_json::to_string_pretty(&schema)?); - return Ok(()); - } - let builder = tracing_subscriber::fmt::fmt().with_env_filter( EnvFilter::try_new(&opt.log_level).context("could not parse log configuration")?, ); @@ -315,15 +347,40 @@ impl Executable { }; GLOBAL_ENV_FILTER.set(opt.log_level.clone()).expect( - "failed setting the global env filter. THe start() function should only be called once", + "failed setting the global env filter. The start() function should only be called once", ); - // The dispatcher we created is passed explicitely here to make sure we display the logs - // in the initialization phase and in the state machine code, before a global subscriber - // is set using the configuration file - Self::inner_start(shutdown, schema, config, opt, dispatcher.clone()) - .with_subscriber(dispatcher) - .await + match opt.command.as_ref().unwrap_or(&Commands::Run) { + Commands::Schema => { + eprintln!("`router schema` is deprecated. Use `router config schema`"); + let schema = generate_config_schema(); + println!("{}", serde_json::to_string_pretty(&schema)?); + return Ok(()); + } + Commands::Config(ConfigSubcommandArgs { + command: ConfigSubcommand::Schema, + }) => { + let schema = generate_config_schema(); + println!("{}", serde_json::to_string_pretty(&schema)?); + return Ok(()); + } + Commands::Config(ConfigSubcommandArgs { + command: ConfigSubcommand::Upgrade { config }, + }) => { + let config = serde_yaml::from_str(&std::fs::read_to_string(config)?)?; + let upgraded_config = upgrade_configuration(config, true)?; + println!("{}", serde_yaml::to_string(&upgraded_config)?); + return Ok(()); + } + Commands::Run => { + // The dispatcher we created is passed explicitly here to make sure we display the logs + // in the initialization phase and in the state machine code, before a global subscriber + // is set using the configuration file + Self::inner_start(shutdown, schema, config, opt, dispatcher.clone()) + .with_subscriber(dispatcher) + .await + } + } } async fn inner_start( From f9864d1e6a3e62b5aaeaf3e9da773c1a58fde35a Mon Sep 17 00:00:00 2001 From: bryn Date: Tue, 22 Nov 2022 13:44:46 +0000 Subject: [PATCH 3/8] Take in feedback around the CLI and add diffing support. Fixes #2076 --- Cargo.lock | 7 + apollo-router/Cargo.toml | 1 + .../src/configuration/migrations/README.md | 3 + apollo-router/src/configuration/mod.rs | 4 +- apollo-router/src/configuration/schema.rs | 1 + ...n__upgrade__test__diff_upgrade_output.snap | 10 ++ ...ration__upgrade__test__upgrade_output.snap | 8 ++ apollo-router/src/configuration/upgrade.rs | 125 ++++++++++++++++-- apollo-router/src/executable.rs | 36 ++--- licenses.html | 94 ++++++++++++- 10 files changed, 255 insertions(+), 34 deletions(-) create mode 100644 apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__diff_upgrade_output.snap create mode 100644 apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__upgrade_output.snap diff --git a/Cargo.lock b/Cargo.lock index ae2c2c0b91..319d9245f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -165,6 +165,7 @@ dependencies = [ "derivative", "derive_more", "dhat", + "diff", "directories", "displaydoc", "flate2", @@ -1379,6 +1380,12 @@ dependencies = [ "tempfile", ] +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "difflib" version = "0.4.0" diff --git a/apollo-router/Cargo.toml b/apollo-router/Cargo.toml index f1ab09ba91..ee7433056c 100644 --- a/apollo-router/Cargo.toml +++ b/apollo-router/Cargo.toml @@ -70,6 +70,7 @@ derive_more = { version = "0.99.17", default-features = false, features = [ "display", ] } dhat = { version = "0.3.2", optional = true } +diff = "0.1.13" directories = "4.0.1" displaydoc = "0.2" flate2 = "1.0.24" diff --git a/apollo-router/src/configuration/migrations/README.md b/apollo-router/src/configuration/migrations/README.md index d6367087c5..494831e27b 100644 --- a/apollo-router/src/configuration/migrations/README.md +++ b/apollo-router/src/configuration/migrations/README.md @@ -50,3 +50,6 @@ See [proteus](https://github.com/rust-playground/proteus) for more options. If a migration is deemed to have changed the configuration then the description of the migration will be output to the user as a warning. In future we will be able to use these files to support offline migrations. + +# Testing +Once you have made a new migration place a config file in `testdata/migrations`. It will automatically be picked up by the `upgrade_old_configuration` test. diff --git a/apollo-router/src/configuration/mod.rs b/apollo-router/src/configuration/mod.rs index 0423360710..7819360d3b 100644 --- a/apollo-router/src/configuration/mod.rs +++ b/apollo-router/src/configuration/mod.rs @@ -13,7 +13,6 @@ use std::net::IpAddr; use std::net::SocketAddr; use std::str::FromStr; -use crate::configuration::schema::Mode; use askama::Template; use bytes::Bytes; use cors::*; @@ -22,7 +21,7 @@ use displaydoc::Display; use expansion::*; use itertools::Itertools; pub(crate) use schema::generate_config_schema; -pub(crate) use schema::upgrade_configuration; +pub(crate) use schema::generate_upgrade; use schemars::gen::SchemaGenerator; use schemars::schema::ObjectValidation; use schemars::schema::Schema; @@ -35,6 +34,7 @@ use serde_json::Map; use serde_json::Value; use thiserror::Error; +use crate::configuration::schema::Mode; use crate::executable::APOLLO_ROUTER_DEV_ENV; use crate::plugin::plugins; diff --git a/apollo-router/src/configuration/schema.rs b/apollo-router/src/configuration/schema.rs index 67200a16c8..f00dff4f04 100644 --- a/apollo-router/src/configuration/schema.rs +++ b/apollo-router/src/configuration/schema.rs @@ -18,6 +18,7 @@ use super::yaml; use super::Configuration; use super::ConfigurationError; use super::APOLLO_PLUGIN_PREFIX; +pub(crate) use crate::configuration::upgrade::generate_upgrade; pub(crate) use crate::configuration::upgrade::upgrade_configuration; /// Generate a JSON schema for the configuration. diff --git a/apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__diff_upgrade_output.snap b/apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__diff_upgrade_output.snap new file mode 100644 index 0000000000..7d35fea46b --- /dev/null +++ b/apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__diff_upgrade_output.snap @@ -0,0 +1,10 @@ +--- +source: apollo-router/src/configuration/upgrade.rs +expression: "generate_upgrade_output(\"changed: bar\\nstable: 1.0\\ndeleted: gone\",\n \"changed: bif\\nstable: 1.0\\nadded: new\",\n true).expect(\"expected successful migration\")" +--- +-changed: bar ++changed: bif + stable: 1.0 +-deleted: gone ++added: new + diff --git a/apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__upgrade_output.snap b/apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__upgrade_output.snap new file mode 100644 index 0000000000..2f1e6fab76 --- /dev/null +++ b/apollo-router/src/configuration/snapshots/apollo_router__configuration__upgrade__test__upgrade_output.snap @@ -0,0 +1,8 @@ +--- +source: apollo-router/src/configuration/upgrade.rs +expression: "generate_upgrade_output(\"changed: bar\\nstable: 1.0\\ndeleted: gone\",\n \"changed: bif\\nstable: 1.0\\nadded: new\",\n false).expect(\"expected successful migration\")" +--- +changed: bif +stable: 1.0 +added: new + diff --git a/apollo-router/src/configuration/upgrade.rs b/apollo-router/src/configuration/upgrade.rs index 8636c6e81c..73d6ad4406 100644 --- a/apollo-router/src/configuration/upgrade.rs +++ b/apollo-router/src/configuration/upgrade.rs @@ -1,10 +1,14 @@ -use crate::error::ConfigurationError; +use std::fmt::Write as _; + use itertools::Itertools; -use proteus::{Parser, TransformBuilder}; +use proteus::Parser; +use proteus::TransformBuilder; use rust_embed::RustEmbed; use serde::Deserialize; use serde_json::Value; +use crate::error::ConfigurationError; + #[derive(RustEmbed)] #[folder = "src/configuration/migrations"] struct Asset; @@ -38,17 +42,21 @@ pub(crate) fn upgrade_configuration( .map(|data| serde_yaml::from_slice(&data).expect("migration must be valid")) .collect(); - for migration in migrations { - let new_config = apply_migration(&config, &migration)?; + let mut effective_migrations = Vec::new(); + for migration in &migrations { + let new_config = apply_migration(&config, migration)?; // If the config has been modified by the migration then let the user know - if log_warnings && new_config != config { - tracing::warn!("router configuration contains deprecated options: {}. This will become an error in the future", migration.description); + if new_config != config { + effective_migrations.push(migration); } // Get ready for the next migration config = new_config; } + if !effective_migrations.is_empty() && log_warnings { + tracing::warn!("router configuration contains deprecated options: \n\n{}\n\nThese will become errors in the future. To upgrade run: `router config upgrade > `", effective_migrations.iter().enumerate().map(|(idx, m)|format!(" {}. {}", idx + 1, m.description)).join("\n\n")); + } Ok(config) } @@ -62,19 +70,19 @@ fn apply_migration(config: &Value, migration: &Migration) -> Result { // Deleting isn't actually supported by protus so we add a magic value to delete later transformer_builder = transformer_builder.add_action( - Parser::parse(REMOVAL_EXPRESSION, &path).expect("migration must be valid"), + Parser::parse(REMOVAL_EXPRESSION, path).expect("migration must be valid"), ); } Action::Copy { from, to } => { transformer_builder = transformer_builder - .add_action(Parser::parse(&from, &to).expect("migration must be valid")); + .add_action(Parser::parse(from, to).expect("migration must be valid")); } Action::Move { from, to } => { transformer_builder = transformer_builder - .add_action(Parser::parse(&from, &to).expect("migration must be valid")); + .add_action(Parser::parse(from, to).expect("migration must be valid")); // Deleting isn't actually supported by protus so we add a magic value to delete later transformer_builder = transformer_builder.add_action( - Parser::parse(REMOVAL_EXPRESSION, &from).expect("migration must be valid"), + Parser::parse(REMOVAL_EXPRESSION, from).expect("migration must be valid"), ); } } @@ -84,7 +92,7 @@ fn apply_migration(config: &Value, migration: &Migration) -> Result Result Result { + let parsed_config = + serde_yaml::from_str(config).map_err(|e| ConfigurationError::MigrationFailure { + error: e.to_string(), + })?; + let upgraded_config = upgrade_configuration(parsed_config, true).map_err(|e| { + ConfigurationError::MigrationFailure { + error: e.to_string(), + } + })?; + let upgraded_config = serde_yaml::to_string(&upgraded_config).map_err(|e| { + ConfigurationError::MigrationFailure { + error: e.to_string(), + } + })?; + generate_upgrade_output(config, &upgraded_config, diff) +} + +pub(crate) fn generate_upgrade_output( + config: &str, + upgraded_config: &str, + diff: bool, +) -> Result { + // serde doesn't deal with whitespace and comments, these are lost in the upgrade process, so instead we try and preserve this in the diff. + // It's not ideal, and ideally the upgrade process should work on a DOM that is not serde, but for now we just make a best effort to preserve comments and whitespace. + // There absolutely are issues where comments will get stripped, but the output should be `correct`. + let mut output = String::new(); + + let diff_result = diff::lines(config, upgraded_config); + + for diff_line in diff_result { + match diff_line { + diff::Result::Left(l) => { + let trimmed = l.trim(); + if !trimmed.starts_with('#') && !trimmed.is_empty() { + if diff { + writeln!(output, "-{}", l).expect("write will never fail"); + } + } else if diff { + writeln!(output, " {}", l).expect("write will never fail"); + } else { + writeln!(output, "{}", l).expect("write will never fail"); + } + } + diff::Result::Both(l, _) => { + if diff { + writeln!(output, " {}", l).expect("write will never fail"); + } else { + writeln!(output, "{}", l).expect("write will never fail"); + } + } + diff::Result::Right(r) => { + let trimmed = r.trim(); + if trimmed != "---" && !trimmed.is_empty() { + if diff { + writeln!(output, "+{}", r).expect("write will never fail"); + } else { + writeln!(output, "{}", r).expect("write will never fail"); + } + } + } + } + } + Ok(output) +} + fn cleanup(value: &mut Value) { match value { Value::Null => {} @@ -118,8 +192,13 @@ fn cleanup(value: &mut Value) { #[cfg(test)] mod test { - use crate::configuration::upgrade::{apply_migration, Action, Migration}; - use serde_json::{json, Value}; + use serde_json::json; + use serde_json::Value; + + use crate::configuration::upgrade::apply_migration; + use crate::configuration::upgrade::generate_upgrade_output; + use crate::configuration::upgrade::Action; + use crate::configuration::upgrade::Migration; fn source_doc() -> Value { json!( { @@ -221,4 +300,24 @@ mod test { ) .expect("expected successful migration")); } + + #[test] + fn diff_upgrade_output() { + insta::assert_snapshot!(generate_upgrade_output( + "changed: bar\nstable: 1.0\ndeleted: gone", + "changed: bif\nstable: 1.0\nadded: new", + true + ) + .expect("expected successful migration")); + } + + #[test] + fn upgrade_output() { + insta::assert_snapshot!(generate_upgrade_output( + "changed: bar\nstable: 1.0\ndeleted: gone", + "changed: bif\nstable: 1.0\nadded: new", + false + ) + .expect("expected successful migration")); + } } diff --git a/apollo-router/src/executable.rs b/apollo-router/src/executable.rs index 2c754a1224..c324d8e391 100644 --- a/apollo-router/src/executable.rs +++ b/apollo-router/src/executable.rs @@ -10,10 +10,12 @@ use std::time::Duration; use anyhow::anyhow; use anyhow::Context; use anyhow::Result; +use clap::AppSettings; +use clap::ArgAction; +use clap::Args; use clap::CommandFactory; use clap::Parser; -use clap::{AppSettings, Subcommand}; -use clap::{ArgAction, Args}; +use clap::Subcommand; use directories::ProjectDirs; use once_cell::sync::OnceCell; use tracing::dispatcher::with_default; @@ -24,7 +26,7 @@ use url::ParseError; use url::Url; use crate::configuration::generate_config_schema; -use crate::configuration::upgrade_configuration; +use crate::configuration::generate_upgrade; use crate::configuration::Configuration; use crate::configuration::ConfigurationError; use crate::router::ConfigurationSource; @@ -125,13 +127,13 @@ enum ConfigSubcommand { /// Print upgraded configuration. Upgrade { - #[clap( - short, - long = "config", - parse(from_os_str), - env = "APOLLO_ROUTER_CONFIG_PATH" - )] - config: PathBuf, + /// The location of the config to upgrade. + #[clap(parse(from_os_str), env = "APOLLO_ROUTER_CONFIG_PATH")] + config_path: PathBuf, + + /// Print a diff. + #[clap(parse(from_flag), long)] + diff: bool, }, } @@ -355,22 +357,22 @@ impl Executable { eprintln!("`router schema` is deprecated. Use `router config schema`"); let schema = generate_config_schema(); println!("{}", serde_json::to_string_pretty(&schema)?); - return Ok(()); + Ok(()) } Commands::Config(ConfigSubcommandArgs { command: ConfigSubcommand::Schema, }) => { let schema = generate_config_schema(); println!("{}", serde_json::to_string_pretty(&schema)?); - return Ok(()); + Ok(()) } Commands::Config(ConfigSubcommandArgs { - command: ConfigSubcommand::Upgrade { config }, + command: ConfigSubcommand::Upgrade { config_path, diff }, }) => { - let config = serde_yaml::from_str(&std::fs::read_to_string(config)?)?; - let upgraded_config = upgrade_configuration(config, true)?; - println!("{}", serde_yaml::to_string(&upgraded_config)?); - return Ok(()); + let config_string = std::fs::read_to_string(config_path)?; + let output = generate_upgrade(&config_string, *diff)?; + println!("{}", output); + Ok(()) } Commands::Run => { // The dispatcher we created is passed explicitly here to make sure we display the logs diff --git a/licenses.html b/licenses.html index a670e5304b..20f74314e9 100644 --- a/licenses.html +++ b/licenses.html @@ -44,8 +44,8 @@

    Third Party Licenses

    Overview of licenses:

      -
    • MIT License (74)
    • -
    • Apache License 2.0 (52)
    • +
    • MIT License (76)
    • +
    • Apache License 2.0 (53)
    • ISC License (9)
    • BSD 3-Clause "New" or "Revised" License (7)
    • Mozilla Public License 2.0 (2)
    • @@ -2136,6 +2136,7 @@

      Apache License 2.0

      Used by:

      @@ -6099,6 +6100,7 @@

      Used by:

    • fnv
    • form_urlencoded
    • fraction
    • +
    • ghost
    • gimli
    • git2
    • group
    • @@ -6115,6 +6117,7 @@

      Used by:

    • idna
    • if_chain
    • indexmap
    • +
    • inventory
    • itertools
    • itoa
    • jobserver
    • @@ -6156,6 +6159,7 @@

      Used by:

    • proc-macro-hack
    • proc-macro2
    • prost
    • +
    • proteus
    • quote
    • regex
    • regex-syntax
    • @@ -6200,6 +6204,8 @@

      Used by:

    • toml
    • try_match
    • typed-builder
    • +
    • typetag
    • +
    • typetag-impl
    • ucd-trie
    • unicase
    • unicode-bidi
    • @@ -9327,6 +9333,30 @@

      Used by:

      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + + +
    • +

      Apache License 2.0

      +

      Used by:

      + +
      # Contributing
      +
      +## License
      +
      +Licensed under either of
      +
      + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
      + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
      +
      +at your option.
      +
      +### Contribution
      +
      +Unless you explicitly state otherwise, any contribution intentionally submitted
      +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any
      +additional terms or conditions.
       
    • @@ -12240,6 +12270,34 @@

      Used by:

      THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +
    • +
    • +

      MIT License

      +

      Used by:

      + +
      MIT License
      +
      +Copyright (c) [2019] [Changseok Han]
      +
      +Permission is hereby granted, free of charge, to any person obtaining a copy
      +of this software and associated documentation files (the "Software"), to deal
      +in the Software without restriction, including without limitation the rights
      +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      +copies of the Software, and to permit persons to whom the Software is
      +furnished to do so, subject to the following conditions:
      +
      +The above copyright notice and this permission notice shall be included in all
      +copies or substantial portions of the Software.
      +
      +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
      +SOFTWARE.
    • MIT License

      @@ -12804,6 +12862,38 @@

      Used by:

      OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +
    • +
    • +

      MIT License

      +

      Used by:

      + +
      The MIT License (MIT)
      +
      +Copyright (c) 2018 pyros2097
      +
      +Permission is hereby granted, free of charge, to any person obtaining a copy
      +of this software and associated documentation files (the "Software"), to deal
      +in the Software without restriction, including without limitation the rights
      +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      +copies of the Software, and to permit persons to whom the Software is
      +furnished to do so, subject to the following conditions:
      +
      +The above copyright notice and this permission notice shall be included in all
      +copies or substantial portions of the Software.
      +
      +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
      +SOFTWARE.
      +
       
    • From c2d3cb63154075d44b545f79a2c84d2444634f0a Mon Sep 17 00:00:00 2001 From: bryn Date: Tue, 22 Nov 2022 16:49:17 +0000 Subject: [PATCH 4/8] Add docs. Make the router fail fast if the upgraded configuration fails to load. Fixes #2076 --- NEXT_CHANGELOG.md | 48 ++++++++++++++ README.md | 40 ++++++++++-- apollo-router/src/configuration/expansion.rs | 2 +- apollo-router/src/configuration/schema.rs | 16 +++-- apollo-router/src/configuration/tests.rs | 9 +-- apollo-router/src/configuration/upgrade.rs | 8 ++- apollo-router/src/executable.rs | 35 +++++----- docs/source/configuration/overview.mdx | 68 +++++++++++++++++++- 8 files changed, 190 insertions(+), 36 deletions(-) diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md index 655a06b84b..6559be8dd2 100644 --- a/NEXT_CHANGELOG.md +++ b/NEXT_CHANGELOG.md @@ -47,7 +47,55 @@ telemetry: By [@bryncooke](https://github.com/bryncooke) in https://github.com/apollographql/router/pull/2116 +### CLI structure changes ([Issue #2076](https://github.com/apollographql/router/issues/2123)) + +As the Router gains functionality the limitations of the current CLI structure are becoming apparent. + +There is now a separate subcommand for config related operations: +* `config` + * `schema` - Output the configuration schema + * `upgrade` - Upgrade the configuration with optional diff support. + +`router --schema` has been deprecated and users should move to `router config schema`. + ## 🚀 Features + +### Configuration upgrades ([Issue #2076](https://github.com/apollographql/router/issues/2123)) + +Occasionally we will make changes to the Router yaml configuration format. +When starting the Router if the configuration can be upgraded it will do so automatically and display a warning: + +``` +2022-11-22T14:01:46.884897Z WARN router configuration contains deprecated options: + + 1. telemetry.tracing.trace_config.attributes.router has been renamed to 'supergraph' for consistency + +These will become errors in the future. Run `router config upgrade ` to see a suggested upgraded configuration. +``` + +Note: If a configuration has errors after upgrading then the configuration will not be upgraded automatically. + +From the CLI users can run: +* `router config upgrade ` to output configuration that has been upgraded to match the latest config format. +* `router config upgrade --diff ` to output a diff e.g. +``` + telemetry: + apollo: + client_name_header: apollographql-client-name + metrics: + common: + attributes: +- router: ++ supergraph: + request: + header: + - named: "1" # foo +``` + +There are situations where comments and whitespace are not preserved. This may be improved in future. + +By [@bryncooke](https://github.com/bryncooke) in https://github.com/apollographql/router/pull/2116 + ## 🐛 Fixes ## 🛠 Maintenance ## 📚 Documentation diff --git a/README.md b/README.md index f529b39272..e34ed0c1c6 100644 --- a/README.md +++ b/README.md @@ -26,11 +26,43 @@ specified via flag, either by an absolute path, or a path relative to the curren directory. ``` +USAGE: + router [OPTIONS] [SUBCOMMAND] + OPTIONS: - -c, --config Configuration file location - -s, --supergraph Supergraph Schema location - --hr, --hot-reload Watches for changes in the supergraph and configuration file - --schema Prints out a JSON schema of the configuration file + --apollo-uplink-endpoints + The endpoints (comma separated) polled to fetch the latest supergraph schema [env: + APOLLO_UPLINK_ENDPOINTS=] + + --apollo-uplink-poll-interval + The time between polls to Apollo uplink. Minimum 10s [env: APOLLO_UPLINK_POLL_INTERVAL=] + [default: 10s] + + -c, --config + Configuration location relative to the project directory [env: + APOLLO_ROUTER_CONFIG_PATH=] + + -h, --help + Print help information + + --hot-reload + Reload configuration and schema files automatically [env: APOLLO_ROUTER_HOT_RELOAD=] + + --log + Log level (off|error|warn|info|debug|trace) [env: APOLLO_ROUTER_LOG=] [default: info] + + -s, --supergraph + Schema location relative to the project directory [env: APOLLO_ROUTER_SUPERGRAPH_PATH=] + + --schema + Prints the configuration schema + + -V, --version + Display version and exit + +SUBCOMMANDS: + config Configuration subcommands + help Print this message or the help of the given subcommand(s) ``` ## Who is Apollo? diff --git a/apollo-router/src/configuration/expansion.rs b/apollo-router/src/configuration/expansion.rs index 4414d4c6c6..52fad3849f 100644 --- a/apollo-router/src/configuration/expansion.rs +++ b/apollo-router/src/configuration/expansion.rs @@ -84,7 +84,7 @@ impl Expansion { pub(crate) fn expand_env_variables( configuration: &serde_json::Value, - expansion: Expansion, + expansion: &Expansion, ) -> Result { let mut configuration = configuration.clone(); visit(&mut configuration, &expansion)?; diff --git a/apollo-router/src/configuration/schema.rs b/apollo-router/src/configuration/schema.rs index f00dff4f04..1a737b57ec 100644 --- a/apollo-router/src/configuration/schema.rs +++ b/apollo-router/src/configuration/schema.rs @@ -79,10 +79,6 @@ pub(crate) fn validate_yaml_configuration( error: e.to_string(), } })?; - if migration == Mode::Upgrade { - yaml = upgrade_configuration(yaml, true)?; - } - let expanded_yaml = expand_env_variables(&yaml, expansion)?; let schema = serde_json::to_value(generate_config_schema()).map_err(|e| { ConfigurationError::InvalidConfiguration { message: "failed to parse schema", @@ -96,6 +92,18 @@ pub(crate) fn validate_yaml_configuration( message: "failed to compile schema", error: e.to_string(), })?; + + if migration == Mode::Upgrade { + let upgraded = upgrade_configuration(&yaml, true)?; + let expanded_yaml = expand_env_variables(&upgraded, &expansion)?; + if schema.validate(&expanded_yaml).is_ok() { + yaml = upgraded; + } else { + tracing::warn!("configuration could not be upgraded automatically as it had errors") + } + } + let expanded_yaml = expand_env_variables(&yaml, &expansion)?; + if let Err(errors) = schema.validate(&expanded_yaml) { // Validation failed, translate the errors into something nice for the user // We have to reparse the yaml to get the line number information for each error. diff --git a/apollo-router/src/configuration/tests.rs b/apollo-router/src/configuration/tests.rs index a4515afa82..b2fb1733dd 100644 --- a/apollo-router/src/configuration/tests.rs +++ b/apollo-router/src/configuration/tests.rs @@ -587,18 +587,19 @@ fn upgrade_old_configuration() { .expect("expected utf8") .to_string(); let new_config = crate::configuration::upgrade::upgrade_configuration( - serde_yaml::from_str(&input).expect("config must be valid yaml"), + &serde_yaml::from_str(&input).expect("config must be valid yaml"), true, ) .expect("configuration could not be updated"); + let new_config = + serde_yaml::to_string(&new_config).expect("must be able to serialize config"); + let result = validate_yaml_configuration( - &new_config.to_string(), + &new_config, Expansion::builder().build(), Mode::NoUpgrade, ); - let new_config = - serde_yaml::to_string(&new_config).expect("must be able to serialize config"); match result { Ok(_) => { insta::with_settings!({snapshot_suffix => file_name}, { diff --git a/apollo-router/src/configuration/upgrade.rs b/apollo-router/src/configuration/upgrade.rs index 73d6ad4406..9ed15c5ea8 100644 --- a/apollo-router/src/configuration/upgrade.rs +++ b/apollo-router/src/configuration/upgrade.rs @@ -31,7 +31,7 @@ const REMOVAL_VALUE: &str = "__PLEASE_DELETE_ME"; const REMOVAL_EXPRESSION: &str = r#"const("__PLEASE_DELETE_ME")"#; pub(crate) fn upgrade_configuration( - mut config: serde_json::Value, + config: &serde_json::Value, log_warnings: bool, ) -> Result { // Transformers are loaded from a file and applied in order @@ -42,6 +42,8 @@ pub(crate) fn upgrade_configuration( .map(|data| serde_yaml::from_slice(&data).expect("migration must be valid")) .collect(); + let mut config = config.clone(); + let mut effective_migrations = Vec::new(); for migration in &migrations { let new_config = apply_migration(&config, migration)?; @@ -55,7 +57,7 @@ pub(crate) fn upgrade_configuration( config = new_config; } if !effective_migrations.is_empty() && log_warnings { - tracing::warn!("router configuration contains deprecated options: \n\n{}\n\nThese will become errors in the future. To upgrade run: `router config upgrade > `", effective_migrations.iter().enumerate().map(|(idx, m)|format!(" {}. {}", idx + 1, m.description)).join("\n\n")); + tracing::warn!("router configuration contains deprecated options: \n\n{}\n\nThese will become errors in the future. Run `router config upgrade ` to see a suggested upgraded configuration.", effective_migrations.iter().enumerate().map(|(idx, m)|format!(" {}. {}", idx + 1, m.description)).join("\n\n")); } Ok(config) } @@ -108,7 +110,7 @@ pub(crate) fn generate_upgrade(config: &str, diff: bool) -> Result, + /// Prints the configuration schema. + #[clap(long, action(ArgAction::SetTrue))] + schema: bool, + /// Subcommands #[clap(subcommand)] command: Option, @@ -352,29 +350,30 @@ impl Executable { "failed setting the global env filter. The start() function should only be called once", ); - match opt.command.as_ref().unwrap_or(&Commands::Run) { - Commands::Schema => { - eprintln!("`router schema` is deprecated. Use `router config schema`"); - let schema = generate_config_schema(); - println!("{}", serde_json::to_string_pretty(&schema)?); - Ok(()) - } - Commands::Config(ConfigSubcommandArgs { + if opt.schema { + eprintln!("`router --schema` is deprecated. Use `router config schema`"); + let schema = generate_config_schema(); + println!("{}", serde_json::to_string_pretty(&schema)?); + return Ok(()); + } + + match opt.command.as_ref() { + Some(Commands::Config(ConfigSubcommandArgs { command: ConfigSubcommand::Schema, - }) => { + })) => { let schema = generate_config_schema(); println!("{}", serde_json::to_string_pretty(&schema)?); Ok(()) } - Commands::Config(ConfigSubcommandArgs { + Some(Commands::Config(ConfigSubcommandArgs { command: ConfigSubcommand::Upgrade { config_path, diff }, - }) => { + })) => { let config_string = std::fs::read_to_string(config_path)?; let output = generate_upgrade(&config_string, *diff)?; println!("{}", output); Ok(()) } - Commands::Run => { + None => { // The dispatcher we created is passed explicitly here to make sure we display the logs // in the initialization phase and in the state machine code, before a global subscriber // is set using the configuration file diff --git a/docs/source/configuration/overview.mdx b/docs/source/configuration/overview.mdx index b81dc9bc89..2a3d8043a2 100644 --- a/docs/source/configuration/overview.mdx +++ b/docs/source/configuration/overview.mdx @@ -182,7 +182,7 @@ The default value is `10s` (ten seconds), which is also the minimum allowed valu -##### `--schema` +##### `schema` @@ -208,6 +208,48 @@ Prints out the Apollo Router's version. +## Config subcommand + + + + + + + + + + + + + + + + + + + + + + +
      Argument / Environment VariableDescription
      + +##### `schema` + + + +Prints out a JSON schema of the Router's configuration file, including [plugin configuration](#plugins). + +
      + +##### `upgrade` + + + +Print configuration that has been upgraded to the current Router version. + +
      + + ## YAML config file The Apollo Router takes an optional YAML configuration file as input via the `--config` option. If the `--hot-reload` flag is also passed (or the `APOLLO_ROUTER_HOT_RELOAD` environment variable is set to `true`), the router automatically restarts when changes to the configuration file are made. @@ -389,7 +431,7 @@ The Apollo Router can generate a JSON schema for config validation in your text Generate the schema with the following command: ```bash -./router --schema > configuration_schema.json +./router config schema > configuration_schema.json ``` After you generate the schema, configure your text editor. Here are the instructions for some commonly used editors: @@ -399,3 +441,25 @@ After you generate the schema, configure your text editor. Here are the instruct - [IntelliJ](https://www.jetbrains.com/help/idea/json.html#ws_json_using_schemas) - [Sublime](https://github.com/sublimelsp/LSP-yaml) - [Vim](https://github.com/Quramy/vison) + +## Upgrading your Router configuration + +Occasionally breaking changes are made to the Apollo Router yaml format. Usually to extend functionality or improve usability. + +When running the Router with old configuration: + +1. if you have errors in your config then a warning will be emitted on startup. +2. if the Router config can be upgraded automatically with no validation errors then it will continue to load. +3. if the Router config had validation errors after automatic upgrade loading will stop. + +When you are notified that configuration can be upgraded use the `router config upgrade` command to see what your configuration should look like: + +```bash +./router config upgrade +``` + +To see what changes were made output the upgraded config diff using: + +```bash +./router config upgrade --diff +``` From 40806714fcd9c685d18d7175dfde456503ce6b37 Mon Sep 17 00:00:00 2001 From: bryn Date: Tue, 22 Nov 2022 17:07:24 +0000 Subject: [PATCH 5/8] Clippy --- apollo-router/src/configuration/expansion.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apollo-router/src/configuration/expansion.rs b/apollo-router/src/configuration/expansion.rs index 52fad3849f..0ceed33868 100644 --- a/apollo-router/src/configuration/expansion.rs +++ b/apollo-router/src/configuration/expansion.rs @@ -87,7 +87,7 @@ pub(crate) fn expand_env_variables( expansion: &Expansion, ) -> Result { let mut configuration = configuration.clone(); - visit(&mut configuration, &expansion)?; + visit(&mut configuration, expansion)?; Ok(configuration) } From 0d7a3ad29061a7a9d3304c41c6d81a93432df44b Mon Sep 17 00:00:00 2001 From: bryn Date: Tue, 22 Nov 2022 17:29:14 +0000 Subject: [PATCH 6/8] Fix failing tests --- apollo-router/src/configuration/snapshots/.skipconfigvalidation | 0 ...configuration@telemetry_router_to_supergraph.router.yaml.snap} | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 apollo-router/src/configuration/snapshots/.skipconfigvalidation rename apollo-router/src/configuration/snapshots/{apollo_router__configuration__tests__update_old_configuration@telemetry_router_to_supergraph.router.yaml.snap => apollo_router__configuration__tests__upgrade_old_configuration@telemetry_router_to_supergraph.router.yaml.snap} (100%) diff --git a/apollo-router/src/configuration/snapshots/.skipconfigvalidation b/apollo-router/src/configuration/snapshots/.skipconfigvalidation new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__update_old_configuration@telemetry_router_to_supergraph.router.yaml.snap b/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__upgrade_old_configuration@telemetry_router_to_supergraph.router.yaml.snap similarity index 100% rename from apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__update_old_configuration@telemetry_router_to_supergraph.router.yaml.snap rename to apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__upgrade_old_configuration@telemetry_router_to_supergraph.router.yaml.snap From ff6b42d751bd8e1fa528104db54914e1151dab44 Mon Sep 17 00:00:00 2001 From: bryn Date: Tue, 22 Nov 2022 17:36:08 +0000 Subject: [PATCH 7/8] Changelog typo --- NEXT_CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md index 9e633721db..8885cd66e2 100644 --- a/NEXT_CHANGELOG.md +++ b/NEXT_CHANGELOG.md @@ -47,7 +47,7 @@ telemetry: By [@bryncooke](https://github.com/bryncooke) in https://github.com/apollographql/router/pull/2116 -### CLI structure changes ([Issue #2076](https://github.com/apollographql/router/issues/2123)) +### CLI structure changes ([Issue #2123](https://github.com/apollographql/router/issues/2123)) As the Router gains functionality the limitations of the current CLI structure are becoming apparent. @@ -97,7 +97,7 @@ helm upgrade --install --create-namespace --namespace router-test --set-file sup By [@garypen](https://github.com/garypen) in https://github.com/apollographql/router/pull/2119 -### Configuration upgrades ([Issue #2076](https://github.com/apollographql/router/issues/2123)) +### Configuration upgrades ([Issue #2123](https://github.com/apollographql/router/issues/2123)) Occasionally we will make changes to the Router yaml configuration format. When starting the Router if the configuration can be upgraded it will do so automatically and display a warning: From ebed3c1dc7b388237ef858a69eefc180f7c32287 Mon Sep 17 00:00:00 2001 From: bryn Date: Wed, 23 Nov 2022 08:42:47 +0000 Subject: [PATCH 8/8] Hide --schema from clap options --- README.md | 3 --- apollo-router/src/executable.rs | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index e34ed0c1c6..5a15025f1f 100644 --- a/README.md +++ b/README.md @@ -54,9 +54,6 @@ OPTIONS: -s, --supergraph Schema location relative to the project directory [env: APOLLO_ROUTER_SUPERGRAPH_PATH=] - --schema - Prints the configuration schema - -V, --version Display version and exit diff --git a/apollo-router/src/executable.rs b/apollo-router/src/executable.rs index 7fc626a33a..dc807b0091 100644 --- a/apollo-router/src/executable.rs +++ b/apollo-router/src/executable.rs @@ -185,7 +185,7 @@ pub(crate) struct Opt { supergraph_path: Option, /// Prints the configuration schema. - #[clap(long, action(ArgAction::SetTrue))] + #[clap(long, action(ArgAction::SetTrue), hide(true))] schema: bool, /// Subcommands