From 9e1008dcca34e9e70a02d819749c5ebee52d8053 Mon Sep 17 00:00:00 2001 From: Simone Ruffilli Date: Tue, 28 May 2024 11:35:13 +0200 Subject: [PATCH] FAST: Obsolete assets cleanup (#2315) Remove obsolete assets --- .../assets/schemas/firewall_rules.schema.yaml | 34 --- .../schemas/hierarchical_rules.schema.yaml | 25 -- fast/assets/schemas/project.schema.yaml | 58 ---- .../schemas/project_defaults.schema.yaml | 28 -- fast/assets/schemas/subnet.schema.yaml | 31 --- fast/stages/COMPANION.md | 257 ------------------ fast/stages/IaC_SA.png | Bin 17535 -> 0 bytes tools/validate_schema.py | 68 ----- 8 files changed, 501 deletions(-) delete mode 100644 fast/assets/schemas/firewall_rules.schema.yaml delete mode 100644 fast/assets/schemas/hierarchical_rules.schema.yaml delete mode 100644 fast/assets/schemas/project.schema.yaml delete mode 100644 fast/assets/schemas/project_defaults.schema.yaml delete mode 100644 fast/assets/schemas/subnet.schema.yaml delete mode 100644 fast/stages/COMPANION.md delete mode 100644 fast/stages/IaC_SA.png delete mode 100755 tools/validate_schema.py diff --git a/fast/assets/schemas/firewall_rules.schema.yaml b/fast/assets/schemas/firewall_rules.schema.yaml deleted file mode 100644 index 74aba4b5e1..0000000000 --- a/fast/assets/schemas/firewall_rules.schema.yaml +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2023 Google LLC -# -# 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. - -egress: map(include('firewall_rule'), required=False) -ingress: map(include('firewall_rule'), required=False) ---- -firewall_rule: - deny: bool(required=False) - description: str(required=False) - destination_ranges: list(str(), required=False) - disabled: bool(required=False) - # enable_logging: - # include_metadata: bool(required=False) - priority: int(required=False) - source_ranges: list(str(), required=False) - sources: list(str(), required=False) - targets: list(str(), required=False) - use_service_accounts: bool(required=False) - rules: list(include('rule'), required=False) ---- -rule: - protocol: str() - ports: list(num()) diff --git a/fast/assets/schemas/hierarchical_rules.schema.yaml b/fast/assets/schemas/hierarchical_rules.schema.yaml deleted file mode 100644 index ad49bfa57a..0000000000 --- a/fast/assets/schemas/hierarchical_rules.schema.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright 2023 Google LLC -# -# 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. - -map(include('hierarchical_rule')) ---- -hierarchical_rule: - description: str() - direction: enum("INGRESS", "EGRESS") - action: enum("allow", "deny", "goto_next") - priority: int() - ranges: list(str()) - target_resources: any(null(), list(str())) - ports: map(list(str(), required=False)) - enable_logging: bool() diff --git a/fast/assets/schemas/project.schema.yaml b/fast/assets/schemas/project.schema.yaml deleted file mode 100644 index 2b3f14b3ac..0000000000 --- a/fast/assets/schemas/project.schema.yaml +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright 2024 Google LLC -# -# 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. - -billing_account_id: str(matches='[A-F0-9]{6}-[A-F0-9]{6}-[A-F0-9]{6}', required=False) -billing_alert: any(include('billing_alert'), null(), required=False) # If set to null, use defaults -dns_zones: list(str(), required=False) -essential_contacts: list(str(), required=False) # Also used for billing alerts -folder_id: str(matches='(organizations/|folders/)[0-9]*$') -iam: map(list(str()), key=str(), required=False) -iam_by_principals: map(list(str()), key=str(), required=False) -kms_service_agents: map(list(str()), key=str(), required=False) -labels: map(str(), key=str(), required=False) -org_policies: include('org_policies', required=False) -secrets: map(list(str()), key=str(), required=False) -service_accounts: map(list(str()), required=False) -service_accounts_iam: map(map(list(str())), required=False) -services: list(str(matches='^[a-z-]*\.googleapis\.com$'), required=False) -service_identities_iam: map(list(str()), key=str(), required=False) -vpc: include('vpc', required=False) ---- -billing_alert: - amount: int() - thresholds: include('billing_alert_thresholds') - credit_treatment: enum("INCLUDE_ALL_CREDITS", "EXCLUDE_ALL_CREDITS") ---- -billing_alert_thresholds: - current: list(num(min=0, max=1)) - forecasted: list(num(min=0, max=1)) ---- -gke_setup: - enable_security_admin: bool(required=False) - enable_host_service_agent: bool(required=False) ---- -org_policies: - policy_boolean: map(bool(), key=str(matches='^constraints/[A-z\.]*$'), required=False) - policy_list: map(include('policy_list'), key=str(matches='^constraints/[A-z\.]*$'), required=False) ---- -policy_list: - inherit_from_parent: any(bool(), null()) - suggested_value: any(str(), null()) - status: any(bool(), null()) - values: list(str()) ---- -vpc: - host_project: str(matches='[a-z]([-a-z0-9]*[a-z0-9])?', min=6, max=30) - gke_setup: include('gke_setup', required=False) - subnets_iam: map(list(str()), key=str(matches='^[a-z0-9-]*/[a-z0-9-]*$'), required=False) diff --git a/fast/assets/schemas/project_defaults.schema.yaml b/fast/assets/schemas/project_defaults.schema.yaml deleted file mode 100644 index 32e98ffc67..0000000000 --- a/fast/assets/schemas/project_defaults.schema.yaml +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2023 Google LLC -# -# 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. - -billing_account_id: str(matches='[A-F0-9]{6}-[A-F0-9]{6}-[A-F0-9]{6}', required=False) -billing_alert: any(include('billing_alert'), null(), required=False) -essential_contacts: list(str(), required=False) -labels: map(str(), key=str(), required=False) -notification_channels: list(str(), required=False) ---- -billing_alert: - amount: int() - thresholds: include('billing_alert_thresholds') - credit_treatment: enum('INCLUDE_ALL_CREDITS', 'EXCLUDE_ALL_CREDITS') ---- -billing_alert_thresholds: - current: list(num(min=0, max=1)) - forecasted: list(num(min=0, max=1)) diff --git a/fast/assets/schemas/subnet.schema.yaml b/fast/assets/schemas/subnet.schema.yaml deleted file mode 100644 index cc5aa10d76..0000000000 --- a/fast/assets/schemas/subnet.schema.yaml +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright 2023 Google LLC -# -# 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. - -region: str() -description: str() -ip_cidr_range: str() -# optional attributes -enable_private_access: bool(required=False) # defaults to true -iam_users: list(str(), required=False) -iam_groups: list(str(), required=False) -iam_service_accounts: list(str(), required=False) -secondary_ip_ranges: map(str(), key=str(), required=False) -flow_logs: any(include('flow_logs'), required=False) ---- -flow_logs: - - aggregation_interval: enum('INTERVAL_5_SEC', 'INTERVAL_30_SEC', 'INTERVAL_1_MIN', 'INTERVAL_5_MIN', 'INTERVAL_10_MIN', 'INTERVAL_15_MIN', required=False) - - filter_expression: str() - - flow_sampling: num(min=0, max=1, required=False) - - metadata: enum('EXCLUDE_ALL_METADATA', 'INCLUDE_ALL_METADATA', 'CUSTOM_METADATA', required=False) - - metadata_fields: map(str(), key=str(), required=False) diff --git a/fast/stages/COMPANION.md b/fast/stages/COMPANION.md deleted file mode 100644 index dc6eb8b5d7..0000000000 --- a/fast/stages/COMPANION.md +++ /dev/null @@ -1,257 +0,0 @@ -# FAST deployment companion guide - -To deploy a GCP Landing Zone using FAST, your organization needs to meet a few prerequisites before starting. This guide serves as quick guide to prepare your GCP organization and also as cheat sheet with the commands and minimal configuration required to deploy FAST. - -The detailed explanation of each stage, their configuration, possible modifications and adaptations are included in the README of stage. This document only outlines the minimal configuration to get from an empty organization to a working FAST deployment. - -**Warning! Executing FAST sets organization policies and authoritative role bindings in your GCP Organization. We recommend using FAST on a clean organization, or to fork and adapt FAST to support your existing Organization needs.** - -## Prerequisites - -1. FAST uses the recommended groups from the [GCP Enterprise Setup checklist](https://cloud.google.com/docs/enterprise/setup-checklist). Go to [Workspace / Cloud Identity](https://admin.google.com) and ensure all the following groups exist: - -- `gcp-billing-admins@` -- `gcp-devops@` -- `gcp-network-admins@` -- `gcp-organization-admins@` -- `gcp-security-admins@` -- `gcp-support@` - -2. If you already executed FAST in your organization, make you [clean it up](CLEANUP.md) before continuing with the rest of this guide. - -3. Grant your user “Organization Administrator” role in your organization and add it to the `gcp-organization-admins@` group. - -4. Login with your user using gcloud. - -```bash -gcloud auth login -gcloud auth application-default login -``` - -5. Clone the Fabric repository. - -```bash -git clone https://github.com/GoogleCloudPlatform/cloud-foundation-fabric.git -cd cloud-foundation-fabric -``` - -6. Grant required roles to your user. - -```bash -# set a variable to the fast folder -export FAST_PWD="$(pwd)/fast/stages" - -# set the initial user variable via gcloud -export FAST_BU=$(gcloud config list --format 'value(core.account)') - -# find your org id. change "fast.example.com" with your own org domain -gcloud organizations list --filter display_name:fast.example.com - -# set your org id -export FAST_ORG_ID=1234567890 - -# set needed roles (do not change this) -export FAST_ROLES="roles/billing.admin roles/logging.admin \ -roles/iam.organizationRoleAdmin roles/resourcemanager.projectCreator" - -for role in $FAST_ROLES; do -gcloud organizations add-iam-policy-binding $FAST_ORG_ID \ ---member user:$FAST_BU --role $role -done -``` - -7. Configure Billing Account permissions. - -If you are using a standalone or different organization billing account, the user applying this stage for the first time needs to be a Billing Administrator. - -```bash -# find your billing account id with gcloud beta billing accounts list -# replace with your billing id! -export FAST_BA_ID=XXXXXX-YYYYYY-ZZZZZZ -# set needed roles (do not change this) -gcloud beta billing accounts add-iam-policy-binding $FAST_BA_ID \ ---member user:$FAST_BU --role roles/billing.admin -``` - -## Stage 0 (Bootstrap) - -This initial stage will create common projects for IaC, Logging & Billing, and bootstrap IAM policies. - -```bash -# move to the 0-bootstrap directory -cd $FAST_PWD/0-bootstrap - -# copy the template terraform tfvars file and save as `terraform.tfvars` -# then edit to match your environment! -edit terraform.tfvars.sample -``` - -Here you have a terraform.tfvars example: - -```hcl -# fetch the required id by running `gcloud beta billing accounts list` -billing_account={ - id="XXXXXX-YYYYYY-ZZZZZZ" - organization_id="01234567890" -} -# get the required info by running `gcloud organizations list` -organization={ - id="01234567890" - domain="fast.example.com" - customer_id="Cxxxxxxx" -} -# create your own 4-letters prefix -prefix="abcd" - -# path for automatic generation of configs -outputs_location = "~/fast-config" -``` - -```bash -# run init and apply -terraform init -terraform apply -var bootstrap_user=$FAST_BU - -# link providers file -ln -s ~/fast-config/providers/0-bootstrap-providers.tf ./ - -# re-run init and apply to remove user-level IAM -terraform init -migrate-state - -# answer 'yes' to terraform's question -terraform apply -``` - -## Stage 1 (Resource Management) - -This stage performs two important tasks: - -- Create the top-level hierarchy of folders, and the associated resources used later on to automate each part of the hierarchy (eg. Networking). -- Set organization policies on the organization, and any exception required on specific folders. - -```bash -# move to the 01-resman directory -cd $FAST_PWD/1-resman - -# link providers and variables from previous stages -ln -s ~/fast-config/providers/1-resman-providers.tf . -ln -s ~/fast-config/tfvars/0-bootstrap.auto.tfvars.json . -ln -s ~/fast-config/tfvars/0-globals.auto.tfvars.json . - -# edit your terraform.tfvars to append Teams configuration (optional) -edit terraform.tfvars -``` - -In the following terraform.tfvars it is shown an example of configuration for teams provisioning: - -```hcl -outputs_location = "~/fast-config" - -# optional -team_folders = { - team-1 = { - descriptive_name = "Team 1" - group_iam = { - "team-1-users@fast.example.com" = ["roles/viewer"] - } - impersonation_groups = [ - "team-1-admins@fast.example.com" - ] - } -} -``` - -```bash -# run init and apply -terraform init -terraform apply -``` - -## Stage 2 (Networking) - -In this stage, we will deploy one of the 3 available Hub&Spoke networking topologies: - -1. VPC Peering -2. HA VPN -3. Multi-NIC appliances (NVA) -4. Multi-NIC appliances (NVA) with NCC / BGP support - -```bash -# move to the 02-networking-XXX directory (where XXX should be one of a-peering|b-vpn|c-nva|d-separate-envs|e-nva-bgp) -cd $FAST_PWD/2-networking-XXX - -# setup providers and variables from previous stages -ln -s ~/fast-config/providers/2-networking-providers.tf . -ln -s ~/fast-config/tfvars/0-bootstrap.auto.tfvars.json . -ln -s ~/fast-config/tfvars/1-resman.auto.tfvars.json . -ln -s ~/fast-config/tfvars/0-globals.auto.tfvars.json . - -# create terraform.tfvars. output_location variable is required to generate networking stage output file -edit terraform.tfvars -``` - -In the following terraform.tfvars we configure output_location variable to generate networking stage output file: - -```hcl -# path for automatic generation of configs -outputs_location = "~/fast-config" -``` - -```bash -# run init and apply -terraform init -terraform apply -``` - -## Stage 2 (Security) - -This stage sets up security resources (KMS and VPC-SC) and configurations which impact the whole organization, or are shared across the hierarchy to other projects and teams. - -```bash -# move to the 02-security directory -cd $FAST_PWD/02-security - -# link providers and variables from previous stages -ln -s ~/fast-config/providers/2-security-providers.tf . -ln -s ~/fast-config/tfvars/0-bootstrap.auto.tfvars.json . -ln -s ~/fast-config/tfvars/1-resman.auto.tfvars.json . -ln -s ~/fast-config/tfvars/0-globals.auto.tfvars.json . - -# edit terraform.tfvars to include KMS and/or VPC-SC configuration -edit terraform.tfvars -``` - -Some examples of terraform.tfvars configurations for KMS and VPC-SC can be found [here](2-security#customizations) - -```bash -# run init and apply -terraform init -terraform apply -``` - -## Stage 3 (Project Factory) - -The Project Factory stage builds on top of your foundations to create and set up projects (and related resources) to be used for your workloads. It is organized in folders representing environments (e.g. "dev", "prod"), each implemented by a stand-alone terraform resource factory. - -```bash -# variable `outputs_location` is set to `~/fast-config` -cd $FAST_PWD/3-project-factory/ENVIRONMENT -ln -s ~/fast-config/providers/3-project-factory-ENVIRONMENT-providers.tf . - -ln -s ~/fast-config/tfvars/0-bootstrap.auto.tfvars.json . -ln -s ~/fast-config/tfvars/1-resman.auto.tfvars.json . -ln -s ~/fast-config/tfvars/2-networking.auto.tfvars.json . -ln -s ~/fast-config/tfvars/0-globals.auto.tfvars.json . - -# define your environment default values (eg for billing alerts and labels) -edit data/defaults.yaml - -# create one YAML file per project to be created with project configuration -# filenames will be used for project ids -cp data/projects/project.yaml.sample data/projects/YOUR_PROJECT_NAME.yaml -edit data/projects/YOUR_PROJECT_NAME.yaml - -# run init and apply -terraform init -terraform apply -``` diff --git a/fast/stages/IaC_SA.png b/fast/stages/IaC_SA.png deleted file mode 100644 index 8247f429e61530c4430e4ead35921ee08f1f6898..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17535 zcmeIaRahL&`tD5@IH)3ouUspuo{d1fp36^-K&eLHF*3W51I)NxIy_MgI|H=O@D^y#rLN+`A+ z>c3C;^jI#V2mceoqC)l;aqq(=z>%+L<;Vlde|`B^LE0>jh*F7~syz+=DLNE;|Gx$P z|0p8oMR#wnn1%*1Ha4~l#qjh0siiskV-}FSXv5Pq; zciE@J#KOwV$x%P7w+<^VX5kdEvDt-f$jHh%obTyz#^2}?vuJ*u()W-TWJ_T<%{gRc zVJZCGRC~!iihof4@Ai8A4J9-JRcby5^e72=vcE)C)_jEC=&~R2iqG~n56?>i28HgK zDl<`|?=dlc1*&;)@gVH!2FE;CVQOm87kFr0)fQuac6V9n%zr2IWW*-6wzftkG&pX+ ze0(ndCY0CI^aNlK|JmGR8a2JYIgX6N`W{Mb;E7kFT_YwVgYJEIX-3Tn)hBZq&6YMg z;+7N<5$W#h3pF-h6crT>4h`-48OI!;iSYFD^5Qh=e$jk?cWoqSIc_&!@9+W*P1N4L z9G4Y8m)~){9f43D%*nkxXf=FjbdGP5ihrVm6|_Xxv)Ya%dHr&)D_wag#w40#+z@Ve zmm8dp>vE{5sDD;^LN!}G8=@i=QbEh(1*-2|_Q>;bLKMu+85g~;ZKBU)6cl==41B_S zLWx&THphY^B8YDDDFj_{Lqb9ld931bDe$v4(mWKvy6eN~?A<+!iq6ZYg zhAK^ef$0L^G<5GyxNzlaX&ixK2fVT_}yPQrz?29y(? z$7Oor11({aPFM5-Wu-;Tp$ajGcyIXHAnF?7XK9hI9-V4az7WZu!i=r2{3_wrOnMEG z-%*oQpi(4^jiRi4d>Stv4`A`Y;+59?X{c}rC}eJ<*7{8XBXW>fIoVQBiEy%~e0+82 zWOcR+_y}4UZa68I1lVb^M2CTfjVC1HhJ4daMqcQ6kW0*8%3dOZoC%xu5T1{d)38Ph zq5~-_E0YK~Tj#v2sHniJ`JlcrR%4~{Mq}>~6b9Ki)ZSlru3-Ve%3g#)Li4FgtAX-*dEU)Xc5;_nB0=Za?6yb0?G9e)) zUG9&jg%1U)e8eDT>jnlNq!Ff~LB~p`7~5DV4`oCjZ*s9yHU@9dQm1ej`r@WW3SYfY z6uzX>)zt+qYkG^Gu*nIMv^g&5I}+jzszeI%$}X_5$3i3zO$`yz{b3MEntXStl{G^7 zvt?`}yfM|VD=;LM=q0X3OUn-$UbQ~eDYL1l*r;r!pEAy}_0|e>B2bTp+}zx33zd@~ zTbD8mmbXf|{6B3C8K|=wQc_Yh$gT~Kqq#%+stH1ej2jPruT^S62@PI6G=L_Q&bLsq zZk+|x6@*jP7k8M+8iEq4Age7u(L_W<(KGc>9WLO^j4eb7KMXr3hY=*k!#$sfg7*or z>~+8htV)au=?^EdNyPuFyR(qvt6wfgh-AJ(@&ee3VY)=25i1xyaN1~6U{olbB75Pg zIIf=NF+d3x@5VjhWG{d+T3LaryfgmykkN#0J&MG!dH1l~1STtX!^d8Gd-hEM3`glW5j!^#b@LQi%LZgo{&?}CY#)2Et zSR`!6exi3>UAE6pE3G6^F4?kxBKK#DM&o4hQp=g6ryW*M*CWV7CW+!@3@8VFFZFfV zgLsJ`Jw4?-e0>*bLdzz`I-KkK*XPSBVviR|1c=T~-)l@rS0Wi(ti#mA8 zdbr0Kkx3kN@BHlcZS5^K=JOq`O8u&kQo5u}c&VeK?>z5*5z#Hlh`ZSFA-617*DlSG z+nb*{WC=3!vRDtTBhC1TQu`biQpVJ&LRflnI@}V;@5 zwHYLNuc^)C6%xcAfMH0szKy|PWwirxBIDc7SM<9T1#p^Uk2wzs7cVLBDzyn#T)&M3Mzvo<*F)kMrKEmk@QLhGGY zdDoly33}-zWQG;haZC`=^SjJC; zPL57HGYOVgI0CKAEV==yc+gstN5%nV(Th;iT3)m27VnA(1mmlU@@ymE?mKXP#F zdLiPIUsPbY)KK-Fh%{uh@*UL0si*&*GC}XpPz-}Tqab-%1Z6ny$4RN*^+EpQYOehJ z;s%n>4Wsd<@reQzhK7RWr8`y%#>iM&dhk*u=04Y8%fXL3YIV(H42lXegF=Y{p$IXY zy604Mg~3R{^GRzGGSb()D{q~PmqL|Q13PQ{Fovjb`UbMhlw%EEdsDtIEW5})Z%M)V z<8J*J3U&IkQ1Zkb`#@aT5btim3AG~HVPs_dgCrIE$D%c3Qm*jp%M+fk10_1R})yx(h!pBd&y;$T9dLsG{ zqpPB%C91fVa>#tCf#S^O^7(G_{6Y5zpPNP~kM&N;@>mrH_MR|}zs3BZGFB@K3kW}2 z29E+i>=@5M^I!#aS}VnPY|)sJjfv_0&F1d+|92qdO z?*3e3(O}*lZjZeQ8LGSYkO*r-HaRh}GIf3D%QNoKu*%3Q3g=r76LtSgWVG^qd19zy zM$+j{zPvAl&Nzg*b0HhIK3xZ0@wtToGqq`R{364$0-Ay@Yaty-JK2(BRM{`HluS6u z2xYE#8$AR&r?iyr&C2ZWlwf%U(a{Bg#PK3xmC;dq!MEt?zmr)Pi@USwy5{>|#7o58 z;}8-Nuc|=_6?(g0U+(Ea9UMANyl)}UjVk#R4$y0qZ)y4$pLWyj%PTu~GDNW|<$M;L zpFo|+Oo}D>rL;)xZ=KP029w*6bUAS=)}G355Y*1OfmOEZUx*gmo$(C#9XG8}t+ll>JwBLO>!0#z3(R!=9(%dlte0t*5jn9`=2x1= zMlUS%5wp&7fZhI!qrZ(+N)QYi;=9h~Sxb}5eVKGv?ousod1P~_EeWr7n!t9jW#D6= z)0G8lTaxMPpNRR{5d%}5R8H`afrpsGi5jZDhdZGzxi!S(jSwvc+JFm3z6S9L4w4QW zgdV#gB0%K!^l9Sx02Oj&hDcG7gz@vycJGsVT+$%+#iRg54Qryv2|~oB>!rkevlq@1 zySKuLR)sK0LKeZnX(#`CH)^sJzpn1jU!2P!20nIB%PEfiBf1rp!dH2vv7Nood0sz~ z(PN#7rv>-TOHt&4wn%0nuY~5TGQ#5m-^aiUigY$NHGS5C^Q)LxS$h__%>0p2)ltn+ zNTn=3NbJz}d&+78WsPn}Z_2;vgDtnNBw}m^y*dQ+e>B0UVzjh9tJ=xQaLuIIpfR)d z4|Rbn*iZQ#wq!&y^I)EgN{Fz_*VnyW@_}WSer^%OLUWL& z5hCPn6lrd9Y0ngrgk*l1`Z}~C&&y-1Y77O|my)%);AY%_>0m2HCbo{jguG8<`*Ywc zs~goU$P$%^UI_2dS-F+5hBUNQyB@Q2Hx{Z!3wX zvIq{(8}(@5tH>24QFac-j7L%*DS_CWMuiDZ;7*VK%9)}k_)eIFa9Taglb`ny^W-xP z{8ToI*_F+ZC!#&S%DkHKZ6j(E1+s3MVq5iU2pmERF5ElDTzzJ)ju?lFRC^p9iSPY+ z&pyVfXuLycUCk{?$u#(tEqak%1_bo8vxx1S68isWWC2?(av3k1mw75@gXK=QfcT2t z)6@P5?rX&5$-XYC(|GUK2<-!BMjz@ZI~Z(eW?Iu<0{m(5;_y8=Wkd`Dkw`-FLeIvg zSlAUe#^cmC`r9M#g(l({_w&Fqk0#idjUbut7$;}qXdaIB;9&Xri3OORe#Ivyy?eB+ zj#hvjM-hv~Cx$TLyKhwRi?G9U$&3V-7K?J^_^Zs$Cs9bxY9Znl!D`O>Dqc_B!hI14 zILH!;iFtC^S1F>bsp-pKCNH^CLuaj5MIii550P2*+=!;Mqo<410dW`{-V8fb4DSrz z9++;RCRt4*dqdggN&h%27fbS^1n-&6wIm_G)wa3udG9Aok{M!&+`#Etce8MKOkC0% z6}3e@f%dsR44<+EtmkGPCzPWE%x&6OO}w>8p$oM61~+0;)0~5ySy4kM|Rba?jwnZhz`NVlE?dTknbu-KC7y^mn+ma zg6JZ3q{k*4b!1&8%8lIgs}5t?0l79NBg050pI*MLwlEDT z$Z}s@3m`$I;tw)QC#QpDrZ^fSEQX4XF9yR^DAdLcbX3)#)jT|hvi2PLi@l4ZD&Q}^ zjnIyaJ>h9*7PaiMd?%=LZ%oT|aPljytm=vNk*xPVyU9`(s*m1HoM@7~TFn#k;20go z@oE-;?VaSBBW6Vm;dzmKz2QaYz7-FBqw-{pC|1-U?+hzHAF0Gm4-zD5;(eAi9Q6UZ zJwwWXlipDf#RVuaz6AjxjM%)qT)lg3zR^Ei2^gOUf*OqvrXMk~FycE2P8s8as}WV9 zK~b+@uWc_(t8fV&t+>|ahXec^i!AXdd~qGv@Pp3$;ji0JYR@J~4%cw;AdQq1k+%Za zv;thRgdff+$x-(A^kp18rbYj?GToA=3Y@>IFzi#-m2VFD{#`|xS-dVelKc;|H=g>- z)Z!ny#WT(%$_{+pa)Pt9L`KFY8SHz#S595tYk!H`dl1-swhIB_rhkprCY|H!9gf+G zS>+GrV>`+@`Fi4tq&3@^Q8!6vjEtW_PusgQ6+h^;xDn@6BBkcT=69puWr*+WGX&MNP81>_>dZGnQp7A|^ zTGF+{yI!p-1KOhU58;Ol>9;&oOhNK%t1S&Ogblweau^j@lw|t zU=JBR#(&PHQ_#t2tU%_Ee-WyU`5QcB+;sALrSl=5dF-fUmEwk3?ii3Yn`5b2Bqz=+xQv3?^~)5EK*?T-+QFbAD<^ zskU39R8aVR-9_^X6HI^!=`s2&dJH?Xm3Ypy!VotYwJ3BkMWNx(U^ZmY50T&LNS#F03H#axUdVA^z z?XLNJ(L=M^94{;^%>LrQa0gf`K7alUBx(4Sgt)k`X+YG})Gx5HiH^jSlm0DG;u&~f zzOlBkaaitr>gw(ekuTdXPvtSJjI?qCFxNaAraB^VuYIs9)&v#H#gx_nL!_*U2Rv;c$&+jN##ffjXJ$pa#6nma-B|O_p%e&gXn( zlTNJf`bai`?WM0ME1;70V@qR&47n_MF^r059$fbhR!6qhfai;9~dEGEluH2Oi(_isu((wLY@vJ&;C$=6$%@?D9V*5?h z?^y^EuO^h3ePvkW9z0d52chemn5e-Ap3~lWPC?!WLn6ZhO%NYpF)O=P#X}Eh=$@w& z8Z?$F>QE_P#!9hpu-QVR6U`Ql)e)S4fWVJJz&TiLGMUHfV6SN(X8R5Sbp8AMp+Fi) zr3602f*GHpFe}<8;%a0;bIJRf^*=HtY?Tp4KuH;4+!qnDvJyhT@KLZ2K9(pD*!THC z?B-?#B)#0f3oAnsaB=07Oz|M}qzs9tood5k(Zp_u%<-2K3rgt0qw2AZ+O3}aASm)H zAiRbeGkKZX+D6zeG=^xC0jJRzf2WtxLtM})#2Ud58J!@*ztcqw1YH0x0goy9ohiCh zCtzUEhyeFMxZ-%cE|2E`k0lMknZQK|N>?)eg)D&v3yO&9Y%YBPuf{bD(6a<3LKjF9 zWZ+ybd%CF4p22*L8!6@ig?w1}x|mTSo|ppUBO?RL&&g~m$tOwj_oI1V*_4Fja&+FC zXR_VM)usTdWn@ygFw^4U(vzx6$3uw76d_EVsj*7PU&`#q371`sDen)(_BzAPsmjk! zT(g)iH#|5#CJ5*q7Ca8aWrqm@kFEmP9htncvI1_w<@>j_q{IqnYK1lWzin$P$6rEJ zkqS((z~HIA0JHVEAT`=F0W_)x4i`%&d^5HgsNG!xYqvLqXF!)uaAL4gkO8~lK$g@ zaXgx!0rXq#?Vc*re;FPR6(D>K9718)`}tq9ET8BR5ygZydm8IMrK13co_=!B_^-?M zp$6!x{7Lsi>wijr`k+Rlgf_kVuai~?0oIk8$xyRj|1F*IVL^MOIUo05Q}2HoR#4}k z(jNhBjo!Xe`p?Z+0-C3C$?3|>Kc%DN0*6qDT>iNkwNRj4EK7)oa%AgI=6u5*06I%oOMcEPs~4i21v z8xpwK;=x7r@FaYdd5Dw-Osx{7(*@n|0oO^)#Ds2Tb(Ir}RN_1U*6z&AOyyPS)|(^Q zFftx=TU%Q!E;Py)!*Wty*n9rU)dl{+mlEPhIUcO}Ivbvf(F)2wM>~AtW zwnR!wy25W;DE>hoYGS3OrsjXWaQ8#)e^`L?{jSbVG2IssFY_|M($>&TEVldnIXCLF zxOkT(v7S?z(sNM6ol-VpbW~-Bb}KI{>j^DwmW=U;TUBM{+uef3GaVp;laZ16WMrhm z`B77oM9TT|?__lg_A#DOx6R06#6`_-NvDr-W{+mwMHEhq(A?$Sd+Tj zpDs|p{UJ_6Zf}e0L$N79bfaWtEoaHDhT08Za$H5Th=V))p3`!0;0n0x5e~m$V92Vj zCY+rG98yhJHRIKRQ0$Vwzf<_4lapb-zK^LXV@J`kv7ZenrtsQy3saN2{*BZ4v%MW! zq)~>B2iWWc!xom7Fr=Zp#~Tnxdx!WtJ!l_@T`Js;KUbJZC4yY`!hqEm-5$97@vctt zlT)wtB(sr5r)}-?S2@~1fWQn-sFt*#_QC(W; z=+ID%T=^9Gvr`~q(OC6VGv;xNm-}-_H}VIr`ti)w*E&LjmDt=3c~S@)VsE>l(NAOy zfY|IWwygAQ1`zA#1BP?L$cPd(e`8~#X8EUYu8Rl+9uSva0LJ0r(J%H;Da7VjUeEE# zkn8Ez1mM6!l$4bGUNg6(PrP2uzk0pZ8<$0acRqKXhv#re z=)q3aaOdKpem~1@Iv%Y=a6yz@)Tb~?p~lIUFZ+u^ojfM)J~5Hx=4{UH0+|)rtzTl(hTsJ93^5aPOc@RNq<2pl z6n?bFF>8RqL1facO-%wLef`M>{)oYf6-|hGk@S~}#>uan-+W8m z4C$UKc$IlB+b1i>6B-7~+huJ;0sSXNV*B0{@-7^S8G}%fy2ul6=$=nJpa7JDLuqr`uDZEgUBO zxQEC4ZTAsC#2M=KVYCUlse<=3J3cm+&LoJ2wr2OGOn}Wy<(ck@dkalQm}X{X z)bI70oL`Ea-kvY{p|1-G2{D&m9uJEUdxX*O*6i}(r_xGB1Vanu;D)_>^PQ!$+rPz^Mw zpJs1aTH4om9^6+n=sgcraMX!@mK<3L7vKN1w@lES6F1@MQK(H*9j4{1*H-FHY3vXu2Zg^V#b*ckB$@Z8s?j>_8Z@SV7Cug{5P zr4gXFD(659R{7x?@^R@6JE&$iE;g2MXR)PeRG6j$E``1O>g^8(G$0mjkV;aHg-otN z@^Wt_zDVHZKg8<&{0k0J7M$*K!f;vw3Ve^5yFAB{woPrTWJPgFs3sJJC^hh6cN%I8 zOnMYn!ZGBJOd&ym8RIW=5=<U#_PR}((rsijG748!4>N=i zt$)t0jnzK|&P({xb2BHwQciE8dWrFIwe13kMZZ~dFo;HyPLU2oxS8qB_5JmOVNF=f zq5`>2KjWDwp5Ug!&j_?;0kMFv8q$-ESb%6fV{jv1B10H1zKLEan^8^BVm^YQCg0Pq z7JD^gmL4(}SzsT7Dy^u912vQV@ZoFlijo6UsKTo4$T}m)abxIZ4^r)u9G+<5Tqg9* z#5f&2{bapHepxe8X$uDPWzvHj$vFvdnGF$gFp-JF#hgT{&(}hut!C}$mPKw)VisHjN%4&sprm*D5;Uuouc0i}WS08D5Ny$T65@|w3LtU?o-?E5Y~ zOCPTKO1SYlAh~!*4E^(4#y6aWyHB3ygk?%mDd_RoZplQ~M4jfGFhK4Z--!im5{5_L z2S3&lDewqD^;eNpKc|*{Azed5r7w*~I-A0?u&3b5o?Z&CkRDv4%4(S6r83o=fMSMX zhboBg=H!wELGlWuj@{nkDbc~Rej*E#lG57y|0wxg!T9jHo22hy@bQ=v$bs`FNoeH9N1^{nX8HLa02a$YC_j0TA z;cBtDIEdr~(YE0+D;t|{Zf>m-Z*MwjPAyx1cXTpzspib7dK6@L56$jD}9 zmg*A=A2!o?S-mQ!?#;KVWC1zhsg!I`#UZ(S^y3=wZ@zs~k>-i?CP%pSgSz_yX^%W# zRK6QCG@L-+Tf8%0Rk`CFC04xiyGX=9@<7Q3S*&}EbKbm}u5uu4d>--D#)_D5ZDVB+ zp;zy=`j(rg#?WesXd#W{8LJINx5N*z{>7S=_X5vd5_M)esywJjNK-UY5G~a@FJ_L- zdRAu}-ODR2{ID0UJ}kAruqcq8u5e~*6yhYO@cJe4^drJ~x!d~()eIrfPYbObSgX8hP4eN*drU$i?n`;VW+SdVDJN}2J-K#WwD z9g>$*l!~1l6?y=M9%0>7n?)jxtv{hKFf(JG?>>@ku#JVA#ZiXlzZvE_14xj-|_#qSw3ZM4+qs?tH zZ=Mro(PQ1GJo=p11bn_Vw;({$`2=M~*5r}RWe!wXg%#$E4r1<%eV znJWDe5Kto|q2NK0pg`5R8AYKqHS!^)cCkK*kQQt)azXsbcwg7zW1Za_qm6Wfj_E;k z-jZ4PYmw)CD`g*+MhLTVTd0Se4!gbUtM}>Gi{xU6DP-q(Pbtev`KwV815AuvlQ97Y zE>pfmHouignCcS7*bfwW@16oa2j=-H#Qh`Dov_2>Z)s^Hn;VfL*gKNr;jb>Blh)bU zdCNs5Isq?E4-VSny20e(!R=#!95QThX}YiPD!`xF;`c<1h9)XHHN1{q&VRsXX%oEt zWp$5&WuX>JPgi%OO8ohch>(r}qL8zZytiq!{8LjdvC9impr?xeWC4!LAVcqVR3E){y^BcaQ4*r|Go>2Z1;G z!3yciTz^&&SXxOI(^q|Y92~q|7g~P}$QZ5OAr6rwU(-Zmy1R%K$lGT|^pHYOGHact zm7kYc=6|QLc}wP2ZYo;Js~y1~d-Y7rax=p{7^&e`n74WTO6qyA(r#}8< zZvD&+L7n+M+-!dwdpQqVOk5R%o`_c}xZ#DDIDzZ+$xG$88S95zvq)A^=xEhXj?Jd) zkqH&QlIUlFE>jX;o1PWRQA$M;A{uaAu!?Xf2`g7-a&?oXxhs#44^8%k+{Ui7NO$7L4Ty&_7skuo^Ak#SF@pzyPJn`Y`Ra+cU@_jZuX82 zvBE2-t*~a_$9QkPR9AcpFCdlq_4DAavBuoWn0ApV5{Y=y($c)GtwKbRgT|Bo#3gm0 zr?5+_yl$lP&yf3p!^F1$Wz!H1HK=xy&ZjX15LSBYff>E#=Q%ou4G4qx1YC&vXlQ%V zw)eK)cBfG$@Fdx{f!w)gHMMAYRw?mSRG0s*vCyRdL#xniHMnkZ4-_n2DcQ*(+>W;A zMwB$fKM3fZoQ(#UZU20O1`Af`+e;&P-*|IsHc{!cf-z+_nCjbI|LVeJ3eP0ltaEbl znO`5y*McOpQVIR8%ofS$m^ch80cK3H1u7!m_29PTVDTpLD7SrKzz8?|lkZ(#UD0m0 zRQ%0Y=lKcM%ySL4-aiLvS&1AT+?|Sg5-X>#I5@_Ga^vX(iX#F%W@UuNIov34=gzG9YSsg)p zyht&USD2ivqd`Ccn=ZaKeDpX-kSPLVq3#hPA;Nf2)vjxPR3S{XGBzglk=GMra1d1) z9q8nyqs>R$a|a^l?pzTS85A-UhmrZ>+Kk}wGdceTb>gMJvVQ7^wI%0s8(Q4%W1L=&Z>)Dj`{4chbt6Q7-qLQjuX(im8*(pRf z3p|-g-c118B4U3>ewN1X=Rz;<8&@G+!~atf`f+D5Acd6F)n}{P^0std3IHd@4Ij)s zDz9F;v)vK3^1YU+2^ky zuPU#m>AEg2uY-jMio} zj*h9?+sm&)i6PPeRSW&T`n)bU`8A9rI#%r^#nnuchX^6j+OqqJTU1(P(j}49pe254 z-qH;b>1M(7(HwsDDe^5`_La9bCPSTq*dLRr%`q!F0Dj*hOw}r5e|h`S=Q>`h?39!@ zYw~cb@MWVj#(OFQwiogfN1;EXl2qvzC%D1PY)@^?5?(WuHGWWzHM3{1X6D?1*N)-1sl<|D`OpDwW7x`%Fc`iCrG+&mvMQhaGpZ;e(?ls4KzQ`CPu$XJXZ~ zNm8D-*9U_+R3x_G(NV>Vik#3ehFS{O;`<$($P(7%O%UmLUZNc`V$_#CDo9AF>+{9zu+TlJ zE&w*^9r%rgHiaJ@pB$J#-0~Hx&02JJ7h26AdT38nzz#dQX|crt=piEPp|kfsg5>vm z!oD+QihYe#e8Otb#S7$pHz8v(*hy?Poh;n9`SE;XG&+V3#?8r$-O43piaCr}QwiF*F=&Fi4MQiMjiDEoysR4F5JW)~6Fe~dd+RYSq zb?(P?t#-yWR*nr)kx{pT-l6l;pmT|&aT0*EwQ1Jwr=q*et;`tEqyDODSj=c$xZYk_ z)?lbr|FFO2y>i;2Tf{Pu3GM?}-cXuhwo+To@+}mh{N3yzIAW)*c7G@8`daa8bD#!M^>f}cdnS)hA0{@`g(Y%UsX*uVrP6y} zNADxzf45Mt#7UMBev+4R4nQiYN$RL#+1OMcfIak1wlXkkwqYSdB`45gkuTu!86~=} z@B}&#fVZ;)lQ~RQ20SZs=YdoJ>){Pwx>XbV*ojaEM*T`Sob*R~PlWevq`%+Nr+Ni(YSt`yV5sPX2Qge_bqIv7eS{UPNrm)=v2kI4XtD+)f0$e9h_YqtBwgagG!{U zYg6WZaI5C&Ey&!w(D;mb439YVYunT=J^%xfYGpBEqp5UXGU|rH#67JadwN{A_fDb< z@a}Z1ne~R|5A~=f91cf_C!TI`7vEc`1|Z27U%jQ_uM8flLAnm=T@;hto$ufISA;|d zlZDiqIqi`Rb9-~Y1F~oTwTun3*1iseDIon7=)2HjQflf z?@JVk&+#N62iqJ#)QFh8D@SPw`u#PlC;`b{lo>toXT&&Rn7p?q<{Du&jXB?xs>=kA zNl)-AX=BGb;{4kanUQ<;=V?PO8O5cAh#XV~7FxbFcM12-(;23wQ86_fBWrE>$w$s3 zByLRC9Vns({V43i+*R|uK3_U; za0yBedd>!H?13AiUv8UQVIVg5)}Qftc#tUql>+iFvTS*pfKj#OJwS zdg|Zan?()R^p;5%F6ZRxS{%#6OG*sBm-oh(y3Vz;2!stXGwZ9*--Q2--3`vz`sQqrGZh`1SNZ;MEFS zu>jvbpw1JP?&_7V_hBJ-=I~N)n*+L)ynY8W1R4F+DEhO$iJs2TxY8cs6YV8J?6{ct zO{7%^0}Bfala$JzN2Hk8u~ZtGFU$ZCOUAi4lASfL-rwy@54suCJKIc$?(7I+rjz)u ztl%pP5WYVU70QgQi5dk26(Sk4$i2>hnCE{cJBUku%tzyTNRJOIB?pbZJHku-u_H&m z(DX68gq!pXU5$0F;)%;UuI`eZN|#BsgNDm#&OvN+-vs!7b z*H@N+2LGn|HAy2<_u)lvc>SyCwpETwuBwczBELnyCw~?eFo+fOJ1cw?UW>3(1@;D# z2-2*VZmEmRW0d+>v>VU$hByuw9QN?ff04Eh=W2$Atx~5W zdWxw}o`upr`2IZ5l=spw&#d7(w3#}}%{o>8Q|(#MYx@JW#6+zSfg~wtK4N1_NiDx> z>2wEHME6o%BR%8knGwLPH@fKJ6#d6-f21q3`DnJi5!%jHLeT3oWO zoZ7)6rc`J^PSF6xxJ9P;5|F5rd(?}(hJL~nWZ@$!;Jh~_16`#7zz(4js42qD-pbRl zNe*BLYH7mxr~t^s_?l7TGh%ta(Vg(RRCvP#QGnC(*_FjhQvD+22}eyOWX@SHWf}8V z9>8N+C**c8^uovfNKtKmeb783p-f)NufVQe!L-89>MVTkX)_=jr#E4WnO@Rx(W{c@* z0usEMc@wOl5}7{>tBJaN%M5}h%q@2X;o;(3IA>$iZ)UtAAt7n+>469?C5G=eKGyz@9BGB$N?aFMW_d{rySch@ zug7-+w3z+XiN*BvbcSl4Lb!Wr^0No&*WyTCW0(L~J_%k^mHx{B7^*cHzz-wm2Vl0p z>0(|7e?+b4>+z^O558UeRon$E3<$6T&Ct;B8{nNapNz_iec`oR3<-2OMO2eZjRh+{%Ub zmR^xV(B{q%&Ea5Tn@$|iMOFB@&TVRhaHVZ(eJB~-v#Ht7A(DPO&6tGhq_YfQW z0W7G5lg#f}HIaY&3?EuM%xfaixDv@tuSePR@$o5MD&Xx|Ke$=V#}Tfa*Gu*_R z`u+QQFzlH?oDx{)IO7=Z7-~^11m#KiMiT|+w7Ys%{IXUs~ z9!wR`km>|1b%*Uq)_-rg)&QgM+04?7rqPkmNj{J~@7L>=ya|SXk%jaGz><}i176WRE8ta*No)^nOSgjg4*!{pLrFAOf zPVkP*L7M!H=Ow<2JQXaKi@le@yfRfZ5<{v_W}~2>Qa`*GfZgPTEphZ?+s#pEep{vs za&d8Sg%jAwm+o_?H4Yl7z*>+wK7Qpc6-Tbv64II#OaqE-%@z_3R}rymJ`9W^F8oWA zL~_1FEc^g@BIoMZ1p%`YVvBV9PFlE3qm>(sG0}(m^eGxYd?TOa4zLqD0q8;9VW-vW z8r&U3!m8Ekv@l*FcXn8S7Brk7v~(m$g$uZ- zeZk>9$*v*0ln9(%mtAsFSJA$^RhiEw;6N1J{p}r%w4G10G_727T}=y;)Zl!40*CBJ zL{f>c>31{Ai|$Wu>ba?@#k$tXGX*q)gBC;BwX3C?pYZYUsB@wVE)=3}0rp}Asjz4L z`yIfS#dX;lfX!}l!hS{}0ZWkVVY@D{u^e*ZKbi&2OKV12PnSOe01oGH`0;|v((>t) zK^eD@44*QY<@w%1TpBX~jYj_fY`u_iGU=$BghpOI#Ijx=YT}1302{H;Ok@4b?cR}b z^05=jkH=A3p)sKVf*nZB*nl^0ynT4PMKud> z`>a+%mzIeivhbsTy<493|9WwR+QW+$-WKBzN275QJXRAIcTq-ctlR(V6%qN50VGdd z^GzD{Uee(TZpEDjG diff --git a/tools/validate_schema.py b/tools/validate_schema.py deleted file mode 100755 index 461b448db9..0000000000 --- a/tools/validate_schema.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright 2023 Google LLC -# -# 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 -# -# https://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. -'''Validate YaML document against yamale schemas. -Fast includes YaML driven resource factories, along with their schemas which -are available at `fast/assets/schemas`. - -An arbitrary number of files and directories can be validated against a given -schema via options (--file and --directory, optionally --recursive). -''' - -import glob -import os - -import click -import yamale - - -@click.command() -@click.argument('schema', type=click.Path(exists=True)) -@click.option('--directory', multiple=True, - type=click.Path(exists=True, file_okay=False, dir_okay=True)) -@click.option('--file', multiple=True, - type=click.Path(exists=True, file_okay=True, dir_okay=False)) -@click.option('--recursive', is_flag=True, default=False) -@click.option('--quiet', is_flag=True, default=False) -def main(directory=None, file=None, schema=None, recursive=False, quiet=False): - 'Program entry point.' - - yamale_schema = yamale.make_schema(schema) - search = "**/*.yaml" if recursive else "*.yaml" - has_errors = [] - - files = list(file) - for d in directory: - files = files + glob.glob(os.path.join(d, search), recursive=recursive) - - for document in files: - yamale_data = yamale.make_data(document) - try: - yamale.validate(yamale_schema, yamale_data) - if quiet: - pass - else: - print(f'✅ {document} -> {os.path.basename(schema)}') - except ValueError as e: - has_errors.append(document) - print(e) - print(f'❌ {document} -> {os.path.basename(schema)}') - - if len(has_errors) > 0: - raise SystemExit(f"❌ Errors found in {len(has_errors)} documents.") - - -if __name__ == '__main__': - main()