From 3d10fbd7ff45f9c50e67fc8dd5807e9f6db5ce7f Mon Sep 17 00:00:00 2001 From: rcmadhankumar Date: Mon, 22 Apr 2024 20:36:50 +0530 Subject: [PATCH] Updating docs for latest minor releases Signed-off-by: rcmadhankumar --- site/config.yaml | 20 +- site/content/imgpkg/docs/v0.41.x/_index.md | 2 +- .../docs/v0.41.x/air-gapped-workflow.md | 2 +- site/content/imgpkg/docs/v0.41.x/auth.md | 2 +- .../docs/v0.41.x/automation-workflow.md | 2 +- .../imgpkg/docs/v0.41.x/basic-workflow.md | 2 +- .../imgpkg/docs/v0.41.x/ca-certs-windows.md | 2 +- site/content/imgpkg/docs/v0.41.x/commands.md | 2 +- site/content/imgpkg/docs/v0.41.x/debugging.md | 2 +- .../imgpkg/docs/v0.41.x/faq-generic.md | 2 +- site/content/imgpkg/docs/v0.41.x/install.md | 2 +- site/content/imgpkg/docs/v0.41.x/proxy.md | 2 +- site/content/imgpkg/docs/v0.41.x/resources.md | 2 +- site/content/imgpkg/docs/v0.41.x/security.md | 2 +- .../v0.41.x/working-directly-with-images.md | 2 +- site/content/imgpkg/docs/v0.42.x/_index.md | 21 + .../docs/v0.42.x/air-gapped-workflow.md | 154 ++++ site/content/imgpkg/docs/v0.42.x/auth.md | 166 ++++ .../docs/v0.42.x/automation-workflow.md | 141 ++++ .../imgpkg/docs/v0.42.x/basic-workflow.md | 150 ++++ .../imgpkg/docs/v0.42.x/ca-certs-windows.md | 28 + site/content/imgpkg/docs/v0.42.x/commands.md | 178 ++++ site/content/imgpkg/docs/v0.42.x/debugging.md | 46 ++ .../imgpkg/docs/v0.42.x/faq-generic.md | 30 + site/content/imgpkg/docs/v0.42.x/install.md | 58 ++ site/content/imgpkg/docs/v0.42.x/proxy.md | 33 + site/content/imgpkg/docs/v0.42.x/resources.md | 145 ++++ site/content/imgpkg/docs/v0.42.x/security.md | 8 + .../v0.42.x/working-directly-with-images.md | 8 + site/content/kapp/docs/v0.60.x/_index.md | 2 +- .../kapp/docs/v0.60.x/apply-ordering.md | 2 +- .../kapp/docs/v0.60.x/apply-waiting.md | 2 +- site/content/kapp/docs/v0.60.x/apply.md | 2 +- site/content/kapp/docs/v0.60.x/apps.md | 2 +- site/content/kapp/docs/v0.60.x/cheatsheet.md | 2 +- .../kapp/docs/v0.60.x/command-reference.md | 2 +- site/content/kapp/docs/v0.60.x/config.md | 2 +- .../kapp/docs/v0.60.x/configmap-migration.md | 2 +- .../kapp/docs/v0.60.x/dangerous-flags.md | 2 +- site/content/kapp/docs/v0.60.x/diff.md | 2 +- site/content/kapp/docs/v0.60.x/faq.md | 2 +- site/content/kapp/docs/v0.60.x/gitops.md | 2 +- .../docs/v0.60.x/hpa-deployment-rebase.md | 2 +- site/content/kapp/docs/v0.60.x/install.md | 2 +- .../v0.60.x/integrating-with-other-tools.md | 2 +- .../content/kapp/docs/v0.60.x/merge-method.md | 2 +- site/content/kapp/docs/v0.60.x/rbac.md | 2 +- site/content/kapp/docs/v0.60.x/rebase-pvc.md | 2 +- site/content/kapp/docs/v0.60.x/security.md | 2 +- .../kapp/docs/v0.60.x/state-namespace.md | 2 +- site/content/kapp/docs/v0.61.x/_index.md | 44 + .../kapp/docs/v0.61.x/apply-ordering.md | 94 +++ .../kapp/docs/v0.61.x/apply-waiting.md | 47 ++ site/content/kapp/docs/v0.61.x/apply.md | 137 ++++ site/content/kapp/docs/v0.61.x/apps.md | 41 + site/content/kapp/docs/v0.61.x/cheatsheet.md | 141 ++++ .../kapp/docs/v0.61.x/command-reference.md | 242 ++++++ site/content/kapp/docs/v0.61.x/config.md | 401 +++++++++ .../kapp/docs/v0.61.x/configmap-migration.md | 125 +++ .../kapp/docs/v0.61.x/dangerous-flags.md | 38 + site/content/kapp/docs/v0.61.x/diff.md | 378 +++++++++ site/content/kapp/docs/v0.61.x/faq.md | 128 +++ site/content/kapp/docs/v0.61.x/gitops.md | 17 + .../docs/v0.61.x/hpa-deployment-rebase.md | 42 + site/content/kapp/docs/v0.61.x/install.md | 58 ++ .../v0.61.x/integrating-with-other-tools.md | 27 + .../content/kapp/docs/v0.61.x/merge-method.md | 27 + site/content/kapp/docs/v0.61.x/preflight.md | 25 + site/content/kapp/docs/v0.61.x/rbac.md | 70 ++ site/content/kapp/docs/v0.61.x/rebase-pvc.md | 102 +++ site/content/kapp/docs/v0.61.x/security.md | 8 + .../kapp/docs/v0.61.x/state-namespace.md | 53 ++ site/content/kbld/docs/v0.42.x/_index.md | 2 +- site/content/kbld/docs/v0.42.x/auth.md | 2 +- site/content/kbld/docs/v0.42.x/building.md | 2 +- .../docs/v0.42.x/cnab-image-relocation.md | 2 +- site/content/kbld/docs/v0.42.x/config.md | 2 +- site/content/kbld/docs/v0.42.x/install.md | 2 +- site/content/kbld/docs/v0.42.x/packaging.md | 2 +- site/content/kbld/docs/v0.42.x/resolving.md | 2 +- site/content/kbld/docs/v0.42.x/security.md | 2 +- site/content/kbld/docs/v0.43.x/_index.md | 30 + site/content/kbld/docs/v0.43.x/auth.md | 119 +++ site/content/kbld/docs/v0.43.x/building.md | 152 ++++ .../docs/v0.43.x/cnab-image-relocation.md | 65 ++ site/content/kbld/docs/v0.43.x/config.md | 538 +++++++++++++ site/content/kbld/docs/v0.43.x/install.md | 58 ++ site/content/kbld/docs/v0.43.x/packaging.md | 190 +++++ site/content/kbld/docs/v0.43.x/resolving.md | 202 +++++ site/content/kbld/docs/v0.43.x/security.md | 8 + site/content/ytt/docs/v0.48.0/_index.md | 2 +- .../data-values-schema-migration-guide.md | 2 +- .../docs/v0.48.0/data-values-vs-overlays.md | 2 +- site/content/ytt/docs/v0.48.0/faq.md | 2 +- site/content/ytt/docs/v0.48.0/file-marks.md | 2 +- site/content/ytt/docs/v0.48.0/how-it-works.md | 2 +- .../ytt/docs/v0.48.0/how-to-export-schema.md | 2 +- .../ytt/docs/v0.48.0/how-to-modularize.md | 2 +- .../docs/v0.48.0/how-to-use-data-values.md | 2 +- .../ytt/docs/v0.48.0/how-to-write-schema.md | 2 +- .../docs/v0.48.0/how-to-write-validations.md | 2 +- .../docs/v0.48.0/index-playground-examples.md | 2 +- .../ytt/docs/v0.48.0/injecting-secrets.md | 2 +- site/content/ytt/docs/v0.48.0/install.md | 2 +- .../ytt/docs/v0.48.0/known-limitations.md | 2 +- .../ytt/docs/v0.48.0/lang-ref-annotation.md | 2 +- site/content/ytt/docs/v0.48.0/lang-ref-def.md | 2 +- .../content/ytt/docs/v0.48.0/lang-ref-dict.md | 2 +- site/content/ytt/docs/v0.48.0/lang-ref-for.md | 2 +- site/content/ytt/docs/v0.48.0/lang-ref-if.md | 2 +- .../content/ytt/docs/v0.48.0/lang-ref-list.md | 2 +- .../content/ytt/docs/v0.48.0/lang-ref-load.md | 2 +- .../ytt/docs/v0.48.0/lang-ref-string.md | 2 +- .../ytt/docs/v0.48.0/lang-ref-structs.md | 2 +- .../docs/v0.48.0/lang-ref-yaml-fragment.md | 2 +- .../ytt/docs/v0.48.0/lang-ref-ytt-assert.md | 2 +- .../ytt/docs/v0.48.0/lang-ref-ytt-library.md | 2 +- .../ytt/docs/v0.48.0/lang-ref-ytt-overlay.md | 2 +- .../ytt/docs/v0.48.0/lang-ref-ytt-schema.md | 2 +- .../ytt/docs/v0.48.0/lang-ref-ytt-struct.md | 2 +- .../ytt/docs/v0.48.0/lang-ref-ytt-template.md | 2 +- .../ytt/docs/v0.48.0/lang-ref-ytt-version.md | 2 +- site/content/ytt/docs/v0.48.0/lang-ref-ytt.md | 2 +- site/content/ytt/docs/v0.48.0/lang.md | 2 +- site/content/ytt/docs/v0.48.0/outputs.md | 2 +- .../v0.48.0/schema-validations-cheat-sheet.md | 2 +- site/content/ytt/docs/v0.48.0/security.md | 2 +- site/content/ytt/docs/v0.48.0/strict.md | 2 +- site/content/ytt/docs/v0.48.0/yaml-primer.md | 2 +- .../ytt/docs/v0.48.0/ytt-data-values.md | 2 +- site/content/ytt/docs/v0.48.0/ytt-overlays.md | 2 +- .../ytt/docs/v0.48.0/ytt-text-templating.md | 2 +- site/content/ytt/docs/v0.48.0/ytt-vs-x.md | 2 +- site/content/ytt/docs/v0.49.x/_index.md | 88 ++ .../data-values-schema-migration-guide.md | 207 +++++ .../docs/v0.49.x/data-values-vs-overlays.md | 221 +++++ site/content/ytt/docs/v0.49.x/faq.md | 189 +++++ site/content/ytt/docs/v0.49.x/file-marks.md | 138 ++++ site/content/ytt/docs/v0.49.x/how-it-works.md | 248 ++++++ .../ytt/docs/v0.49.x/how-to-export-schema.md | 233 ++++++ .../ytt/docs/v0.49.x/how-to-modularize.md | 477 +++++++++++ .../docs/v0.49.x/how-to-use-data-values.md | 141 ++++ .../ytt/docs/v0.49.x/how-to-write-schema.md | 283 +++++++ .../docs/v0.49.x/how-to-write-validations.md | 573 +++++++++++++ .../docs/v0.49.x/index-playground-examples.md | 160 ++++ .../ytt/docs/v0.49.x/injecting-secrets.md | 160 ++++ site/content/ytt/docs/v0.49.x/inputs.md | 94 +++ site/content/ytt/docs/v0.49.x/install.md | 66 ++ .../ytt/docs/v0.49.x/known-limitations.md | 12 + .../ytt/docs/v0.49.x/lang-ref-annotation.md | 54 ++ site/content/ytt/docs/v0.49.x/lang-ref-def.md | 73 ++ .../content/ytt/docs/v0.49.x/lang-ref-dict.md | 80 ++ site/content/ytt/docs/v0.49.x/lang-ref-for.md | 40 + site/content/ytt/docs/v0.49.x/lang-ref-if.md | 82 ++ .../content/ytt/docs/v0.49.x/lang-ref-list.md | 60 ++ .../content/ytt/docs/v0.49.x/lang-ref-load.md | 115 +++ .../ytt/docs/v0.49.x/lang-ref-string.md | 130 +++ .../ytt/docs/v0.49.x/lang-ref-structs.md | 94 +++ .../docs/v0.49.x/lang-ref-yaml-fragment.md | 104 +++ .../ytt/docs/v0.49.x/lang-ref-ytt-assert.md | 160 ++++ .../ytt/docs/v0.49.x/lang-ref-ytt-library.md | 400 +++++++++ .../ytt/docs/v0.49.x/lang-ref-ytt-overlay.md | 759 ++++++++++++++++++ .../ytt/docs/v0.49.x/lang-ref-ytt-schema.md | 701 ++++++++++++++++ .../ytt/docs/v0.49.x/lang-ref-ytt-struct.md | 203 +++++ .../ytt/docs/v0.49.x/lang-ref-ytt-template.md | 71 ++ .../ytt/docs/v0.49.x/lang-ref-ytt-version.md | 25 + site/content/ytt/docs/v0.49.x/lang-ref-ytt.md | 272 +++++++ site/content/ytt/docs/v0.49.x/lang.md | 34 + site/content/ytt/docs/v0.49.x/outputs.md | 17 + .../v0.49.x/schema-validations-cheat-sheet.md | 214 +++++ site/content/ytt/docs/v0.49.x/security.md | 34 + site/content/ytt/docs/v0.49.x/strict.md | 48 ++ site/content/ytt/docs/v0.49.x/yaml-primer.md | 228 ++++++ .../ytt/docs/v0.49.x/ytt-data-values.md | 446 ++++++++++ site/content/ytt/docs/v0.49.x/ytt-overlays.md | 77 ++ .../v0.49.x/ytt-playground-screenshot.png | Bin 0 -> 363691 bytes .../ytt/docs/v0.49.x/ytt-text-templating.md | 54 ++ site/content/ytt/docs/v0.49.x/ytt-vs-x.md | 61 ++ site/data/imgpkg/docs/imgpkg-v0-42-x-toc.yml | 43 + site/data/imgpkg/docs/toc-mapping.yml | 1 + site/data/kapp/docs/kapp-v0-61-x-toc.yml | 57 ++ site/data/kapp/docs/toc-mapping.yml | 1 + site/data/kbld/docs/kbld-v0-43-x-toc.yml | 33 + site/data/kbld/docs/toc-mapping.yml | 1 + site/data/ytt/docs/toc-mapping.yml | 1 + site/data/ytt/docs/ytt-v0-49-x-toc.yml | 115 +++ 186 files changed, 13050 insertions(+), 95 deletions(-) create mode 100644 site/content/imgpkg/docs/v0.42.x/_index.md create mode 100644 site/content/imgpkg/docs/v0.42.x/air-gapped-workflow.md create mode 100644 site/content/imgpkg/docs/v0.42.x/auth.md create mode 100644 site/content/imgpkg/docs/v0.42.x/automation-workflow.md create mode 100644 site/content/imgpkg/docs/v0.42.x/basic-workflow.md create mode 100644 site/content/imgpkg/docs/v0.42.x/ca-certs-windows.md create mode 100644 site/content/imgpkg/docs/v0.42.x/commands.md create mode 100644 site/content/imgpkg/docs/v0.42.x/debugging.md create mode 100644 site/content/imgpkg/docs/v0.42.x/faq-generic.md create mode 100644 site/content/imgpkg/docs/v0.42.x/install.md create mode 100644 site/content/imgpkg/docs/v0.42.x/proxy.md create mode 100644 site/content/imgpkg/docs/v0.42.x/resources.md create mode 100644 site/content/imgpkg/docs/v0.42.x/security.md create mode 100644 site/content/imgpkg/docs/v0.42.x/working-directly-with-images.md create mode 100644 site/content/kapp/docs/v0.61.x/_index.md create mode 100644 site/content/kapp/docs/v0.61.x/apply-ordering.md create mode 100644 site/content/kapp/docs/v0.61.x/apply-waiting.md create mode 100644 site/content/kapp/docs/v0.61.x/apply.md create mode 100644 site/content/kapp/docs/v0.61.x/apps.md create mode 100644 site/content/kapp/docs/v0.61.x/cheatsheet.md create mode 100644 site/content/kapp/docs/v0.61.x/command-reference.md create mode 100644 site/content/kapp/docs/v0.61.x/config.md create mode 100644 site/content/kapp/docs/v0.61.x/configmap-migration.md create mode 100644 site/content/kapp/docs/v0.61.x/dangerous-flags.md create mode 100644 site/content/kapp/docs/v0.61.x/diff.md create mode 100644 site/content/kapp/docs/v0.61.x/faq.md create mode 100644 site/content/kapp/docs/v0.61.x/gitops.md create mode 100644 site/content/kapp/docs/v0.61.x/hpa-deployment-rebase.md create mode 100644 site/content/kapp/docs/v0.61.x/install.md create mode 100644 site/content/kapp/docs/v0.61.x/integrating-with-other-tools.md create mode 100644 site/content/kapp/docs/v0.61.x/merge-method.md create mode 100644 site/content/kapp/docs/v0.61.x/preflight.md create mode 100644 site/content/kapp/docs/v0.61.x/rbac.md create mode 100644 site/content/kapp/docs/v0.61.x/rebase-pvc.md create mode 100644 site/content/kapp/docs/v0.61.x/security.md create mode 100644 site/content/kapp/docs/v0.61.x/state-namespace.md create mode 100644 site/content/kbld/docs/v0.43.x/_index.md create mode 100644 site/content/kbld/docs/v0.43.x/auth.md create mode 100644 site/content/kbld/docs/v0.43.x/building.md create mode 100644 site/content/kbld/docs/v0.43.x/cnab-image-relocation.md create mode 100644 site/content/kbld/docs/v0.43.x/config.md create mode 100644 site/content/kbld/docs/v0.43.x/install.md create mode 100644 site/content/kbld/docs/v0.43.x/packaging.md create mode 100644 site/content/kbld/docs/v0.43.x/resolving.md create mode 100644 site/content/kbld/docs/v0.43.x/security.md create mode 100644 site/content/ytt/docs/v0.49.x/_index.md create mode 100644 site/content/ytt/docs/v0.49.x/data-values-schema-migration-guide.md create mode 100644 site/content/ytt/docs/v0.49.x/data-values-vs-overlays.md create mode 100644 site/content/ytt/docs/v0.49.x/faq.md create mode 100644 site/content/ytt/docs/v0.49.x/file-marks.md create mode 100644 site/content/ytt/docs/v0.49.x/how-it-works.md create mode 100644 site/content/ytt/docs/v0.49.x/how-to-export-schema.md create mode 100644 site/content/ytt/docs/v0.49.x/how-to-modularize.md create mode 100644 site/content/ytt/docs/v0.49.x/how-to-use-data-values.md create mode 100644 site/content/ytt/docs/v0.49.x/how-to-write-schema.md create mode 100644 site/content/ytt/docs/v0.49.x/how-to-write-validations.md create mode 100644 site/content/ytt/docs/v0.49.x/index-playground-examples.md create mode 100644 site/content/ytt/docs/v0.49.x/injecting-secrets.md create mode 100644 site/content/ytt/docs/v0.49.x/inputs.md create mode 100644 site/content/ytt/docs/v0.49.x/install.md create mode 100644 site/content/ytt/docs/v0.49.x/known-limitations.md create mode 100644 site/content/ytt/docs/v0.49.x/lang-ref-annotation.md create mode 100644 site/content/ytt/docs/v0.49.x/lang-ref-def.md create mode 100644 site/content/ytt/docs/v0.49.x/lang-ref-dict.md create mode 100644 site/content/ytt/docs/v0.49.x/lang-ref-for.md create mode 100644 site/content/ytt/docs/v0.49.x/lang-ref-if.md create mode 100644 site/content/ytt/docs/v0.49.x/lang-ref-list.md create mode 100644 site/content/ytt/docs/v0.49.x/lang-ref-load.md create mode 100644 site/content/ytt/docs/v0.49.x/lang-ref-string.md create mode 100644 site/content/ytt/docs/v0.49.x/lang-ref-structs.md create mode 100644 site/content/ytt/docs/v0.49.x/lang-ref-yaml-fragment.md create mode 100644 site/content/ytt/docs/v0.49.x/lang-ref-ytt-assert.md create mode 100644 site/content/ytt/docs/v0.49.x/lang-ref-ytt-library.md create mode 100644 site/content/ytt/docs/v0.49.x/lang-ref-ytt-overlay.md create mode 100644 site/content/ytt/docs/v0.49.x/lang-ref-ytt-schema.md create mode 100644 site/content/ytt/docs/v0.49.x/lang-ref-ytt-struct.md create mode 100644 site/content/ytt/docs/v0.49.x/lang-ref-ytt-template.md create mode 100644 site/content/ytt/docs/v0.49.x/lang-ref-ytt-version.md create mode 100644 site/content/ytt/docs/v0.49.x/lang-ref-ytt.md create mode 100644 site/content/ytt/docs/v0.49.x/lang.md create mode 100644 site/content/ytt/docs/v0.49.x/outputs.md create mode 100644 site/content/ytt/docs/v0.49.x/schema-validations-cheat-sheet.md create mode 100644 site/content/ytt/docs/v0.49.x/security.md create mode 100644 site/content/ytt/docs/v0.49.x/strict.md create mode 100644 site/content/ytt/docs/v0.49.x/yaml-primer.md create mode 100644 site/content/ytt/docs/v0.49.x/ytt-data-values.md create mode 100644 site/content/ytt/docs/v0.49.x/ytt-overlays.md create mode 100644 site/content/ytt/docs/v0.49.x/ytt-playground-screenshot.png create mode 100644 site/content/ytt/docs/v0.49.x/ytt-text-templating.md create mode 100644 site/content/ytt/docs/v0.49.x/ytt-vs-x.md create mode 100644 site/data/imgpkg/docs/imgpkg-v0-42-x-toc.yml create mode 100644 site/data/kapp/docs/kapp-v0-61-x-toc.yml create mode 100644 site/data/kbld/docs/kbld-v0-43-x-toc.yml create mode 100644 site/data/ytt/docs/ytt-v0-49-x-toc.yml diff --git a/site/config.yaml b/site/config.yaml index 7dbcd38f4..18d04e40b 100644 --- a/site/config.yaml +++ b/site/config.yaml @@ -23,15 +23,16 @@ params: ytt: name: ytt root_link: /ytt/ - latest_docs_link: /ytt/docs/v0.48.0/ + latest_docs_link: /ytt/docs/v0.49.x/ github_url: https://github.com/carvel-dev/ytt search: true search_index_name: carvel-ytt search_api_key: a38560864c2e9128ae57d5734df438ff versioning: true - version_latest: v0.48.0 + version_latest: v0.49.x versions: - develop + - v0.49.x - v0.48.0 - v0.47.x - v0.46.x @@ -47,15 +48,16 @@ params: name: kbld short_name: kbld root_link: /kbld/ - latest_docs_link: /kbld/docs/v0.42.x/ + latest_docs_link: /kbld/docs/v0.43.x/ github_url: https://github.com/carvel-dev/kbld search: true search_index_name: carvel-kbld search_api_key: a38560864c2e9128ae57d5734df438ff versioning: true - version_latest: v0.42.x + version_latest: v0.43.x versions: - develop + - v0.43.x - v0.42.x - v0.41.x - v0.40.x @@ -71,15 +73,16 @@ params: name: kapp short_name: kapp root_link: /kapp/ - latest_docs_link: /kapp/docs/v0.60.x/ + latest_docs_link: /kapp/docs/v0.61.x/ github_url: https://github.com/carvel-dev/kapp search: true search_index_name: carvel-kapp search_api_key: a38560864c2e9128ae57d5734df438ff versioning: true - version_latest: v0.60.x + version_latest: v0.61.x versions: - develop + - v0.61.x - v0.60.x - v0.59.x - v0.58.x @@ -101,15 +104,16 @@ params: name: imgpkg short_name: imgpkg root_link: /imgpkg/ - latest_docs_link: /imgpkg/docs/v0.41.x/ + latest_docs_link: /imgpkg/docs/v0.42.x/ github_url: https://github.com/carvel-dev/imgpkg search: true search_index_name: carvel-imgpkg search_api_key: a38560864c2e9128ae57d5734df438ff versioning: true - version_latest: v0.41.x + version_latest: v0.42.x versions: - develop + - v0.42.x - v0.41.x - v0.40.x - v0.39.x diff --git a/site/content/imgpkg/docs/v0.41.x/_index.md b/site/content/imgpkg/docs/v0.41.x/_index.md index 72133cfbd..6090ec9d3 100644 --- a/site/content/imgpkg/docs/v0.41.x/_index.md +++ b/site/content/imgpkg/docs/v0.41.x/_index.md @@ -1,5 +1,5 @@ --- -aliases: [/imgpkg/docs/latest/] + title: "About imgpkg" toc: "false" cascade: diff --git a/site/content/imgpkg/docs/v0.41.x/air-gapped-workflow.md b/site/content/imgpkg/docs/v0.41.x/air-gapped-workflow.md index a3d589126..146fff0b7 100644 --- a/site/content/imgpkg/docs/v0.41.x/air-gapped-workflow.md +++ b/site/content/imgpkg/docs/v0.41.x/air-gapped-workflow.md @@ -1,5 +1,5 @@ --- -aliases: [/imgpkg/docs/latest/air-gapped-workflow] + title: Air-gapped Workflow --- diff --git a/site/content/imgpkg/docs/v0.41.x/auth.md b/site/content/imgpkg/docs/v0.41.x/auth.md index df015c0a0..f4a10756b 100644 --- a/site/content/imgpkg/docs/v0.41.x/auth.md +++ b/site/content/imgpkg/docs/v0.41.x/auth.md @@ -1,5 +1,5 @@ --- -aliases: [/imgpkg/docs/latest/auth] + title: Authentication --- diff --git a/site/content/imgpkg/docs/v0.41.x/automation-workflow.md b/site/content/imgpkg/docs/v0.41.x/automation-workflow.md index 0e131150c..1fb36d327 100644 --- a/site/content/imgpkg/docs/v0.41.x/automation-workflow.md +++ b/site/content/imgpkg/docs/v0.41.x/automation-workflow.md @@ -1,5 +1,5 @@ --- -aliases: [/imgpkg/docs/latest/automation-workflow] + title: Automation Workflow --- diff --git a/site/content/imgpkg/docs/v0.41.x/basic-workflow.md b/site/content/imgpkg/docs/v0.41.x/basic-workflow.md index 271cb07e3..dab6abf3a 100644 --- a/site/content/imgpkg/docs/v0.41.x/basic-workflow.md +++ b/site/content/imgpkg/docs/v0.41.x/basic-workflow.md @@ -1,5 +1,5 @@ --- -aliases: [/imgpkg/docs/latest/basic-workflow] + title: Basic Workflow --- diff --git a/site/content/imgpkg/docs/v0.41.x/ca-certs-windows.md b/site/content/imgpkg/docs/v0.41.x/ca-certs-windows.md index 2f0404248..950141a4e 100644 --- a/site/content/imgpkg/docs/v0.41.x/ca-certs-windows.md +++ b/site/content/imgpkg/docs/v0.41.x/ca-certs-windows.md @@ -1,5 +1,5 @@ --- -aliases: [/imgpkg/docs/latest/ca-certs-windows] + title: CA Certs on Windows --- diff --git a/site/content/imgpkg/docs/v0.41.x/commands.md b/site/content/imgpkg/docs/v0.41.x/commands.md index 36e39e270..de4129e8a 100644 --- a/site/content/imgpkg/docs/v0.41.x/commands.md +++ b/site/content/imgpkg/docs/v0.41.x/commands.md @@ -1,5 +1,5 @@ --- -aliases: [/imgpkg/docs/latest/commands] + title: Commands --- diff --git a/site/content/imgpkg/docs/v0.41.x/debugging.md b/site/content/imgpkg/docs/v0.41.x/debugging.md index 9000ae487..e669f95e8 100644 --- a/site/content/imgpkg/docs/v0.41.x/debugging.md +++ b/site/content/imgpkg/docs/v0.41.x/debugging.md @@ -1,5 +1,5 @@ --- -aliases: [/imgpkg/docs/latest/debugging] + title: Debugging --- diff --git a/site/content/imgpkg/docs/v0.41.x/faq-generic.md b/site/content/imgpkg/docs/v0.41.x/faq-generic.md index 879d4bd09..5ff292950 100644 --- a/site/content/imgpkg/docs/v0.41.x/faq-generic.md +++ b/site/content/imgpkg/docs/v0.41.x/faq-generic.md @@ -1,5 +1,5 @@ --- -aliases: [/imgpkg/docs/latest/faq-generic] + title: FAQ --- diff --git a/site/content/imgpkg/docs/v0.41.x/install.md b/site/content/imgpkg/docs/v0.41.x/install.md index d74978ec7..0ba30f7a4 100644 --- a/site/content/imgpkg/docs/v0.41.x/install.md +++ b/site/content/imgpkg/docs/v0.41.x/install.md @@ -1,5 +1,5 @@ --- -aliases: [/imgpkg/docs/latest/install] + title: Install --- diff --git a/site/content/imgpkg/docs/v0.41.x/proxy.md b/site/content/imgpkg/docs/v0.41.x/proxy.md index 0998e7dc2..cc509f507 100644 --- a/site/content/imgpkg/docs/v0.41.x/proxy.md +++ b/site/content/imgpkg/docs/v0.41.x/proxy.md @@ -1,5 +1,5 @@ --- -aliases: [/imgpkg/docs/latest/proxy] + title: Proxy --- diff --git a/site/content/imgpkg/docs/v0.41.x/resources.md b/site/content/imgpkg/docs/v0.41.x/resources.md index 6a0300834..7ae340dca 100644 --- a/site/content/imgpkg/docs/v0.41.x/resources.md +++ b/site/content/imgpkg/docs/v0.41.x/resources.md @@ -1,5 +1,5 @@ --- -aliases: [/imgpkg/docs/latest/resources] + title: Resources --- diff --git a/site/content/imgpkg/docs/v0.41.x/security.md b/site/content/imgpkg/docs/v0.41.x/security.md index ca69db28d..c70b75d5e 100644 --- a/site/content/imgpkg/docs/v0.41.x/security.md +++ b/site/content/imgpkg/docs/v0.41.x/security.md @@ -1,5 +1,5 @@ --- -aliases: [/imgpkg/docs/latest/security] + title: Security --- diff --git a/site/content/imgpkg/docs/v0.41.x/working-directly-with-images.md b/site/content/imgpkg/docs/v0.41.x/working-directly-with-images.md index cdac04918..21dc3ac34 100644 --- a/site/content/imgpkg/docs/v0.41.x/working-directly-with-images.md +++ b/site/content/imgpkg/docs/v0.41.x/working-directly-with-images.md @@ -1,5 +1,5 @@ --- -aliases: [/imgpkg/docs/latest/working-directly-with-images] + title: Working directly with images --- diff --git a/site/content/imgpkg/docs/v0.42.x/_index.md b/site/content/imgpkg/docs/v0.42.x/_index.md new file mode 100644 index 000000000..f631f443b --- /dev/null +++ b/site/content/imgpkg/docs/v0.42.x/_index.md @@ -0,0 +1,21 @@ +--- +aliases: [/imgpkg/docs/latest/] +title: "About imgpkg" +toc: "false" +cascade: + version: v0.42.x + toc: "true" + type: docs + layout: docs +--- + +`imgpkg` is a tool that allows users to store a set of arbitrary files as an OCI image. One of the driving use cases is to store Kubernetes configuration (plain YAML, ytt templates, Helm templates, etc.) in OCI registry as an image. + +`imgpkg`'s primary concept is a [bundle](resources.md#bundle), which is an OCI image that holds 0+ arbitrary files and 0+ references to dependent OCI images (which *may* also be [bundles](resources.md/#nested-bundle)). With this concept, `imgpkg` is able to copy bundles and their dependent images across registries (both online and offline). + +![Bundle diagram](/images/imgpkg/bundle-diagram.png) + +## Workflows + +- [Basic Workflow](basic-workflow.md) shows how to create, push, and pull bundles with a simple Kubernetes application +- [Air-gapped Workflow](air-gapped-workflow.md) shows how to copy bundles from one registry to another, to enable running Kubernetes applications without relying on external (public) registries diff --git a/site/content/imgpkg/docs/v0.42.x/air-gapped-workflow.md b/site/content/imgpkg/docs/v0.42.x/air-gapped-workflow.md new file mode 100644 index 000000000..a3d589126 --- /dev/null +++ b/site/content/imgpkg/docs/v0.42.x/air-gapped-workflow.md @@ -0,0 +1,154 @@ +--- +aliases: [/imgpkg/docs/latest/air-gapped-workflow] +title: Air-gapped Workflow +--- + +## Scenario + +You want to ensure Kubernetes application does not rely on images from external registries when deployed. + +This scenario _also_ applies when trying to ensure that all images are consolidated into a single registry, even if that registry is not air-gapped. + +## Prerequisites + +To complete this workflow you will need access to an OCI registry like Docker Hub, and optionally, +a Kubernetes cluster. (If you would like to use a local registry and Kubernetes cluster, try using [Kind](https://kind.sigs.k8s.io/docs/user/local-registry/)) + +If you would like to deploy the results of this scenario to your Kubernetes cluster, you will additionally need [`kbld`](/kbld) and kubectl. + +If any of your bundles contain [non-distributable layers](commands.md#non-distributable-or-foreign-layers) you will need to include +the `--include-non-distributable-layers` flag to each copy command in the examples provided. + +--- +## Step 1: Finding bundle in source registry + +If you have already pushed a bundle to the registry, continue to the next step. + +If you are trying to bundle your own or third-part software, you will need to create a bundle. Refer to basic workflow's ["Step 1: Creating the bundle" and "Step 2: Pushing the bundle to registry"](basic-workflow.md#step-1-creating-the-bundle). + +--- +## Step 2: Two methods of copying bundles + +You have two options how to transfer bundle from one registry to another: + +- Option 1: From a common location connected to both registries. This option is more efficient because only changed image layers will be transfered between registries. +- Option 2: With intermediate tarball. This option works best when registries have no common network access. + +### Option 1: From a location connected to both registries + +1. Get to a location that can access both registries + + This may be a server that has access to both internal and external networks. If there is no such location, you will have to use "Option 2" below. + +1. [Authenticate](auth.md) with both source, and destination registries + +1. Run following command to copy bundle from one registry to another: + + ```bash-plain + $ imgpkg copy -b index.docker.io/user1/simple-app-bundle:v1.0.0 --to-repo registry.corp.com/apps/simple-app-bundle + + copy | exporting 2 images... + copy | will export index.docker.io/user1/simple-app-bundle@sha256:4c8b96d4fffdfae29258d94a22ae4ad1fe36139d47288b8960d9958d1e63a9d0 + copy | will export index.docker.io/user1/simple-app-bundle@sha256:70225df0a05137ac385c95eb69f89ded3e7ef3a0c34db43d7274fd9eba3705bb + copy | exported 2 images + copy | importing 2 images... + copy | importing index.docker.io/user1/simple-app-bundle@sha256:70225df0a05137ac385c95eb69f89ded3e7ef3a0c34db43d7274fd9eba3705bb + -> registry.corp.com/apps/simple-app-bundle@sha256:70225df0a05137ac385c95eb69f89ded3e7ef3a0c34db43d7274fd9eba3705bb... + copy | importing index.docker.io/user1/simple-app-bundle@sha256:4c8b96d4fffdfae29258d94a22ae4ad1fe36139d47288b8960d9958d1e63a9d0 + -> registry.corp.com/apps/simple-app-bundle@sha256:4c8b96d4fffdfae29258d94a22ae4ad1fe36139d47288b8960d9958d1e63a9d0... + copy | imported 2 images + Succeeded + ``` + + The bundle, and all images referenced in the bundle, are copied to the destination registry. + + Flags used in the command: + * `-b` (`--bundle`) indicates the bundle location in the source registry + * `--to-repo` indicates the registry where the bundle and associated images should be copied to + +### Option 2: With intermediate tarball + +1. Get to a location that can access source registry + +1. [Authenticate with the source registry](auth.md) + +1. Save the bundle to a tarball + + ```bash-plain + $ imgpkg copy -b index.docker.io/user1/simple-app-bundle:v1.0.0 --to-tar /tmp/my-image.tar + + copy | exporting 2 images... + copy | will export index.docker.io/user1/simple-app-bundle@sha256:4c8b96d4fffdfae29258d94a22ae4ad1fe36139d47288b8960d9958d1e63a9d0 + copy | will export index.docker.io/user1/simple-app-bundle@sha256:70225df0a05137ac385c95eb69f89ded3e7ef3a0c34db43d7274fd9eba3705bb + copy | exported 2 images + copy | writing layers... + copy | done: file 'manifest.json' (13.71µs) + copy | done: file 'sha256-233f1d0dbdc8cf675af965df8639b0dfd4ef7542dfc9fcfd03bfc45c570b0e4d.tar.gz' (47.616µs) + copy | done: file 'sha256-8ece9ac45f2b7228b2ed95e9f407b4f0dc2ac74f93c62ff1156f24c53042ba54.tar.gz' (43.204905ms) + Succeeded + ``` + + Flags used in the command: + * `-b` (`--bundle`) indicates the bundle location in the source registry + * `--to-tar` indicates the local location to write a tar file containing the bundle assets + +1. Transfer the local tarball `/tmp/my-image.tar` to a location with access to the destination registry + +1. [Authenticate with the destination registry](auth.md) + +1. Import the bundle from your tarball to the destination registry: + + ```bash-plain + $ imgpkg copy --tar /tmp/my-image.tar --to-repo registry.corp.com/apps/simple-app-bundle + + copy | importing 2 images... + copy | importing index.docker.io/user1/simple-app-bundle@sha256:70225df0a05137ac385c95eb69f89ded3e7ef3a0c34db43d7274fd9eba3705bb -> registry.corp.com/apps/simple-app-bundle@sha256:70225df0a05137ac385c95eb69f89ded3e7ef3a0c34db43d7274fd9eba3705bb... + copy | importing index.docker.io/user1/simple-app-bundle@sha256:4c8b96d4fffdfae29258d94a22ae4ad1fe36139d47288b8960d9958d1e63a9d0 -> registry.corp.com/apps/simple-app-bundle@sha256:4c8b96d4fffdfae29258d94a22ae4ad1fe36139d47288b8960d9958d1e63a9d0... + copy | imported 2 images + Succeeded + ``` + + The bundle, and all images referenced in the bundle, are copied to the destination registry. + + Flags used in the command: + * `--tar` indicates the path to a tar file containing the assets to be copied to a registry + * `--to-repo` indicates destination bundle location in the registry + +--- +## Step 3: Pulling bundle from destination registry + +1. [Authenticate with the destination registry](auth.md) + +1. Pull the bundle from the destination registry: + + ```bash-plain + $ imgpkg pull -b registry.corp.com/apps/simple-app-bundle:v1.0.0 -o /tmp/bundle + + Pulling image 'registry.corp.com/apps/simple-app-bundle@sha256:70225df0a05137ac385c95eb69f89ded3e7ef3a0c34db43d7274fd9eba3705bb' + Extracting layer 'sha256:233f1d0dbdc8cf675af965df8639b0dfd4ef7542dfc9fcfd03bfc45c570b0e4d' (1/1) + Locating image lock file images... + All images found in bundle repo; updating lock file: /tmp/bundle/.imgpkg/images.yml + + Succeeded + ``` + + Flags used in the command: + * `-b` (`--bundle`) indicates to pull a particular bundle from a registry + * `-o` (`--output`) indicates the local folder where the bundle will be unpacked + + Note that the `.imgpkg/images.yml` file was updated with the destination registry locations of the images. This happened because, in the prior step, the images referenced by the bundle were copied into the destination registry. + + ```bash-plain + $ cat /tmp/bundle/.imgpkg/images.yml + apiVersion: imgpkg.carvel.dev/v1alpha1 + kind: ImagesLock + images: + - image: registry.corp.com/apps/simple-app-bundle@sha256:4c8b96d4fffdfae29258d94a22ae4ad1fe36139d47288b8960d9958d1e63a9d0 + annotations: + kbld.carvel.dev/id: docker.io/dkalinin/k8s-simple-app + ``` + +--- +## Step 4: Use pulled bundle contents + +Regardless which location the bundle is downloaded from, source registry or destination registry, use of the pulled bundle contents remains the same. Continue with ["Step 4: Use pulled bundle contents"](basic-workflow.md#step-4-use-pulled-bundle-contents) in the basic workflow. diff --git a/site/content/imgpkg/docs/v0.42.x/auth.md b/site/content/imgpkg/docs/v0.42.x/auth.md new file mode 100644 index 000000000..df015c0a0 --- /dev/null +++ b/site/content/imgpkg/docs/v0.42.x/auth.md @@ -0,0 +1,166 @@ +--- +aliases: [/imgpkg/docs/latest/auth] +title: Authentication +--- + +# Ordering + +imgpkg has multiple ways to provide authentication details to registries. + +The order at which imgpkg chooses which authentication details to use is the following: + +1. [Via Environment Variables](#via-environment-variables) +1. [Via IaaS](#via-iaas) +1. [Via Command Flags](#via-command-flags) +1. [Via Docker Config](#via-docker-config) + +## Via Environment Variables + +As of v0.7.0+, `imgpkg` can also use following environment variables: + +- `IMGPKG_REGISTRY_HOSTNAME` to specify registry hostname (e.g. gcr.io, docker.io, https://gcr.io, docker.io/v2/) + - As of v0.18.0+ `IMGPKG_REGISTRY_HOSTNAME` also supports providing glob wildcards. for e.g. `*.*.docker.io` will match `bar.foo.docker.io`. + - Note: if there is overlap between 2 HOSTNAMES, one using globbing and the other not, the HOSTNAME not using globbing will be applied. e.g. `IMGPKG_REGISTRY_HOSTNAME_0=*.docker.io` vs `IMGPKG_REGISTRY_HOSTNAME_1=foo.docker.io` for the image `foo.docker.io/image` will result in auth details from `IMGPKG_REGISTRY_HOSTNAME_1` being used. + - As of v0.18.0+ `IMGPKG_REGISTRY_HOSTNAME` also supports providing the fully qualified repository. for e.g. `gcr.io/repo/image`. +- `IMGPKG_REGISTRY_USERNAME` to specify registry username +- `IMGPKG_REGISTRY_PASSWORD` to specify registry password +- `IMGPKG_REGISTRY_IDENTITY_TOKEN` to authenticate the user and get an access token for the registry via an [oauth2 refresh token grant type](https://docs.docker.com/registry/spec/auth/oauth/). +- `IMGPKG_REGISTRY_REGISTRY_TOKEN` to specify the access token to be used in the Authorization Header as a [Bearer Token](https://docs.docker.com/registry/spec/auth/token/#using-the-bearer-token). + +Since you may need to provide multiple registry credentials, the environment variables above may be specified multiple times with a suffix of 1+ alphanumeric characters, + +e.g. If you had 2 registries you wish to provide authentication credentials for, you would require 2 sets of env variables. + +For Registry #1: + +``` +IMGPKG_REGISTRY_HOSTNAME_0=hostname.for.registry.1 +IMGPKG_REGISTRY_USERNAME_0=username +IMGPKG_REGISTRY_PASSWORD_0=password +``` + +For Registry #2: + +``` +IMGPKG_REGISTRY_HOSTNAME_1=hostname.for.registry.2 +IMGPKG_REGISTRY_IDENTITY_TOKEN_1=token +``` + +When imgpkg interacts with `hostname.for.registry.1`, it will use the env variables with the suffix `_0`. And when interacting with `hostname.for.registry.2`, it will use the env variables with the suffix `_1` + + +Note: Credentials provided via an env variable for a specific registry will take precedence over Command Flags. + +## Via IaaS + +By default, `imgpkg` will **NOT** attempt to authenticate itself via the underlying IaaS: + +To activate this behavior you can set the environment variable `IMGPKG_ACTIVE_KEYCHAINS` with the keychains to the IaaS that you are currently using. + +*Note:* To mimic the old behavior of `imgpkg` set the environment variable as follows `export IMGPKG_ACTIVE_KEYCHAINS=gke,aks,ecr` + +Below is a list of IaaS providers that `imgpkg` can authenticate with: + +- [GCP](https://cloud.google.com/compute/docs/metadata/overview) + + To activate it use `export IMGPKG_ACTIVE_KEYCHAINS=gke` + +- [AWS](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html) + + To activate it use `export IMGPKG_ACTIVE_KEYCHAINS=ecr` + For more information [check the helper](https://github.com/awslabs/amazon-ecr-credential-helper#configuration) + +- [Azure](https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/how-managed-identities-work-vm) + + To activate it use `export IMGPKG_ACTIVE_KEYCHAINS=aks` + For more information check [this library](https://github.com/chrismellard/docker-credential-acr-env) + +- Github + + To activate use `export IMGPKG_ACTIVE_KEYCHAINS=github` + Requires the environment variable `GITHUB_TOKEN` to be set to connect to ghcr.io + +**Deprecation:** The environment variable `IMGPKG_ENABLE_IAAS_AUTH` can be used only to activate all the keychains. +This behavior will be removed in a future version. + + +## Via Command Flags + +You can explicitly specify credentials via command flags or associated environment variables. See `imgpkg push -h` for further details. + +- `--registry-username` (or `$IMGPKG_USERNAME`) +- `--registry-password` (or `$IMGPKG_PASSWORD`) +- `--registry-token` (or `$IMGPKG_TOKEN`): to specify the access token to be used in the Authorization Header as a [Bearer Token](https://docs.docker.com/registry/spec/auth/token/#using-the-bearer-token). +- `--registry-anon` (or `$IMGPKG_ANON=true`): used for anonymous access (commonly for pulling) + +## Via Docker config + +Even though `imgpkg` commands use registry APIs directly, by default it uses credentials stored in `~/.docker/config.json` which are typically generated via a `docker login` command. + +Example generated `~/.docker/config.json`: + +```json +{ + "auths": { + "https://index.docker.io/v1/": { + "auth": "dXNlcjpwYXNzd29yZA==" + }, + }, + "HttpHeaders": { + "User-Agent": "Docker-Client/18.09.6 (darwin)" + } +} +``` + +where `dXNlcjpwYXNzd29yZA==` is `base64("username:password")`. + +## gcr.io + +- Create a service account with "Storage Admin" permissions for push access + - See [Permissions and Roles](https://cloud.google.com/container-registry/docs/access-control#permissions_and_roles) +- Download a JSON service account key and place it somewhere on filesystem (e.g. `/tmp/key`) + - See [Advanced authentication](https://cloud.google.com/container-registry/docs/advanced-authentication#json_key_file) +- Run `cat /tmp/key | docker login -u _json_key --password-stdin https://gcr.io` to authenticate + +## AWS ECR + +- Create an ECR repository +- Create an IAM user with an ECR policy that allows read/write + - See [Amazon ECR Policies](https://docs.aws.amazon.com/AmazonECR/latest/userguide/ecr_managed_policies.html) +- Run `aws configure` and specify access key ID, secret access key and region + - To install on Ubuntu, run `apt-get install pip3` and `pip3 install awscli` + - See [Installing the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html) +- Run `eval $(aws ecr get-login --no-include-email)` to authenticate + - See [get-login command](https://docs.aws.amazon.com/cli/latest/reference/ecr/get-login.html) + +Example ECR policy from [Amazon ECR](https://docs.aws.amazon.com/AmazonECR/latest/userguide/ecr_managed_policies.html): + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "ecr:GetAuthorizationToken", + "ecr:BatchCheckLayerAvailability", + "ecr:GetDownloadUrlForLayer", + "ecr:GetRepositoryPolicy", + "ecr:DescribeRepositories", + "ecr:ListImages", + "ecr:DescribeImages", + "ecr:BatchGetImage", + "ecr:InitiateLayerUpload", + "ecr:UploadLayerPart", + "ecr:CompleteLayerUpload", + "ecr:PutImage" + ], + "Resource": "*" + } + ] +} +``` + +## Harbor + +You may have to provide `--registry-ca-cert-path` flag with a path to a CA certificate file for Harbor Registry API. diff --git a/site/content/imgpkg/docs/v0.42.x/automation-workflow.md b/site/content/imgpkg/docs/v0.42.x/automation-workflow.md new file mode 100644 index 000000000..0e131150c --- /dev/null +++ b/site/content/imgpkg/docs/v0.42.x/automation-workflow.md @@ -0,0 +1,141 @@ +--- +aliases: [/imgpkg/docs/latest/automation-workflow] +title: Automation Workflow +--- + +## Scenario + +When using an automated CI tool you might want to promote a given Bundle between steps of the pipeline + +## Prerequisites + +To complete this workflow you will need access to an OCI registry like Docker Hub. + +### Step 1: Creating the Bundle + +1. Prepare bundle contents + + The [examples/basic-step-1/](https://github.com/carvel-dev/imgpkg/tree/develop/examples/basic-step-1) + directory has a `config.yml` file, which contains a very simple Kubernetes application. Your application may have as + many configuration files as necessary in various formats such as plain YAML, ytt templates, Helm templates, etc. + + In our example `config.yml` includes an image reference to `docker.io/dkalinin/k8s-simple-app`. This reference does + not point to an exact image (via digest) meaning that it may change over time. To ensure we get precisely the bits we + expect, we will lock it down to an exact image next. + +1. Add `.imgpkg/` directory + + [examples/basic-step-2](https://github.com/carvel-dev/imgpkg/tree/develop/examples/basic-step-2) shows what + a `.imgpkg/` directory may look like. It contains: + + - **optional** [bundle.yml](resources.md#bundle-metadata): a file which records informational metadata + - **required** [images.yml](resources.md#imageslock): a file which records image references used by the + configuration + + ```bash-plain + examples/basic-step-2 + ├── .imgpkg + │   ├── bundle.yml + │   └── images.yml + └── config.yml + ``` + + Note that `.imgpkg/images.yml` contains a list of images, each with fully resolved digest reference ( + e.g `index.docker.io/dkalinin/k8s-simple-app@sha256:4c8b96d4...`) and a some additional metadata ( + e.g. `annotations` section). See [ImagesLock configuration](resources.md#imageslock-configuration) for details. + + ```yaml + apiVersion: imgpkg.carvel.dev/v1alpha1 + kind: ImagesLock + images: + - image: index.docker.io/dkalinin/k8s-simple-app@sha256:4c8b96d4fffdfae29258d94a22ae4ad1fe36139d47288b8960d9958d1e63a9d0 + annotations: + kbld.carvel.dev/id: docker.io/dkalinin/k8s-simple-app + ``` + +--- + +### Step 2: Creating the Bundle + +1. [Authenticate with a registry](auth.md) where we will push our bundle + +2. Push the bundle to the registry + + You can push the bundle with our specified contents to an OCI registry using the following command: + + ```bash-plain + $ imgpkg push -b index.docker.io/user1/simple-app-bundle:v1.0.0 -f examples/basic-step-2 --lock-output /tmp/bundle-lock.yml + + dir: . + dir: .imgpkg + file: .imgpkg/bundle.yml + file: .imgpkg/images.yml + file: config.yml + Pushed 'index.docker.io/user1/simple-app-bundle@sha256:5c2dafe3c70c13990190d643c91e9f67b8129b179257674888178868474f6511' + + Succeeded + ``` + + Flags used in the command: + - `-b` (`--bundle`) refers to a location for a bundle within an OCI registry + - `-f` (`--file`) indicates directory contents to include + - `--lock-output` indicates the destination of the [BundleLock](resources.md#bundlelock-configuration) file + +--- + +## Step 3: Promoting the BundleLock file + +Since in the previous step we generated a BundleLock we can promote this file and in the next steps of the pipeline we +can reference it. + +Examples of usage: + +1. Promote the Bundle to a different registry + + ```bash-plain + $ imgpkg copy --lock /tmp/bundle-lock.yml --to-repo production.registry.io/simple-app-bundle + copy | exporting 2 images... + copy | will export index.docker.io/dkalinin/k8s-simple-app@sha256:4c8b96d4fffdfae29258d94a22ae4ad1fe36139d47288b8960d9958d1e63a9d0 + copy | will export production.registry.io/simple-app-bundle@sha256:5c2dafe3c70c13990190d643c91e9f67b8129b179257674888178868474f6511 + copy | exported 2 images + copy | importing 2 images... + + 3.56 MiB / 3.57 MiB [========================================================================================================================================================================] 99.68% 8.80 MiB/s 0s + + copy | done uploading images + + Succeeded + ``` + + Flags used in the command: + - `--lock` refers to a location for a BundleLock file + - `--to-repo` indicates the destination Repository where the Bundle is copied to + +2. Download the Bundle contents to disk + + ```bash-plain + $ imgpkg pull --lock /tmp/bundle-lock.yml -o /tmp/simple-app-bundle + + Pulling image 'index.docker.io/user1/simple-app-bundle@sha256:ec3f870e958e404476b9ec67f28c598fa8f00f819b8ae05ee80d51bac9f35f5d' + Extracting layer 'sha256:7906b9650be657359ead106e354f2728e16c8f317e1d87f72b05b5c5ec3d89cc' (1/1) + + Locating image lock file images... + The bundle repo (index.docker.io/user1/simple-app-bundle@sha256:5c2dafe3c70c13990190d643c91e9f67b8129b179257674888178868474f6511) is hosting every image specified in the bundle's Images Lock file (.imgpkg/images.yml) + + Succeeded + ``` + + Flags used in the command: + - `--lock`e`) refers to a location for a BundleLock file + - `-o` (`--output`) indicates the destination directory on your local machine where the bundle contents will be + placed + + Bundle contents will be extracted into `/tmp/simple-app-bundle` directory: + + ```bash-plain + /tmp/simple-app-bundle + ├── .imgpkg + │   ├── bundle.yml + │   └── images.yml + └── config.yml + ``` diff --git a/site/content/imgpkg/docs/v0.42.x/basic-workflow.md b/site/content/imgpkg/docs/v0.42.x/basic-workflow.md new file mode 100644 index 000000000..271cb07e3 --- /dev/null +++ b/site/content/imgpkg/docs/v0.42.x/basic-workflow.md @@ -0,0 +1,150 @@ +--- +aliases: [/imgpkg/docs/latest/basic-workflow] +title: Basic Workflow +--- + +## Scenario + +You want to create an immutable artifact containing Kubernetes configuration and images used in that configuration. Later, you want to grab that artifact and deploy it to Kubernetes. + +## Prerequisites + +To complete this workflow you will need access to an OCI registry like Docker Hub, and optionally, +a Kubernetes cluster. (If you would like to use a local registry and Kubernetes cluster, try using [Kind](https://kind.sigs.k8s.io/docs/user/local-registry/)) + +If you would like to deploy the results of this scenario to your Kubernetes cluster, you will additionally need [`kbld`](/kbld) and kubectl. + +## Step 1: Creating the bundle + +1. Prepare bundle contents + + The [examples/basic-step-1/](https://github.com/carvel-dev/imgpkg/tree/develop/examples/basic-step-1) directory has a `config.yml` file, which contains a very simple Kubernetes application. Your application may have as many configuration files as necessary in various formats such as plain YAML, ytt templates, Helm templates, etc. + + In our example `config.yml` includes an image reference to `docker.io/dkalinin/k8s-simple-app`. This reference does not point to an exact image (via digest) meaning that it may change over time. To ensure we get precisely the bits we expect, we will lock it down to an exact image next. + +1. Add `.imgpkg/` directory + + [examples/basic-step-2](https://github.com/carvel-dev/imgpkg/tree/develop/examples/basic-step-2) shows what a `.imgpkg/` directory may look like. It contains: + + - **optional** [bundle.yml](resources.md#bundle-metadata): a file which records informational metadata + - **required** [images.yml](resources.md#imageslock): a file which records image references used by the configuration + + ```bash-plain + examples/basic-step-2 + ├── .imgpkg + │   ├── bundle.yml + │   └── images.yml + └── config.yml + ``` + + Note that `.imgpkg/images.yml` contains a list of images, each with fully resolved digest reference (e.g `index.docker.io/dkalinin/k8s-simple-app@sha256:4c8b96d4...`) and a little bit of additional metadata (e.g. `annotations` section). See [ImagesLock configuration](resources.md#imageslock-configuration) for details. + + ```yaml + apiVersion: imgpkg.carvel.dev/v1alpha1 + kind: ImagesLock + images: + - image: index.docker.io/dkalinin/k8s-simple-app@sha256:4c8b96d4fffdfae29258d94a22ae4ad1fe36139d47288b8960d9958d1e63a9d0 + annotations: + kbld.carvel.dev/id: docker.io/dkalinin/k8s-simple-app + ``` + + This allows us to record the exact image that will be used by our Kubernetes configuration. We expect that `.imgpkg/images.yml` would be created either manually, or in an automated way. Our recommendation is to use [kbld](/kbld) to generate `.imgpkg/images.yml`: + + ```bash-plain + $ cd examples/basic-bundle/ + + $ kbld -f config.yml --imgpkg-lock-output .imgpkg/images.yml + ``` + +--- +## Step 2: Pushing the bundle to a registry + +1. [Authenticate with a registry](auth.md) where we will push our bundle + +1. Push the bundle to the registry + + You can push the bundle with our specified contents to an OCI registry using the following command: + + ```bash-plain + $ imgpkg push -b index.docker.io/user1/simple-app-bundle:v1.0.0 -f examples/basic-step-2 + + dir: . + dir: .imgpkg + file: .imgpkg/bundle.yml + file: .imgpkg/images.yml + file: config.yml + Pushed 'index.docker.io/user1/simple-app-bundle@sha256:ec3f870e958e404476b9ec67f28c598fa8f00f819b8ae05ee80d51bac9f35f5d' + + Succeeded + ``` + + Flags used in the command: + * `-b` (`--bundle`) refers to a location for a bundle within an OCI registry + * `-f` (`--file`) indicates directory contents to include + +1. The pushed bundle is now available at `index.docker.io/user1/simple-app-bundle:v1.0.0` + +--- +## Step 3: Pulling the bundle from registry + +Now that we have pushed a bundle to a registry, other users can pull it. + +1. [Authenticate with the registry](auth.md) from which we'll pull our bundle + +1. Download the bundle by running the following command: + + ```bash-plain + $ imgpkg pull -b index.docker.io/user1/simple-app-bundle:v1.0.0 -o /tmp/simple-app-bundle + + Pulling image 'index.docker.io/user1/simple-app-bundle@sha256:ec3f870e958e404476b9ec67f28c598fa8f00f819b8ae05ee80d51bac9f35f5d' + Extracting layer 'sha256:7906b9650be657359ead106e354f2728e16c8f317e1d87f72b05b5c5ec3d89cc' (1/1) + Locating image lock file images... + One or more images not found in bundle repo; skipping lock file update + + Succeeded + ``` + + Flags used in the command: + * `-b` (`--bundle`) refers to a location for a bundle within an OCI registry + * `-o` (`--output`) indicates the destination directory on your local machine where the bundle contents will be placed + + Bundle contents will be extracted into `/tmp/simple-app-bundle` directory: + + ```bash-plain + /tmp/simple-app-bundle + ├── .imgpkg + │   ├── bundle.yml + │   └── images.yml + └── config.yml + ``` + + __Note:__ The message `One or more images not found in bundle repo; skipping lock file update` is expected, and indicates that `/tmp/simple-app-bundle/.imgpkg/images.yml` (ImagesLock configuration) was not modified. + + If imgpkg had been able to find all images that were referenced in the [ImagesLock configuration](resources.md#imageslock-configuration) in the registry where bundle is located, then it would update `.imgpkg/images.yml` file to point to the registry-local locations. + + See what happens to the lock file if you run the same pull command after [copying](air-gapped-workflow.md#option-1-from-a-location-connected-to-both-registries) the bundle to another registry! + +--- +## Step 4: Use pulled bundle contents + +1. Now that we have have pulled bundle contents to a local directory, we can deploy Kubernetes configuration: + + Before we apply Kubernetes configuration, let's use [kbld](/kbld) to ensure that Kubernetes configuration uses exact image reference from `.imgpkg/images.yml`. (You can of course use other tools to take advantage of data stored in `.imgpkg/images.yml`). + + ```bash-plain + $ cd /tmp/simple-app-bundle/ + + $ kbld -f ./config.yml -f .imgpkg/images.yml | kubectl apply -f- + + resolve | final: docker.io/dkalinin/k8s-simple-app@sha256:4c8b96d4fffdfae29258d94a22ae4ad1fe36139d47288b8960d9958d1e63a9d0 -> index.docker.io/dkalinin/k8s-simple-app@sha256:4c8b96d4fffdfae29258d94a22ae4ad1fe36139d47288b8960d9958d1e63a9d0 + resolve | final: index.docker.io/dkalinin/k8s-simple-app@sha256:4c8b96d4fffdfae29258d94a22ae4ad1fe36139d47288b8960d9958d1e63a9d0 -> index.docker.io/dkalinin/k8s-simple-app@sha256:4c8b96d4fffdfae29258d94a22ae4ad1fe36139d47288b8960d9958d1e63a9d0 + + service/simple-app configured + deployment/simple-app configured + ``` + + kbld found `docker.io/dkalinin/k8s-simple-app` in Kubernetes configuration and replaced it with `index.docker.io/dkalinin/k8s-simple-app@sha256:4c8b96d4fffdfae29258d94a22ae4ad1fe36139d47288b8960d9958d1e63a9d0` before forwarding configuration to kubectl. + +## Next steps + +In this workflow we saw how to publish and download a bundle to distribute a Kubernetes application. Next, follow the [Air-gapped workflow](air-gapped-workflow.md) to see how we can use the `imgpkg copy` command to copy a bundle between registries. diff --git a/site/content/imgpkg/docs/v0.42.x/ca-certs-windows.md b/site/content/imgpkg/docs/v0.42.x/ca-certs-windows.md new file mode 100644 index 000000000..2f0404248 --- /dev/null +++ b/site/content/imgpkg/docs/v0.42.x/ca-certs-windows.md @@ -0,0 +1,28 @@ +--- +aliases: [/imgpkg/docs/latest/ca-certs-windows] +title: CA Certs on Windows +--- + +## Known issue verifying certificates on Windows + +If you are using imgpkg v0.19.0 or earlier, and use imgpkg with a registry over https, you will likely encounter the following error: +``` +imgpkg: Error: Fetching image: + Get "https://some.registry/v2/": x509: certificate signed by unknown authority +``` + +imgpkg v0.20.0+ supports loading Windows root ca certs. Meaning, that imgpkg is able to verify registry certificates signed by a trusted certificate authority! + +## Known issue providing custom ca certificates on Windows + +imgpkg allows specifying the `--registry-ca-cert-path` flag as a way to add custom ca certificates to use when verifying a registry server certificate. + +However, on Windows, the entire set of ca certificates to use during verify is loaded from the flag (Windows root ca store is skipped in this case). +Meaning that if you are targeting multiple registries, and some are signed with a trusted certificate authority and others signed with a custom ca certificate, +both ca certificates will need to be provided. (via the `--registry-ca-cert-path` flag) + +An example workflow: +1. Build a single ca certificate file (containing multiple ca certificates) from a trusted source. e.g. [extract ca certs provided by Mozilla](https://github.com/curl/curl/blob/4d2f8006777d6354d9b62eae38ebd0a0256d0f94/lib/firefox-db2pem.sh) +1. Provide that single ca certificate file to imgpkg. `--registry-ca-cert-path ./mozilla-ca-certs.pem` +1. Provide any additional custom ca certificates to imgpkg. `--registry-ca-cert-path ./dev-registry.pem` + diff --git a/site/content/imgpkg/docs/v0.42.x/commands.md b/site/content/imgpkg/docs/v0.42.x/commands.md new file mode 100644 index 000000000..36e39e270 --- /dev/null +++ b/site/content/imgpkg/docs/v0.42.x/commands.md @@ -0,0 +1,178 @@ +--- +aliases: [/imgpkg/docs/latest/commands] +title: Commands +--- + +## Push + +### Overview + +`push` command allows users to create a bundle in the registry from files and/or directories on their local file systems. For example, + +```bash-plain +$ imgpkg push -b index.docker.io/k8slt/sample-bundle -f my-bundle/ +``` + +will push a bundle contents containing in the `my-bundle/` directory to `index.docker.io/k8slt/sample-bundle`. + +Use the `-b`/`--bundle` flag to specify the destination of the push. If the specified destination does not include a tag, the artifact will be pushed with the default tag `:latest`. + +The `-f` flag can be used multiple times to add different files or directories to the bundle. + +Use the flag `--preserve-permissions=true` to preserve the current permission of the files of the image or bundle being pushed. If this flag is not present `imgpkg` will remove the Group and All permissions before uploading the image, when pull is done `imgpkg` will try to copy the User permissions to Group and All, respecting umask + +### Generating a BundleLock + +`push` command can output a [`BundleLock` configuration](resources.md#bundlelock-configuration) for users that would like a deterministic reference to a pushed bundle. For example, running: + +```bash-plain +$ impgpkg push -b index.docker.io/k8slt/sample-bundle:v0.1.0 -f my-bundle/ --lock-output +/tmp/bundle.lock.yml +``` + +will create `/tmp/bundle.lock.yml` with BundleLock configuration. If another bundle image in the repository is later given the same tag (`v0.1.0`), the BundleLock configuration will continue to provide immutable reference (via digest) to the original pushed bundle. + +--- +## Pull + +### Overview + +After pushing bundles to a registry, users can retrieve them with `imgpkg pull`. For example, + +```bash-plain +$ imgpkg pull -b index.docker.io/k8slt/sample-bundle -o my-bundle/ +``` + +will pull a bundle from `index.docker.io/k8slt/sample-bundle` and extract its contents into the `my-bundle/` directory, which gets created if it does not already exist. + +When pulling a bundle, imgpkg ensures that the referenced images are updated to account for any relocations. It will search for each referenced image by digest in the same repository as the bundle. If all referenced digests are found, imgpkg will update image references in the bundle's [`.imgpkg/images.yml` file](resources.md#imgpkg-directory). If any of the digests are not found in the repository, imgpkg will not update any references. + +### Pulling via lock file + +[BundleLock configuration](resources.md#bundlelock-configuration) can be used as input to the pull command via the `--lock` flag. + +```bash-plain +$ imgpkg pull --lock bundle.lock.yml -o my-bundle/ +``` + +### Pulling nested bundles + +If pulling a bundle that references another bundle (via it's ImagesLock file), in order to *also* pull down the contents of every nested bundle, use the `--recursive` flag. + +```bash-plain +$ imgpkg pull --recursive -b bundle-with-nested-bundles +``` + +Contents of *every* nested bundle are written to the 'parent' bundle's `.imgpkg/bundles` directory, namespaced by the bundle's sha256. + +For e.g. pulling a bundle with a nested bundle having sha of `123` would result in: +``` +parent-bundle-path/.imgpkg/bundles/sha256-123/ +``` + +--- +## Copy + +### Overview + +The `copy` command copies a bundle from a registry to another registry (as long as both registries are accessible from where the command is running): + +```bash-plain +$ imgpkg copy -b index.docker.io/k8slt/sample-bundle --to-repo registry.corp.com/user2/sample-bundle-name +``` + +Alternatively `copy` can copy a bundle between registries which are not both accessible from a single location, by creating an intermediate tarball: + +```bash-plain +$ imgpkg copy -b index.docker.io/k8slt/sample-bundle --to-tar=/Volumes/secure-thumb/bundle.tar +# ... take thumb driver to a different location ... +$ imgpkg copy --tar=/Volumes/secure-thumb/bundle.tar --to-repo registry.corp.com/user2/sample-bundle-name +``` + +In either case, the bundle image and all dependent images are copied to the destination location `registry.corp.com/user2/sample-bundle-name`. + +**Note:** To generate tags that provide information on the origin of the images use the flag `--repo-based-tags` + + +### Resume copy of image or bundle to tar + +If the copy to tar was interrupted by a network problem it can be resumed by providing the flag `--resume` to the `copy` command + +```bash-plain +$ imgpkg copy -b index.docker.io/k8slt/sample-bundle --to-tar=/Volumes/secure-thumb/bundle.tar --resume +``` + +### Copying via lock file + +[BundleLock configuration](resources.md#bundlelock-configuration) can be used as input to the copy command via the `--lock` flag. + +```bash-plain +$ imgpkg copy --lock bundle.lock.yml --to-repo registry.corp.com/user2/sample-bundle-name --lock-output /tmp/new-bundle.lock.yml +``` + +### Non-Distributable or Foreign Layers + +Some images contain layers which should not be uploaded when copying, such as a proprietary base image. +Instead, to comply with license requirements, it is expected to get them directly from the source registry. +These layers are interchangeably known as +[Non-Distributable](https://github.com/opencontainers/image-spec/blob/79b036d80240ae530a8de15e1d21c7ab9292c693/layer.md#non-distributable-layers) +(by the OCI) or +[Foreign](https://docs.docker.com/registry/spec/manifest-v2-2/) (by Docker) and denoted in the layer's MediaType. + +By default, imgpkg will not relocate any layers marked as non-distributable. + +This can cause issues when dealing with [air-gapped environments](air-gapped-workflow.md) as they may be unable to reach the external registries. +To allow this use case, imgpkg supports the `--include-non-distributable-layers` flag to copy all layers, even those marked as non-distributable. + +Note that usage of this flag shall not preclude your obligation to comply with the terms of the image license(s). + +### Image Signatures + +`imgpkg` can copy Signature created by [cosign](https://github.com/sigstore/cosign). By +default `imgpkg` will not search for Signatures for Images. To enable the search and copy of the signatures the +flag `--cosign-signatures` needs to be provided to copy command + +```bash-plain +$ imgpkg copy -b index.docker.io/k8slt/sample-bundle --to-repo some.repo.io/some-bundle --cosign-signatures +``` + +This feature will work while copying to a different repository as well as copying to a tarball. + +--- + +## Tag + +`imgpkg tag` supports a `list` subcommand that allows users to list the image tags pushed to registries. The command features an `--image`/`-i` option that allows a user to specify an image name. + +An example of this is shown below: + +```bash-plain +$ imgpkg tag list -i index.docker.io/k8slt/image +``` + +The output shows the names of all non-imgpkg internal tags associated with the image. + +Additionally, you can request to see the tag digests or all tags, to include the imgpkg internally generated tags, using the following flags. + +```bash-plain +$ imgpkg tag list --digests -i index.docker.io/k8slt/image +$ imgpkg tag list --imgpkg-internal-tags -i index.docker.io/k8slt/image +``` + +--- + +## Describe + +`imgpkg describe` Provides a summary of all the images that are part of the provided Bundle. + +An example of this is shown below: + +```bash-plain +$ imgpkg describe -b carvel.dev/app1-bundle +``` + +This command provides 2 different types of output, `yaml` and `text`, that can be selected via the flag `--output-type`. +By default `text` is selected. + +The flag `--cosign-artifacts` provides the user the ability to select if they want or not `imgpkg` to check and display +any [cosign](https://github.com/sigstore/cosign) artifact that is part of the Bundle. diff --git a/site/content/imgpkg/docs/v0.42.x/debugging.md b/site/content/imgpkg/docs/v0.42.x/debugging.md new file mode 100644 index 000000000..9000ae487 --- /dev/null +++ b/site/content/imgpkg/docs/v0.42.x/debugging.md @@ -0,0 +1,46 @@ +--- +aliases: [/imgpkg/docs/latest/debugging] +title: Debugging +--- + +## Debugging + +In the process of communicating with remote OCI registries, it is possible that an error will occur. In order to help debug an error situation, use the `--debug` command line argument. Specifying this argument will output detailed logs of all communications between `imgpkg` and the OCI registries. + +> This feature is available in v0.20.0 and later + +As an example, consider this pull command along with the additional information logged. The record of all HTTP communication will be displayed to assist in resolving a problem or error condition. + +```bash-plain +imgpkg pull -b registry.example.com/foo/bar:latest -o temp --debug + +2021/09/21 20:50:12 --> GET https://registry.example.com/v2/ +2021/09/21 20:50:12 GET /v2/ HTTP/1.1 +Host: registry.example.com +User-Agent: Go-http-client/1.1 +Accept-Encoding: gzip + +2021/09/21 20:50:12 <-- 401 https://registry.example.com/v2/ (207.596107ms) +2021/09/21 20:50:12 HTTP/1.1 401 Unauthorized +Content-Length: 76 +Connection: keep-alive +Content-Type: application/json; charset=utf-8 +Date: Wed, 22 Sep 2021 02:50:12 GMT +Docker-Distribution-Api-Version: registry/2.0 +Set-Cookie: sid=f9752c01ce47ab50791d4a845a78d996; Path=/; HttpOnly; Secure +Strict-Transport-Security: max-age=31536000; includeSubDomains +Www-Authenticate: Bearer realm="https://registry.example.com/service/token",service="harbor-registry" +X-Request-Id: 2fe97b25-ca40-4012-9105-bbf8284995b6 + +{"errors":[{"code":"UNAUTHORIZED","message":"unauthorized: unauthorized"}]} + +2021/09/21 20:50:12 --> GET https://registry.example.com/service/token?scope=repository%3Afoo%2Fbar%3Apull&service=harbor-registry [body redacted: basic token response contains credentials] +2021/09/21 20:50:12 GET /service/token?scope=repository%3Afoo%2Fbar%3Apull&service=harbor-registry HTTP/1.1 +Host: registry.example.com +User-Agent: go-containerregistry/v0.6.0 +Authorization: +Accept-Encoding: gzip +... +``` + +> Note that sensitive information, such as basic authentication parameters and Authorization strings, are not displayed. diff --git a/site/content/imgpkg/docs/v0.42.x/faq-generic.md b/site/content/imgpkg/docs/v0.42.x/faq-generic.md new file mode 100644 index 000000000..879d4bd09 --- /dev/null +++ b/site/content/imgpkg/docs/v0.42.x/faq-generic.md @@ -0,0 +1,30 @@ +--- +aliases: [/imgpkg/docs/latest/faq-generic] +title: FAQ +--- + +## Using `registry:2` and non-distributable layer + +**We do not recommend the usage of `registry:2` in production** + +There is a known issue when using `registry:2` image as the registry and using it as the destination of a Bundle or +Image that contains non-distributable layers. + +The problem is surfaced with an error similar to + +```shell +imgpkg copy \ + -b my.registry.io/some-bundle:0.0.1 \ + --to-repo localhost:5000/some-bundle +imgpkg: Error: PUT http://localhost:5000/v2/some-bundle/manifests/sha256-6195153fbf1af788bb68124fe2e0b016a1d0b6d3d8ca16cc6d23823d8a7b5445.imgpkg: multiple errors returned: + UNKNOWN: unknown error; UNKNOWN: unknown error; map[]; MANIFEST_BLOB_UNKNOWN: blob unknown to registry; sha256:3a78847ea829208edc2d7b320b7e602b9d12e47689499d5180a9cc7790dec4d7 +``` + +This error happens because the `registry:2` registry does a validation on non-distributable layers and checks the URL +against the provided allowed list, which is empty so it fails. + +For local development this validation can be turned off. To do so start the registry using the following command + +```shell +docker run --env REGISTRY_VALIDATION_DISABLED=true -d -p 5000:5000 --restart always --name registry registry:2 +``` diff --git a/site/content/imgpkg/docs/v0.42.x/install.md b/site/content/imgpkg/docs/v0.42.x/install.md new file mode 100644 index 000000000..d74978ec7 --- /dev/null +++ b/site/content/imgpkg/docs/v0.42.x/install.md @@ -0,0 +1,58 @@ +--- +aliases: [/imgpkg/docs/latest/install] +title: Install +--- + +## Via script (macOS or Linux) + +(Note that `install.sh` script installs other Carvel tools as well.) + +Install binaries into specific directory: + +```bash +$ mkdir local-bin/ +$ curl -L https://carvel.dev/install.sh | K14SIO_INSTALL_BIN_DIR=local-bin bash + +$ export PATH=$PWD/local-bin/:$PATH +$ imgpkg version +``` + +Or system wide: + +```bash +$ wget -O- https://carvel.dev/install.sh > install.sh + +# Inspect install.sh before running... +$ sudo bash install.sh +$ imgpkg version +``` + +## Via Homebrew (macOS or Linux) + +Based on [github.com/carvel-dev/homebrew](https://github.com/carvel-dev/homebrew). + +```bash +$ brew tap carvel-dev/carvel +$ brew install imgpkg +$ imgpkg version +``` + +## Specific version from a GitHub release + +To download, click on one of the assets in a [chosen GitHub release](https://github.com/carvel-dev/imgpkg/releases), for example for 'imgpkg-darwin-amd64'. + +```bash +# **Compare binary checksum** against what's specified in the release notes +# (if checksums do not match, binary was not successfully downloaded) +$ shasum -a 256 ~/Downloads/imgpkg-darwin-amd64 +08b25d21675fdc77d4281c9bb74b5b36710cc091f30552830604459512f5744c /Users/pivotal/Downloads/imgpkg-darwin-amd64 + +# Move binary next to your other executables +$ mv ~/Downloads/imgpkg-darwin-amd64 /usr/local/bin/imgpkg + +# Make binary executable +$ chmod +x /usr/local/bin/imgpkg + +# Check its version +$ imgpkg version +``` diff --git a/site/content/imgpkg/docs/v0.42.x/proxy.md b/site/content/imgpkg/docs/v0.42.x/proxy.md new file mode 100644 index 000000000..0998e7dc2 --- /dev/null +++ b/site/content/imgpkg/docs/v0.42.x/proxy.md @@ -0,0 +1,33 @@ +--- +aliases: [/imgpkg/docs/latest/proxy] +title: Proxy +--- + +## Using Proxy + +When using `imgpkg` to connect with a registry via a proxy you will need to provide one of following environment variables + +- `HTTP_PROXY` or `http_proxy` when using the flag `--registry-insecure` +- `HTTPS_PROXY` or `https_proxy` when the communication with the registry need to be using TLS + +### No TLS example + +Assuming the proxy to access the registry is located in `http://proxy.company.com` + +When executing `imgpkg` do the following: +```bash +export http_proxy=http://proxy.company.com + +imgpkg pull -b registry.company.com/my-image@sha256:265d4a5ed8bf0df27d1107edb00b70e658ee9aa5acb3f37336c5a17db634481e -o folder --registry-insecure +``` + +### TLS example + +Assuming the proxy to access the registry is located in `https://proxy.company.com` + +When executing `imgpkg` do the following: +```bash +export https_proxy=https://proxy.company.com + +imgpkg pull -b registry.company.com/my-image@sha256:265d4a5ed8bf0df27d1107edb00b70e658ee9aa5acb3f37336c5a17db634481e -o folder +``` diff --git a/site/content/imgpkg/docs/v0.42.x/resources.md b/site/content/imgpkg/docs/v0.42.x/resources.md new file mode 100644 index 000000000..6a0300834 --- /dev/null +++ b/site/content/imgpkg/docs/v0.42.x/resources.md @@ -0,0 +1,145 @@ +--- +aliases: [/imgpkg/docs/latest/resources] +title: Resources +--- + +## Image + +An OCI image is an artifact that lives within an OCI registry (such as DockerHub). It can contain an arbitrary number of files. + +--- +## Bundle + +A bundle is an OCI image that holds 0+ arbitrary files _and_ 0+ references to dependent OCI images (which *may* also be [bundles](#nested-bundle)). By tracking dependent images, imgpkg can copy bundles across registries. + +Referenced images are stored within the [`.imgpkg` directory](#imgpkg-directory) at the root level of the bundle image. + +![Bundle diagram](/images/imgpkg/bundle-diagram.png) + +Implementation note: A bundle OCI image has the `dev.carvel.imgpkg.bundle` [label](https://docs.docker.com/config/labels-custom-metadata/) set. + +--- +## `.imgpkg` directory + +`.imgpkg` directory contains metadata files describing bundle: + +- `images.yml` (required) contains [ImagesLock configuration](#imageslock-configuration) that describes 0+ dependent OCI images. Consumers of bundles can rely on this file being always present. + +- `bundle.yml` (optional) file contains [Bundle configuration](#bundle-configuration) that contains details about bundle authors, associated websites, etc. + +Restrictions for location of `.imgpkg` directory: + +- Only one `.imgpkg` directory is allowed across all directories provided via `-f` to the `push` command. This restriction ensures there is a single source of bundle metadata and referenced images. + +- The `.imgpkg` directory must be a direct child of one of the input directories. This prevents any confusion around the scope of the `.imgpkg` metadata. + +--- +## Bundle configuration + +Used by bundle creators to store general information about the bundle. Stored in `.imgpkg/bundle.yml`. + +Example: + +```yaml +apiVersion: imgpkg.carvel.dev/v1alpha1 +kind: Bundle +metadata: + name: my-bundle +authors: +- name: Full Name + email: name@example.com +websites: +- url: example.com +``` + +- `authors` (array of structs; optional) + - `name` (string) Author name + - `email` (string) Author email +- `websites` (array of structs; optional) + - `url` (string) Related website URL + +--- +## ImagesLock configuration + +An ImagesLock configuration is used to track a collection of image references. + +Bundle's `.imgpkg/images.yml` contains ImagesLock configuration. That's how bundle knows which OCI images it references. When copying a bundle `imgpkg` uses this configuration to know which images to copy. + +It can be conveniently generated with [kbld](/kbld): + +```bash-plain +$ kbld -f config.yml --imgpkg-lock-output .imgpkg/images.yml +``` + +Example: + +```yaml +apiVersion: imgpkg.carvel.dev/v1alpha1 +kind: ImagesLock +images: +- image: docker.io/user1/my-app@sha256:42462d0cb227497976754bb67348bdd7471c7bd159819d6bd63fdf479eb7eb19 + annotations: + kbld.carvel.dev/id: "my-app:v1" +- image: gcr.io/projectX/controller@sha256:6ecba6f14373a449f8d54fa4286f57fb8ef37c4ffa637969551f2fda52672206 +``` + +- `images` (array of images): 0+ images + - `image` (string; required) digest reference to OCI image (tag references are not allowed) + - `annotations` (map[string]string; optional) arbitrary additional data about image reference. Expected to be used by tools that create or read ImagesLock configuration. Example: [kbld](/kbld) uses annotations to store an identifier that can later tell it which location(s) within a Kubernetes configuration to update with the digest reference. + +Advanced non-bundle use: See [copying via lock files](commands.md#copying-via-lock-file). + +--- +## BundleLock configuration + +Stores a digest reference to a bundle (as well as the tag it was pushed with). + +This configuration is generated by the `--lock-output` flag during a `push` command. + +```yaml +$ imgpkg push -b ... --lock-output /tmp/lock.yml + +$ cat /tmp/lock.yml + +apiVersion: imgpkg.carvel.dev/v1alpha1 +kind: BundleLock +bundle: + image: docker.io/my-app@sha256:b12026c7a0a6a1756a82a2a74ac759e9a7036523faca0e33dbddebc214e097df + tag: v1.0 +``` + +--- +## Nested Bundle + +A nested bundle is a bundle referenced from a 'parent' bundle in its `ImagesLock` configuration. + +Having a bundle 'reference' another bundle is no different from referencing any other OCI image. The copy and pull commands work the same as dealing with any OCI image. + +![Nested Bundle diagram](/images/imgpkg/nested-bundle-diagram.png) + +One key difference between nested bundles and other OCI images, is the directory structure when `imgpkg pull` writes the nested bundle's content to disk. + +Bundles can be nested repeatedly without limits on depth or breadth. +Imgpkg optimizes both network requests and storage on the destination, so we +would not expect any issues short of hard storage limits at the destination +repository. + +For further details refer to [pulling a nested bundle.](commands.md#pulling-nested-bundles) + +--- +## Locations OCI Image + +`imgpkg` when copying Bundles and Images now creates a new OCI Images that will act as a Cache that contain information +about the Images that were copied and if these Images are a Bundle or not. This OCI Image will contain a single layer +with a single file `image-locations.yml` at the root of the image. This is the file structure + +```yaml +apiVersion: imgpkg.carvel.dev/v1alpha1 +kind: ImageLocations +images: +- image: some.image.io/test@sha256:4c8b96d4fffdfae29258d94a22ae4ad1fe36139d47288b8960d9958d1e63a9d0 + isBundle: true +``` + +The OCI Image will be pushed into the same repository as the Bundle and will have the tag +`sha256-{Bundle SHA}.image-locations.imgpkg` diff --git a/site/content/imgpkg/docs/v0.42.x/security.md b/site/content/imgpkg/docs/v0.42.x/security.md new file mode 100644 index 000000000..ca69db28d --- /dev/null +++ b/site/content/imgpkg/docs/v0.42.x/security.md @@ -0,0 +1,8 @@ +--- +aliases: [/imgpkg/docs/latest/security] +title: Security +--- + +## Vulnerability Disclosure + +If you believe you have found a security issue in `imgpkg`, please privately and responsibly disclose it by following the directions in our [security policy](/shared/docs/latest/security-policy). diff --git a/site/content/imgpkg/docs/v0.42.x/working-directly-with-images.md b/site/content/imgpkg/docs/v0.42.x/working-directly-with-images.md new file mode 100644 index 000000000..cdac04918 --- /dev/null +++ b/site/content/imgpkg/docs/v0.42.x/working-directly-with-images.md @@ -0,0 +1,8 @@ +--- +aliases: [/imgpkg/docs/latest/working-directly-with-images] +title: Working directly with images +--- + +In rare cases imgpkg's [bundle](resources.md#bundle) concept is not wanted (or necessary). imgpkg provides a `--image` flag for push, pull and copy commands. When the `--image` flag is used, there is no need for a `.imgpkg` directory to store metadata. + +For most use cases, we suggest using the bundle concept and `--bundle` flag. diff --git a/site/content/kapp/docs/v0.60.x/_index.md b/site/content/kapp/docs/v0.60.x/_index.md index 9d2f176df..295ffc2f8 100644 --- a/site/content/kapp/docs/v0.60.x/_index.md +++ b/site/content/kapp/docs/v0.60.x/_index.md @@ -1,5 +1,5 @@ --- -aliases: [/kapp/docs/latest/] + title: "About kapp" toc: "false" cascade: diff --git a/site/content/kapp/docs/v0.60.x/apply-ordering.md b/site/content/kapp/docs/v0.60.x/apply-ordering.md index 6a60e1044..6f870b2ec 100644 --- a/site/content/kapp/docs/v0.60.x/apply-ordering.md +++ b/site/content/kapp/docs/v0.60.x/apply-ordering.md @@ -1,5 +1,5 @@ --- -aliases: [/kapp/docs/latest/apply-ordering] + title: Apply Ordering --- diff --git a/site/content/kapp/docs/v0.60.x/apply-waiting.md b/site/content/kapp/docs/v0.60.x/apply-waiting.md index 93ff9f7ab..77c767455 100644 --- a/site/content/kapp/docs/v0.60.x/apply-waiting.md +++ b/site/content/kapp/docs/v0.60.x/apply-waiting.md @@ -1,5 +1,5 @@ --- -aliases: [/kapp/docs/latest/apply-waiting] + title: Apply Waiting --- diff --git a/site/content/kapp/docs/v0.60.x/apply.md b/site/content/kapp/docs/v0.60.x/apply.md index 70f63a637..7cd36ddd4 100644 --- a/site/content/kapp/docs/v0.60.x/apply.md +++ b/site/content/kapp/docs/v0.60.x/apply.md @@ -1,5 +1,5 @@ --- -aliases: [/kapp/docs/latest/apply] + title: Apply stage --- diff --git a/site/content/kapp/docs/v0.60.x/apps.md b/site/content/kapp/docs/v0.60.x/apps.md index ebaf13fab..2abf48417 100644 --- a/site/content/kapp/docs/v0.60.x/apps.md +++ b/site/content/kapp/docs/v0.60.x/apps.md @@ -1,5 +1,5 @@ --- -aliases: [/kapp/docs/latest/apps] + title: Applications --- diff --git a/site/content/kapp/docs/v0.60.x/cheatsheet.md b/site/content/kapp/docs/v0.60.x/cheatsheet.md index 9ac162011..021b6aa5c 100644 --- a/site/content/kapp/docs/v0.60.x/cheatsheet.md +++ b/site/content/kapp/docs/v0.60.x/cheatsheet.md @@ -1,5 +1,5 @@ --- -aliases: [/kapp/docs/latest/cheatsheet] + title: Cheatsheet --- diff --git a/site/content/kapp/docs/v0.60.x/command-reference.md b/site/content/kapp/docs/v0.60.x/command-reference.md index 6b48d8122..dd5f2a6e0 100644 --- a/site/content/kapp/docs/v0.60.x/command-reference.md +++ b/site/content/kapp/docs/v0.60.x/command-reference.md @@ -1,5 +1,5 @@ --- -aliases: [/kapp/docs/latest/command-reference] + title: Command Reference --- diff --git a/site/content/kapp/docs/v0.60.x/config.md b/site/content/kapp/docs/v0.60.x/config.md index 4d8953da1..c3dc89e73 100644 --- a/site/content/kapp/docs/v0.60.x/config.md +++ b/site/content/kapp/docs/v0.60.x/config.md @@ -1,5 +1,5 @@ --- -aliases: [/kapp/docs/latest/config] + title: Configuration --- diff --git a/site/content/kapp/docs/v0.60.x/configmap-migration.md b/site/content/kapp/docs/v0.60.x/configmap-migration.md index 9d1d3a4f7..d31a49ff5 100644 --- a/site/content/kapp/docs/v0.60.x/configmap-migration.md +++ b/site/content/kapp/docs/v0.60.x/configmap-migration.md @@ -1,5 +1,5 @@ --- -aliases: [/kapp/docs/latest/configmap-migration] + title: Configmap Migration (experimental) --- diff --git a/site/content/kapp/docs/v0.60.x/dangerous-flags.md b/site/content/kapp/docs/v0.60.x/dangerous-flags.md index 14bccdb53..1aecb4909 100644 --- a/site/content/kapp/docs/v0.60.x/dangerous-flags.md +++ b/site/content/kapp/docs/v0.60.x/dangerous-flags.md @@ -1,5 +1,5 @@ --- -aliases: [/kapp/docs/latest/dangerous-flags] + title: Dangerous Flags --- diff --git a/site/content/kapp/docs/v0.60.x/diff.md b/site/content/kapp/docs/v0.60.x/diff.md index d0535d9ec..01eadfb70 100644 --- a/site/content/kapp/docs/v0.60.x/diff.md +++ b/site/content/kapp/docs/v0.60.x/diff.md @@ -1,5 +1,5 @@ --- -aliases: [/kapp/docs/latest/diff] + title: Diff stage --- ## Overview diff --git a/site/content/kapp/docs/v0.60.x/faq.md b/site/content/kapp/docs/v0.60.x/faq.md index 50318f573..2bbe6317c 100644 --- a/site/content/kapp/docs/v0.60.x/faq.md +++ b/site/content/kapp/docs/v0.60.x/faq.md @@ -1,5 +1,5 @@ --- -aliases: [/kapp/docs/latest/faq] + title: FAQ --- diff --git a/site/content/kapp/docs/v0.60.x/gitops.md b/site/content/kapp/docs/v0.60.x/gitops.md index 156a06c19..c7ce67312 100644 --- a/site/content/kapp/docs/v0.60.x/gitops.md +++ b/site/content/kapp/docs/v0.60.x/gitops.md @@ -1,5 +1,5 @@ --- -aliases: [/kapp/docs/latest/gitops] + title: GitOps --- diff --git a/site/content/kapp/docs/v0.60.x/hpa-deployment-rebase.md b/site/content/kapp/docs/v0.60.x/hpa-deployment-rebase.md index 722c9bc68..ce4ce0d2e 100644 --- a/site/content/kapp/docs/v0.60.x/hpa-deployment-rebase.md +++ b/site/content/kapp/docs/v0.60.x/hpa-deployment-rebase.md @@ -1,5 +1,5 @@ --- -aliases: [/kapp/docs/latest/hpa-deployment-rebase] + title: HPA and Deployment rebase --- ## HPA and Deployment rebase diff --git a/site/content/kapp/docs/v0.60.x/install.md b/site/content/kapp/docs/v0.60.x/install.md index c4d30dbad..da1c6ea4d 100644 --- a/site/content/kapp/docs/v0.60.x/install.md +++ b/site/content/kapp/docs/v0.60.x/install.md @@ -1,5 +1,5 @@ --- -aliases: [/kapp/docs/latest/install] + title: Install --- diff --git a/site/content/kapp/docs/v0.60.x/integrating-with-other-tools.md b/site/content/kapp/docs/v0.60.x/integrating-with-other-tools.md index aa84510bf..0bdc967b8 100644 --- a/site/content/kapp/docs/v0.60.x/integrating-with-other-tools.md +++ b/site/content/kapp/docs/v0.60.x/integrating-with-other-tools.md @@ -1,5 +1,5 @@ --- -aliases: [/kapp/docs/latest/integrating-with-other-tools] + title: Integrating with Other Tools --- diff --git a/site/content/kapp/docs/v0.60.x/merge-method.md b/site/content/kapp/docs/v0.60.x/merge-method.md index ae06ce516..f555150fe 100644 --- a/site/content/kapp/docs/v0.60.x/merge-method.md +++ b/site/content/kapp/docs/v0.60.x/merge-method.md @@ -1,5 +1,5 @@ --- -aliases: [/kapp/docs/latest/merge-method] + title: "Resource Merge Method" --- diff --git a/site/content/kapp/docs/v0.60.x/rbac.md b/site/content/kapp/docs/v0.60.x/rbac.md index 7eb89bd96..33b568951 100644 --- a/site/content/kapp/docs/v0.60.x/rbac.md +++ b/site/content/kapp/docs/v0.60.x/rbac.md @@ -1,5 +1,5 @@ --- -aliases: [/kapp/docs/latest/rbac] + title: Permissions --- diff --git a/site/content/kapp/docs/v0.60.x/rebase-pvc.md b/site/content/kapp/docs/v0.60.x/rebase-pvc.md index 0e9087088..519370c1c 100644 --- a/site/content/kapp/docs/v0.60.x/rebase-pvc.md +++ b/site/content/kapp/docs/v0.60.x/rebase-pvc.md @@ -1,5 +1,5 @@ --- -aliases: [/kapp/docs/latest/rebase-pvc] + title: PersistentVolumeClaim rebase --- ## PersistentVolumeClaim rebase diff --git a/site/content/kapp/docs/v0.60.x/security.md b/site/content/kapp/docs/v0.60.x/security.md index 6b16ba957..30752f23a 100644 --- a/site/content/kapp/docs/v0.60.x/security.md +++ b/site/content/kapp/docs/v0.60.x/security.md @@ -1,5 +1,5 @@ --- -aliases: [/kapp/docs/latest/security] + title: Security --- diff --git a/site/content/kapp/docs/v0.60.x/state-namespace.md b/site/content/kapp/docs/v0.60.x/state-namespace.md index 82185f326..17548c4f1 100644 --- a/site/content/kapp/docs/v0.60.x/state-namespace.md +++ b/site/content/kapp/docs/v0.60.x/state-namespace.md @@ -1,5 +1,5 @@ --- -aliases: [/kapp/docs/latest/state-namespace] + title: Namespace for State Storage --- diff --git a/site/content/kapp/docs/v0.61.x/_index.md b/site/content/kapp/docs/v0.61.x/_index.md new file mode 100644 index 000000000..ca8ee1617 --- /dev/null +++ b/site/content/kapp/docs/v0.61.x/_index.md @@ -0,0 +1,44 @@ +--- +aliases: [/kapp/docs/latest/] +title: "About kapp" +toc: "false" +cascade: + version: v0.61.x + toc: "true" + type: docs + layout: docs +--- + +`kapp` (pronounced: `kap`) CLI encourages Kubernetes users to manage resources in bulk by working with "Kubernetes applications" (sets of resources with the same label). It focuses on resource diffing, labeling, deployment and deletion. Unlike tools like Helm, `kapp` considers YAML templating and management of packages outside of its scope, though it works great with tools that generate Kubernetes configuration. + +![Kapp Deploy](/images/kapp/kapp-deploy-screenshot.png) + +Features: + +- Works with standard Kubernetes YAMLs +- Focuses exclusively on deployment workflow, not packaging or templating + - but plays well with tools (such as [ytt](/ytt)) that produce Kubernetes configuration +- Converges application resources (creates, updates and/or deletes resources) in each deploy + - based on comparison between provided files and live objects in the cluster +- Separates calculation of changes ([diff stage](diff.md)) from application of changes ([apply stage](apply.md)) +- [Waits for resources](apply-waiting.md) to be "ready" +- Creates CRDs and Namespaces first and supports [custom change ordering](apply-ordering.md) +- Works [without admin privileges](rbac.md) and does not use custom CRDs + - making it possible to use kapp as a regular user in a single namespace +- Records application deployment history +- Opt-in resource version management + - for example, to trigger Deployment rollout when ConfigMap changes +- Optionally streams Pod logs during deploy +- Works with any group of labeled resources (`kapp -a label:tier=web inspect -t`) +- Works without server side components +- GitOps friendly (`kapp app-group deploy -g all-apps --directory .`) + +## Blog posts + +- [Deploying Kubernetes Applications with ytt, kbld, and kapp](/blog/deploying-apps-with-ytt-kbld-kapp) + +## Talks + +- [ytt and kapp @ TGI Kubernetes 079](https://www.youtube.com/watch?v=CSglwNTQiYg) with Joe Beda +- [Managing Applications in Production: Helm vs ytt & kapp @ Kubecon 2020](https://www.youtube.com/watch?v=WJw1MDFMVuk) +- [Introduction to Carvel @ Rawkode Live](https://www.youtube.com/watch?v=LBCmMTofNxw) diff --git a/site/content/kapp/docs/v0.61.x/apply-ordering.md b/site/content/kapp/docs/v0.61.x/apply-ordering.md new file mode 100644 index 000000000..6a60e1044 --- /dev/null +++ b/site/content/kapp/docs/v0.61.x/apply-ordering.md @@ -0,0 +1,94 @@ +--- +aliases: [/kapp/docs/latest/apply-ordering] +title: Apply Ordering +--- + +## Overview + +kapp includes builtin rules to make sure certain changes are applied in particular order: + +- Creates/updates + - CRDs are created/updated before custom resources + - Namespaces are created/updated before namespaced resources + - Pod related resources (ServiceAccount, ConfigMap, Secret, etc.) are created/updated before other resources (v0.25.0+) + - RBAC related resources (Role, RoleBinding, etc.) are created/updated before other resources (v0.25.0+) +- Deletions (below is order as of v0.29.0+) + - Custom resources are deleted first + - CRDs are deleted next + - Rest of resoures are deleted + +As of v0.25.0+, builtin rules are specified via [changeGroupBindings and changeRuleBindings](config.md#changegroupbindings) configurations. Custom rules can be added via same mechanism. + +Additionally kapp allows to customize order of changes via following resource annotations: + +- `kapp.k14s.io/change-group` annotation to group one or more resource changes into arbitrarily named group. Example: `apps.big.co/db-migrations`. You can specify multiple change groups by suffixing each annotation with a `.x` where `x` is unique identifier (e.g. `kapp.k14s.io/change-group.istio-sidecar-order`). +- `kapp.k14s.io/change-rule` annotation to control when resource change should be applied (created, updated, or deleted) relative to other changes. You can specify multiple change rules by suffixing each annotation with a `.x` where `x` is unique identifier (e.g. `kapp.k14s.io/change-rule.istio-sidecar-order`). + +`kapp.k14s.io/change-rule` annotation value format is as follows: `(upsert|delete) (after|before) (upserting|deleting) `. For example: + +- `kapp.k14s.io/change-rule: "upsert after upserting apps.big.co/db-migrations"` +- `kapp.k14s.io/change-rule: "delete before upserting apps.big.co/service"` + +As of v0.41.0+, kapp provides change group placeholders, which can be used in change-group and change-rule annotation values and are later replaced by values from the resource manifest of the resource they are associated with. For example: + +- `kapp.k14s.io/change-group: apps.co/db-migrations-{name}` - Here `{name}` would later be replaced by the name of the resource. +- `kapp.k14s.io/change-rule: upsert after upserting apps.co/namespaces-{namespace}` - Here `{namespace}` would later be replaced by the namespace of the resource. + +kapp provides the following placeholders: + +- `{api-group}` - apiGroup +- `{kind}` - kind +- `{name}` - name +- `{namespace}` - namespace +- `{crd-kind}` - spec.names.kind (available for CRDs only) +- `{crd-group}` - spec.group (available for CRDs only) + +These placeholders can also be used in changeGroupBindings and changeRuleBindings. By default, they are used for CRDs, CRs, namespaces and namespaced resources. Due to this, CRs now wait for their respective CRDs only and namespaced resources now wait for their respective namespaces only. + +## Example + +Following example shows how to run `job/migrations`, start and wait for `deployment/app`, and finally `job/app-health-check`. + +```yaml +kind: ConfigMap +metadata: + name: app-config + annotations: {} +#... +--- +kind: Job +metadata: + name: migrations + annotations: + kapp.k14s.io/change-group: "apps.big.co/db-migrations" +#... +--- +kind: Service +metadata: + name: app + annotations: + kapp.k14s.io/change-group: "apps.big.co/deployment" +#... +--- +kind: Ingress +metadata: + name: app + annotations: + kapp.k14s.io/change-group: "apps.big.co/deployment" +#... +--- +kind: Deployment +metadata: + name: app + annotations: + kapp.k14s.io/change-group: "apps.big.co/deployment" + kapp.k14s.io/change-rule: "upsert after upserting apps.big.co/db-migrations" +#... +--- +kind: Job +metadata: + name: app-health-check + annotations: + kapp.k14s.io/change-rule: "upsert after upserting apps.big.co/deployment" +#... +``` diff --git a/site/content/kapp/docs/v0.61.x/apply-waiting.md b/site/content/kapp/docs/v0.61.x/apply-waiting.md new file mode 100644 index 000000000..93ff9f7ab --- /dev/null +++ b/site/content/kapp/docs/v0.61.x/apply-waiting.md @@ -0,0 +1,47 @@ +--- +aliases: [/kapp/docs/latest/apply-waiting] +title: Apply Waiting +--- + +## Overview + +kapp includes builtin rules on how to wait for the following resource types: + +- any resource with `metadata.deletionTimestamp`: wait for resource to be fully removed +- any resource matching Config's waitRules: [see "Custom waiting behaviour" below](#custom-waiting-behaviour) +- [`apiextensions.k8s.io//CustomResourceDefinition`](https://github.com/carvel-dev/kapp/blob/develop/pkg/kapp/resourcesmisc/api_extensions_vx_crd.go): wait for Established and NamesAccepted conditions to be `True` (note that this is wait rule for CustomResourceDefinition resource itself, not CRs) +- `apps/v1/DaemonSet`: wait for `status.numberUnavailable` to be 0 +- `apps/v1/Deployment`: [see "apps/v1/Deployment resource" below](#apps-v1-deployment-resource) +- `apps/v1/ReplicaSet`: wait for `status.replicas == status.availableReplicas` +- `batch/v1/Job`: wait for `Complete` or `Failed` conditions to appear +- `batch//CronJob`: immediately considered done +- `/v1/Pod`: looks at `status.phase` +- `/v1/Service`: wait for `spec.clusterIP` and/or `status.loadBalancer.ingress` to become set +- `apps/v1/StatefulSet`: [see "apps/v1/StatefulSet resource" below](#appsv1statefulset-resource) + +If resource is not affected by the above rules, its waiting behaviour depends on aggregate of waiting states of its associated resources (associated resources are resources that share same `kapp.k14s.io/association` label value). + +## Controlling waiting via resource annotations + +- `kapp.k14s.io/disable-wait` annotation controls whether waiting will happen at all. Possible values: "". +- `kapp.k14s.io/disable-associated-resources-wait` annotation controls whether associated resources impact resource's waiting state. Possible values: "". + +## apps/v1/Deployment resource + +kapp by default waits for `apps/v1/Deployment` resource to have `status.unavailableReplicas` equal to zero. Additionally waiting behaviour can be controlled via following annotations: + +- `kapp.k14s.io/apps-v1-deployment-wait-minimum-replicas-available` annotation controls how many new available replicas are enough to consider waiting successful. Example values: `"10"`, `"5%"`. + +## apps/v1/StatefulSet resource + +Available in v0.32.0+. + +kapp will wait for any pods created from the updated template to be ready based on StatefulSet's status. This behaviour depends on the [update strategy](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies) used. + +Note: kapp does not do anything special when `OnDelete` strategy is used. It will wait for StatefulSet to report it's reconciled (expecting some actor in the system to delete Pods per `OnDelete` requirements). + +## Custom waiting behaviour + +Available in v0.29.0+. + +kapp can be extended with custom waiting behaviour by specifying [wait rules as additional config](config.md#wait-rules). (If this functionality is not enough to wait for resources in your use case, please reach out on Slack to discuss further.) diff --git a/site/content/kapp/docs/v0.61.x/apply.md b/site/content/kapp/docs/v0.61.x/apply.md new file mode 100644 index 000000000..70f63a637 --- /dev/null +++ b/site/content/kapp/docs/v0.61.x/apply.md @@ -0,0 +1,137 @@ +--- +aliases: [/kapp/docs/latest/apply] +title: Apply stage +--- + +## Overview + +Once change set is calculated (see [Diff](diff.md) section for details), kapp asks for user confirmation (unless `--yes` flag is specified) to proceed with changes. + +Changes are applied in particular order as described in [Apply ordering](apply-ordering.md). + +All created resources are labeled with several labels: + +- `kapp.k14s.io/app` to track which application "owns" resource +- `kapp.k14s.io/identity` to identify preferred API version used when creating resource +- `kapp.k14s.io/association` to track (best effort) parent-child relationships between resources + +Every time application is deployed, new application change record is saved. They can be viewed via `kapp app-change ls -a app-name`. + +Related: [ownership label rules](config.md#ownershiplabelrules) and [label scoping rules](config.md#labelscopingrules). + +## Controlling apply via resource annotations + +### kapp.k14s.io/create-strategy + +`kapp.k14s.io/create-strategy` annotation controls create behaviour (rarely necessary) + +Possible values: "" (default), `fallback-on-update`, `fallback-on-update-or-noop`. + +In some cases creation of a resource may conflict with that resource being created in the cluster by other means (often automated). An example of that is creation of default ServiceAccount by kapp racing with Kubernetes service accounts controller doing the same thing. By specifying `fallback-on-update` value, kapp will catch resource creation conflicts and apply resource as an update. + +`fallback-on-update-or-noop` (Available in v0.47.0+) also allows to use `noop` operation if `kapp.k14.io/noop` is added through rebase rules, else it behaves the same way as `fallback-on-update`. + +### kapp.k14s.io/update-strategy + +`kapp.k14s.io/update-strategy` annotation controls update behaviour + +Possible values: "" (default), `fallback-on-replace`, `always-replace`, `skip`. + +In some cases entire resources or subset resource fields are immutable which forces kapp users to specify how to apply wanted update. + +- "" means to issue plain update call +- `fallback-on-replace` causes kapp to fallback to resource replacement if update call results in `Invalid` error. Note that if resource is replaced (= delete + create), it may be negatively affected (loss of persistent data, loss of availability, etc.). For example, if Deployment or DaemonSet is first deleted and then created then associated Pods will be recreated as well, but all at the same time (even if rolling update is enabled), which likely causes an availability gap. +- `always-replace` causes kapp to always delete and then create resource (See note above as well.) +- `skip` causes kapp to not apply update (it will show up in a diff next time). Available in v0.33.0+. + +### kapp.k14s.io/delete-strategy + +`kapp.k14s.io/delete-strategy` annotation controls deletion behaviour + +Possible values: "" (default), `orphan`. + +By default resource is deleted, however; choosing `orphan` value will make kapp forget about this resource. Note that if this resource is owned by a different resource that's being deleted, it might still get deleted. Orphaned resources are labeled with `kapp.k14s.io/orphaned` label. As of v0.31.0+, resource is also disassociated from owning app so that it can be owned by future apps. + +### kapp.k14s.io/owned-for-deletion + +`kapp.k14s.io/owned-for-deletion` annotation controls resource deletion during `kapp delete` command + +Possible values: "". + +By default non-kapp owned resources are not explicitly deleted by kapp, but expected to be deleted by the cluster (for example Endpoints resource for each Service). In some cases it's desired to annotate non-kapp owned resource so that it does get explicitly deleted, possibly because cluster does not plan to delete it (e.g. PVCs created by StatefulSet are not deleted by StatefulSet controller; [https://github.com/carvel-dev/kapp/issues/36](https://github.com/carvel-dev/kapp/issues/36)). + +### kapp.k14s.io/nonce + +`kapp.k14s.io/nonce` annotation allows to inject unique ID + +Possible values: "" (default). + +Annotation value will be replaced with a unique ID on each deploy. This allows to force resource update as value changes every time. + +### kapp.k14s.io/renew-duration + +Available in v0.54.0+ + +`kapp.k14s.io/renew-duration` annotation allows specifying an interval which facilitates kapp to update or recreate the resource during next `kapp deploy` if this duration has lapsed. + +Possible values: [ParseDuration](https://pkg.go.dev/time#ParseDuration). + +If `kapp deploy` is run after this interval has lapsed, `kapp` will force an update irrespective of no changes to the resource's configuration by injecting an annotation with the current timestamp (`kapp.k14s.io/last-renewed-time=`) to the resource. + +**Note**: For precise results use `versioned resources` or `always-replace` update strategy for `non-versioned resources`. + +This annotation is helpful in scenario when you want a resource (like `secret`) get updated/recreated automatically irrespective of no changes to the resource's configuration at predetermined intervals. + +### kapp.k14s.io/deploy-logs + +`kapp.k14s.io/deploy-logs` annotation indicates which Pods' log output to show during deploy + +Possible values: + +- "" (default; equivalent to `for-new`) +- `for-new` (only newly created Pods are tailed) +- `for-existing` (only existing Pods are tailed) +- `for-new-or-existing` (both newly created and existing Pods are tailed) + +Especially useful when added to Jobs. For example, see [examples/resource-ordering/sync-check.yml](https://github.com/carvel-dev/kapp/blob/develop/examples/resource-ordering/sync-check.yml) + +### kapp.k14s.io/deploy-logs-container-names + +`kapp.k14s.io/deploy-logs-container-names` annotation indicates which containers' log output to show during deploy + +Possible values: "" (default), `containerName1`, `containerName1,containerName2` + +### kapp.k14s.io/exists + +Available in v0.43.0+ + +`kapp.k14s.io/exists` verifies that the resource exists in Kubernetes. Kapp does not consider the resource a part of the app (not labeled). + +If the resource is not present, then kapp uses the `exists` operation and asserts that the resource exists in Kubernetes. + +If the resource already exists, kapp does not perform any operation on it (using the `noop` operation). + +Possible values: "". + +Especially useful in scenarios where an external agency such as a controller might be creating a resource that we want to wait for. + +### kapp.k14s.io/noop + +Available in v0.43.0+ + +`kapp.k14s.io/noop` ensures that kapp is aware of the resource. It will not be considered to be part of the app (not labeled). + +kapp always uses the `noop` operation for these resources. + +Possible values: "". + +--- +## Controlling apply via deploy flags + +- `--apply-ignored=bool` explicitly applies ignored changes; this is useful in cases when controllers lose track of some resources instead of for example deleting them +- `--apply-default-update-strategy=string` controls default strategy for all resources (see `kapp.k14s.io/update-strategy` annotation above) +- `--apply-exit-status=bool` (default `false`) controls exit status (`0`: unused, `1`: any error, `2`: no changes applied, `3`: at least one change applied) +- `--wait=bool` (default `true`) controls whether kapp will wait for resource to "stabilize". See [Apply waiting](apply-waiting.md) +- `--wait-ignored=bool` controls whether kapp will wait for ignored changes (regardless whether they were initiated by kapp or by controllers) +- `--logs=bool` (default `true`) controls whether to show logs as part of deploy output for Pods annotated with `kapp.k14s.io/deploy-logs: ""` +- `--logs-all=bool` (deafult `false`) controls whether to show all logs as part of deploy output for all Pods diff --git a/site/content/kapp/docs/v0.61.x/apps.md b/site/content/kapp/docs/v0.61.x/apps.md new file mode 100644 index 000000000..ebaf13fab --- /dev/null +++ b/site/content/kapp/docs/v0.61.x/apps.md @@ -0,0 +1,41 @@ +--- +aliases: [/kapp/docs/latest/apps] +title: Applications +--- + +## Overview + +kapp considers a set of resources with the same label as an application. These resources could span any number of namespaces or could be cluster-wide (e.g. CRDs). + +kapp has two methods of finding resources: + +1. via unique-to-Namespace application name (via `-a my-name` flag), or +2. via user provided label (via `-a label:my-label=val` flag) + +First approach is most common as kapp generates a unique label for each tracked application and associates that with an application name. + +## List + +Applications can be listed via `ls` command: + +```bash +$ kapp ls +``` + +## Deploy + +To create or update an application use `deploy` command: + +```bash +$ kapp deploy -a my-name -f my-app-config/ +``` + +Deploy command consists of two stages: [resource "diff" stage](diff.md), and [resource "apply" stage](apply.md). + +## Delete + +To delete an application use `delete` command: + +```bash +$ kapp delete -a my-name +``` diff --git a/site/content/kapp/docs/v0.61.x/cheatsheet.md b/site/content/kapp/docs/v0.61.x/cheatsheet.md new file mode 100644 index 000000000..9ac162011 --- /dev/null +++ b/site/content/kapp/docs/v0.61.x/cheatsheet.md @@ -0,0 +1,141 @@ +--- +aliases: [/kapp/docs/latest/cheatsheet] +title: Cheatsheet +--- + + +## List + +List all app in the cluster (across all namespaces) + +```bash +kapp ls -A +``` + +Show only specific columns while listing apps + +```bash +kapp ls --column=namespace,name,label +``` + +## Deploy + +Deploy app named `app1` with configuration from `config/`: + +```bash +kapp deploy -a app1 -f config/ -c +``` + +Deploy app named `app1` with configuration piped in (see alternative that does not require `--yes` next): + +```bash +ytt -f config/ | kapp deploy -a app1 -f- -c -y +``` + +Deploy app named `app1` with configuration generated inline and with confirmation dialog: + +```bash +kapp deploy -a app1 -f <(ytt -f config/ ) +``` + +Show more diff context when reviewing changes during deploy: + +```bash +kapp deploy -a app1 -f config/ -c --diff-context=10 +``` + +Show diff and exit successfully (without applying any changes): + +```bash +kapp deploy -a app1 -f config/ --diff-run +``` + +Show logs from all app `Pods` throughout deploy: + +```bash +kapp deploy -a app1 -f config/ --logs-all +``` + +Rewrite all resources to specify `app1-ns` namespace: + +```bash +kapp deploy -a app1 -f config/ --into-ns app1-ns +``` + +## Inspect + +Show summary of all resources in app `app1`: + +```bash +kapp inspect -a app1 +``` + +Show summary organized as a tree of all resources in app `app1`: + +```bash +kapp inspect -a app1 --tree +``` + +Show status subresources for each resource in app `app1`: + +```bash +kapp inspect -a app1 --status +``` + +Show all resources in the cluster: + +```bash +kapp inspect -a 'label:' +``` + +Show all resources in particular namespace (note that it currently does namespace filtering client-side): + +```bash +kapp inspect -a 'label:' --filter-ns some-ns +``` + +Show all resources labeled `tier=web` in the cluster: + +```bash +kapp inspect -a 'label:tier=web' +``` + +Show all `Deployment` resources in the cluster **not** managed by kapp: + +```bash +kapp inspect -a 'label:!kapp.k14s.io/app' --filter-kind Deployment +``` + +## Delete + +Delete resources under particular label (in this example deleting resources associated with some app): + +```bash +kapp delete -a 'label:kapp.k14s.io/app=1578599579922603000' +``` + +## Environment variables + +Environment Variables: + - `FORCE_COLOR`: set to `1` to force colors to the printed. Useful to preserve colors when piping output such as in `kapp list --all-namespaces --tty |& less -R` + +## Misc + +See which labels are used in your cluster (add `--values` to see label values): + +```bash +kapp tools list-labels +``` + +Shows app labels that are still present in the cluster (could be combined with delete command below): + +```bash +kapp tools list-labels --values --tty=false | grep kapp.k14s.io/app +``` + +Delete all app changes older than 500h (v0.12.0+): + +```bash +kapp deploy -a label:kapp.k14s.io/is-app-change --filter-age 500h+ --dangerous-allow-empty-list-of-resources --apply-ignored +``` + diff --git a/site/content/kapp/docs/v0.61.x/command-reference.md b/site/content/kapp/docs/v0.61.x/command-reference.md new file mode 100644 index 000000000..6b48d8122 --- /dev/null +++ b/site/content/kapp/docs/v0.61.x/command-reference.md @@ -0,0 +1,242 @@ +--- +aliases: [/kapp/docs/latest/command-reference] +title: Command Reference +--- + +## App Commands +App commands provides options to deploy, delete, inspect and list apps. + +### deploy +The `kapp deploy` command can be used to deploy resources as a single app to your cluster. + +```bash +# Deploy app 'app1' based on config files in config/ +$ kapp deploy -a app1 -f config/ + +# Deploy app 'app1' while showing full text diff +$ kapp deploy -a app1 -f config/ --diff-changes + +# Deploy app 'app1' based on remote file +$ kapp deploy -a app1 \ + -f https://github.com/...download/v0.6.0/crds.yaml \ + -f https://github.com/...download/v0.6.0/release.yaml +``` + +##### Supported flags: +Common flags: +- `-a`, `--app` _string_, Set app name (or label selector) (format: name, label:key=val, !key) +- `-c`, `--diff-changes` _boolean_, Show changes +- `-f`, `--file`, _strings_, Set file (format: /tmp/foo, https://..., -) (can repeat) +- `-n`, `--namespace` _string_, Specified namespace ($KAPP_NAMESPACE or default from kubeconfig) + +Diff Flags: +- `--diff-against-last-applied`, _boolean_, Show changes against last applied copy when possible (default true) +- `-c`, `--diff-changes` _boolean_, Show changes +- `--diff-changes-yaml`, _boolean_, Print YAML to be applied +- `--diff-context`, _int_, Show number of lines around changed lines (default 2) +- `--diff-exit-status`, _boolean_, Return specific exit status based on number of changes +- `--diff-filter` _string_, Set changes filter (example: {"and":[{"ops":["update"]},{"existingResource":{"kinds":["Deployment"]}]}) +- `--diff-line-numbers`, _boolean_, Show line numbers (default true) +- `--diff-mask`, _boolean_, Apply masking rules (default true) +- `--diff-run`, _boolean_, Show diff and exit successfully without any further action +- `--diff-summary`, _boolean_, Show diff summary (default true) +- `--diff-ui-alpha`, _boolean_, Start UI server to inspect changes (alpha feature) + +Apply flags: +- `--apply-check-interval`, _duration_, Amount of time to sleep between applies (default 1s) +- `--apply-concurrency`, _int_, Maximum number of concurrent apply operations (default 5) +- `--apply-default-update-strategy`, _string_, Change default update strategy +- `--apply-exit-status`, _boolean_, Return specific exit status based on number of changes +- `--apply-ignored`, _boolean_, Set to apply ignored changes +- `--apply-timeout`, _duration_, Maximum amount of time to wait in apply phase (default 15m0s) +- `--dangerous-allow-empty-list-of-resources`, _boolean_, Allow to apply empty set of resources (same as running kapp delete) +- `--dangerous-override-ownership-of-existing-resources`, _boolean_, Steal existing resources from another app + +Wait flags: +- `--wait`, _boolean_, Set to wait for changes to be applied (default true) +- `--wait-check-interval`, _duration_, Amount of time to sleep between checks while waiting (default 3s) +- `--wait-concurrency`, _int_, Maximum number of concurrent wait operations (default 5) +- `--wait-ignored`, _boolean_, Set to wait for ignored changes to be applied +- `--wait-resource-timeout`, _duration_, Maximum amount of time to wait for a resource in wait phase (0s means no timeout) +- `--wait-timeout`, _duration_, Maximum amount of time to wait in wait phase (default 15m0s) + +Resource Filter Flags: +- `--filter`, _string_, Set filter (example: {"and":[{"not":{"resource":{"kinds":["foo%"]}}},{"resource":{"kinds":["!foo"]}}]}) +- `--filter-age`, _string_, Set age filter (example: 5m-, 500h+, 10m-) +- `--filter-kind`, _strings_, Set kinds filter (example: Pod) (can repeat) +- `--filter-kind-name`, _strings_, Set kind-name filter (example: Pod/controller) (can repeat) +- `--filter-kind-ns`, _strings_, Set kind-namespace filter (example: Pod/, Pod/knative-serving) (can repeat) +- `--filter-kind-ns-name`, _strings_, Set kind-namespace-name filter (example: Deployment/knative-serving/controller) (can repeat) +- `--filter-labels`, _strings_, Set label filter (example: x=y) +- `--filter-name`, _strings_, Set name filter (example: controller) (can repeat) +- `--filter-ns`, _strings_. Set namespace filter (example: knative-serving) (can repeat) + +Resource Validation Flags: +- `--allow-all-ns`, _boolean_, Set to allow all namespaces for resources (does not apply to the app itself) +- `--allow-check`, _boolean_, Enable client-side allowing +- `--allow-cluster`, _boolean_, Set to allow cluster level for resources (does not apply to the app itself) +- `--allow-ns`, _strings_, Set allowed namespace for resources (does not apply to the app itself) + +Resource Mangling Flags: +- `--into-ns`, _string_, Place resources into namespace +- `--map-ns`, _strings_, Map resources from one namespace into another (could be specified multiple times) + +Logs Flags: +- `--logs`, _boolean_, Show logs from Pods annotated as 'kapp.k14s.io/deploy-logs' (default true) +- `--logs-all`, _boolean_, Show logs from all Pods + +Available/Other Flags: +- `--app-changes-max-to-keep`, _int_, Maximum number of app changes to keep (default 200) +- `--app-metadata-file-output`, _string_, Set filename to write app metadata +- `--dangerous-disable-gk-scoping`, _boolean_, Disable scoping of resource searching to used GroupKinds +- `--dangerous-ignore-failing-api-services`, _boolean_, Allow to ignore failing APIServices +- `--dangerous-scope-to-fallback-allowed-namespaces`, _boolean_, Scope resource searching to fallback allowed namespaces +- `--default-label-scoping-rules`, _boolean_, Use default label scoping rules (default true) +- `--existing-non-labeled-resources-check`, _boolean_, Find and consider existing non-labeled resources in diff (default true) +- `--existing-non-labeled-resources-check-concurrency`, _int_, Concurrency to check for existing non-labeled resources (default 100) +- `--exit-early-on-apply-error`, _boolean_, Exit quickly on apply failure (default true) +- `--exit-early-on-wait-error`, _boolean_, Exit quickly on wait failure (default true) +- `-h`, `--help`, _boolean_, help for deploy +- `--labels`, _strings_, Set app label (format: key=val, key=) (can repeat) +- `-p`, `--patch`, _boolean_, Add or update existing resources only, never delete any +- `--prev-app`, _string_, Set previous app name +- `--sort`, _boolean_, Sort by namespace, name, etc. (default true) +- `--tty`, _boolean_, Force TTY-like output (default true) + +### inspect +The `kapp inspect` command can be used inspect the resources present in an app. + +```bash +# Inspect app 'app1' +$ kapp inspect -a app1 +``` + +Supported flags: +- `-a`, `--app`, _string_, Set app name (or label selector) (format: name, label:key=val, !key) +- `--dangerous-disable-gk-scoping`, _boolean_, Disable scoping of resource searching to used GroupKinds +- `--dangerous-ignore-failing-api-services`, _boolean_, Allow to ignore failing APIServices +- `--dangerous-scope-to-fallback-allowed-namespaces`, _boolean_, Scope resource searching to fallback allowed namespaces +- `--filter`, _string_, Set filter (example: {"and":[{"not":{"resource":{"kinds":["foo%"]}}},{"resource":{"kinds":["!foo"]}}]}) +- `--filter-age`, _string_, Set age filter (example: 5m-, 500h+, 10m-) +- `--filter-kind`, _strings_, Set kinds filter (example: Pod) (can repeat) +- `--filter-kind-name`, _strings_, Set kind-name filter (example: Pod/controller) (can repeat) +- `--filter-kind-ns`, _strings_, Set kind-namespace filter (example: Pod/, Pod/knative-serving) (can repeat) +- `--filter-kind-ns-name`, _strings_, Set kind-namespace-name filter (example: Deployment/knative-serving/controller) (can repeat) +- `--filter-labels`, _strings_, Set label filter (example: x=y) +- `--filter-name`, _strings_, Set name filter (example: controller) (can repeat) +- `--filter-ns`, _strings_, Set namespace filter (example: knative-serving) (can repeat) +- `-h`, `--help`, _boolean_, help for inspect +- `--managed-fields`, _boolean_ Keep the metadata.managedFields when printing objects +- `-n`, `--namespace`, _string_, Specified namespace ($KAPP_NAMESPACE or default from kubeconfig) +- `--raw`, _boolean_ Output raw YAML resource content +- `--status`, _boolean_ Output status content +- `-t`, `--tree`, _boolean_ Tree view +- `--tty`, _boolean_ Force TTY-like output + +### list +The `kapp list` command can be used to list resources present on the cluster. + +```bash +$ kapp list +``` + +Supported flags: +- `-A`, `--all-namespaces`, _boolean_, List apps in all namespaces +- `--filter-age`, _string_, Set age filter (example: 5m-, 500h+, 10m-) +- `--filter-labels`, _strings_, Set label filter (example: x=y) +- `-h`, `--help`, _boolean_, help for list +- `-n`, `--namespace`, _string_, Specified namespace ($KAPP_NAMESPACE or default from kubeconfig) +- `--tty`, _boolean_, Force TTY-like output + +### logs +The `kapp list` command can be used to print app's pod logs. + +```bash +# Follow all pod logs in app 'app1' +$ kapp logs -a app1 -f + +# Show logs from pods that start with 'web' +$ kapp logs -a app1 -f -m web% +``` + +Supported flags: +- `-a`, `--app`, _string_, Set app name (or label selector) (format: name, label:key=val, !key) +- `-c`, `--container-name`, _strings_, Set container name to filter logs (% acts as wildcard, e.g. 'app%') (can repeat) +- `--container-tag`, _boolean_, Include container tag (default true) +- `-f`, `--follow`, _boolean_, As new pods are added, new pod logs will be printed +- `-h`, `--help`, _boolean_, help for logs +- `--lines`, _int_, Limit to number of lines (use -1 to remove limit) (default 10) +- `-n`, `--namespace`, _string_, Specified namespace ($KAPP_NAMESPACE or default from kubeconfig) +- `-m`, `--pod-name`, _string_, Set pod name to filter logs (% acts as wildcard, e.g. 'app%') +- `--tty`, _boolean_, Force TTY-like output + +### delete +The `kapp delete` command can be used to delete an app from your cluster. + +```bash +$ kapp delete -a app1 +``` + +Supported flags: +- `-a`, `--app`, _string_, Set app name (or label selector) (format: name, label:key=val, !key) +- `--apply-check-interval`, _duration_, Amount of time to sleep between applies (default 1s) +- `--apply-concurrency`, _int_, Maximum number of concurrent apply operations (default 5) +- `--apply-default-update-strategy`, _string_, Change default update strategy +- `--apply-exit-status`, _boolean_, Return specific exit status based on number of changes +- `--apply-ignored`, _boolean_, Set to apply ignored changes +- `--apply-timeout`, _duration_, Maximum amount of time to wait in apply phase (default 15m0s) +- `--dangerous-disable-gk-scoping`, _boolean_, Disable scoping of resource searching to used GroupKinds +- `--dangerous-ignore-failing-api-services`, _boolean_, Allow to ignore failing APIServices +- `--dangerous-scope-to-fallback-allowed-namespaces`, _boolean_, Scope resource searching to fallback allowed namespaces +- `--diff-against-last-applied`, _boolean_, Show changes against last applied copy when possible (default true) +- `-c`, `--diff-changes`, _boolean_, Show changes +- `--diff-changes-yaml`, _boolean_, Print YAML to be applied +- `--diff-context`, _int_, Show number of lines around changed lines (default 2) +- `--diff-exit-status`, _boolean_, Return specific exit status based on number of changes +- `--diff-filter`, _string_, Set changes filter (example: {"and":[{"ops":["update"]},{"existingResource":{"kinds":["Deployment"]}]}) +- `--diff-line-numbers`, _boolean_, Show line numbers (default true) +- `--diff-mask`, _boolean_, Apply masking rules (default true) +- `--diff-run`, _boolean_, Show diff and exit successfully without any further action +- `--diff-summary`, _boolean_, Show diff summary (default true) +- `--diff-ui-alpha`, _boolean_, Start UI server to inspect changes (alpha feature) +- `--exit-early-on-apply-error`, _boolean_, Exit quickly on apply failure (default true) +- `--exit-early-on-wait-error`, _boolean_, Exit quickly on wait failure (default true) +- `--filter`, _string_, Set filter (example: {"and":[{"not":{"resource":{"kinds":["foo%"]}}},{"resource":{"kinds":["!foo"]}}]}) +- `--filter-age`, _string_, Set age filter (example: 5m-, 500h+, 10m-) +- `--filter-kind`, _strings_, Set kinds filter (example: Pod) (can repeat) +- `--filter-kind-name`, _strings_, Set kind-name filter (example: Pod/controller) (can repeat) +- `--filter-kind-ns`, _strings_, Set kind-namespace filter (example: Pod/, Pod/knative-serving) (can repeat) +- `--filter-kind-ns-name`, _strings_, Set kind-namespace-name filter (example: Deployment/knative-serving/controller) (can repeat) +- `--filter-labels`, _strings_, Set label filter (example: x=y) +- `--filter-name`, _strings_, Set name filter (example: controller) (can repeat) +- `--filter-ns`, _strings_, Set namespace filter (example: knative-serving) (can repeat) +- `-h`, `--help`, _boolean_, help for delete +- `-n`, `--namespace`, _string_, Specified namespace ($KAPP_NAMESPACE or default from kubeconfig) +- `--prev-app`, _string_, Set previous app name +- `--tty`, _boolean_, Force TTY-like output (default true) +- `--wait`, _boolean_, Set to wait for changes to be applied (default true) +- `--wait-check-interval`, _duration_, Amount of time to sleep between checks while waiting (default 3s) +- `--wait-concurrency`, _int_, Maximum number of concurrent wait operations (default 5) +- `--wait-ignored`, _boolean_, Set to wait for ignored changes to be applied (default true) +- `--wait-resource-timeout`, _duration_, Maximum amount of time to wait for a resource in wait phase (0s means no timeout) +- `--wait-timeout`, _duration_, Maximum amount of time to wait in wait phase (default 15m0s) + +## Global Flags +- `--color` _boolean_, Set color output (default true) +- `--column` _string_, Filter to show only given columns +- `--debug` _boolean_, Include debug output +- `-h`, `--help` _boolean_, help for kctrl +- `--json` _boolean_, Output as JSON +- `--kube-api-burst`, _int_, Set Kubernetes API client burst limit (default 1000) +- `--kube-api-qps` _float32_, Set Kubernetes API client QPS limit (default 1000) +- `--kubeconfig` _string_, Path to the kubeconfig file ($KCTRL_KUBECONFIG), +- `--kubeconfig-context`_string_, Kubeconfig context override ($KCTRL_KUBECONFIG_CONTEXT) +- `--kubeconfig-yaml` _string_, Kubeconfig contents as YAML ($KCTRL_KUBECONFIG_YAML) +- `--tty` _boolean_, Force TTY-like output (default true) +- `-v`, `--version` _boolean_, version for kctrl +- `-y`, `--yes`, _boolean_, Assumes yes for any prompt + +## Environment variables + +Environment Variables: + - `FORCE_COLOR`: set to `1` to force colors to the printed. Useful to preserve colors when piping output such as in `kctrl app list --tty --all-namespaces |& less -R` diff --git a/site/content/kapp/docs/v0.61.x/config.md b/site/content/kapp/docs/v0.61.x/config.md new file mode 100644 index 000000000..2cb2f326e --- /dev/null +++ b/site/content/kapp/docs/v0.61.x/config.md @@ -0,0 +1,401 @@ +--- +aliases: [/kapp/docs/latest/config] +title: Configuration +--- + +## Overview + +kapp supports custom `Config` resource to specify its own configuration. It's expected to be included with your other Kubernetes configuration. Config resource is never applied to the cluster, though it follows general Kubernetes resource format. Multiple config resources are allowed. + +kapp comes with __built-in configuration__ (see it via `kapp deploy-config`) that includes rules for common resources. + +## Format + +```yaml +apiVersion: kapp.k14s.io/v1alpha1 +kind: Config + +minimumRequiredVersion: 0.23.0 + +rebaseRules: +- path: [spec, clusterIP] + type: copy + sources: [new, existing] + resourceMatchers: + - apiVersionKindMatcher: {apiVersion: v1, kind: Service} + +ownershipLabelRules: +- path: [metadata, labels] + resourceMatchers: + - allMatcher: {} + +labelScopingRules: +- path: [spec, selector] + resourceMatchers: + - apiVersionKindMatcher: {apiVersion: v1, kind: Service} + +templateRules: +- resourceMatchers: + - apiVersionKindMatcher: {apiVersion: v1, kind: ConfigMap} + affectedResources: + objectReferences: + - path: [spec, template, spec, containers, {allIndexes: true}, env, {allIndexes: true}, valueFrom, configMapKeyRef] + resourceMatchers: + - apiVersionKindMatcher: {apiVersion: apps/v1, kind: Deployment} + - path: [spec, template, spec, containers, {allIndexes: true}, envFrom, {allIndexes: true}, configMapRef] + resourceMatchers: + - apiVersionKindMatcher: {apiVersion: apps/v1, kind: Deployment} + +additionalLabels: + department: marketing + cost-center: mar201 + +diffAgainstLastAppliedFieldExclusionRules: +- path: [metadata, annotations, "deployment.kubernetes.io/revision"] + resourceMatchers: + - apiVersionKindMatcher: {apiVersion: apps/v1, kind: Deployment} + +diffAgainstExistingFieldExclusionRules: + - path: [status] + resourceMatchers: + - allMatcher: {} + +diffMaskRules: +- path: [data] + resourceMatchers: + - apiVersionKindMatcher: {apiVersion: v1, kind: Secret} + +preflightRules: +- name: [preflight] + config: + [preflightSpecific]: [data] +``` + +### minimumRequiredVersion + +`minimumRequiredVersion` forces kapp to exit with a validation error if kapp's version is below minimum required version. Available in v0.23.0+. + +### rebaseRules + +`rebaseRules` specify origin of field values. + +kapp rebase rules explicitly define how to merge resources during an update. To read more about why rebase rules are necessary, see [Resource Merge Method](merge-method.md). +For examples of rebase rules in use, see [HPA and Deployment rebase](hpa-deployment-rebase.md) or [PersistentVolumeClaim rebase](rebase-pvc.md). + +- `rebaseRules` (array) list of rebase rules + - `path` (array of strings) specifies location within a resource to rebase. Mutually exclusive with `paths`. Example: `[spec, clusterIP]` + - `paths` (array of `path`) specifies multiple locations within a resource to rebase. This is a convenience for specifying multiple rebase rules with only different paths. Mutually exclusive with `path`. Available in v0.27.0+. + - `type` (string) specifies strategy to modify field values. Allowed values: `copy` or `remove`. `copy` will update the field value; `remove` will delete the field. + - `sources` (array of `new` or `existing`) specifies a preference order for the source of the referenced field value being rebased. `new` refers to an updated resource from user input, where `existing` refers to a resource already in the cluster. If the field value being rebased is not found in any of the sources provided, kapp will error. Only used with `type: copy`. \ + Examples: + - `[existing, new]` – If field value is present in the `existing` resource on cluster, use that value, otherwise use the value in the `new` user input. + - `[existing]` – Only look for field values in resources already on cluster, corresponding value you provide in new resource will be overwritten. + - `resourceMatchers` (array) specifies rules to find matching resources. See various resource matchers below. + - `ytt` specifies choice as [ytt](https://carvel.dev/ytt/) for rebase rule. Available in v0.38.0+. + - `overlayContractV1` allows to use ytt overlay to modify provided resource based on existing resource. + - `overlay.yml` overlay YAML file. + - Following fields are accessible via `data.values` inside ytt: + - `data.values.existing` resource from live cluster + - `data.values.new` resource from config (post-prep) + - `data.values._current` resource after previous rebase rules already applied + +Rebase rule to `copy` the `clusterIP` field value to `Service`/`v1` resources; if `clusterIp` is present in the `new` user input, use that value, otherwise use the value in `existing` resource on cluster: +```yaml +rebaseRules: +- path: [spec, clusterIP] + type: copy + sources: [new, existing] + resourceMatchers: + - apiVersionKindMatcher: {apiVersion: v1, kind: Service} +``` + +Rebase rule to `copy` the `clusterIP` and `healthCheckNodePort` field values from the `existing` resource on cluster, to `Service`/`v1` resources: +```yaml +rebaseRules: +- paths: + - [spec, clusterIP] + - [spec, healthCheckNodePort] + type: copy + sources: [existing] + resourceMatchers: + - apiVersionKindMatcher: {apiVersion: v1, kind: Service} +``` + +See [ytt rebase rule](https://github.com/carvel-dev/kapp/blob/d3ee9a01b5f0d7d5632b6a157ea7d0338730d497/pkg/kapp/config/default.go#L123-L154) (included in default configuration) for retaining cluster added token secret in ServiceAccount's secrets array. + +### ownershipLabelRules + +`ownershipLabelRules` specify locations for inserting kapp generated labels. These labels allow kapp to track which resources belong to which application. For resources that describe creation of other resources (e.g. `Deployment` or `StatefulSet`), configuration may need to specify where to insert labels for child resources that will be created. `kapp.k14s.io/disable-default-ownership-label-rules: ""` (value must be empty) annotation can be be used to exclude an individual resource from default onwership label rules. + +### labelScopingRules + +`labelScopingRules` specify locations for inserting kapp generated labels that scope resources to resources within current application. `kapp.k14s.io/disable-default-label-scoping-rules: ""` (as of v0.33.0+, or use `kapp.k14s.io/disable-label-scoping: ""` in earlier versions) annotation can be used to exclude an individual resource from label scoping. + +### waitRules + +Available in v0.29.0+. + +`waitRules` specify how to wait for resources that kapp does not wait for by default. Each rule provides a way to specify which `status.conditions` indicate success or failure. Once any of the condition matchers successfully match against one of the resource's conditions, kapp will stop waiting for the matched resource and report any failures. (If this functionality is not enough to wait for resources in your use case, please reach out on Slack to discuss further.) + +```yaml +waitRules: +- supportsObservedGeneration: true + conditionMatchers: + - type: Failed + status: "True" + failure: true + - type: Deployed + status: "True" + success: true + resourceMatchers: + - apiVersionKindMatcher: {apiVersion: corp.com/v1, kind: DatabaseInstance} +``` + +```yaml +waitRules: +- supportsObservedGeneration: true + conditionMatchers: + - type: Ready + status: "False" + failure: true + - type: Ready + status: "True" + success: true + supportsObservedGeneration: true # available at condition level from v0.47.0+ + resourceMatchers: + - apiVersionKindMatcher: {apiVersion: corp.com/v1, kind: Application} +``` + +Available in v0.48.0+. + +ytt `waitRules` can be for Custom Resources that don't have `conditions` field in their `status`. This allows users to configure arbitrary rules. `is_done(resource)` method can be defined as part of a ytt waitRule to return the done state based on resource fields. + +```yaml +waitRules: + - ytt: + funcContractV1: + resource.star: | + def is_done(resource): + state = resource.status.currentState + if state == "Failed": + return {"done": True, "successful": False, "message": "Current state as Failed"} + elif state == "Running": + return {"done": True, "successful": True, "message": "Current state as Running"} + else: + return {"done": False, "successful": False, "message": "Not in Failed or Running state"} + end + end + resourceMatchers: + - apiVersionKindMatcher: {apiVersion: , kind: } +``` + +Available in v0.50.0+ + +`unblockChanges` can be used for conditions to unblock any dependent resources. These conditions are treated as non success/failure conditions. It can also be used along with ytt waitRules. + +```yaml +waitRules: +- conditionMatchers: + - type: Progressing + status: "True" + unblockChanges: true + resourceMatchers: + - apiVersionKindMatcher: {apiVersion: corp.com/v1, kind: Application} +``` + +### templateRules + +`templateRules` specify how versioned resources affect other resources. In above example, versioned config maps are said to affect deployments. [Read more about versioned resources](diff.md#versioned-resources). + +### additionalLabels + +`additionalLabels` specify additional labels to apply to all resources for custom uses by the user (added based on `ownershipLabelRules`). + +### diffAgainstLastAppliedFieldExclusionRules + +`diffAgainstLastAppliedFieldExclusionRules` specify which fields should be removed before diff-ing against last applied resource. These rules are useful for fields are "owned" by the cluster/controllers, and are only later updated. For example `Deployment` resource has an annotation that gets set after a little bit of time after resource is created/updated (not during resource admission). It's typically not necessary to use this configuration. + +### diffAgainstExistingFieldExclusionRules + +`diffAgainstExistingFieldExclusionRules` specify which fields should be removed before diff-ing against a resource. These rules are useful for fields that are "owned" by the cluster/controllers, and are only updated later. For example a `Custom Resource Definition` resource has a `status` field that gets altered now and then, especially between a diff and the actual apply step. It's typically not necessary to use this configuration. + +### diffMaskRules + +`diffMaskRules` specify which field values should be masked in diff. By default `v1/Secret`'s `data` fields are masked. Currently only applied to `deploy` command. + +### preflightRules + +Available in v0.61.0+. + +`preflightRules` specify configuration for [preflight checks](preflight.md). Specifying the `name` of a preflight check enables it; additional configuration via the `config` field may be optionally provided. The contents of the `config` field are specific to each preflight check. + +The `--preflight` flag overrides the enabled setting in the configuration: +* If a preflight check is omitted from the `--preflight` flag, it is disabled regardless of its presence in the configuration file. +* If a preflight check is specified in the `--preflight` flag, it is enabled regardless of its absence in the configuration file. + +### changeGroupBindings + +Available in v0.25.0+. + +`changeGroupBindings` bind specified change group to resources matched by resource matchers. This is an alternative to using `kapp.k14s.io/change-group` annotation to add change group to resources. See `kapp deploy-config` for default bindings. + +### changeRuleBindings + +Available in v0.25.0+. + +`changeRuleBindings` bind specified change rules to resources matched by resource matchers. This is an alternative to using `kapp.k14s.io/change-rule` annotation to add change rules to resources. See `kapp deploy-config` for default bindings. + +--- +## Resource matchers + +Resource matchers (as used by `rebaseRules`, `ownershipLabelRules`, `labelScopingRules`, `templateRules`, `diffAgainstLastAppliedFieldExclusionRules`, `diffAgainstExistingFieldExclusionRules` and `diffMaskRules`): + +### allMatcher + +Matches all resources + +```yaml +allMatcher: {} +``` + +### anyMatcher + +Matches resources that match one of matchers + +```yaml +anyMatcher: + matchers: + - apiVersionKindMatcher: {apiVersion: apps/v1, kind: Deployment} + - apiVersionKindMatcher: {apiVersion: extensions/v1alpha1, kind: Deployment} +``` + +### notMatcher + +Matches any resource that does not match given matcher + +```yaml +notMatcher: + matcher: + apiVersionKindMatcher: {apiVersion: apps/v1, kind: Deployment} +``` + +### andMatcher + +Matches any resource that matches all given matchers + +```yaml +andMatcher: + matchers: + - apiVersionKindMatcher: {apiVersion: apps/v1, kind: Deployment} + - hasNamespaceMatcher: {} +``` + +### apiGroupKindMatcher + +```yaml +apiGroupKindMatcher: {apiGroup: apps, kind: Deployment} +``` + +### apiVersionKindMatcher + +```yaml +apiVersionKindMatcher: {apiVersion: apps/v1, kind: Deployment} +``` + +### kindNamespaceNameMatcher + +```yaml +kindNamespaceNameMatcher: {kind: Deployment, namespace: mysql, name: mysql} +``` + +### hasAnnotationMatcher + +Matches resources that have particular annotation + +```yaml +hasAnnotationMatcher: + keys: + - kapp.k14s.io/change-group +``` + +### hasNamespaceMatcher + +Matches any resource that has a non-empty namespace + +```yaml +hasNamespaceMatcher: {} +``` + +Matches any resource with namespace that equals to one of the specified names + +```yaml +hasNamespaceMatcher: + names: [app1, app2] +``` + +### customResourceMatcher + +Matches any resource that is not part of builtin k8s API groups (e.g. apps, batch, etc.). It's likely that over time some builtin k8s resources would not be matched. + +```yaml +customResourceMatcher: {} +``` + +### emptyFieldMatcher + +Available in v0.34.0+. + +Matches any resource that has empty specified field + +```yaml +emptyFieldMatcher: + path: [aggregationRule] +``` + +--- +## Paths + +Path specifies location within a resource (as used `rebaseRules` and `ownershipLabelRules`): + +``` +[spec, clusterIP] +``` + +``` +[spec, volumeClaimTemplates, {allIndexes: true}, metadata, labels] +``` + +``` +[spec, volumeClaimTemplates, {index: 0}, metadata, labels] +``` + +--- +## Config wrapped in ConfigMap + +Available of v0.34.0+. + +Config resource could be wrapped in a ConfigMap to support same deployment configuration by tools that do not understand kapp's `Config` resource directly. ConfigMap carrying kapp config must to be labeled with `kapp.k14s.io/config` and have `config.yml` data key. Such config maps will be applied to the cluster, unlike config given as `Config` resource. + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: my-kapp-config + labels: + kapp.k14s.io/config: "" +data: + config.yml: | + apiVersion: kapp.k14s.io/v1alpha1 + kind: Config + rebaseRules: + - path: [rules] + type: copy + sources: [existing, new] + resourceMatchers: + - notMatcher: + matcher: + emptyFieldMatcher: + path: [aggregationRule] +``` + +NOTE: `kapp` is _only_ affected by a `Config` (whether wrapped in a `ConfigMap` or not) when supplied as a direct input (i.e. as a `-f` argument). Any `ConfigMap` containing a `Config` is already present in the target cluster has _no affect whatsoever_ on `kapp`'s behavior. diff --git a/site/content/kapp/docs/v0.61.x/configmap-migration.md b/site/content/kapp/docs/v0.61.x/configmap-migration.md new file mode 100644 index 000000000..9d1d3a4f7 --- /dev/null +++ b/site/content/kapp/docs/v0.61.x/configmap-migration.md @@ -0,0 +1,125 @@ +--- +aliases: [/kapp/docs/latest/configmap-migration] +title: Configmap Migration (experimental) +--- + +## Overview + +Kapp internally uses a configmap to store information about an application. + +This configmap name has defaulted to the app name supplied during a deploy. `kapp deploy -a `. + +Example: + +```bash +kapp deploy -a my-app -f app.yml --yes + +$ kapp ls +Namespace Name Namespaces Lcs Lca +default my-app default true 7s + +$ kubectl get configmaps + +NAME DATA AGE +my-app 1 1m +``` + +This is challenging when users also want to create a configmap named `my-app` for their application. It is not expected that `kapp` is already using this configmap name. + +## Enabling Configmap migration + +As of v0.47.0+, kapp now supports a new optional boolean environment variable `KAPP_FQ_CONFIGMAP_NAMES` which can be used to migrate **both new and existing configmaps** to the new naming convention: `.apps.k14s.io`. + +- `KAPP_FQ_CONFIGMAP_NAMES=true` opts into the new kapp behavior. +- `KAPP_FQ_CONFIGMAP_NAMES=false` maintains the current kapp behavior. + +*Important Note: The app name is not being changed, only the configmap name, all references to the app name can remain the same.* + +### Examples + +#### Deploy new App + +```bash +export KAPP_FQ_CONFIGMAP_NAMES=true + +kapp deploy -a my-app -f app.yml --yes + +$ kapp ls +Namespace Name Namespaces Lcs Lca +default my-app default true 7s + +$ kubectl get configmaps + +NAME DATA AGE +my-app.apps.k14s.io 1 1m +``` + +#### Deploy existing App + +```bash +$ kapp ls +Namespace Name Namespaces Lcs Lca +default my-app default true 7s + +export KAPP_FQ_CONFIGMAP_NAMES=true + +$ kapp deploy -a my-app -f app.yml --yes + +$ kubectl get configmaps + +NAME DATA AGE +my-app.apps.k14s.io 1 1m +``` + +#### Delete + +```bash +# With migration enabled +$ KAPP_FQ_CONFIGMAP_NAMES=true kapp delete -a my-app + +Changes + +Namespace Name Kind Conds. Age Op Op st. Wait to Rs Ri +default simple-app Deployment 2/2 t 28m delete - delete ok - + +# With migration disabled +$ KAPP_FQ_CONFIGMAP_NAMES=false kapp delete -a my-app + +App 'my-app' (namespace: default) does not exist + +``` + +### Caveats + +1. Migrated apps will show up with the suffix with previous versions of kapp (0.46.0-): + +```bash +export KAPP_FQ_CONFIGMAP_NAMES=true + +kapp deploy -a my-app -f app.yml --yes + +$ kapp ls +Namespace Name Namespaces Lcs Lca +default my-app default true 7s + +# With old kapp versions +$ kapp ls +Namespace Name Namespaces Lcs Lca +default my-app.apps.k14s.io default true 7s +``` + +### Opting out after migration + +To return to the previous configmap naming convention, the following steps must be followed: + +1. `kubectl get configmap my-app.apps.k14s.io -o yaml > app.yml` + +2. Find the `metadata.name` field in `app.yml` and remove the suffix `.apps.k14s.io` + +3. Find the annotation named `kapp.k14s.io/is-configmap-migrated` in `metadata.annotations` and remove it + +4. `kubectl create -f app.yml` + +5. `kubectl delete configmap my-app.apps.k14s.io` + +*Important Note: Ensure the configmap with suffix `apps.k14s.io` is deleted after opting-out!* diff --git a/site/content/kapp/docs/v0.61.x/dangerous-flags.md b/site/content/kapp/docs/v0.61.x/dangerous-flags.md new file mode 100644 index 000000000..14bccdb53 --- /dev/null +++ b/site/content/kapp/docs/v0.61.x/dangerous-flags.md @@ -0,0 +1,38 @@ +--- +aliases: [/kapp/docs/latest/dangerous-flags] +title: Dangerous Flags +--- + +## Overview + +There are several flags in `kapp deploy/delete/etc.` commands that might be helpful in rare cases, but can cause problems if used improperly. These are their stories: + +## `--dangerous-allow-empty-list-of-resources` + +This flag allows `kapp deploy` to accept empty set of new resources. Given that kapp deploy converges set of resources, when empty set is provided, kapp will delete all existing resources. + +This commonly happens unintentionally. When configuration is piped into kapp (e.g. `ytt -f config/ | kapp deploy -f- ...`) and resource producing command fails (ytt in this example), kapp will not receive any resources by the time is closes. Since providing empty set of resources intentionally is pretty rare, this functionality is behind a flag. + +## `--dangerous-override-ownership-of-existing-resources` + +This flag allows `kapp deploy` to take ownership of resources that are already associated with another application (i.e. already has `kapp.k14s.io/app` label with a different value). + +Most commonly user may have _unintentionally_ included resource that is already deployed, hence by default we do not want to override that resource with a new copy. This may happen when multiple apps accidently specified same resource (i.e. same name under same namespace). In most cases this is not what user wants. + +This flag may be useful in cases when multiple applications (managed by kapp) need to be merged into one, or may be previously owning application have been deleted but its resources were kept. + +Note that by default if resource is given to kapp and it already exists in the cluster, and is not owned by another application, kapp will label it to belong to deploying app. + +## `--dangerous-ignore-failing-api-services` + +In some cases users may encounter that they have misbehaving `APIServices` within they cluster. Since `APIServices` affect how one finds existing resources within a cluster, by default kapp will show error similar to below and stop: + +``` +Error: ... unable to retrieve the complete list of server APIs: <...>: the server is currently unable to handle the request +``` + +In cases when APIService cannot be fixed, this flag can be used to let kapp know that it is okay to proceed even though it's not able to see resources under that `APIService`. Note when this flag is used, kapp will effectively think that resources under misbehaving `APIService` do not exist. + +## `--dangerous-disable-gk-scoping` + +In `kapp deploy` resource searching is scoped to the GroupKinds used in the app, to keep the number of api requests to the api server to a minimum. This flag can be used to disable the scoping and search across all GroupKinds. diff --git a/site/content/kapp/docs/v0.61.x/diff.md b/site/content/kapp/docs/v0.61.x/diff.md new file mode 100644 index 000000000..d0535d9ec --- /dev/null +++ b/site/content/kapp/docs/v0.61.x/diff.md @@ -0,0 +1,378 @@ +--- +aliases: [/kapp/docs/latest/diff] +title: Diff stage +--- +## Overview + +kapp compares resources specified in files against resources that exist in Kubernetes API. Once change set is calculated, it provides an option to apply it (see [Apply](apply.md) section for further details). + +There are five different types of operations: `create`, `update`, `delete`, `noop` (shown as empty), `exists` (added in v0.43.0). Seen in `Op` column of diff summary table. Additionally there is `Op strategy` column (shorted as `Op st.`), added in v0.31.0+, that shows supplemental information how operation will be performed (for example [`fallback on replace`](apply.md#kappk14sioupdate-strategy) for `update` operation). + +There are three different types of waiting: `reconcile` (waits until resource has converged to its desired state; see [apply waiting](apply-waiting.md) for waiting semantics), `delete` (waits until resource is gone), `noop` (shown as empty). Seen in `Wait to` column of diff summary table. + +## Diff strategies + +There are two diff strategies used by kapp: + +1. kapp compares against last applied resource content (previously applied by kapp; stored in annotation `kapp.k14s.io/original`) **if** there were no outside changes done to the resource (i.e. done outside of kapp, for example, by another team member or controller); kapp tries to use this strategy as much as possible to produce more user-friendly diffs. + +2. kapp compares against live resource content **if** it detects there were outside changes to the resource (hence, sometimes you may see a diff that shows several deleted fields even though these fields are not specified in the original file) + +Strategy is selected for each resource individually. You can control which strategy is used for all resources via `--diff-against-last-applied=bool` flag. + +Related: [rebase rules](config.md/#rebaserules). + +## Versioned Resources + +In some cases it's useful to represent an update to a resource as an entirely new resource. Common example is a workflow to update ConfigMap referenced by a Deployment. Deployments do not restart their Pods when ConfigMap changes making it tricky for wide variety of applications for pick up ConfigMap changes. kapp provides a solution for such scenarios, by offering a way to create uniquely named resources based on an original resource. + +Anytime there is a change to a resource marked as a versioned resource, entirely new resource will be created instead of updating an existing resource. + +To make resource versioned, add `kapp.k14s.io/versioned` annotation with an empty value. Created resource follow `{resource-name}-ver-{n}` naming pattern by incrementing `n` any time there is a change. + +Example: +```yaml +--- +apiVersion: v1 +kind: Secret +metadata: + name: secret-sa-sample + annotations: + kapp.k14s.io/versioned: "" +``` +This will create versioned resource named `secret-sa-sample-ver-1` + +```bash +Namespace Name Kind Conds. Age Op Op st. Wait to Rs Ri +default secret-sa-sample-ver-1 Secret - - create - reconcile - - + +Op: 1 create, 0 delete, 0 update, 0 noop, 0 exists +Wait to: 1 reconcile, 0 delete, 0 noop +``` + +Additionally kapp follows configuration rules (default ones, and ones that can be provided as part of application) to find and update object references (since new resource name is not something that configuration author knew about). + +{{< detail-tag "Example" >}} +Sample Config +```yaml +apiVersion: kapp.k14s.io/v1alpha1 +kind: Config +templateRules: + - resourceMatchers: + - apiVersionKindMatcher: {apiVersion: v1, kind: ConfigMap} + affectedResources: + objectReferences: + - resourceMatchers: + - apiVersionKindMatcher: {apiVersion: apps/v1, kind: Deployment} + path: [spec, template, spec, containers, {allIndexes: true}, env, {allIndexes: true}, valueFrom, configMapKeyRef] +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: special-config + annotations: + kapp.k14s.io/versioned: "" +data: + special.how: very-good +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-deployment + labels: + app: nginx +spec: + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx:1.14.2 + ports: + - containerPort: 80 + env: + - name: SPECIAL_LEVEL_KEY + valueFrom: + configMapKeyRef: + name: special-config + key: special.how +``` +Here we have specified the configuration rules that will update the ConfigMap object reference in resources of Kind Deployment. Here `ConfigMap` special-config is marked as versioned so anytime there is an update it will create a new resource with name `special-config-ver-{n}` and update the same name in resource of kind `Deployment` under `configMapKeyRef`. This example is part of [default configuration rule](https://github.com/carvel-dev/kapp/blob/28b17b775558ef4c64ce27a5655b81c00c8a2f59/pkg/kapp/config/default.go#L299) that kapp follows. +{{< /detail-tag >}} + +As of v0.38.0+, `kapp.k14s.io/versioned-keep-original` annotation can be used in conjunction with `kapp.k14s.io/versioned` to have the original resource (resource without `-ver-{n}` suffix in name) along with versioned resource. + +Example: +```yaml +--- +apiVersion: v1 +kind: Secret +metadata: + name: secret-sa-sample + annotations: + kapp.k14s.io/versioned: "" + kapp.k14s.io/versioned-keep-original: "" +``` +This will create two resources one with original name `secret-sa-sample` and one with `-ver-{n}` suffix in name `secret-sa-sample-ver-1`. +```bash +Namespace Name Kind Conds. Age Op Op st. Wait to Rs Ri +default secret-sa-sample Secret - - create - reconcile - - +^ secret-sa-sample-ver-1 Secret - - create - reconcile - - + +Op: 2 create, 0 delete, 0 update, 0 noop, 0 exists +Wait to: 2 reconcile, 0 delete, 0 noop +``` + +You can control number of kept resource versions via `kapp.k14s.io/num-versions=str(int)` annotation, e.g. `kapp.k14s.io/num-versions: "5"`. + +As of v0.41.0+, the `kapp.k14s.io/versioned-explicit-ref` can be used to explicitly refer to a versioned resource. This annotation allows a resource to be updated whenever a new version of the referred resource is created. + +Multiple annotations with the prefix `kapp.k14s.io/versioned-explicit-ref.`(Note the "." at the end) can be used to define multiple explicit references. + +Example: +```yaml +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: config-1 + annotations: + kapp.k14s.io/versioned: "" +data: + foo: bar +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: config-2 + annotations: + kapp.k14s.io/versioned-explicit-ref: | + apiVersion: v1 + kind: ConfigMap + name: config-1 +data: + foo: bar +``` +Here, `config-2` explicitly refers `config-1` and is updated with the latest versioned name when `config-1` is versioned. +```bash +@@ create configmap/config-1-ver-2 (v1) namespace: default @@ + ... + 1, 1 data: + 2 - foo: bar + 2 + foo: alpha + 3, 3 kind: ConfigMap + 4, 4 metadata: +@@ update configmap/config-2 (v1) namespace: default @@ + ... + 8, 8 kind: ConfigMap + 9 - name: config-1-ver-1 + 9 + name: config-1-ver-2 + 10, 10 creationTimestamp: "2021-09-29T17:27:34Z" + 11, 11 labels: + +Changes + +Namespace Name Kind Conds. Age Op Op st. Wait to Rs Ri +default config-1-ver-2 ConfigMap - - create - reconcile - - +^ config-2 ConfigMap - 14s update - reconcile ok - +``` + +Try deploying [redis-with-configmap example](https://github.com/carvel-dev/kapp/tree/develop/examples/gitops/redis-with-configmap) and changing `ConfigMap` in a next deploy. + +--- +## Controlling diff via resource annotations + +### kapp.k14s.io/disable-original + +kapp, by default, records the resource copy into its annotation `kapp.k14s.io/original` while applying the resource to the cluster. + +{{< detail-tag "Example" >}} +Sample Config +```yaml +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: config-1 + namespace: default +data: + foo: bar +``` +After deploying the resource, kapp added the annotation `kapp.k14s.io/original` with the content of the resource that was given to kapp: + +```bash +apiVersion: v1 +kind: ConfigMap +metadata: + name: config-1 + namespace: default + annotations: + kapp.k14s.io/original: '{ "apiVersion": "v1", "kind": "ConfigMap", ...snip... }' +data: + foo: bar +``` +{{< /detail-tag >}} + +`kapp.k14s.io/disable-original` annotation controls whether to record provided resource copy (rarely wanted) + +Possible values: "" (empty). In some cases it's not possible or wanted to record applied resource copy into its annotation `kapp.k14s.io/original`. One such case might be when resource is extremely lengthy (e.g. long ConfigMap or CustomResourceDefinition) and will exceed annotation value max length of 262144 bytes. + +{{< detail-tag "Example" >}} +Sample Config +```yaml +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: config-1 + namespace: default + annotations: + kapp.k14s.io/disable-original: "" +data: + foo: bar +``` +After deploying the resource, kapp didn't add the annotation `kapp.k14s.io/original` this time: + +```bash +apiVersion: v1 +kind: ConfigMap +metadata: + name: config-1 + namespace: default + annotations: + kapp.k14s.io/disable-original: "" +data: + foo: bar +``` +{{< /detail-tag >}} + +--- +## Controlling diff via deploy flags + +Diff summary shows quick information about what's being changed: +- `--diff-summary=bool` (default `true`) shows diff summary, listing how resources have changed + {{< detail-tag "Example" >}} +Sample config +```yaml +--- +apiVersion: v1 +kind: Secret +metadata: + name: sample +stringData: + foo: bar +``` +```bash +$ kapp deploy -a sample-secret -f config.yaml --diff-summary=true +Target cluster 'https://127.0.0.1:56540' (nodes: kind-control-plane) + +Changes + +Namespace Name Kind Conds. Age Op Op st. Wait to Rs Ri +default sample Secret - - create - reconcile - - + +Op: 1 create, 0 delete, 0 update, 0 noop, 0 exists +Wait to: 1 reconcile, 0 delete, 0 noop + +Continue? [yN]: +``` + {{< /detail-tag >}} + +Diff changes (line-by-line diffs) are useful for looking at actual changes, when app is re-deployed: +- `--diff-changes=bool` (`-c`) (default `false`) shows line-by-line diffs +- `--diff-context=int` (default `2`) controls number of lines to show around changed lines +- `--diff-mask=bool` (default `true`) controls whether to mask sensitive fields + {{< detail-tag "Example" >}} +Sample config +```yaml +--- +apiVersion: v1 +kind: Secret +metadata: + name: sample +stringData: + foo: bar +``` +```bash +# deploy sample-secre app +$ kapp deploy -a sample-secret -f config.yaml + +#update config +... +stringData: + foo: bars +... + +# re-deploy sample-secret app with required diff-changes flag to see line by line changes +$ kapp deploy -a sample-secret -f config.yaml --diff-changes=true --diff-context=4 +Target cluster 'https://127.0.0.1:56540' (nodes: kind-control-plane) + +@@ update secret/sample (v1) namespace: default @@ + ... + 30, 30 resourceVersion: "244751" + 31, 31 uid: b2453c2a-8dc8-4ed1-9b59-791547f78ea8 + 32, 32 stringData: + 33 - foo: <-- value not shown (#1) + 33 + foo: <-- value not shown (#2) + 34, 34 + +Changes + +Namespace Name Kind Conds. Age Op Op st. Wait to Rs Ri +default sample Secret - 7m update - reconcile ok - + +Op: 0 create, 0 delete, 1 update, 0 noop, 0 exists +Wait to: 1 reconcile, 0 delete, 0 noop + +Continue? [yN]: + +# --diff-mask=true by default, note the masked value for secret data + +# try out kapp deploy -a sample-secret -f config.yaml --diff-mask=false --diff-changes=true --diff-context=2 +``` + {{< /detail-tag >}} + +Controlling how diffing is done: + +- `--diff-against-last-applied=bool` (default `true`) forces kapp to use particular diffing strategy (see above). +- `--diff-run=bool` (default `false`) set the flag to true, to stop after showing diff information. +- `--diff-exit-status=bool` (default `false`) controls exit status for diff runs (`0`: unused, `1`: any error, `2`: no changes, `3`: pending changes) + {{< detail-tag "Example" >}} + Sample config +```yaml +--- +apiVersion: v1 +kind: Secret +metadata: + name: sample +stringData: + foo: bar +``` +```bash +# deploy secret-sample app +$ kapp deploy -a secret-sample -f config.yaml --diff-run=true --diff-exit-status=true +Target cluster 'https://127.0.0.1:56540' (nodes: kind-control-plane) + +Changes + +Namespace Name Kind Conds. Age Op Op st. Wait to Rs Ri +default sample Secret - - create - reconcile - - + +Op: 1 create, 0 delete, 0 update, 0 noop, 0 exists +Wait to: 1 reconcile, 0 delete, 0 noop + +kapp: Error: Exiting after diffing with pending changes (exit status 3) + +# note that kapp exits after diff and displays the exit status + +``` + {{< /detail-tag >}} + +Diff filter allows to filter changes based on operation (add/update/delete), newResource (configuration provided to kapp) and existingResource (resources in Kubernetes cluster) + +- `--diff-filter='{"and":[{"ops":["update"]},{"existingResource":{"kinds":["Deployment"]}]}'` will keep the resources which are getting updated and were of kind Deployment. diff --git a/site/content/kapp/docs/v0.61.x/faq.md b/site/content/kapp/docs/v0.61.x/faq.md new file mode 100644 index 000000000..50318f573 --- /dev/null +++ b/site/content/kapp/docs/v0.61.x/faq.md @@ -0,0 +1,128 @@ +--- +aliases: [/kapp/docs/latest/faq] +title: FAQ +--- + +## Migrating from `kubectl apply` to kapp + +Switching from `kubectl apply` to `kapp deploy` will allow kapp to adopt resources mentioned in a given config. +However, kapp will try to insert a few of its labels in bodies of some resources, like Deployments, which may fail due to those resources having immutable fields that kapp tries to update (spec.selector on Deployments). + +To prevent this failure, add the [`kapp.k14s.io/disable-default-label-scoping-rules: ""` annotation](config.md#labelscopingrules) as a [kapp configuration](config.md) to prevent kapp from touching the immutable fields when adopting a resource. + +Additional Resources: [GitHub Issue](https://github.com/carvel-dev/kapp/issues/204), [Slack Thread](https://kubernetes.slack.com/archives/CH8KCCKA5/p1606079730457700) + +## `Error: Asking for confirmation: EOF` + +This probably means you have piped configuration into kapp and did not specify `--yes` (`-y`) flag to continue. It's necessary because kapp can no longer ask for confirmation via stdin. Feel free to re-run the command with `--diff-changes` (`-c`) to make sure pending changes are correct. Instead of using a pipe you can also use an anonymous fifo keeping stdin free for the confirmation prompt, e.g. `kapp deploy -a app1 -f <(ytt -f config/)` + +--- +## Where to store app resources (i.e. in which namespace)? + +See [state namespace](state-namespace.md) doc page. + +--- +## `... Field is immutable` error + +> After changing the labels/selectors in one of my templates, I'm getting the `MatchExpressions:[]v1.LabelSelectorRequirement(nil)}: field is immutable (reason: Invalid)` errors on deployment resource. Is there a way to tell kapp to force the change? + +[via slack](https://kubernetes.slack.com/archives/CH8KCCKA5/p1565600090224400) + +Some fields on a resource are immutable. kapp provides a `kapp.k14s.io/update-strategy` annotation that controls how kapp will update resource. One of the strategies is `fallback-on-replace` which will have kapp recreate an object (delete, wait, then create) if initial update results in `Invalid` error. See [Controlling apply via resource annotations](apply.md#controlling-apply-via-resource-annotations) for details. + +--- +## `Job.batch is invalid: ... spec.selector: Required value` error + +`batch.Job` resource is augmented by the Job controller with unique labels upon its creation. When using kapp to subsequently update existing Job resource, API server will return `Invalid` error since given configuration does not include `spec.selector`, and `job-name` and `controller-uid` labels. kapp's [rebase rules](config.md#rebaserules) can be used to copy over necessary configuration from server side copy; however, since Job resource is mostly immutable, we recommend to use [`kapp.k14s.io/update-strategy` annotation](apply.md#kappk14sioupdate-strategy) set to `fallback-on-replace` to recreate Job resource with any updates. + +--- +## Updating Deployments when ConfigMap changes + +> Can kapp force update on ConfigMaps in Deployments/DaemonSets? Just noticed that it didn't do that and I somehow expected it to. + +[via slack](https://kubernetes.slack.com/archives/CH8KCCKA5/p1565624685226400) + +kapp has a feature called [versioned resources](diff.md#versioned-resources) that allows kapp to create uniquely named resources instead of updating resources with changes. Resources referencing versioned resources are forced to be updated with new names, and therefore are changed, thus solving a problem of how to propagate changes safely. + +--- +## Quick way to find common kapp command variations + +See [cheatsheet](cheatsheet.md). + +--- +## Limit number of ReplicaSets for Deployments + +> Everytime I do a new deploy w/ kapp I see a new replicaset, along with all of the previous ones. + +[via slack](https://kubernetes.slack.com/archives/CH8KCCKA5/p1565887856281400) + +`Deployment` resource has a field `.spec.revisionHistoryLimit` that controls how many previous `ReplicaSets` to keep. See [Deployment's clean up polciy](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#clean-up-policy) for more details. + +--- +## Changes detected immediately after successful deploy + +Sometimes Kubernetes API server will convert submitted field values into their canonical form server-side. This will be detected by kapp as a change during a next deploy. To avoid such changes in future, you will have to change your provided field values to what API server considers as canonical. + +``` +... +186 - cpu: "2" +187 - memory: 1Gi + 170 + cpu: 2000m + 171 + memory: 1024Mi +... +``` + +Consider using [ytt](/ytt) and [its overlay feature](/ytt/docs/latest/lang-ref-ytt-overlay/) to change values if you do not control source configuration. + +--- +## Changes detected after resource is modified server-side + +There might be cases where other system actors (various controllers) may modify resource outside of kapp. Common example is Deployment's `spec.replicas` field is modified by Horizontal Pod Autoscaler controller. To let kapp know of such external behaviour use custom `rebaseRules` configuration (see [HPA and Deployment rebase](hpa-deployment-rebase.md) for details). + +--- +## Colors are not showing up in my CI build, in my terminal, etc. + +Try setting `FORCE_COLOR=1` environment variable to force enabling color output. Available in v0.23.0+. + +--- +## How can I version apps deployed by kapp? + +kapp itself does not provide any notion of versioning, since it's just a tool to reconcile config. We recommend to include a ConfigMap in your deployment with application metadata e.g. git commit, release notes, etc. + +--- +## `Resource ... is associated with a different label value` + +Resource ownership is tracked by app labels. kapp expects that each resource is owned by exactly one app. + +If you are receiving this error and are using correct app name, it might be that you are targeting wrong namespace where app is located. Use `--namespace` to set correct namespace. + +Additional resources: [State Namespace](state-namespace.md), [Slack Thread](https://kubernetes.slack.com/archives/CH8KCCKA5/p1589264289257000) + +--- +## Why does kapp hang when trying to delete a resource? + +By default, kapp won't delete resources it didn't create. You can see which resources are owned by kapp in output of `kapp inspect -a app-name` in its `Owner` column. You can force kapp to apply this ignored change using `--apply-ignored` [flag](apply.md#controlling-apply-via-deploy-flags). Alternatively if you are able to set [kapp.k14s.io/owned-for-deletion](apply.md#kappk14sioowned-for-deletion) annotation on resource that will be created, kapp will take that as a request to "own it" for deletion. This comes in handy for example with PVCs created by StatefulSet. + +--- +## How does kapp handle merging? + +kapp explicitly decided against _basic_ 3 way merge, instead allowing the user to specify how to resolve conflicts via rebase rules. + +Resources: [merge method](merge-method.md), [rebase rules](config.md#rebaserules) + +--- +## Can I force an update for a change that does not produce a diff? + +If kapp does not detect changes, it won't perform an update. To force changes every time you can set [`kapp.k14s.io/nonce`](apply.md#kappk14siononce) annotation. That way, every time you deploy the resource will appear to have changes. + +--- +## How can I remove decorative headings from kapp inspect output? + +Use `--tty=false` flag which will disable decorative output. Example: `kapp inspect --raw --tty=false`. + +Additional resources: [tty flag in kapp code](https://github.com/carvel-dev/kapp/blob/3f3e207d7198cdedd6985761ecb0d9616a84e305/pkg/kapp/cmd/ui_flags.go#L20) + +--- +## How can I get kapp to skip waiting on some resources? + +kapp allows to control waiting behavior per resource via [resource annotations](apply-waiting.md#controlling-waiting-via-resource-annotations). When used, the resource will be applied to the cluster, but will not wait to reconcile. diff --git a/site/content/kapp/docs/v0.61.x/gitops.md b/site/content/kapp/docs/v0.61.x/gitops.md new file mode 100644 index 000000000..156a06c19 --- /dev/null +++ b/site/content/kapp/docs/v0.61.x/gitops.md @@ -0,0 +1,17 @@ +--- +aliases: [/kapp/docs/latest/gitops] +title: GitOps +--- + +## Using kapp with GitOps workflow + +kapp provides a set of commands to make GitOps workflow very easy. Assuming that you have a CI environment or some other place where `kapp` can run based on a trigger (e.g. for every Git repo change) or continuously (e.g. every 5 mins), you can use following command: + +```bash +$ ls my-repo +. .. app1/ app2/ app3/ + +$ kapp app-group deploy -g my-env --directory my-repo +``` + +Above command will deploy an application for each subdirectory in `my-repo` directory (in this case `app1`, `app2` and `app3`). It will also remove old applications if subdirectories are deleted. diff --git a/site/content/kapp/docs/v0.61.x/hpa-deployment-rebase.md b/site/content/kapp/docs/v0.61.x/hpa-deployment-rebase.md new file mode 100644 index 000000000..722c9bc68 --- /dev/null +++ b/site/content/kapp/docs/v0.61.x/hpa-deployment-rebase.md @@ -0,0 +1,42 @@ +--- +aliases: [/kapp/docs/latest/hpa-deployment-rebase] +title: HPA and Deployment rebase +--- +## HPA and Deployment rebase + +Here is an example on how to use custom `rebaseRules` to "prefer" server chosen value for `spec.replicas` field for a particular Deployment. + +```yaml +apiVersion: kapp.k14s.io/v1alpha1 +kind: Config +rebaseRules: +- path: [spec, replicas] + type: copy + sources: [existing, new] + resourceMatchers: + - kindNamespaceNameMatcher: + kind: Deployment + namespace: my-ns + name: my-app +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-app + namespace: my-ns +... +--- +apiVersion: autoscaling/v1 +kind: HorizontalPodAutoscaler +metadata: + name: my-app + namespace: my-ns +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: my-app + minReplicas: 1 + maxReplicas: 10 + targetCPUUtilizationPercentage: 50 +``` diff --git a/site/content/kapp/docs/v0.61.x/install.md b/site/content/kapp/docs/v0.61.x/install.md new file mode 100644 index 000000000..c4d30dbad --- /dev/null +++ b/site/content/kapp/docs/v0.61.x/install.md @@ -0,0 +1,58 @@ +--- +aliases: [/kapp/docs/latest/install] +title: Install +--- + +## Via script (macOS or Linux) + +(Note that `install.sh` script installs other Carvel tools as well.) + +Install binaries into specific directory: + +```bash +$ mkdir local-bin/ +$ curl -L https://carvel.dev/install.sh | K14SIO_INSTALL_BIN_DIR=local-bin bash + +$ export PATH=$PWD/local-bin/:$PATH +$ kapp version +``` + +Or system wide: + +```bash +$ wget -O- https://carvel.dev/install.sh > install.sh + +# Inspect install.sh before running... +$ sudo bash install.sh +$ kapp version +``` + +## Via Homebrew (macOS or Linux) + +Based on [github.com/carvel-dev/homebrew](https://github.com/carvel-dev/homebrew). + +```bash +$ brew tap carvel-dev/carvel +$ brew install kapp +$ kapp version +``` + +## Specific version from a GitHub release + +To download, click on one of the assets in a [chosen GitHub release](https://github.com/carvel-dev/kapp/releases), for example for 'kapp-darwin-amd64'. + +```bash +# **Compare binary checksum** against what's specified in the release notes +# (if checksums do not match, binary was not successfully downloaded) +$ shasum -a 256 ~/Downloads/kapp-darwin-amd64 +08b25d21675fdc77d4281c9bb74b5b36710cc091f30552830604459512f5744c /Users/pivotal/Downloads/kapp-darwin-amd64 + +# Move binary next to your other executables +$ mv ~/Downloads/kapp-darwin-amd64 /usr/local/bin/kapp + +# Make binary executable +$ chmod +x /usr/local/bin/kapp + +# Check its version +$ kapp version +``` diff --git a/site/content/kapp/docs/v0.61.x/integrating-with-other-tools.md b/site/content/kapp/docs/v0.61.x/integrating-with-other-tools.md new file mode 100644 index 000000000..aa84510bf --- /dev/null +++ b/site/content/kapp/docs/v0.61.x/integrating-with-other-tools.md @@ -0,0 +1,27 @@ +--- +aliases: [/kapp/docs/latest/integrating-with-other-tools] +title: Integrating with Other Tools +--- + +**Note:** This is a non-exhaustive list of integrations + +## ytt and kbld + +We recommend to use kapp with [ytt](/ytt) and [kbld](/kbld) to cover your configuration templating and image building needs. Typical workflow may look like this: + +```bash +ytt -f config/ | kbld -f - | kapp deploy -a app1 -f- -c -y +``` + +## Helm + +If you want to take advantage of both Helm templating and kapp deployment mechanisms, you can use `helm template` command to build configuration, and have kapp apply to the cluster: + +```bash +helm template ... | kapp deploy -a app1 -f- -c -y +``` + +## PV labeling controller + +If you want to have better visibility into which persistent volumes (PVs) are associated with persistent volume claims (PVCs), you can install [https://github.com/k14s/pv-labeling-controller](https://github.com/k14s/pv-labeling-controller) so that it copies several kapp applied labels to associated PVs. Once that's done you will see PVs in `kapp inspect` output. + diff --git a/site/content/kapp/docs/v0.61.x/merge-method.md b/site/content/kapp/docs/v0.61.x/merge-method.md new file mode 100644 index 000000000..ae06ce516 --- /dev/null +++ b/site/content/kapp/docs/v0.61.x/merge-method.md @@ -0,0 +1,27 @@ +--- +aliases: [/kapp/docs/latest/merge-method] +title: "Resource Merge Method" +--- + +## Why not basic 3 way merge? + +kapp explicitly decided to _not_ do basic 3 way merge, and instead allow the user to specify how to resolve "conflicts". Here is our thinking: + +- you as an operator have a set of files (input files given to kapp via -f) which describe desired configuration +- cluster has resources that need to be converged to whatever input files specify, with one exception: in some cases, cluster is the source of truth for certain information (but not most) and should keep that state on resources (common examples: some annotation on Deployment, clusterIP on Service, etc.) + +Given information above there are multiple ways to converge: + +- make assumptions about how to merge things (basic 3 way merge, what kubectl and helm does afaik) +- be explicit about how to merge things (kapp with rebase rules) +- or, just override + +Overriding is not really an option as it removes potentially important cluster changes (e.g. removes replicas value as scaled by HPA). + +Regarding explicit vs implicit: we decided to go with the explicit option. kapp allows users to add [rebase rules](config.md#rebaserules) to specify exactly which information to retain from existing resources. That gives control to the user to decide what's important to be kept based on cluster state and what's not. This method ensures that there are no _surprising_ changes left in the cluster (if basic 3 way merge was used, then user cannot confidently know how final resource will look like; ... imagine if you had a field `allowUnauthenticatedRequests: true` in some resource that someone flipped on in your cluster, and your configs never specified it; it would not be removed unless you decide to also specify this field in your configs). + +kapp comes with some common k8s rebase rules. you can see them via `kapp deploy-config`. + +tldr: kapp takes user provided config as the only source of truth, but also allows to explicitly specify that certain fields are cluster controlled. This method guarantees that clusters don't drift, which is better than what basic 3 way merge provides. + +Originally answered [here](https://github.com/carvel-dev/kapp/issues/58#issuecomment-559214883). diff --git a/site/content/kapp/docs/v0.61.x/preflight.md b/site/content/kapp/docs/v0.61.x/preflight.md new file mode 100644 index 000000000..230619c1e --- /dev/null +++ b/site/content/kapp/docs/v0.61.x/preflight.md @@ -0,0 +1,25 @@ +--- +aliases: [/kapp/docs/latest/preflight] +title: Preflight Checks +--- + +## Overview + +Once the change set is calculated (see [Diff](diff.md) section for details), kapp will run any of the optional preflight checks that have been enabled. + +If all enabled preflight checks are successful, kapp will continue to apply the changes in the change set (see [Apply](apply.md) section for further details). + +Preflight checks are enabled using the new `--preflight` flag when running `kapp deploy...` or `kapp app-group deploy...`. The `--preflight` flag follows the pattern `--preflight=CheckName,OtherCheck,...` to enable the specified preflight checks. Preflight checks not specified are disabled. + +Currently available preflight checks are: +- `PermissionValidation` - *disabled by default* - Validates that a user has the permissions necessary to apply the changes in the change set. If a user does not have the appropriate permissions the preflight check will fail and no changes will be applied to the cluster. + +## PermissionValidation + +The `PermissionValidation` preflight check validates that a user has the permissions necessary to apply the changes in the change set to the cluster. If a user does not have the appropriate permissions to apply *all* of the changes, this check will fail and result in no changes being applied to the cluster. + +This preflight check is disabled by default but can be enabled with `--preflight=PermissionValidation` when running `kapp deploy...` or `kapp app-group deploy...`. + +The following permission checks are run when this check is enabled: +- For all resources, verification that a user has the permissions to perform the change operation (`create`, `update`, `delete`). +- For `ClusterRole`, `ClusterRoleBinding`, `Role`, and `RoleBinding` resources, verification that no privilege escalation occurs. This is done by checking each rule specified in the `(Cluster)Role` resource (or in the case of `(Cluster)RoleBinding` the referenced `(Cluster)Role`) and ensuring that a user has the same level of permissions. This check also accounts for users with the `escalate` and `bind` permissions that are allowed to perform privilege escalation. diff --git a/site/content/kapp/docs/v0.61.x/rbac.md b/site/content/kapp/docs/v0.61.x/rbac.md new file mode 100644 index 000000000..7eb89bd96 --- /dev/null +++ b/site/content/kapp/docs/v0.61.x/rbac.md @@ -0,0 +1,70 @@ +--- +aliases: [/kapp/docs/latest/rbac] +title: Permissions +--- + +## Running kapp under restricted permissions + +In a multi-tenant Kubernetes cluster, user's actions may be limited to one or more namespaces via `Role` and `RoleBinding` configuration. + +Following setup is currently expected by kapp (v0.10.0+): + +- [required] kapp requires list/get/create/update/delete for `v1/ConfigMap` in [state namespace](state-namespace.md) so that it can store record of application and deployment history. +- [optional] kapp requires one `ClusterRole` rule: listing of namespaces. This requirement is necessary for kapp to find all namespaces so that it can search in each namespace resources that belong to a particular app (via a label). As of v0.11.0+, kapp will fallback to only [state namespace](state-namespace.md) if it is forbidden to list all namespaces. +- otherwise, kapp does _not_ require permissions to resource types that are not used in deployed configuration. In other words, if you are not deploying `Job` resource then kapp does not need any permissions for `Job`. Note that some resources are "cluster" created (e.g. `Pods` are created by k8s deployment controller when `Deployment` resource is created) hence users may not see all app associated resources in `kapp inspect` command if they are restricted (this could be advantageous and disadvantegeous in different setups). + +Please reach out to us in #carvel channel in k8s slack (linked at the bottom of the page) if current kapp permissions model isn't compatible with your use cases. We are eager to learn about your setup and potentially improve kapp. + +Example of `Namespace` listing permission needed by kapp: + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: kapp-restricted-cr +rules: +- apiGroups: [""] + resources: ["namespaces"] + verbs: ["list"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: kapp-restricted-cr-binding +subjects: +- kind: ServiceAccount + name: # ??? + namespace: # ??? (some tenant ns) +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: kapp-restricted-cr +``` + +Example of `ConfigMap` permissions needed by kapp: + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: kapp-restricted-role + namespace: # ??? (some tenant ns) +rules: +- apiGroups: [""] + resources: ["configmaps"] + verbs: ["list", "get", "create", "update", "delete"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: kapp-restricted-role-binding + namespace: # ??? (some tenant ns) +subjects: +- kind: ServiceAccount + name: # ??? + namespace: # ??? (some tenant ns) +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: kapp-restricted-role +``` diff --git a/site/content/kapp/docs/v0.61.x/rebase-pvc.md b/site/content/kapp/docs/v0.61.x/rebase-pvc.md new file mode 100644 index 000000000..0e9087088 --- /dev/null +++ b/site/content/kapp/docs/v0.61.x/rebase-pvc.md @@ -0,0 +1,102 @@ +--- +aliases: [/kapp/docs/latest/rebase-pvc] +title: PersistentVolumeClaim rebase +--- +## PersistentVolumeClaim rebase + +Here is an example on how to use custom `rebaseRules` to "prefer" server chosen value for several annotations added by PVC controller (in other words, cluster owned fields), instead of removing them based on given configuration. + +Let's deploy via `kapp deploy -a test -f config.yml -c` with following configuration `config.yml`: + +```yaml +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: mysqlclaim +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 5Gi +``` + +Without additional rebase rules following diff will be presented upon next deploy, stating that several annotations will be removed (since they were not present in the initial configuration): + +```bash +$ kapp deploy -a test -f config.yml -c + +Target cluster 'https://x.x.x.x' (nodes: gke-dk-jan-9-default-pool-a218b1c9-55sl, 3+) + +--- update persistentvolumeclaim/mysqlclaim (v1) namespace: default + ... + 2, 2 metadata: + 3 - annotations: + 4 - pv.kubernetes.io/bind-completed: "yes" + 5 - pv.kubernetes.io/bound-by-controller: "yes" + 6 - volume.beta.kubernetes.io/storage-provisioner: kubernetes.io/gce-pd + 7, 3 creationTimestamp: "2020-03-08T22:17:29Z" + 8, 4 finalizers: + ... + 24, 20 storageClassName: standard + 25 - volumeMode: Filesystem + 26, 21 volumeName: pvc-1be63b2b-20de-429c-863a-9e7eb062f5d3 + 27, 22 status: + +Changes + +Namespace Name Kind Conds. Age Op Wait to Rs Ri +default mysqlclaim PersistentVolumeClaim - 43s update reconcile ok - + +Op: 0 create, 0 delete, 1 update, 0 noop, 0 exists +Wait to: 1 reconcile, 0 delete, 0 noop + +Continue? [yN]: +``` + +To let kapp know that these annotations should be copied from the live resource copy, we can augment deploys with following configuration `kapp-config.yml`: + +```yaml +--- +apiVersion: kapp.k14s.io/v1alpha1 +kind: Config + +rebaseRules: +- path: [metadata, annotations, pv.kubernetes.io/bind-completed] + type: copy + sources: [new, existing] + resourceMatchers: &pvcs + - apiVersionKindMatcher: + apiVersion: v1 + kind: PersistentVolumeClaim + +- path: [metadata, annotations, pv.kubernetes.io/bound-by-controller] + type: copy + sources: [new, existing] + resourceMatchers: *pvcs + +- path: [metadata, annotations, volume.beta.kubernetes.io/storage-provisioner] + type: copy + sources: [new, existing] + resourceMatchers: *pvcs + +- path: [spec, volumeMode] + type: copy + sources: [new, existing] + resourceMatchers: *pvcs +``` + +```bash +$ kapp deploy -a test -f config.yml -f kapp-config.yml -c + +Target cluster 'https://x.x.x.x' (nodes: gke-dk-jan-9-default-pool-a218b1c9-55sl, 3+) + +Changes + +Namespace Name Kind Conds. Age Op Wait to Rs Ri + +Op: 0 create, 0 delete, 0 update, 0 noop, 0 exists +Wait to: 0 reconcile, 0 delete, 0 noop + +Succeeded +``` diff --git a/site/content/kapp/docs/v0.61.x/security.md b/site/content/kapp/docs/v0.61.x/security.md new file mode 100644 index 000000000..6b16ba957 --- /dev/null +++ b/site/content/kapp/docs/v0.61.x/security.md @@ -0,0 +1,8 @@ +--- +aliases: [/kapp/docs/latest/security] +title: Security +--- + +## Vulnerability Disclosure + +If you believe you have found a security issue in `kapp`, please privately and responsibly disclose it by following the directions in our [security policy](/shared/docs/latest/security-policy). diff --git a/site/content/kapp/docs/v0.61.x/state-namespace.md b/site/content/kapp/docs/v0.61.x/state-namespace.md new file mode 100644 index 000000000..82185f326 --- /dev/null +++ b/site/content/kapp/docs/v0.61.x/state-namespace.md @@ -0,0 +1,53 @@ +--- +aliases: [/kapp/docs/latest/state-namespace] +title: Namespace for State Storage +--- + +## Overview + +To show list of deployed applications (via `kapp ls`), kapp manages metadata `ConfigMap` for each saved application. Each metadata `ConfigMap` contains generated label used to label all application resources. Additionally kapp creates `ConfigMap` per each deploy to record deployment history (seen via `kapp app-change list -a app1`). + +`--app-namespace` flag allows to control which namespace is used for finding and storing metadata `ConfigMaps`. If namespace is not explicitly specified the app-wide namespace is used as specified by `-n` or `--namespace`); if `-n` is not specified your current namespace is selected from kube config (typically `~/.kube/config`). + +There are currently two approaches to deciding which namespace to use for storing metadata `ConfigMaps`: + +- for each application, keep metadata `ConfigMap` and app resources themselves in the same namespace. That namespace will have to be created before running `kapp deploy` since kapp will first want to create a `ConfigMap` representing application. + + ```bash + $ kubectl create ns app1 + $ kapp deploy -n app1 -f config.yml + $ kapp ls -n app1 + ``` + +- create a dedicated namespace to store metadata `ConfigMaps` representing apps, and have kapp create `Namespace` resources for applications from their config. With this approach namespace management (creation and deletion) is tied to a particular app configuration which makes it a bit easier to track `Namespaces` via configuration. + + ```bash + $ kubectl create ns apps + $ kapp deploy --app-namespace apps -f app1/config.yml + $ kapp deploy --app-namespace apps -f app2/config.yml + $ kapp ls -n apps + ``` + + for example, `app1/config.yml` may look like this: + + ```yaml + apiVersion: v1 + kind: Namespace + metadata: + name: app1 + --- + apiVersion: apps/v1 + kind: Deployment + metadata: + name: dep + namespace: app1 + ... + ``` + +Note: It's currently not possible to have kapp place app `ConfigMap` resource into `Namespace` that kapp creates for that application. + +## App Changes + +As mentioned above, app changes (stored as `ConfigMap`) are stored in state namespace. App changes do not store any information necessary for kapp to operate, but rather act as informational records. There is currently no cap on how many app changes are kept per app. + +To remove older app changes, use `kapp app-change gc -a app1` which by default will keep 200 most recent changes (as of v0.12.0). Alternatively use `--app-changes-max-to-keep` flag on the `deploy` command to control number of changes kept at the time of deploy. diff --git a/site/content/kbld/docs/v0.42.x/_index.md b/site/content/kbld/docs/v0.42.x/_index.md index 8a74289fb..b73dca1a6 100644 --- a/site/content/kbld/docs/v0.42.x/_index.md +++ b/site/content/kbld/docs/v0.42.x/_index.md @@ -1,5 +1,5 @@ --- -aliases: [/kbld/docs/latest/] + title: "About kbld" toc: "false" cascade: diff --git a/site/content/kbld/docs/v0.42.x/auth.md b/site/content/kbld/docs/v0.42.x/auth.md index 9c52daf11..fbdada8ba 100644 --- a/site/content/kbld/docs/v0.42.x/auth.md +++ b/site/content/kbld/docs/v0.42.x/auth.md @@ -1,5 +1,5 @@ --- -aliases: [/kbld/docs/latest/auth] + title: Authentication --- diff --git a/site/content/kbld/docs/v0.42.x/building.md b/site/content/kbld/docs/v0.42.x/building.md index afd16045c..897a5a9c5 100644 --- a/site/content/kbld/docs/v0.42.x/building.md +++ b/site/content/kbld/docs/v0.42.x/building.md @@ -1,5 +1,5 @@ --- -aliases: [/kbld/docs/latest/building] + title: Building images --- diff --git a/site/content/kbld/docs/v0.42.x/cnab-image-relocation.md b/site/content/kbld/docs/v0.42.x/cnab-image-relocation.md index 152de7a64..2c7b5a644 100644 --- a/site/content/kbld/docs/v0.42.x/cnab-image-relocation.md +++ b/site/content/kbld/docs/v0.42.x/cnab-image-relocation.md @@ -1,5 +1,5 @@ --- -aliases: [/kbld/docs/latest/cnab-image-relocation] + title: CNAB Image Maps --- diff --git a/site/content/kbld/docs/v0.42.x/config.md b/site/content/kbld/docs/v0.42.x/config.md index 4a07efd49..97422cd1c 100644 --- a/site/content/kbld/docs/v0.42.x/config.md +++ b/site/content/kbld/docs/v0.42.x/config.md @@ -1,5 +1,5 @@ --- -aliases: [/kbld/docs/latest/config] + title: Configuration --- diff --git a/site/content/kbld/docs/v0.42.x/install.md b/site/content/kbld/docs/v0.42.x/install.md index c1f352a2c..60c11677c 100644 --- a/site/content/kbld/docs/v0.42.x/install.md +++ b/site/content/kbld/docs/v0.42.x/install.md @@ -1,5 +1,5 @@ --- -aliases: [/kbld/docs/latest/install] + title: Install --- diff --git a/site/content/kbld/docs/v0.42.x/packaging.md b/site/content/kbld/docs/v0.42.x/packaging.md index 95d65a0bd..0c2ca04bd 100644 --- a/site/content/kbld/docs/v0.42.x/packaging.md +++ b/site/content/kbld/docs/v0.42.x/packaging.md @@ -1,5 +1,5 @@ --- -aliases: [/kbld/docs/latest/packaging] + title: Packaging and Relocation --- diff --git a/site/content/kbld/docs/v0.42.x/resolving.md b/site/content/kbld/docs/v0.42.x/resolving.md index 9bc1e9839..41c320ea7 100644 --- a/site/content/kbld/docs/v0.42.x/resolving.md +++ b/site/content/kbld/docs/v0.42.x/resolving.md @@ -1,5 +1,5 @@ --- -aliases: [/kbld/docs/latest/resolving] + title: Resolving images --- diff --git a/site/content/kbld/docs/v0.42.x/security.md b/site/content/kbld/docs/v0.42.x/security.md index 0888a2254..437ba2873 100644 --- a/site/content/kbld/docs/v0.42.x/security.md +++ b/site/content/kbld/docs/v0.42.x/security.md @@ -1,5 +1,5 @@ --- -aliases: [/kbld/docs/latest/security] + title: Security --- diff --git a/site/content/kbld/docs/v0.43.x/_index.md b/site/content/kbld/docs/v0.43.x/_index.md new file mode 100644 index 000000000..bf540ae05 --- /dev/null +++ b/site/content/kbld/docs/v0.43.x/_index.md @@ -0,0 +1,30 @@ +--- +aliases: [/kbld/docs/latest/] +title: "About kbld" +toc: "false" +cascade: + version: v0.43.x + toc: "true" + type: docs + layout: docs +--- + +`kbld` (pronounced: `kei·bild`) seamlessly incorporates image building and image pushing into your development and deployment workflows. + +Features: + +- Orchestrates image builds (delegates to tools like Docker, pack, kubectl-buildkit) and registry pushes +- Works with local Docker daemon and remote registries, for development and production cases +- Records metadata about image sources in annotation on Kubernetes resources (see examples below) +- Resolves image references to their digest (immutable) form +- Not specific to Kubernetes, but works really well with Kubernetes configuration files + +See [building and deploying simple Go application to Kubernetes example](/blog/deploying-apps-with-ytt-kbld-kapp/#building-container-images-locally) that uses kbld. + +## Why digest references? + +Docker images can be referenced by their name (`nginx`), name-tag pair (`nginx:1.14`), or a digest (`nginx@sha256:c398dc3f2...`). One can avoid potential deployment inconsistencies by using digest references as they are immutable, and therefore always points to an exact image. kbld helps Kubernetes users convert image references to their digest form to make sure exact image is used during deploys. + +## Blog posts + +- [Deploying Kubernetes Applications with ytt, kbld, and kapp](/blog/deploying-apps-with-ytt-kbld-kapp) diff --git a/site/content/kbld/docs/v0.43.x/auth.md b/site/content/kbld/docs/v0.43.x/auth.md new file mode 100644 index 000000000..9c52daf11 --- /dev/null +++ b/site/content/kbld/docs/v0.43.x/auth.md @@ -0,0 +1,119 @@ +--- +aliases: [/kbld/docs/latest/auth] +title: Authentication +--- + +## Via Docker config + +Even though `kbld` commands use registry APIs directly, by default it uses credentials stored in `~/.docker/config.json` which are typically generated via `docker login` command. + +Example generated `~/.docker/config.json`: + +```json +{ + "auths": { + "https://index.docker.io/v1/": { + "auth": "dXNlcjpwYXNzd29yZA==" + }, + }, + "HttpHeaders": { + "User-Agent": "Docker-Client/18.09.6 (darwin)" + } +} +``` + +where `dXNlcjpwYXNzd29yZA==` is `base64("username:password")`. + +## Via Environment Variables + +As of v0.23.0+, kbld can also use following environment variables: + +- `KBLD_REGISTRY_HOSTNAME` to specify registry hostname (e.g. gcr.io, docker.io) +- `KBLD_REGISTRY_USERNAME` to specify registry username +- `KBLD_REGISTRY_PASSWORD` to specify registry password + +Since you may need to provide multiple registry credentials, above environment variables multiple times with a suffix like so `KBLD_REGISTRY_HOSTNAME_0` (suffix can be 1+ alphanumeric characters). Use same suffix for hostname, username and password. + +Currently credentials provided via environment variables do not apply when building images with Docker. Continue using `docker login` to authenticate Docker daemon. + +## gcr.io + +- Create service account with "Storage Admin" for push access + - See [Permissions and Roles](https://cloud.google.com/container-registry/docs/access-control#permissions_and_roles) +- Download JSON service account key and place it somewhere on filesystem (e.g. `/tmp/key`) + - See [Advanced authentication](https://cloud.google.com/container-registry/docs/advanced-authentication#json_key_file) +- Run `cat /tmp/key | docker login -u _json_key --password-stdin https://gcr.io` to authenticate + +## Amazon Web Services Elastic Container Registry (AWS ECR) + +- Create an ECR repository \ + _(see [Amazon ECR User Guide: Getting started](https://docs.aws.amazon.com/AmazonECR/latest/userguide/getting-started-console.html))_ +- Create an IAM user with an ECR policy that allows to read/write \ + _(see [Amazon ECR User Guide: Private repository policies](https://docs.aws.amazon.com/AmazonECR/latest/userguide/repository-policies.html))_ \ + Example: + ``` + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "ecr:GetAuthorizationToken", + "ecr:BatchCheckLayerAvailability", + "ecr:GetDownloadUrlForLayer", + "ecr:GetRepositoryPolicy", + "ecr:DescribeRepositories", + "ecr:ListImages", + "ecr:DescribeImages", + "ecr:BatchGetImage", + "ecr:GetLifecyclePolicy", + "ecr:GetLifecyclePolicyPreview", + "ecr:ListTagsForResource", + "ecr:DescribeImageScanFindings", + "ecr:InitiateLayerUpload", + "ecr:UploadLayerPart", + "ecr:CompleteLayerUpload", + "ecr:PutImage" + ], + "Resource": "*" + } + ] + } + ``` + +- To authenticate from _the command line_, use the AWS CLI to generate a docker authentication token. \ + _(see [Amazon ECR User Guide: Private registry authentication](https://docs.aws.amazon.com/AmazonECR/latest/userguide/registry_auth.html))_ \ + Example: + ``` + $ aws ecr get-login-password --region us-east-1 \ + | docker login \ + --username AWS \ + --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com + ``` +- To authenticate from _a GitHub Action_, setup Carvel, AWS authentication, and ECR login. \ + _(see [Amazon ECR "Login" Action for GitHub Actions](https://github.com/aws-actions/amazon-ecr-login))_ \ + Example: + + ``` + ... + - name: carvel-setup-action + uses: carvel-dev/setup-action@v1.3.0 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: us-east-1 + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v1 + ... + ``` + + + +## Harbor + +You may have to provide `--registry-ca-cert-path` flag with a path to a CA certificate file for Harbor Registry API. diff --git a/site/content/kbld/docs/v0.43.x/building.md b/site/content/kbld/docs/v0.43.x/building.md new file mode 100644 index 000000000..afd16045c --- /dev/null +++ b/site/content/kbld/docs/v0.43.x/building.md @@ -0,0 +1,152 @@ +--- +aliases: [/kbld/docs/latest/building] +title: Building images +--- + +## Building images from source + +kbld can be used to orchestrate build tools such as [Docker](https://docs.docker.com/engine/reference/commandline/cli/) and [pack](https://github.com/buildpacks/pack) to build images from source and record resulting image reference in a YAML file. This is especially convenient during local development when working with one or more changing applications. + +```yaml +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kbld-test1 +spec: + selector: + matchLabels: + app: kbld-test1 + template: + metadata: + labels: + app: kbld-test1 + spec: + containers: + - name: my-app + image: simple-app #! <-- unresolved image ref +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kbld-test2 +spec: + selector: + matchLabels: + app: kbld-test2 + template: + metadata: + labels: + app: kbld-test2 + spec: + containers: + - name: my-app + image: another-simple-app #! <-- unresolved image ref +--- +apiVersion: kbld.k14s.io/v1alpha1 +kind: Config +sources: +- image: simple-app + path: src/simple-app +- image: another-simple-app + path: src/another-simple-app +``` + +(See [Configuration](config.md) for more details about `Sources`.) + +Running above example via `kbld -f file.yml` will start two `docker build` processes and produce following output + +```yaml +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kbld-test1 +spec: + selector: + matchLabels: + app: kbld-test1 + template: + metadata: + labels: + app: kbld-test1 + spec: + containers: + - name: my-app + image: kbld:1556053998479026000-simple-app #! <-- resolved image ref +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kbld-test2 +spec: + selector: + matchLabels: + app: kbld-test2 + template: + metadata: + labels: + app: kbld-test2 + spec: + containers: + - name: my-app + image: kbld:1556053998479039000-another-simple-app #! <--resolved image ref +``` + +Note that because we are using Docker daemon for local images and are not pushing them into a remote registry we cannot unfortunately use digest reference form (a limitation of Docker daemon); however, tags generated by kbld uniquely identify produced images. As soon as images are pushed to a remote registry, tags are converted into digest references. + +**Hint**: [Minikube](https://kubernetes.io/docs/setup/minikube/) comes with Docker daemon inside its VM. You can expose by running `eval $(minikube docker-env)` before executing kbld. + +## Pushing images + +As long as building tool has proper push access (run `docker login` for Docker), kbld can push out built images to specified repositories. Just add following configuration: + +```yaml +--- +apiVersion: kbld.k14s.io/v1alpha1 +kind: Config +destinations: +- image: simple-app + newImage: docker.io/dkalinin/simple-app +- image: another-simple-app + newImage: docker.io/dkalinin/another-simple-app +``` + +With addition of above configuration, kbld will produce following YAML: + +```yaml +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kbld-test1 +spec: + selector: + matchLabels: + app: kbld-test1 + template: + metadata: + labels: + app: kbld-test1 + spec: + containers: + - name: my-app + image: index.docker.io/dkalinin/simple-app@sha256:f7988fb6c02e... #! <-- pushed image ref +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kbld-test2 +spec: + selector: + matchLabels: + app: kbld-test2 + template: + metadata: + labels: + app: kbld-test2 + spec: + containers: + - name: my-app + image: index.docker.io/dkalinin/another-simple-app@sha256:a7355fb1007e... #! <-- pushed image ref +``` diff --git a/site/content/kbld/docs/v0.43.x/cnab-image-relocation.md b/site/content/kbld/docs/v0.43.x/cnab-image-relocation.md new file mode 100644 index 000000000..152de7a64 --- /dev/null +++ b/site/content/kbld/docs/v0.43.x/cnab-image-relocation.md @@ -0,0 +1,65 @@ +--- +aliases: [/kbld/docs/latest/cnab-image-relocation] +title: CNAB Image Maps +--- + +CNAB spec mentions [Image Relocation](https://github.com/cnabio/cnab-spec/blob/master/103-bundle-runtime.md#image-relocation) as part of bundle runtime. + +kbld supports applying `relocation-mapping.json` on top of YAML configuration via `kbld --image-map-file /cnab/app/relocation-mapping.json ...`. For example: + +/cnab/app/relocation-mapping.json: + +```json +{ + "gabrtv/microservice@sha256:cca460afa270d4c527981ef9ca4989346c56cf9b20217dcea37df1ece8120687": "my.registry/microservice@sha256:cca460afa270d4c527981ef9ca4989346c56cf9b20217dcea37df1ece8120687", + "technosophos/helloworld:0.1.0": "my.registry/helloworld:0.1.0" +} +``` + +and kbld input: + +```yaml +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kbld-test1 +spec: + selector: + matchLabels: + app: kbld-test1 + template: + metadata: + labels: + app: kbld-test1 + spec: + containers: + - name: my-app + image: gabrtv/microservice@sha256:cca460afa270d4c527981ef9ca4989346c56cf9b20217dcea37df1ece8120687 + - name: my-app2 + image: technosophos/helloworld:0.1.0 +``` + +would result in: + +```yaml +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kbld-test1 +spec: + selector: + matchLabels: + app: kbld-test1 + template: + metadata: + labels: + app: kbld-test1 + spec: + containers: + - name: my-app + image: my.registry/microservice@sha256:cca460afa270d4c527981ef9ca4989346c56cf9b20217dcea37df1ece8120687 + - name: my-app2 + image: my.registry/helloworld:0.1.0 +``` diff --git a/site/content/kbld/docs/v0.43.x/config.md b/site/content/kbld/docs/v0.43.x/config.md new file mode 100644 index 000000000..4a07efd49 --- /dev/null +++ b/site/content/kbld/docs/v0.43.x/config.md @@ -0,0 +1,538 @@ +--- +aliases: [/kbld/docs/latest/config] +title: Configuration +--- + +## Overview + +Customize how `kbld`: +- searches for image references, +- resolves image names, +- builds images from source, and +- pushes built images to container registries. + +This is done by supplying one or more configuration files. These files are in the Kubernetes resource format (i.e. has `apiVersion` and `kind`). `kbld` consumes and removes such files from its output. + +A `kbld` configuration file is structured like so: + +```yaml +--- +apiVersion: kbld.k14s.io/v1alpha1 +kind: Config +minimumRequiredVersion: 0.31.0 + +searchRules: ... +overrides: ... +sources: ... +destinations: ... +``` + +where: +- `minimumRequiredVersion` (optional; semver) — specifies the minimum required version of `kbld` needed to work with this configuration. + +and any combination of the follow four sections: +- `searchRules` — a list of [Search Rules](#search-rules) that `kbld` follows to locate container image references within the input files. +- `overrides` — a list of [Overrides](#overrides) that `kbld` applies to found container image references before it attempts to resolve or build the actual image. +- `sources` — a list of [Sources](#sources), each of which describes where to find the source that makes up the contents of a given image _and_ which build tool to use to construct it. +- `destinations` — a list of [Destinations](#destinations), each of which describes where (i.e. to which container registry) to publish a given image. + +_(Note: prior to v0.28.0, different types of configuration were supplied in different `kind` of files: `kind: Sources`, `kind: ImageDestinations`, `kind: ImageOverrides`, `kind: ImageKeys`. Since v0.28.0, all such configuration can be specified in one kind of file: `kind: Config`; this is recommended.)_ + +--- +## Search Rules + +`kbld` scans input files for references to container images. Search rules describe how `kbld` should identify those references and how to process them. + +A search rule has two parts: +- conditions for matching (either by key, by value, or both), and +- a strategy for how to parse and update a matched item. + +A search rule is expressed like this: + +```yaml +--- +apiVersion: kbld.k14s.io/v1alpha1 +kind: Config +searchRules: +- keyMatcher: ... + valueMatcher: ... + updateStrategy: ... +``` + +where a rule consists of one or both of the following... +- `keyMatcher` a [Key Matcher](#key-matcher) — identifies container image references based on an item's key. +- `valueMatcher` a [Value Matcher](#value-matcher) — identifies container image references based on an item's value. + +_(If both are specified, their results are "and'ed" together.)_ + +...and optionally, +- `updateStrategy` an [Update Strategy](#update-strategy) — what `kbld` should do with the matched container image reference. + +**Multiple Matching search rules** \ +If multiple search rules match the same item, the rule that was defined first is applied. + +Note: `kbld` includes its own [Default Search Rules](#default-search-rules) + +### Key Matcher + +Specifies whether a given key indicates that its corresponding value is an image reference. + +```yaml +--- +apiVersion: kbld.k14s.io/v1alpha1 +kind: Config +searchRules: +- keyMatcher: + name: + path: +``` + +where: +- `name` (string) specifies the key name (e.g. `sidecarImage`) +- `path` (array) specifies key path from the root of the YAML document.\ + Each path part can be one of: + - the literal key name. (e.g. `[data, sidecarImage]` maps to `data.sidecarImage`) + - an array indexing tactic (choose one): + - `index:` (int) — search one element in the array at the (zero-based) index given. \ + (example: `[spec, template, spec, containers, {index: 0}, image]`) + - `allIndexes:` (bool) — search all elements in the array. \ + (example: `[spec, images, {allIndexes: true}]`) + +### Value Matcher + +Specifies whether a given value contains an image reference. + +```yaml +--- +apiVersion: kbld.k14s.io/v1alpha1 +kind: Config +searchRules: +- valueMatcher: + ( image | imageRepo ): +``` +where (choose one): +- `image` (string) value to match exactly +- `imageRepo` (string) of values in the format (`[registry]repo[:tag]\[@sha256:...]`), the value of the `repo` portion. \ + (example: `imageRepo: gcr.io/project/app` \ + matches `gcr.io/projects/app:v1.1` and `gcr.io/projects/app@sha256:f33e111...` \ + but not `gcr.io/projects/app_v1`) + +### Update Strategy + +Given a container image reference identified by one or more matchers, an Update Strategy describes how to update that value (v0.21.0+). + +```yaml +--- +apiVersion: kbld.k14s.io/v1alpha1 +kind: Config +searchRules: +- ( keyMatcher | valueMatcher ): ... + updateStrategy: + yaml: + searchRules: ... + json: + searchRules: ... + none: {} + entireValue: {} +``` + +where (choose one): +- `yaml` parses YAML and identifies image refs by specified search rules + - `searchRules` — one or more [Search Rules](#search-rules), scoped to the parsed content. +- `json` parses JSON and identifies image refs by specified search rules + - `searchRules` — one or more [Search Rules](#search-rules), scoped to the parsed content. +- `none` (empty) excludes value from processing (v0.22.0+) +- `entireValue` (empty; default) uses the exact value as the image reference. + +#### Example for `updateStrategy` that parses YAML + +```yaml +--- +kind: ConfigMap +metadata: + name: config +data: + data.yml: | + name: nginx + image: nginx # <-- below config finds and updates this image +--- +apiVersion: kbld.k14s.io/v1alpha1 +kind: Config +minimumRequiredVersion: 0.15.0 +searchRules: +- keyMatcher: + name: data.yml + updateStrategy: + yaml: + searchRules: + - keyMatcher: + name: image +``` + +Note that in the `ConfigMap`, that `/data/data.yml` holds a multi-line string. That string happens to also be in YAML format. When _that_ YAML is parsed, the `image` key holds a container image reference. + + +### Default Search Rules + +After custom search rules have been processed, `kbld` appends the following search rule: + +```yaml +--- +apiVersion: kbld.k14s.io/v1alpha1 +kind: Config +searchRules: +- keyMatcher: + name: image + updateStrategy: + entireValue: {} +``` + +--- +## Overrides + +After `kbld` searches for container image references, it applies a set of "overrides" which effectively rewrite those references. It does this _before_ attempting to build the corresponding image or resolve it to a digest reference. + +```yaml +--- +apiVersion: kbld.k14s.io/v1alpha1 +kind: Config +overrides: +- image: + newImage: + preresolved: + tagSelection: ... + platformSelection: ... +``` + +where: +- `image` (required; string) exact value found while searching for container image references. +- `newImage` (required; string) value with which to replace/override. This ought to be an image reference in the format `[registry]repo[:tag]\[@sha256:...]`. \ + Examples: + - `nginx` + - `quay.io/bitnami/nginx` + - `nginx:1.21.1` + - `nginx@sha256:a05b0cd...` + - `index.docker.io/library/nginx@sha256:a05b0cd...` +- `preresolved` (optional; bool) specifies if `newImage` should be used as is (rather than be [re]resolved to a digest reference). +- `tagSelection` (optional; [VersionSelection](/vendir/docs/latest/versions/#versionselection-type)) when `newImage` _is_ being resolved, specifies how to select the tag part of the reference before resolving to a digest reference. (Available as of v0.28.0+) + - In this case, `newImage` must not have a tag or digest part (e.g. `gcr.io/my-corp/app`). +- `platformSelection` (optional; map) specifies a way to select particular image within an image index. Available in 0.35.0+. + - `architecture` (string) selects via CPU architecture. ex: `amd64` + - `os` (string) selects via OS name. ex: `linux` + - `os.version` (string) selects via OS version (commonly used for Windows). ex: `10.0.10586` + - `os.features` (string array) selects via OS features (commonly used for Windows). ex: `["win32k"]` + - `variant` (string) selects via architecture variant. ex: `armv6l` + - `features` (string array) selects via architecture features. ex: `["sse4"]` + +**Example: Static Rewrite** + +With the following configuration, any container image reference of the value `"unknown"` will get rewritten to `"docker.io/library/nginx:1.14.2"` before being resolved to a digest reference. + +```yaml +--- +apiVersion: kbld.k14s.io/v1alpha1 +kind: Config +overrides: +- image: unknown + newImage: docker.io/library/nginx:1.14.2 +``` + +**Example: Preresolved** + +This configuration replaces references of `"unknown"` with `"docker.io/library/nginx:1.14.2"`. It short-circuits any building, resolution, or even connecting to a registry to obtain metadata about the image reference. + +```yaml +--- +apiVersion: kbld.k14s.io/v1alpha1 +kind: Config +overrides: +- image: unknown + newImage: docker.io/library/nginx:1.14.2 + preresolved: true +``` + +**Example: Dynamic Tag Selection** + +This configuration first rewrites references of `"unknown"` with `"docker.io/library/nginx"`. It then queries the registry to locate the latest version just prior to 1.15.0 (which turns out to be 1.14.2). + +```yaml +--- +apiVersion: kbld.k14s.io/v1alpha1 +kind: Config +overrides: +- image: unknown + newImage: docker.io/library/nginx + tagSelection: + semver: + constraints: "<1.15.0" +``` + + +--- +## Sources + +Rather than resolve an image reference, `kbld` can build the image from source. More precisely, `kbld` can be configured to use one of the many image building tools it integrates with to construct the image from source. For an overview, see [Building Images](building.md). + +Such integration is enabled by configuring a "Source": + +```yaml +--- +apiVersion: kbld.k14s.io/v1alpha1 +kind: Config +sources: +- image: image1 + path: src/ + ( docker | pack | kubectlBuildkit | ko | bazel ): ... +``` + +where: +- `image` (required; string) exact value found while searching for container image references. +- `path` (required; string) filesystem path to the source for the to-be-built image. + This path also acts as the container file context; therefore, any paths in the + container file (when one is present) must be relative to the value of the field + `path`. +- a builder configuration (optional; choose one) — name/configure a specific image builder tool: + - `docker:` (default) use [Docker](#docker) or [Docker buildx](#docker-buildx) to build from source. + - `pack:` use [Pack](#pack) to build the image via buildpacks from source. + - `kubectlBuildkit:` use [Buildkit CLI for kubectl](#buildkit-cli-for-kubectl) to build the image via a Kubernetes cluster form source. + - `ko:` use [ko](#ko) to build the image from Go source. + - `bazel:` use [Bazel](#bazel) to build the image via Bazel Container Image Rules. + + +### Docker + +Using this integration requires: +- Docker — https://docs.docker.com/get-docker + +The [Docker CLI](https://docs.docker.com/engine/reference/commandline/cli/) must be on the `$PATH`. + +To configure an image to be built via Docker, include a `docker:`-flavored [Source](#sources): +```yaml +--- +apiVersion: kbld.k14s.io/v1alpha1 +kind: Config +sources: +- image: image1 + path: src/ + docker: + build: + target: some-target + pull: true + noCache: true + file: hack/Dockerfile.dev + buildkit: true + rawOptions: ["--squash", "--build-arg", "ARG_IN_DOCKERFILE=value"] +``` +where (all options are optional): +- `target` (string): the target build stage to build (no default) +- `pull` (bool): always attempt to pull a newer version of the image (default is false) +- `noCache` (bool): do not use cache when building the image (default is false) +- `file` (string): name of the Dockerfile (default is Dockerfile) +- `buildkit` (bool): enable [Builtkit](https://docs.docker.com/develop/develop-images/build_enhancements) for this build. (Also see [Docker buildx](#docker-buildx) integration.) +- `rawOptions` ([]string): Refer to [`docker build` reference](https://docs.docker.com/engine/reference/commandline/build/) for all available options + +### Docker buildx + +Available as of v0.34.0+ + +Using this integration requires: +- Docker - https://docs.docker.com/get-docker +- Docker buildx plugin + +The [Docker CLI](https://docs.docker.com/engine/reference/commandline/cli/) must be on the `$PATH`. + +To configure an image to be built via Docker buildx, include a `docker:`-flavored [Source](#sources): +```yaml +--- +apiVersion: kbld.k14s.io/v1alpha1 +kind: Config +sources: +- image: image1 + path: src/ + docker: + buildx: + target: some-target + pull: true + noCache: true + file: hack/Dockerfile.dev + rawOptions: ["--platform=linux/amd64,linux/arm64,linux/arm/v7"] +``` +where (all options are optional): +- `target` (string): the target build stage to build (no default) +- `pull` (bool): always attempt to pull a newer version of the image (default is false) +- `noCache` (bool): do not use cache when building the image (default is false) +- `file` (string): name of the Dockerfile (default is Dockerfile) +- `rawOptions` ([]string): Refer to [`docker buildx build` reference](https://docs.docker.com/engine/reference/commandline/buildx_build/) for all available options + +### Pack + +Using this integration requires: +- Docker — https://docs.docker.com/get-docker +- Pack — https://buildpacks.io/docs/tools/pack/ + +The [Pack CLI](https://buildpacks.io/docs/tools/pack/cli/pack/) must be on the `$PATH`. + +```yaml +--- +apiVersion: kbld.k14s.io/v1alpha1 +kind: Config +sources: +- image: image1 + path: src/ + pack: + build: + builder: cloudfoundry/cnb:bionic +``` +where (all options are optional): +- `builder` (string): Set builder image (required) +- `buildpacks` ([]string): Set list of buildpacks to be used (no default) +- `clearCache` (bool): Clear cache before building image (default is false) +- `rawOptions` ([]string): Refer to `pack build -h` for all available flags + +### BuildKit CLI for kubectl + +Available as of v0.28.0+ + +Using this integration requires: +- `kubectl` — https://kubernetes.io/docs/tasks/tools/ +- Buildkit for kubectl — https://github.com/vmware-tanzu/buildkit-cli-for-kubectl#installing-the-tarball \ + _(`kbld` v0.28.0+ is tested with buildkit-for-kubectl v0.1.0, but may work well with other versions.)_ + +The [`kubectl` CLI](https://kubernetes.io/docs/reference/kubectl/kubectl/) must be on the `$PATH`. + +```yaml +--- +apiVersion: kbld.k14s.io/v1alpha1 +kind: Config +sources: +- image: image1 + path: src/ + kubectlBuildkit: + build: + target: "some-target" + pull: true + noCache: true + file: "hack/Dockefile.dev" + rawOptions: ["--platform=..."] +``` + +where (all options are optional): +- `target` (string): Set the target build stage to build (no default) +- `pull` (bool): Always attempt to pull a newer version of the image (default is false) +- `noCache` (bool): Do not use cache when building the image (default is false) +- `file` (string): Name of the Dockerfile (default is Dockerfile) +- `rawOptions` ([]string): Refer to `kubectl buildkit build -h` for all available options + +#### Authenticating to Registry for Pushing Images + +To provide registry credentials to the builder, create a Kubernetes `docker-registry` secret: + +``` +kubectl create secret docker-registry buildkit --docker-server=https://index.docker.io/v1/ --docker-username=my-user --docker-password=my-password +``` + +See project site for details: [buildkit-cli-for-kubectl](https://github.com/vmware-tanzu/buildkit-cli-for-kubectl). + +### ko + +Available as of v0.28.0+ + +Using this integration requires: +- `ko` — https://github.com/google/ko \ + (`kbld` v0.28.0+ is tested with `ko` v0.8.0, but may work well with other versions.) + +The `ko` CLI must be on the `$PATH`. + +```yaml +--- +apiVersion: kbld.k14s.io/v1alpha1 +kind: Config +sources: +- image: image1 + path: ./src/ + ko: + build: + rawOptions: ["--disable-optimizations"] +``` + +where: +- `rawOptions` (optional; []string): Refer to `ko publish -h` for all available options. + +By default `kbld` provides the `--local` flag + +### Bazel + +Available as of v0.31.0+ + +Using this integration requires: +- Docker — https://docs.docker.com/get-docker +- Bazel — https://docs.bazel.build/versions/main/install.html \ + _(`kbld` v0.31.0+ is tested with Bazel v4.2.0 and [Container Image Rules](https://github.com/bazelbuild/rules_docker/tree/v0.18.0) v0.18.0, but may work well with other versions.)_ + +The `bazel` CLI must be on the `$PATH`. + +```yaml +--- +apiVersion: kbld.k14s.io/v1alpha1 +kind: Config +sources: + - image: image1 + path: ./src/ + bazel: + run: + target: :image1-go-container + rawOptions: ["--platforms=@io_bazel_rules_go//go/toolchain:linux_amd64"] +``` + +where: +- `target` (string): bazel build target; when passed to `bazel run` will build and load the desired image. +- `rawOptions` ([]string): Refer to https://docs.bazel.build/versions/main/user-manual.html for all available options. + +#### Using Language-specific Container Rules + +This integration invokes the `bazel run` command (as opposed to the `bazel build` command). + +Typically, when configuring the Bazel integration with `kbld`, the specified target is one of the [Container Image Rules](https://github.com/bazelbuild/rules_docker/tree/master#bazel-container-image-rules). + +With such rules, the `bazel build` command _produces_ the artifacts that make up all images described in the `BUILD`, but stops short of loading any image into the Docker daemon. In order for an image to be pushed to a registry (i.e. published), it must first be loaded into the local Docker daemon. The `bazel run` command takes that additional step to execute the script that performs that `docker load` _for the named target, only_. + +For so-called [`lang_image` rules](https://github.com/bazelbuild/rules_docker/tree/master#language-rules), this also results in launching a container from that image (i.e. `docker run` the built image). For our purposes, this is undesirable because it effectively halts the build. + +To skip this that launching step, append the args `-- --norun` via the `rawOptions:` key: + +```yaml +... + bazel: + run: + target: :image1-go-container + rawOptions: ["--", "--norun"] +``` + +See also https://github.com/bazelbuild/rules_docker#using-with-docker-locally. + +--- +## Destinations + +Once `kbld` has processed the found container image references (either resolved or built them), it can publish those images to a different registry (on an image-by-image basis). + +Pushing images to registries through this configuration requires: +- Docker — https://docs.docker.com/get-docker + +(In the case where all published images are built through [BuildKit CLI for kubectl](#buildkit-cli-for-kubectl), Docker is not required; publishing happens from within the Kubernetes cluster). + +Do this by configuring one or more "destinations": + +```yaml +--- +apiVersion: kbld.k14s.io/v1alpha1 +kind: Config +destinations: +- image: adservice + newImage: docker.io/dkalinin/microservices-demo-adservice + tags: [latest, tag2] +``` + +where: +- `image` (required; string) exact value found while searching for container image references. +- `newImage` (required; string) image destination (i.e. fully qualified registry location for the image) +- `tags` (optional; array of strings) tags to apply to pushed images (v0.26.0+) diff --git a/site/content/kbld/docs/v0.43.x/install.md b/site/content/kbld/docs/v0.43.x/install.md new file mode 100644 index 000000000..c1f352a2c --- /dev/null +++ b/site/content/kbld/docs/v0.43.x/install.md @@ -0,0 +1,58 @@ +--- +aliases: [/kbld/docs/latest/install] +title: Install +--- + +## Via script (macOS or Linux) + +(Note that `install.sh` script installs other Carvel tools as well.) + +Install binaries into specific directory: + +```bash +$ mkdir local-bin/ +$ curl -L https://carvel.dev/install.sh | K14SIO_INSTALL_BIN_DIR=local-bin bash + +$ export PATH=$PWD/local-bin/:$PATH +$ kbld version +``` + +Or system wide: + +```bash +$ wget -O- https://carvel.dev/install.sh > install.sh + +# Inspect install.sh before running... +$ sudo bash install.sh +$ kbld version +``` + +## Via Homebrew (macOS or Linux) + +Based on [github.com/carvel-dev/homebrew](https://github.com/carvel-dev/homebrew). + +```bash +$ brew tap carvel-dev/carvel +$ brew install kbld +$ kbld version +``` + +## Specific version from a GitHub release + +To download, click on one of the assets in a [chosen GitHub release](https://github.com/carvel-dev/kbld/releases), for example for 'kbld-darwin-amd64'. + +```bash +# **Compare binary checksum** against what's specified in the release notes +# (if checksums do not match, binary was not successfully downloaded) +$ shasum -a 256 ~/Downloads/kbld-darwin-amd64 +08b25d21675fdc77d4281c9bb74b5b36710cc091f30552830604459512f5744c /Users/pivotal/Downloads/kbld-darwin-amd64 + +# Move binary next to your other executables +$ mv ~/Downloads/kbld-darwin-amd64 /usr/local/bin/kbld + +# Make binary executable +$ chmod +x /usr/local/bin/kbld + +# Check its version +$ kbld version +``` diff --git a/site/content/kbld/docs/v0.43.x/packaging.md b/site/content/kbld/docs/v0.43.x/packaging.md new file mode 100644 index 000000000..95d65a0bd --- /dev/null +++ b/site/content/kbld/docs/v0.43.x/packaging.md @@ -0,0 +1,190 @@ +--- +aliases: [/kbld/docs/latest/packaging] +title: Packaging and Relocation +--- + +### Deprecation +This functionality was deprecated in `kbld` starting from version v0.30.0 and will be removed soon. +These features are now available on [imgpkg](https://carvel.dev/imgpkg). + +## Overview + +kbld provides a way to relocate (i.e. copy) images between multiple registries. Two approaches are available: + +- `kbld relocate` (available v0.23.0+) allows to efficiently copy images between registries as long as running `relocate` command has connectivity to both registries. +- `kbld package` and `kbld unpackage` allows to export images into a single tarball, and later import them from given tarball into a different (or same) registry. This approach does _not_ require connectivity to source registry during the `pkg unpackage` time. + +Use cases: + +- packaging applications for fully offline environments with a private registry +- copying images from one registry to another +- backing up images + +There are two approaches to do this: + +- With lock file (recommended) +- Directly against configuration + +## With lock file + +For example, to package referenced images into a single tarball: + +1. Produce lock file for referenced images in configuration + + ```bash + $ cat /tmp/manifest + images: + - image: nginx + - image: haproxy + + $ kbld -f /tmp/manifest --lock-output /tmp/manifest.lock + ... + + $ cat /tmp/manifest.lock + apiVersion: kbld.k14s.io/v1alpha1 + kind: Config + minimumRequiredVersion: 0.21.0 + overrides: + - image: haproxy + newImage: index.docker.io/library/haproxy@sha256:e6f9faf0c2a0cf2d2d5a53307351fa896d90ca9ccd62817c24026460d97dde92 + preresolved: true + - image: nginx + newImage: index.docker.io/library/nginx@sha256:86ae264c3f4acb99b2dee4d0098c40cb8c46dcf9e1148f05d3a51c4df6758c12 + preresolved: true + ``` + +1. Feed `/tmp/manifest.lock` to `kbld pkg` command to download images and pack them into a single tarball `/tmp/packaged-images.tar`: + + ```bash + $ kbld pkg -f /tmp/manifest.lock --output /tmp/packaged-images.tar + package | exporting 2 images... + package | will export index.docker.io/library/nginx@sha256:e71b1bf4281f25533cf15e6e5f9be4dac74d2328152edf7ecde23abc54e16c1c + package | will export index.docker.io/library/haproxy@sha256:6dae9c8674e2e5f418c3dd040041a05f6b490597315139c0bcacadf65a46cfd5 + package | exported 2 images + + $ ls -lah /tmp/packaged-images.tar + -rw-r--r-- 1 root root 314M May 3 18:59 /tmp/packaged-images.tar + ``` + + Note: Depending on your internet connection this may be slow. + +To import packaged images from a tarball: + +1. Specify new repository location `docker.io/dkalinin/app1` and provide tarball: + + ```bash + $ kbld unpkg -f /tmp/manifest.lock --input /tmp/packaged-images.tar --repository docker.io/dkalinin/app1 --lock-output /tmp/manifest.lock.copied + unpackage | importing 2 images... + unpackage | importing index.docker.io/library/nginx@sha256:e71b1bf4281f25533cf15e6e5f9be4dac74d2328152edf7ecde23abc54e16c1c -> docker.io/dkalinin/app1@sha256:e71b1bf4281f25533cf15e6e5f9be4dac74d2328152edf7ecde23abc54e16c1c... + unpackage | importing index.docker.io/library/haproxy@sha256:6dae9c8674e2e5f418c3dd040041a05f6b490597315139c0bcacadf65a46cfd5 -> docker.io/dkalinin/app1@sha256:6dae9c8674e2e5f418c3dd040041a05f6b490597315139c0bcacadf65a46cfd5... + unpackage | imported 2 images + ``` + + Images will be imported under a single new repository `docker.io/dkalinin/app1`. **You are guaranteed that images are exactly same as they are referenced by the same digests in produced YAML configuration (though under a different repository name)**. + +1. **Alternatively**, using `relocate` command against a lock file: + + ```bash + $ kbld relocate -f /tmp/manifest.lock --repository docker.io/dkalinin/app1 --lock-output /tmp/manifest.lock.copied + relocate | ... + ``` + +1. Use newely generated lock file with configuration to get updated results + + ```bash + $ kbld -f /tmp/manifest -f /tmp/manifest.lock.copied + images: + - image: docker.io/dkalinin/app1@sha256:e71b1bf4281f25533cf15e6e5f9be4dac74d2328152edf7ecde23abc54e16c1c + - image: docker.io/dkalinin/app1@sha256:6dae9c8674e2e5f418c3dd040041a05f6b490597315139c0bcacadf65a46cfd5 + ``` + +--- +## Directly against configuration + +For example, to package referenced images into a single tarball: + +1. First make sure all image references are in their digest form + + ```bash + $ cat /tmp/manifest + images: + - image: nginx + - image: haproxy + + $ kbld -f /tmp/manifest > /tmp/resolved-manifest + resolve | final: haproxy -> index.docker.io/library/haproxy@sha256:6dae9c8674e2e5f418c3dd040041a05f6b490597315139c0bcacadf65a46cfd5 + resolve | final: nginx -> index.docker.io/library/nginx@sha256:e71b1bf4281f25533cf15e6e5f9be4dac74d2328152edf7ecde23abc54e16c1c + + $ cat /tmp/resolved-manifest + images: + - image: index.docker.io/library/nginx@sha256:e71b1bf4281f25533cf15e6e5f9be4dac74d2328152edf7ecde23abc54e16c1c + - image: index.docker.io/library/haproxy@sha256:6dae9c8674e2e5f418c3dd040041a05f6b490597315139c0bcacadf65a46cfd5 + ``` + +1. Feed `/tmp/resolved-manifest` to `kbld pkg` command to download images and pack them into a single tarball `/tmp/packaged-images.tar`: + + ```bash + $ kbld pkg -f /tmp/resolved-manifest --output /tmp/packaged-images.tar + package | exporting 2 images... + package | will export index.docker.io/library/nginx@sha256:e71b1bf4281f25533cf15e6e5f9be4dac74d2328152edf7ecde23abc54e16c1c + package | will export index.docker.io/library/haproxy@sha256:6dae9c8674e2e5f418c3dd040041a05f6b490597315139c0bcacadf65a46cfd5 + package | exported 2 images + + $ ls -lah /tmp/packaged-images.tar + -rw-r--r-- 1 root root 314M May 3 18:59 /tmp/packaged-images.tar + ``` + + Note: Depending on your internet connection this may be slow. + +To import packaged images from a tarball: + +1. Specify new repository location `docker.io/dkalinin/app1` and provide tarball: + + ```bash + $ kbld unpkg -f /tmp/resolved-manifest --input /tmp/packaged-images.tar --repository docker.io/dkalinin/app1 + unpackage | importing 2 images... + unpackage | importing index.docker.io/library/nginx@sha256:e71b1bf4281f25533cf15e6e5f9be4dac74d2328152edf7ecde23abc54e16c1c -> docker.io/dkalinin/app1@sha256:e71b1bf4281f25533cf15e6e5f9be4dac74d2328152edf7ecde23abc54e16c1c... + unpackage | importing index.docker.io/library/haproxy@sha256:6dae9c8674e2e5f418c3dd040041a05f6b490597315139c0bcacadf65a46cfd5 -> docker.io/dkalinin/app1@sha256:6dae9c8674e2e5f418c3dd040041a05f6b490597315139c0bcacadf65a46cfd5... + unpackage | imported 2 images + images: + - image: docker.io/dkalinin/app1@sha256:e71b1bf4281f25533cf15e6e5f9be4dac74d2328152edf7ecde23abc54e16c1c + - image: docker.io/dkalinin/app1@sha256:6dae9c8674e2e5f418c3dd040041a05f6b490597315139c0bcacadf65a46cfd5 + ``` + + Images will be imported under a single new repository `docker.io/dkalinin/app1`. **You are guaranteed that images are exactly same as they are referenced by the same digests in produced YAML configuration (though under a different repository name)**. + +1. **Alternatively**, using `relocate` command: + + ```bash + $ kbld relocate -f /tmp/resolved-manifest --repository docker.io/dkalinin/app1 + relocate | ... + images: + - image: docker.io/dkalinin/app1@sha256:e71b1bf4281f25533cf15e6e5f9be4dac74d2328152edf7ecde23abc54e16c1c + - image: docker.io/dkalinin/app1@sha256:6dae9c8674e2e5f418c3dd040041a05f6b490597315139c0bcacadf65a46cfd5 + ``` + +--- +## Authentication + +See general authentication steps in [Authentication doc](auth.md). + +## Using with gcr.io + +- Run `kbld unpkg -f /tmp/resolved-manifest --input /tmp/packaged-images.tar --repository gcr.io/{project-id}/app1` to import images (e.g. project id is `dkalinin`) + +## Using with AWS ECR + +**Note**: AWS ECR does not support manifest list media types from Docker Registry v2 API. Manifest lists are used for images that are built against multiple architectures and platforms and are referenced through a single digest or tag. Most user-built images do not use manifest lists (as it's a single image); however, common Docker Hub library images are represented by manifest lists and will fail upon import into AWS ECR. You will see following error: `Writing image index: Retried 5 times: uploading manifest2: UNSUPPORTED: Invalid parameter at 'imageTag' failed to satisfy constraint: 'must satisfy regular expression '[a-zA-Z0-9-_.]+'`. Related [AWS feature request](https://forums.aws.amazon.com/thread.jspa?threadID=292294)). + +- Run `kbld unpkg -f /tmp/resolved-manifest --input /tmp/packaged-images.tar --repository {uri}` to import images (e.g. uri is `823869848626.dkr.ecr.us-east-1.amazonaws.com/k14s/kbld-test`) + +## Using with Harbor + +You may have to provide `--registry-ca-cert-path` flag with a path to a CA certificate file for Harbor Registry API. + +## Notes + +- Produced tarball does not have duplicate image layers, as they are named by their digest (see `tar tvf /tmp/packaged-images.tar`). +- If digest reference points to an image index, all children (images and other image indexes) will be included in the export. Saving only a portion of contents would of course change the digest. +- Only Docker v2 and OCI images and indexes are supported. Docker v1 format is not supported, hence, not all images out there could be exported and only registries supporting v2 format can be used for imports. +- Images that once were in different repositories are imported into the same repository to make it easier to manage them in bulk. diff --git a/site/content/kbld/docs/v0.43.x/resolving.md b/site/content/kbld/docs/v0.43.x/resolving.md new file mode 100644 index 000000000..9bc1e9839 --- /dev/null +++ b/site/content/kbld/docs/v0.43.x/resolving.md @@ -0,0 +1,202 @@ +--- +aliases: [/kbld/docs/latest/resolving] +title: Resolving images +--- + +## Resolving image references to digests + +kbld looks for `image` keys within YAML documents and tries to resolve image reference to its full digest form. + +For example, following + +```yaml +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kbld-test1 +spec: + selector: + matchLabels: + app: kbld-test1 + template: + metadata: + labels: + app: kbld-test1 + spec: + containers: + - name: my-app + image: nginx:1.14.2 + #! ^-- image reference in its tag form +``` + +will be transformed to + +```yaml +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kbld-test1 +spec: + selector: + matchLabels: + app: kbld-test1 + template: + metadata: + labels: + app: kbld-test1 + spec: + containers: + - name: my-app + image: index.docker.io/library/nginx@sha256:f7988fb6c02e0ce69257d9bd9cf37ae20a60f1df7563c3a2a6abe24160306b8d + #! ^-- resolved image reference to its digest form +``` + +via + +```bash +kbld -f file.yml +``` + +Few other variations + +```bash +pbpaste | kbld -f- +kbld -f . +kbld -f file.yml -f config2.yml +``` + +### Resolving image references to digests + +Available in 0.35.0+ + +Use `--platform` flag to resolve image indexes (a type of OCI artifact) to their particular child image based on a platform (architecture, OS, OS variant) associated with an image. If platform flag is specified and image is not an image index, no special resolution is performed. + +Examples: + +- `kbld -f ... --platform linux/386` selects based on an OS (`linux`) and an architecture (`386`) +- `kbld -f ... --platform linux/arm/v6` selects based on an OS (`linux`), architecture (`arm`) and variant (`v6`) + +### Generating resolution `imgpkg` lock output + +Available in 0.28.0+ + +Using the `--imgpkg-lock-output` flag, users are able to create an [ImagesLock](https://github.com/carvel-dev/imgpkg/blob/develop/docs/resources.md#imageslock) file that can be used as input for the packaging and distribution tool: [`imgpkg`](https://github.com/carvel-dev/imgpkg) + +For example, the command `kbld -f input.yml --imgpkg-lock-output /tmp/imgpkg.lock.yml` with `input.yml`: + +```yaml +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kbld-test1 +spec: + selector: + matchLabels: + app: kbld-test1 + template: + metadata: + labels: + app: kbld-test1 + spec: + containers: + - name: my-app + image: nginx:1.14.2 + #! ^-- image reference in its tag form +``` + +will produce `/tmp/imgpkg.lock.yml`: + +```yaml +apiVersion: imgpkg.carvel.dev/v1alpha1 +kind: ImagesLock +images: +- image: index.docker.io/library/nginx@sha256:f7988fb6c02e0ce69257d9bd9cf37ae20a60f1df7563c3a2a6abe24160306b8d + annotations: + kbld.carvel.dev/id: nginx:1.14.2 +``` + +An ImagesLock can be included with configuration via `-f` to produce same resolved configuration, for example, `kbld -f input.yml -f /tmp/imgpkg.lock.yml` produces: + +```yaml +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kbld-test1 +spec: + selector: + matchLabels: + app: kbld-test1 + template: + metadata: + labels: + app: kbld-test1 + spec: + containers: + - name: my-app + image: index.docker.io/library/nginx@sha256:f7988fb6c02e0ce69257d9bd9cf37ae20a60f1df7563c3a2a6abe24160306b8d +``` + +### Generating resolution lock output + +In some cases recording resolution results may be useful. To do so add `--lock-output /path-to-file` to the `kbld` command. + +For example, command `kbld -f input.yml --lock-output /tmp/kbld.lock.yml` with `input.yml`: + +```yaml +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kbld-test1 +spec: + selector: + matchLabels: + app: kbld-test1 + template: + metadata: + labels: + app: kbld-test1 + spec: + containers: + - name: my-app + image: nginx:1.14.2 + #! ^-- image reference in its tag form +``` + +will produce `/tmp/kbld.lock.yml`: + +```yaml +apiVersion: kbld.k14s.io/v1alpha1 +kind: Config +minimumRequiredVersion: 0.17.0 +overrides: +- image: nginx:1.14.2 + newImage: index.docker.io/library/nginx@sha256:f7988fb6c02e0ce69257d9bd9cf37ae20a60f1df7563c3a2a6abe24160306b8d + preresolved: true +``` + +Lock content can be included with configuration via `-f` to produce same resolved configuration, for example, `kbld -f input.yml -f /tmp/kbld.lock.yml` produces: + +```yaml +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kbld-test1 +spec: + selector: + matchLabels: + app: kbld-test1 + template: + metadata: + labels: + app: kbld-test1 + spec: + containers: + - name: my-app + image: index.docker.io/library/nginx@sha256:f7988fb6c02e0ce69257d9bd9cf37ae20a60f1df7563c3a2a6abe24160306b8d +``` diff --git a/site/content/kbld/docs/v0.43.x/security.md b/site/content/kbld/docs/v0.43.x/security.md new file mode 100644 index 000000000..0888a2254 --- /dev/null +++ b/site/content/kbld/docs/v0.43.x/security.md @@ -0,0 +1,8 @@ +--- +aliases: [/kbld/docs/latest/security] +title: Security +--- + +## Vulnerability Disclosure + +If you believe you have found a security issue in `kbld`, please privately and responsibly disclose it by following the directions in our [security policy](/shared/docs/latest/security-policy). diff --git a/site/content/ytt/docs/v0.48.0/_index.md b/site/content/ytt/docs/v0.48.0/_index.md index 1a5ddaac6..635e4a94a 100644 --- a/site/content/ytt/docs/v0.48.0/_index.md +++ b/site/content/ytt/docs/v0.48.0/_index.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/] + title: "About ytt" cascade: version: v0.48.0 diff --git a/site/content/ytt/docs/v0.48.0/data-values-schema-migration-guide.md b/site/content/ytt/docs/v0.48.0/data-values-schema-migration-guide.md index f272646f9..12cee5cc4 100644 --- a/site/content/ytt/docs/v0.48.0/data-values-schema-migration-guide.md +++ b/site/content/ytt/docs/v0.48.0/data-values-schema-migration-guide.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/data-values-schema-migration-guide] + title: Schema Migration Guide toc: "true" --- diff --git a/site/content/ytt/docs/v0.48.0/data-values-vs-overlays.md b/site/content/ytt/docs/v0.48.0/data-values-vs-overlays.md index 78353da0c..e81b9bbfe 100644 --- a/site/content/ytt/docs/v0.48.0/data-values-vs-overlays.md +++ b/site/content/ytt/docs/v0.48.0/data-values-vs-overlays.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/data-values-vs-overlays] + title: "Data Values vs Overlays" --- diff --git a/site/content/ytt/docs/v0.48.0/faq.md b/site/content/ytt/docs/v0.48.0/faq.md index 09f859583..ea0a1ad96 100644 --- a/site/content/ytt/docs/v0.48.0/faq.md +++ b/site/content/ytt/docs/v0.48.0/faq.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/faq] + title: FAQ --- diff --git a/site/content/ytt/docs/v0.48.0/file-marks.md b/site/content/ytt/docs/v0.48.0/file-marks.md index 4f87f2e7f..43652038a 100644 --- a/site/content/ytt/docs/v0.48.0/file-marks.md +++ b/site/content/ytt/docs/v0.48.0/file-marks.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/file-marks] + title: File Marks --- diff --git a/site/content/ytt/docs/v0.48.0/how-it-works.md b/site/content/ytt/docs/v0.48.0/how-it-works.md index f9a4828c7..bdd21c43f 100644 --- a/site/content/ytt/docs/v0.48.0/how-it-works.md +++ b/site/content/ytt/docs/v0.48.0/how-it-works.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/how-it-works] + title: "How it works" --- diff --git a/site/content/ytt/docs/v0.48.0/how-to-export-schema.md b/site/content/ytt/docs/v0.48.0/how-to-export-schema.md index e6fae01d3..1246c3480 100644 --- a/site/content/ytt/docs/v0.48.0/how-to-export-schema.md +++ b/site/content/ytt/docs/v0.48.0/how-to-export-schema.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/how-to-export-schema] + title: Export Schema in OpenAPI Format --- diff --git a/site/content/ytt/docs/v0.48.0/how-to-modularize.md b/site/content/ytt/docs/v0.48.0/how-to-modularize.md index 780c748fa..bdb649dc5 100644 --- a/site/content/ytt/docs/v0.48.0/how-to-modularize.md +++ b/site/content/ytt/docs/v0.48.0/how-to-modularize.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/how-to-modularize] + title: Getting started --- diff --git a/site/content/ytt/docs/v0.48.0/how-to-use-data-values.md b/site/content/ytt/docs/v0.48.0/how-to-use-data-values.md index 99c243ae1..c0962793f 100644 --- a/site/content/ytt/docs/v0.48.0/how-to-use-data-values.md +++ b/site/content/ytt/docs/v0.48.0/how-to-use-data-values.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/how-to-use-data-values] + title: Using Data Values --- diff --git a/site/content/ytt/docs/v0.48.0/how-to-write-schema.md b/site/content/ytt/docs/v0.48.0/how-to-write-schema.md index 2f29ba188..27a8fa7e9 100644 --- a/site/content/ytt/docs/v0.48.0/how-to-write-schema.md +++ b/site/content/ytt/docs/v0.48.0/how-to-write-schema.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/how-to-write-schema] + title: Writing Schema --- diff --git a/site/content/ytt/docs/v0.48.0/how-to-write-validations.md b/site/content/ytt/docs/v0.48.0/how-to-write-validations.md index 2419e7503..6cdfa93e6 100644 --- a/site/content/ytt/docs/v0.48.0/how-to-write-validations.md +++ b/site/content/ytt/docs/v0.48.0/how-to-write-validations.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/how-to-write-validations] + title: Writing Schema Validations --- diff --git a/site/content/ytt/docs/v0.48.0/index-playground-examples.md b/site/content/ytt/docs/v0.48.0/index-playground-examples.md index 7d04cbfca..786b1c60e 100644 --- a/site/content/ytt/docs/v0.48.0/index-playground-examples.md +++ b/site/content/ytt/docs/v0.48.0/index-playground-examples.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/index-playground-examples] + title: Playground Examples Index --- diff --git a/site/content/ytt/docs/v0.48.0/injecting-secrets.md b/site/content/ytt/docs/v0.48.0/injecting-secrets.md index 9e0d095ff..dbd59f8e5 100644 --- a/site/content/ytt/docs/v0.48.0/injecting-secrets.md +++ b/site/content/ytt/docs/v0.48.0/injecting-secrets.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/injecting-secrets] + title: Injecting Secrets --- diff --git a/site/content/ytt/docs/v0.48.0/install.md b/site/content/ytt/docs/v0.48.0/install.md index 61deecb21..74f88931a 100644 --- a/site/content/ytt/docs/v0.48.0/install.md +++ b/site/content/ytt/docs/v0.48.0/install.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/install] + title: Install --- diff --git a/site/content/ytt/docs/v0.48.0/known-limitations.md b/site/content/ytt/docs/v0.48.0/known-limitations.md index a02a65476..281a8705e 100644 --- a/site/content/ytt/docs/v0.48.0/known-limitations.md +++ b/site/content/ytt/docs/v0.48.0/known-limitations.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/known-limitations] + title: Known Limitations --- diff --git a/site/content/ytt/docs/v0.48.0/lang-ref-annotation.md b/site/content/ytt/docs/v0.48.0/lang-ref-annotation.md index bfe6a4a12..9647df7a6 100644 --- a/site/content/ytt/docs/v0.48.0/lang-ref-annotation.md +++ b/site/content/ytt/docs/v0.48.0/lang-ref-annotation.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/lang-ref-annotation] + title: Annotations --- diff --git a/site/content/ytt/docs/v0.48.0/lang-ref-def.md b/site/content/ytt/docs/v0.48.0/lang-ref-def.md index 7b418f365..7229d6e5a 100644 --- a/site/content/ytt/docs/v0.48.0/lang-ref-def.md +++ b/site/content/ytt/docs/v0.48.0/lang-ref-def.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/lang-ref-def] + title: Functions --- diff --git a/site/content/ytt/docs/v0.48.0/lang-ref-dict.md b/site/content/ytt/docs/v0.48.0/lang-ref-dict.md index e04eaa843..d57eea0ba 100644 --- a/site/content/ytt/docs/v0.48.0/lang-ref-dict.md +++ b/site/content/ytt/docs/v0.48.0/lang-ref-dict.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/lang-ref-dict] + title: Dictionaries --- diff --git a/site/content/ytt/docs/v0.48.0/lang-ref-for.md b/site/content/ytt/docs/v0.48.0/lang-ref-for.md index d73222ef5..da3e517d4 100644 --- a/site/content/ytt/docs/v0.48.0/lang-ref-for.md +++ b/site/content/ytt/docs/v0.48.0/lang-ref-for.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/lang-ref-for] + title: For loop --- diff --git a/site/content/ytt/docs/v0.48.0/lang-ref-if.md b/site/content/ytt/docs/v0.48.0/lang-ref-if.md index 914115d3a..0c39cf97b 100644 --- a/site/content/ytt/docs/v0.48.0/lang-ref-if.md +++ b/site/content/ytt/docs/v0.48.0/lang-ref-if.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/lang-ref-if] + title: If Statements --- diff --git a/site/content/ytt/docs/v0.48.0/lang-ref-list.md b/site/content/ytt/docs/v0.48.0/lang-ref-list.md index 33661f57b..71839f007 100644 --- a/site/content/ytt/docs/v0.48.0/lang-ref-list.md +++ b/site/content/ytt/docs/v0.48.0/lang-ref-list.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/lang-ref-list] + title: Lists --- diff --git a/site/content/ytt/docs/v0.48.0/lang-ref-load.md b/site/content/ytt/docs/v0.48.0/lang-ref-load.md index a2c764088..9ee9b1b45 100644 --- a/site/content/ytt/docs/v0.48.0/lang-ref-load.md +++ b/site/content/ytt/docs/v0.48.0/lang-ref-load.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/lang-ref-load] + title: Load Statements --- diff --git a/site/content/ytt/docs/v0.48.0/lang-ref-string.md b/site/content/ytt/docs/v0.48.0/lang-ref-string.md index 4be6bbf5b..ee303f10b 100644 --- a/site/content/ytt/docs/v0.48.0/lang-ref-string.md +++ b/site/content/ytt/docs/v0.48.0/lang-ref-string.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/lang-ref-string] + title: Strings --- diff --git a/site/content/ytt/docs/v0.48.0/lang-ref-structs.md b/site/content/ytt/docs/v0.48.0/lang-ref-structs.md index aa4ca708c..30f005f74 100644 --- a/site/content/ytt/docs/v0.48.0/lang-ref-structs.md +++ b/site/content/ytt/docs/v0.48.0/lang-ref-structs.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/lang-ref-structs] + title: Structs --- diff --git a/site/content/ytt/docs/v0.48.0/lang-ref-yaml-fragment.md b/site/content/ytt/docs/v0.48.0/lang-ref-yaml-fragment.md index 547c8a7b8..ec4d02dbf 100644 --- a/site/content/ytt/docs/v0.48.0/lang-ref-yaml-fragment.md +++ b/site/content/ytt/docs/v0.48.0/lang-ref-yaml-fragment.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/lang-ref-yaml-fragment] + title: YAMLFragments --- diff --git a/site/content/ytt/docs/v0.48.0/lang-ref-ytt-assert.md b/site/content/ytt/docs/v0.48.0/lang-ref-ytt-assert.md index a5f3c3ae3..ea89b95e9 100644 --- a/site/content/ytt/docs/v0.48.0/lang-ref-ytt-assert.md +++ b/site/content/ytt/docs/v0.48.0/lang-ref-ytt-assert.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/lang-ref-ytt-assert] + title: Assert Module --- diff --git a/site/content/ytt/docs/v0.48.0/lang-ref-ytt-library.md b/site/content/ytt/docs/v0.48.0/lang-ref-ytt-library.md index 3a7a312fb..55043f730 100644 --- a/site/content/ytt/docs/v0.48.0/lang-ref-ytt-library.md +++ b/site/content/ytt/docs/v0.48.0/lang-ref-ytt-library.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/lang-ref-ytt-library] + title: Library Module --- diff --git a/site/content/ytt/docs/v0.48.0/lang-ref-ytt-overlay.md b/site/content/ytt/docs/v0.48.0/lang-ref-ytt-overlay.md index 7dff6289d..178f008c5 100644 --- a/site/content/ytt/docs/v0.48.0/lang-ref-ytt-overlay.md +++ b/site/content/ytt/docs/v0.48.0/lang-ref-ytt-overlay.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/lang-ref-ytt-overlay] + title: Overlay module --- diff --git a/site/content/ytt/docs/v0.48.0/lang-ref-ytt-schema.md b/site/content/ytt/docs/v0.48.0/lang-ref-ytt-schema.md index 42160d800..1e10a4849 100644 --- a/site/content/ytt/docs/v0.48.0/lang-ref-ytt-schema.md +++ b/site/content/ytt/docs/v0.48.0/lang-ref-ytt-schema.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/lang-ref-ytt-schema] + title: Data Values Schema Reference --- diff --git a/site/content/ytt/docs/v0.48.0/lang-ref-ytt-struct.md b/site/content/ytt/docs/v0.48.0/lang-ref-ytt-struct.md index a85bc898e..0c6ca4761 100644 --- a/site/content/ytt/docs/v0.48.0/lang-ref-ytt-struct.md +++ b/site/content/ytt/docs/v0.48.0/lang-ref-ytt-struct.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/lang-ref-ytt-struct] + title: Struct module --- diff --git a/site/content/ytt/docs/v0.48.0/lang-ref-ytt-template.md b/site/content/ytt/docs/v0.48.0/lang-ref-ytt-template.md index b813ee923..d3cde5b80 100644 --- a/site/content/ytt/docs/v0.48.0/lang-ref-ytt-template.md +++ b/site/content/ytt/docs/v0.48.0/lang-ref-ytt-template.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/lang-ref-ytt-template] + title: Template Module --- diff --git a/site/content/ytt/docs/v0.48.0/lang-ref-ytt-version.md b/site/content/ytt/docs/v0.48.0/lang-ref-ytt-version.md index 0b70659f2..50fe19940 100644 --- a/site/content/ytt/docs/v0.48.0/lang-ref-ytt-version.md +++ b/site/content/ytt/docs/v0.48.0/lang-ref-ytt-version.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/lang-ref-ytt-version] + title: Version module --- diff --git a/site/content/ytt/docs/v0.48.0/lang-ref-ytt.md b/site/content/ytt/docs/v0.48.0/lang-ref-ytt.md index fb8777d63..5162aff64 100644 --- a/site/content/ytt/docs/v0.48.0/lang-ref-ytt.md +++ b/site/content/ytt/docs/v0.48.0/lang-ref-ytt.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/lang-ref-ytt] + title: Built-in ytt Library --- diff --git a/site/content/ytt/docs/v0.48.0/lang.md b/site/content/ytt/docs/v0.48.0/lang.md index d46f0a34c..afd0f178d 100644 --- a/site/content/ytt/docs/v0.48.0/lang.md +++ b/site/content/ytt/docs/v0.48.0/lang.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/lang] + title: Language --- diff --git a/site/content/ytt/docs/v0.48.0/outputs.md b/site/content/ytt/docs/v0.48.0/outputs.md index 6c9548312..bf995f80b 100644 --- a/site/content/ytt/docs/v0.48.0/outputs.md +++ b/site/content/ytt/docs/v0.48.0/outputs.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/outputs] + title: Outputs --- diff --git a/site/content/ytt/docs/v0.48.0/schema-validations-cheat-sheet.md b/site/content/ytt/docs/v0.48.0/schema-validations-cheat-sheet.md index e20254858..8ea725539 100644 --- a/site/content/ytt/docs/v0.48.0/schema-validations-cheat-sheet.md +++ b/site/content/ytt/docs/v0.48.0/schema-validations-cheat-sheet.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/schema-validations-cheat-sheet] + title: "Schema Validations Cheat Sheet" --- diff --git a/site/content/ytt/docs/v0.48.0/security.md b/site/content/ytt/docs/v0.48.0/security.md index c8868cf88..8fad7a260 100644 --- a/site/content/ytt/docs/v0.48.0/security.md +++ b/site/content/ytt/docs/v0.48.0/security.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/security] + title: Security --- diff --git a/site/content/ytt/docs/v0.48.0/strict.md b/site/content/ytt/docs/v0.48.0/strict.md index 43a8b7e39..996bf89d6 100644 --- a/site/content/ytt/docs/v0.48.0/strict.md +++ b/site/content/ytt/docs/v0.48.0/strict.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/strict] + title: Strict YAML --- diff --git a/site/content/ytt/docs/v0.48.0/yaml-primer.md b/site/content/ytt/docs/v0.48.0/yaml-primer.md index cf95a8a6d..3c6c8d552 100644 --- a/site/content/ytt/docs/v0.48.0/yaml-primer.md +++ b/site/content/ytt/docs/v0.48.0/yaml-primer.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/yaml-primer] + title: "YAML and Annotations" --- diff --git a/site/content/ytt/docs/v0.48.0/ytt-data-values.md b/site/content/ytt/docs/v0.48.0/ytt-data-values.md index 123d1e62f..8a25f1fce 100644 --- a/site/content/ytt/docs/v0.48.0/ytt-data-values.md +++ b/site/content/ytt/docs/v0.48.0/ytt-data-values.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/ytt-data-values] + title: Data Values --- diff --git a/site/content/ytt/docs/v0.48.0/ytt-overlays.md b/site/content/ytt/docs/v0.48.0/ytt-overlays.md index e3bec98f7..dfbb083b0 100644 --- a/site/content/ytt/docs/v0.48.0/ytt-overlays.md +++ b/site/content/ytt/docs/v0.48.0/ytt-overlays.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/ytt-overlays] + title: Overlays --- diff --git a/site/content/ytt/docs/v0.48.0/ytt-text-templating.md b/site/content/ytt/docs/v0.48.0/ytt-text-templating.md index f2e588508..84b9a0671 100644 --- a/site/content/ytt/docs/v0.48.0/ytt-text-templating.md +++ b/site/content/ytt/docs/v0.48.0/ytt-text-templating.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/ytt-text-templating] + title: Text Templating --- diff --git a/site/content/ytt/docs/v0.48.0/ytt-vs-x.md b/site/content/ytt/docs/v0.48.0/ytt-vs-x.md index fbafe2fcd..2fc329dfb 100644 --- a/site/content/ytt/docs/v0.48.0/ytt-vs-x.md +++ b/site/content/ytt/docs/v0.48.0/ytt-vs-x.md @@ -1,5 +1,5 @@ --- -aliases: [/ytt/docs/latest/ytt-vs-x] + title: ytt vs x --- diff --git a/site/content/ytt/docs/v0.49.x/_index.md b/site/content/ytt/docs/v0.49.x/_index.md new file mode 100644 index 000000000..a15c8732b --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/_index.md @@ -0,0 +1,88 @@ +--- +aliases: [/ytt/docs/latest/] +title: "About ytt" +cascade: + version: v0.49.x + toc: "true" + type: docs + layout: docs +--- + +## Overview + +`ytt` is a command-line tool used to template and patch YAML files. It also provides the means to collect fragments and piles of YAML into modular chunks for easy re-use. + +In practice, these YAML files are [Kubernetes configuration](https://kubernetes.io/docs/concepts/cluster-administration/manage-deployment/), [Concourse Pipeline](https://concourse-ci.org/pipelines.html#schema.pipeline), [Docker Compose](https://github.com/compose-spec/compose-spec/blob/master/spec.md#compose-file), [GitHub Action workflow](https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions) files..., really anything that is in YAML format. + +`ytt` is most useful when manually maintaining these files has or will become too much work. + +## Templating + +A plain YAML file is turned into a `ytt` template by adding specially formatted comments, that is: annotating. Through these annotations, one can inject input values, logic like conditionals and looping, and perform transformations on the content. + +Those "input values" are called "Data Values" in `ytt`. Such inputs are included in a separate file. + +For more: +* see it in action in the [load data files](/ytt/#example:example-load-data-values) +example on the playground +* check out the [Using Data Values](how-to-use-data-values.md) guide +- look up reference details of the programming language used in templates in [Language](lang.md). + +## Patching (aka Overlaying) + +`ytt` can also be used to patch YAML files. These edits are called "Overlays" and are themselves written in YAML. + +For more around overlaying... +- see overlaying in action through a progressive set of examples in [the `ytt` Playground](/ytt/#example:example-match-all-docs); +- learn more about Overlays in [ytt Overlays overview](ytt-overlays.md); +- for a reference of all Overlay functionality, see [Overlay module](lang-ref-ytt-overlay.md); +- for a screencast-formatted in-depth introduction to writing and using overlays, watch [Primer on `ytt` Overlays](/blog/primer-on-ytt-overlays/). + +## Modularizing + +`ytt` provides powerful techniques for extracting and reusing chunks of YAML and logic. + +Functions in `ytt` capture either a calculation or fragment of YAML. Functions can be used in the same templates that define them, or — if defined in a module file — loaded into any template. Entire sets of templates, overlays, and library code can be encapsulated in a `ytt` library. + +For more about modular code... +- see live examples in the `ytt` Playground around [functions](ytt/#example:example-function) and [`ytt` libraries](/ytt/#example:example-ytt-library-module); +- read further about [functions](lang-ref-def.md), [YAML Fragments](lang-ref-yaml-fragment.md), and [loading reusable modules and libraries](lang-ref-load.md); +- catch-up on a particularly relevant [discussion about using modules and libraries in `ytt`](https://github.com/carvel-dev/ytt/discussions/392#discussioncomment-766445). + +## Further Reading + +Hopefully, the pointers above helped get you started. If you're looking to go either deeper or broader, here are some resources we can recommend. + +### Documentation + +- [How it Works](how-it-works.md) \ + _a more detailed look of how all these parts fit together._ + +- [ytt vs. X](ytt-vs-x.md) \ + _how `ytt` differs from similar tools._ + +### Articles + +- [ytt: The YAML Templating Tool that simplifies complex configuration management](https://developer.ibm.com/blogs/yaml-templating-tool-to-simplify-complex-configuration-management/) \ + _a complete introduction of `ytt` including motivations and contrasts with other tools._ + +- [Deploying Kubernetes Applications with ytt, kbld, and kapp](/blog/deploying-apps-with-ytt-kbld-kapp) \ + _how `ytt` works well with other Carvel tools._ + +### Presentations and Interviews + +- IBM Developer podcast: [Introducing the YAML Templating Tool (ytt)](https://www.youtube.com/watch?v=KbB5tI_g3bo) \ + _a thorough introduction to `ytt` especially contrasted with Helm._ + +- TGI Kubernetes: [#079: YTT and Kapp](https://www.youtube.com/watch?v=CSglwNTQiYg) \ + _Joe Beda puts both `ytt` and sister tool `kapp` through their paces._ + +- Helm Summit 2019: [ytt: An Alternative to Text Templating of YAML Configuration in Helm](https://www.youtube.com/watch?v=7-PqgpkxC7E) + ([slides](https://github.com/k14s/meetups/blob/develop/ytt-2019-sep-helm-summit.pdf)) \ + _in-depth review of the philosophy behind `ytt` and how it differs from other YAML templating approaches._ + +- Kubecon 2020: [Managing Applications in Production: Helm vs ytt & kapp @ Kubecon 2020](https://www.youtube.com/watch?v=WJw1MDFMVuk) \ + _how `ytt` and `kapp` can provide an alternative way to manage the lifecycle of application on Kubernetes._ + +- Rawkode Live: [Introduction to Carvel](https://www.youtube.com/watch?v=LBCmMTofNxw) \ + _Dmitriy joins David McKay to talk through many tools in the Carvel suite, including `ytt`._ diff --git a/site/content/ytt/docs/v0.49.x/data-values-schema-migration-guide.md b/site/content/ytt/docs/v0.49.x/data-values-schema-migration-guide.md new file mode 100644 index 000000000..f272646f9 --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/data-values-schema-migration-guide.md @@ -0,0 +1,207 @@ +--- +aliases: [/ytt/docs/latest/data-values-schema-migration-guide] +title: Schema Migration Guide +toc: "true" +--- + +Schema documents provide a way to declare Data Values with their types, and default values. Without Schema, validating the presence of Data Values requires additional `ytt` configuration containing Starlark assertions. + +- Learn more about [writing Schema](how-to-write-schema.md) +- Read the detailed [Data Vaues Schema Reference](lang-ref-ytt-schema.md) + +## How do I, a configuration author, migrate my `ytt` library to use Schemas? + +To make use of the Schema feature, your `ytt` invocation must first contain files using the [Data Values feature](ytt-data-values.md). Migrating to Schemas involves converting your Data Values files into a Schema file. + +### Single Data Values file + +Starting with a single Data Values file, `values.yml`: +```yaml +#@data/values +--- +key1: myVal +key2: 8080 +``` + +Convert this Data Values file into Schema by changing the top level annotation in the document to say `#@data/values-schema`, and (optional) rename `values.yml` to `values-schema.yml`: +```yaml +#@data/values-schema +--- +key1: myVal +key2: 8080 +``` +Now simply include this Schema file in your `ytt` invocation to receive the benefits of `ytt` Schemas. + + +**Note: If your Data Values file contains arrays (ie. `["example"]`, `- example`), be sure to [provide default values for arrays](#how-do-i-provide-default-values-for-an-array).** + +### Multiple Data Values files +Sometimes, it makes sense to [split Data Values into multiple files](ytt-data-values.md#splitting-data-values-overlays-into-multiple-files). +If this is your situation, there are a few things to note. + +Given a `ytt` configuration with two Data Values files: +```bash +$ tree . +. +├── config.yml +├── values-1.yml +└── values-2.yml +``` + +`values-1.yml`: +```yaml +#@data/values +--- +key1: myVal +key2: 8080 +``` + +`values-2.yml`: +```yaml +#@data/values +--- +key2: 8088 +#@overlay/match missing_ok=True +key3: + host: registry.dev.io + port: 8080 +``` +You can convert each Data Values document into its own Schema document by [following the steps to convert a single Data Values file](#single-data-values-file). + +[Multiple Schemas combine exactly like Data Values, via overlays](lang-ref-ytt-schema.md#multiple-schema-documents): +the first Schema establishes the base set of "data value" declarations, and subsequent Schema files are overlays on top of that base. + +`values-1-schema.yml`: +```yaml +#@data/values-schema +--- +key1: myVal +key2: 8080 +``` + +`values-2-schema.yml`: +```yaml +#@data/values-schema +--- +key2: 8088 +#@overlay/match missing_ok=True +key3: + host: registry.dev.io + port: 8080 +``` + +Now just include these Schema files in your `ytt` invocation instead of the Data Values files. + +### Multiple Data Values files + Private Libraries +If your configuration depends on a ytt library — outlined in the [library module docs](lang-ref-ytt-library.md) — there are a few points to note. + +```bash +$ tree . +. +└── config + ├── config.yml + ├── values-1.yml + ├── values-2.yml + └── _ytt_lib + └── lib + ├── service.yml + └── values.yml +``` +`config.yml`: +```yaml +#@ load("@ytt:data", "data") +#@ load("@ytt:library", "library") +#@ load("@ytt:template", "template") + +#@ lib = library.get("lib").with_data_values(data.values) +--- #@ template.replace(lib.eval()) +``` +Using the same `values-1.yml` and `values-2.yml` files from the [multiple Data Values files Schema migration example above](#multiple-data-values-files). + +Migrating to Schema happens one library at a time. Let's start with the root library, which includes everything at and below the file system level where the `ytt` invocation was called, not including the `_ytt_lib` folder: +```bash +. +└── config + ├── config.yml + ├── values-1.yml + └── values-2.yml +``` +As seen in the [previous example](#multiple-data-values-files), migrating this library to Schemas simply involves converting each `values-1.yml` and `values-2.yml`into a Schema file. + +Now we have migrated the root library to use Schemas, and the `ytt` invocation will succeed as the same as before. Each library can independently opt-in to using Schemas. +```bash +$ tree . +. +└── config + ├── config.yml + ├── values-1-schema.yml + ├── values-2-schema.yml + └── _ytt_lib + └── lib + ├── service.yml + └── values.yml +``` +Migrating a private library to use Schemas involves the same process as the root library. You can narrow the context to just the children of the `_ytt_lib` directory: +```bash +└── _ytt_lib + └── lib + ├── service.yml + └── values.yml +``` +Now simply follow the steps in either of the previous examples to migrate the private library to use Schemas. + +--- + +## How do I provide default values for an array? +Arrays in Schemas are [handled differently](lang-ref-ytt-schema.md#defaults-for-arrays) than other types: +exactly one element is specified in the array, and that value is _only_ used to infer the type of that array's elements — +the default value, by default, is an empty list (i.e. `[]`). + +The example below shows how to define an array in a Schema and then provide default values via the `@schema/default` annotation. + +`values-schema.yml`: +```yaml +#@ def default_conns(): +- host: registry.dev.io + port: 8080 + transport: tcp +#@ end + +#@data/values-schema +--- +#@schema/default default_conns() +key: +- host: "" + port: 0 + transport: "" + insecure_disable_tls_validation: false +``` + +Given that schema, if a template file were to use the `key` data value: + +```yaml +key: #@ data.values.key +``` + +this would output + +```yaml +key: +- host: registry.dev.io + port: 8080 + transport: tcp + insecure_disable_tls_validation: false +``` + + +## How do I mark a section of Data Values as "optional"? +Sometimes your configuration includes a section of Data Values that are not typically used or in some way optional. + +If this the case, consider the guidance in [Writing Schema: Marking a Data Value as Optional](how-to-write-schema.md#marking-a-data-value-as-optional), use the +[@schema/nullalble](lang-ref-ytt-schema.md#schemanullable) annotation to default such a section to `null`. + +## How do I mark a Data Value as containing any kind of YAML? +For those looking to relax the typing that Schema applies to Data Values, the [@schema/type any=True](lang-ref-ytt-schema.md#schematype) annotation +can be used to override the inferred typing on the node it annotates and its children. + +Situations like this are covered in detail in [Writing Schema: Specific Use-Cases](how-to-write-schema.md#specific-use-cases) diff --git a/site/content/ytt/docs/v0.49.x/data-values-vs-overlays.md b/site/content/ytt/docs/v0.49.x/data-values-vs-overlays.md new file mode 100644 index 000000000..78353da0c --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/data-values-vs-overlays.md @@ -0,0 +1,221 @@ +--- +aliases: [/ytt/docs/latest/data-values-vs-overlays] +title: "Data Values vs Overlays" +--- + +## Overview + +As folks get started with `ytt`, a common question that arises is, “when should +I use data values versus overlays?” While these features do address a similar +problem space, we recommend using one feature versus the other depending on the +use case. We will detail our guidance below. + +## Data Values + +[Data values](ytt-data-values.md) provide a way to inject input data into a +template. If you think about a ytt template as a function, then data values are +the varying parameters. The configuration author will expose values that are +likely to change often, such as with every new environment, as data values. +Authors can also set data values to reasonable defaults or leave them empty and +require the consumers to input their own. + +Common use cases: + +1. Set default values (for generic or specific use-cases) +1. Provide visibility to values that could be updated by the consumer +1. Enable simple conditional behavior (such as requiring a value to be set) + +Consider this simple example provided by the configuration author: + +`config.yml` +```yaml +#@ load("@ytt:data", "data") +#@ load("@ytt:assert", "assert") +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: #@ data.values.deployment_name or assert.fail("missing deployment_name") + labels: + app: nginx +spec: + replicas: #@ data.values.replicas + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: #@ data.values.nginx_image + ports: + - containerPort: 80 +``` + +`values.yml` +```yaml +#@data/values +--- +deployment_name: "" +replicas: 1 +nginx_image: nginx:1.14.2 +``` + +The configuration consumer can provide their own data values files that override the ones +provided. + +This example portrays how to: + +1. Set default values. The configuration author has set appropriate default + values for `replicas` and `nginx_image`. +1. Provide visibility to values that could be updated by the consumer. + `deployment_name`, `replicas`, and `nginx_image` are all configurable by the + consumer. +1. Enable simple conditional behavior. `deployment_name` must be set by the + consumer, otherwise an error will be returned. + +Pros: + +- Simple (easy to use, easy to understand) + +Cons: + +- Limited in capability (by design) + +--- +## Overlays + +When consumers would like to configure fields beyond what the original +author has exposed as data values, they should turn to +[Overlays](lang-ref-ytt-overlay.md). These documents provide a way to specify +locations within configuration and either add to, remove from, or replace within +that existing configuration. With basic usage, overlays can act as an extension +of data values, but as situations inevitably become more complex, overlays +provide many more capabilities. + +Common use cases: +1. To replace, delete, or append configuration +1. Situation-specific values + +Consider this extension of our previous example: + +`config.yml` +```yaml +#@ load("@ytt:data", "data") +#@ load("@ytt:assert", "assert") +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: #@ data.values.deployment_name or assert.fail("missing deployment_name") + labels: + app: nginx +spec: + replicas: #@ data.values.replicas + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: #@ data.values.nginx_image + ports: + - containerPort: 80 + - name: to-be-removed + image: image:1.2.3 + ports: + - containerPort: 80 +``` + +`values.yml` +```yaml +#@data/values +--- +deployment_name: "" +replicas: 1 +nginx_image: nginx:1.14.2 +``` + +`add-namespace.yml` +```yaml +#@ load("@ytt:overlay", "overlay") + +#@overlay/match by=overlay.all, expects="1+" +--- +metadata: + #@overlay/match missing_ok=True + namespace: my-namespace +``` + +`remove-container.yml` +```yaml +#@ load("@ytt:overlay", "overlay") + +#@overlay/match by=overlay.all, expects="1+" +--- +spec: + template: + spec: + containers: + #@overlay/match by="name" + #@overlay/remove + - name: to-be-removed +``` + +`add-container.yml` +```yaml +#@ load("@ytt:overlay", "overlay") + +#@overlay/match by=overlay.all, expects="1+" +--- +spec: + template: + spec: + containers: + - name: appended-container + image: image:1.2.3 + ports: + - containerPort: #@ data.values.appended_container_port +``` +Prior to v0.32.0 append array items with [`#@overlay/append`](lang-ref-ytt-overlay.md#overlayappend) + +`prod-values.yml` +```yaml +#@data/values +--- +deployment_name: 3 +replicas: 3 +#@overlay/match missing_ok=True +appended_container_port: 8080 +``` + +This example demonstrates a number of overlay capabilities: + +1. Appending configuration via `add-namespace.yml` and `add-container.yml` +1. Removing configuration via `remove-container.yml` +1. Extending and updating data values via `prod-values.yml` + +Use the [ytt playground to play with this +example](/ytt/#gist:https://gist.github.com/aaronshurley/b6868b76e25fcb24aedde42f522734af). + +Pros: +- Has more functionality +- Doesn’t change the source +- Programmatic capabilities (this wasn't demonstrated here, see + [example](lang-ref-ytt-overlay.md#programmatic-access)) + +Cons: +- Added complexity + +# If you want to learn more... + +Check out the [Getting Started +tutorial](/ytt/#example:example-hello-world) on the ytt website +for a detailed introduction to ytt. diff --git a/site/content/ytt/docs/v0.49.x/faq.md b/site/content/ytt/docs/v0.49.x/faq.md new file mode 100644 index 000000000..09f859583 --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/faq.md @@ -0,0 +1,189 @@ +--- +aliases: [/ytt/docs/latest/faq] +title: FAQ +--- + +## Data Values + +[Data values doc](ytt-data-values.md) + +## Is it possible to add a new key to my values via the `--data-value` command line argument? +No. As with all data values, those passed through `--data-value` must be overrides, not new values. Instead, overlays are the intended way to provide new keys. +See the [data values vs overlays doc](data-values-vs-overlays.md) for more information. + +## How can I dynamically set or replace map key as a data value in my template? +You can use data.values value as a key in a map by using [Text Templating](ytt-text-templating.md) feature. +That way, you can dynamically set keys in a map using data values. +```yaml +#@yaml/text-templated-strings +(@= data.values.some_key @): some-value +``` +Additionally, see this [playground example](/ytt/#example:example-text-template) which illustrates the use of text templating to set key of a map item. + + +## How do I load json for use as a data value? +An important note here is that json is valid yaml. yaml syntax is a superset of json syntax.\ +ytt can naturally parse json by passing it through `--data-value-yaml`, or json can be loaded by passing the file as a `--data-value-file`. + +Additional resources: [json is valid yaml](https://gist.github.com/pivotaljohn/debe4596df5b4158c7c09f6f1841dd47), [loading by file](https://gist.github.com/pivotaljohn/d3468c3239f79fea7e232751757e779a) + +## How do I check if a key is set? +You can check for the existence of a key by using `hasattr`.\ +For example, to check if struct `foo` has attribute `bar`, use `hasattr(foo, "bar")`. + +## How do I provide a default for a data value when it may not be defined? +When a value may be null, you can use `or` to specify a default. +```yaml +#@ data.values.foo or "bar" +``` + +## How do I error if a data value is not provided? + +ytt library's `assert` package is useful for such situations. It can be used like so: + +```yaml +password: #@ data.values.env.mysql_password if data.values.env.mysql_password else assert.fail("missing env.mysql_password") +``` + +or even more compactly, + +```yaml +password: #@ data.values.env.mysql_password or assert.fail("missing env.mysql_password") +``` + +Note that empty strings are falsy in Starlark. + +--- + +## Overlays + +[Overlays doc](lang-ref-ytt-overlay.md) + +## How do I remove a document subset? + +`#@overlay/remove` in conjunction with `#@overlay/match by=overlay.subset()` annotations are useful for removing a subset of a document. + +Additional resources: [overlay remove docs](lang-ref-ytt-overlay.md#overlayremove), [overlay subset docs](lang-ref-ytt-overlay.md#overlaysubset) + +## How do I add items to an existing array?   + +Using v0.32.0 or later, the default behavior of overlays is to append array items. Simply put your array item in an overlay. + +Prior to v0.32.0, To add an item, either provide the matching annotation (eg. `#@overlay/match by="field_name"`), or use the `#@overlay/append` annotation to add to the end of the list. Note that the append annotation must be applied to each item you want to insert. + +Additional resources: [overlay append docs](lang-ref-ytt-overlay.md#overlayappend), [example gist on playground](/ytt/#gist:https://gist.github.com/pivotaljohn/8c7f48e183158ce12107f576eeab937c), [replace-list gist](/ytt/#gist:https://gist.github.com/pivotaljohn/2b3a9b3367137079195971e1409d539e), [edit-list gist](/ytt/#gist:https://gist.github.com/pivotaljohn/217e8232dc080bb764bfd064ffa9c115) + +## Why am I getting an exception when trying to append to an array? + +A common append issue is incorrectly setting the `#@overlay/match missing_ok=True` annotation on the key which gets replaced by new key-values. Instead, it should be applied to each child (made convenient with the `#@overlay/match-child-defaults missing_ok=True` annotation). See this [illustrative gist](https://gist.github.com/cppforlife/bf42f2d3d23dacf07affcd4150370cb9) for an example. + +## How do I rename a key without changing the value? + +An `#@overlay/replace` annotation with a lambda `via`. For example, to replace the key `bad_name` with `better_name` while retaining the value, you can use: +```yaml +#@overlay/replace via=lambda a,b: {"better_name": a["bad_name"]} +``` +See [this gist](/ytt/#gist:https://gist.github.com/gcheadle-vmware/3c41645a80201caaeefa878e84fff958) for the full example. + +## How do I add or replace a value in a dictionary? + +A `#@ template.replace()` annotation can be used for these purposes. See [this example](/ytt/#example:example-replace). You can also use overlays to edit a dictionary, an example can be found on [this gist playground](/ytt/#gist:https://gist.github.com/gcheadle-vmware/af8aeb3120386e58922c816d76f47ab6). + +## How do I match a field.name that starts with a string? + +```yaml +overlay/match by=lambda a,_: a["field"]["name"].startswith("string") +``` + +## How do I match a struct based on the presence of a key? + +To match a dictionary from a list of dictionaries if the `foo` key is present, you can use +```#@overlay/match by=lambda idx,old,new: "foo" in old, expects="1+"```. + +## How do I modify only part of a multi-line string? + +An `#@overlay/replace` annotation with a lambda `via` function can modify part of a string. See this [modify-string gist](/ytt/#gist:https://gist.github.com/cppforlife/7633c2ed0560e5c8005e05c8448a74d2) for an example. + +## How can I match a regex pattern in the subset matcher? + +The subset matcher does not directly support regex patterns. Instead, a custom matcher can be written. See this [playground gist](/ytt/#gist:https://gist.github.com/ewrenn8/3409e44252f93497a9b447900f3fb5b7) for an example. + +--- + +## When should I include a space in my ytt comment? Does it matter? Is it `#@load` or `#@ load`? `#@overlay/match` or `#@ overlay/match` +The space is subtly meaningful, and directly, load needs a space, while overlay/match does not – but why? + +ytt wraps two concepts in its comment syntax: +1. Annotations on a node +1. Directives for ytt + +Annotations do not have a space, and they refer to a given node in the tree. These comments attach metadata to the annotated node, which can be used during templating. +Some examples of annotations are: +- When inserting a node via an [overlay](#ytt-overlays.md), we would annotate that node with `#@overlay/insert`. +- When we want to mark a document as containing [data values](#ytt-data-values.md), we annotate the document start marker with `#@data/values`. + +Directives, on the other hand, do include a space, and are used to _direct_ ytt to execute the arguments. +Some examples of directives are: +- [Loading](#lang-ref-load.md) a library, we add the `#@ load` directive to the doc, unattached to any particular node. +- To begin and end a [for loop](#lang-ref-for.md) or [conditional](lang-ref-if.md), we use the `#@ for`, `#@ if`, and `#@ end` directives. + +For further exploration, investigate what happens when you move an annotation comment and compare it with moving a directive comment! + +## Why is `ytt` complaining about "Unknown comment syntax"; can't I write standard YAML comments (#)? + +You can, but it is discouraged to avoid tricky errors that can go unchecked. + +The recommended approach is when writing `ytt` templated files, use `ytt` comments: + +``` +#! this is a ytt comment; it's like she-bang! +``` + +If for some reason you need to disable this "linting" check (e.g. you're gradually migrating an existing YAML file to become a `ytt` template and it's onerous to convert all those comments at once), include the `--ignore-unknown-comments` flag. + +For a detailed explanation of how `ytt` detects and processes YAML files, see [File Marks > type detection for YAML files](file-marks.md#type-detection-for-yaml-files). + + +## Why is my anchor reference null despite my anchor's successful template? + +This is a [known limitation](known-limitations.md) of ytt. + +## Can I generate random strings with ytt? +No. A design goal of ytt is determinism, which keeps randomness out of scope. + +If you want to generate secrets, see the [injecting secrets doc](injecting-secrets.md) or the [kubernetes secretgen-controller](https://github.com/carvel-dev/secretgen-controller) + +## Can I load multiple functions without having to name each one? + +Yes! Functions can be stored in a struct which can be imported all together. You can then call individual functions from within that struct. Note that because Starlark does not provide forward references, you must declare the struct that collects the functions to export at the end of the file. + +Storing functions in struct: + +```yaml +#@ load("@ytt:struct", "struct") +#@ mod = struct.make(func1=func1, func2=func2) +``` + +Loading and calling functions in template: + +```yaml +#@ load("helpers.lib.yml", "mod") +something: #@ mod.func1() +``` + +Additional resources: [Load Statement doc](lang-ref-load.md) + +## How do I inject secrets? +See the [injecting secrets doc](injecting-secrets.md). + +## How do I template values within text? +See the [text templating doc](ytt-text-templating.md). Additionally, see this [playground example](/ytt/#example:example-text-template) which illustrates some ways text templating can be done. + +## How can I use files that are symlinks? +ytt takes a secure by default approach to symlinks. It disables use of symlinks to avoid [the risk](security.md#attack-vectors) of malicious template code loading symlinked file contents from sensitive locations. + +If you would like to override this behavior, use `--allow-symlink-destination` flag for allowing symlinks in specific directories or files, or `--dangerous-allow-all-symlink-destinations` to allow all symlinks. + +## What templating language does ytt use? + +ytt uses a fork of [Starlark](https://github.com/bazelbuild/starlark), with a few changes. See the [Language reference](lang.md#Language) for more information. diff --git a/site/content/ytt/docs/v0.49.x/file-marks.md b/site/content/ytt/docs/v0.49.x/file-marks.md new file mode 100644 index 000000000..4f87f2e7f --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/file-marks.md @@ -0,0 +1,138 @@ +--- +aliases: [/ytt/docs/latest/file-marks] +title: File Marks +--- + +## Overview + +ytt allows to control certain metadata about files via `--file-mark` flag. + +```bash +$ ytt ... --file-mark := +``` + +where: +- `path` — location to the file(s) being marked + - exact path (use `--files-inspect` to see paths as seen by ytt) + - path with `*` to match files in a directory + - path with `**/*` to match files and directories recursively +- `mark` — metadata to modify on the file(s) selected by `path` +- `value` — the value for the mark + +Note that this flag can be specified multiple times. + +Example: + +```bash +ytt -f . \ + --file-mark 'alt-example**/*:type=data' \ + --file-mark 'example**/*:type=data' \ + --file-mark 'generated.go.txt:exclusive-for-output=true' \ + --output-files ../../tmp/ +``` + +--- +## Available Marks + +### path + +Changes the relative path. + +``` +--file-mark ':path=' +``` + +Example: + +``` +--file-mark 'generated.go.txt:path=gen.go.txt' +``` + +renames `generated.go.txt` to `gen.go.txt` + +### exclude + +Exclude file from any kind of processing. + +``` +--file-mark ':exclude=true' +``` + +### type + +Change type of file, affecting how `ytt` processes it. + +``` +--file-mark ':type=' +``` + +By default `file-type` is determined based on file extension and — as of v0.32.0 — content. + +`file-type` can be: +- `yaml-template` (default for: `.yml` or `.yaml`) — parsed as a YAML document and evaluated for templating. +- `yaml-plain` — parsed as a simple YAML document (_not_ evaluated for templating). +- `text-template` (default for: `.txt`) — parsed as a text document containing text templating. +- `text-plain` — included in output, as is. +- `starlark` (default for: `.star`) — a Starlark source file (executed) +- `data` (default for all other files) — a text file that can be loaded by [`data.read()`](lang-ref-ytt.md#data) + +Example: + +``` +--file-mark 'config.yml:type=data' +``` + +indicates that `config.yml` is _not_ a `yaml-template`, but is `data`. This file will _not_ be parsed, evaluated, or included in the output, but _can_ be loaded using [`data.read()`](lang-ref-ytt.md#data). + +#### type detection for YAML files +_(as of v0.32.0)_ + +First, `ytt` determines the type of each YAML file: +1. if it has a `.yml` or `.yaml` extension, it's assumed to be a `yaml-template` +2. if it is `yaml-template` but does not contain any ytt templating, it is treated as if it were `yaml-plain` +3. if it is marked with `type=yaml-plain`, it is treated as `yaml-plain` + +and then processes each: +- `yaml-template` files are: + 1. parsed as YAML, + 2. evaluated (i.e. executes `ytt` the template), and + 3. linted (i.e. detects when non-ytt comments are included) + - this catches errors where the `@` is accidentally omitted, + - if the `--ignore-unknown-comments` flag is included, this "linting" is disabled. +- `yaml-plain` files are simply parsed as YAML (with no evaluation or linting). + + +### for-output + +Marks a file to be included in the output. + +Files of [type](#type) `yaml-template`, `yaml-plain`, `text-template`, and `text-plain` are part of the output by default. + +``` +--file-mark ':for-output=true' +``` + +Example +``` +--file-mark 'config.lib.yml:for-output=true' +``` + +By default, `.lib.yml` files are not included in the rendered output (they are loaded +by other templates). With this file mark, `config.lib.yml` _is_ included in the output. + +### exclusive-for-output + +Limits output to only marked files. + +If there is at least one file marked this way, only these files will be used in output. + +``` +--file-mark ':exclusive-for-output=true' +``` + +Example: +``` +--file-mark 'config.lib.yml:exclusive-for-output=true' +``` + +Causes output to only include `config.lib.yml`. diff --git a/site/content/ytt/docs/v0.49.x/how-it-works.md b/site/content/ytt/docs/v0.49.x/how-it-works.md new file mode 100644 index 000000000..f9a4828c7 --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/how-it-works.md @@ -0,0 +1,248 @@ +--- +aliases: [/ytt/docs/latest/how-it-works] +title: "How it works" +--- + +## Overview + +Let's get an idea of how `ytt` works by looking at the high-level concepts and flow. + +## The `ytt` Pipeline + +When you invoke `ytt` ... + +```console +$ ytt -f config/ --data-values-file values/ +``` + +... you can think of it as a pipeline in four stages, looking something like this: + +![ytt pipeline overview](/images/ytt/ytt-pipeline-overview.jpg) + + +_(Configuration documents (grey, pale yellow and blue) flow through four pipeline steps (black), into evaluated intermediary documents (bright yellow and blue), and combined ultimately into plain YAML output (green).)_ + +A quick note about files and documents, in general, followed by a detailed description of the elements in this diagram. + +**About YAML files and their documents** + +Each YAML file can have one or more YAML documents in them (separated by `---`). _(from here on, we'll refer to YAML documents as just "documents".)_ + +```yaml +foo: here's the first document (the initial `---` is optional) +--- +bar: this is a separate document +--- +qux: third document's a charm! +``` + +If a given "configuration file" contains one or more `ytt` annotations (i.e. lines that contain `#@`), it's a `ytt` template. + +This is a `ytt` template... +```yaml +--- +foo: 14 +bar: +#@ for/end name in ["Alice", "Bob", "world"]: +- #@ "Hello, " + name +``` +... and, this is plain YAML ... +```yaml +--- +foo: 14 +bar: +- Hello, Alice +- Hello, Bob +- Hello, world +``` + + +### Configuration + +The top-left section of [the diagram](#the-ytt-pipeline) shows the configuration files: the templated configuration and supporting files. + +These files are written and maintained by those who understand how the final result should be shaped. We refer to these folks as Configuration Authors. + +Configuration includes a mixture of these kinds of files: + +- [Data Values Schema Documents](#data-value-schema-documents) +- [Data Values Documents](#data-values-documents) +- [Plain Documents](#plain-documents) +- [Templated Documents](#templated-documents) +- [Overlay Documents](#overlay-documents) + +Now, let's look at each type of document, in turn. + +#### Data Value Schema Documents + +If a document begins with the [`@data/values-schema`](how-to-write-schema.md#starting-a-schema-document) annotation, we call it a "Data Values Schema Document" (the light grey dashed box in the illustration, [above](#the-ytt-pipeline)). + +```yaml +#@data/values-schema +--- +instances: 1 +... +``` + +These files _declare_ variables (Data Values) by setting their name, default value, and type. + +#### Data Values Documents + +When a document starts with the [`@data/values`](how-to-use-data-values.md) annotation, it's called a "Data Values Document" (the light grey dashed box in the illustration, [above](#the-ytt-pipeline)). + +```yaml +#@data/values +--- +instances: 8 +... +``` + +These contain the variables that provide values for templates (explained in more detail, in [Step 2: Evaluate Templates](#step-2-evaluate-templates)). + +#### Plain Documents + +If a document has _no_ `ytt` annotations, we'll call those "Plain Documents" (like the bright yellow item in "Configuration", [above](#the-ytt-pipeline)). +```yaml +--- +notes: +- this will be part of the output +- it does get parsed as YAML, but that's about it +``` +These documents need no processing (outside of being parsed as YAML), and are included as part of the output of the pipeline. + +#### Templated Documents + +If a document _does_ contain templating (i.e. lines containing `#@`) it's known as a "Templated Document" (the _pale_ yellow one, [above](#the-ytt-pipeline)). + ```yaml + #@ load("@ytt:data", "data") + + --- + apiVersion: apps/v1 + kind: Deployment + metadata: + name: my-app + spec: + replicas: #@ data.values.instances + ... + ``` +These documents are — after being processed — also included as part of the output of the pipeline. + +#### Overlay Documents + +When a document _starts_ with the [`@overlay/match...`](lang-ref-ytt-overlay.md#overlays-as-files) annotation (i.e. above the `---`), it's referred to as an "Overlay Document" (denoted as a pale blue item, [above](#the-ytt-pipeline)): + ```yaml + #@overlay/match by=overlay.all + --- + metadata: + #@overlay/match missing_ok=True + namespace: staging + ... + ``` +These documents describe edits to apply just before generating the output (described in detail, below). + +#### Kinds of Documents in Configuration + +Note that most configuration files can contain any combination of "Plain Documents", "Templated Documents", and "Overlay Documents". + +The exceptions are "Data Values Schema Documents" and "Data Values Documents". These documents must be in their own file, as illustrated [above](#the-ytt-pipeline). + +### Custom Values + +The top-middle section of [the diagram](#the-ytt-pipeline) shows where custom values are injected. + +These are values supplied by those who use `ytt` to generate the final output. We refer to these people as Configuration Consumers. + +They customize the result by supplying their situation-specific settings for Data Values. This can be done as command-line arguments, OS environment variables, and/or plain YAML files. In all cases, these override Data Values that must first be declared in [Data Values Schema Documents](#data-value-schema-documents). + +### Step 1: Calculate Data Values + +Let's explore what happens at each step in the pipeline. + +As the first pipeline step (black box) shows, [above](#the-ytt-pipeline) : + +1. process all the "Data Values Schema" documents (light grey input) — evaluating any templating in them; +2. merge those documents, [in order](lang-ref-ytt-schema.md#multiple-schema-documents), generating a "Data Values Schema" document populated with default values and type information. +3. process all the "Data Values" documents (light grey input) — evaluating any templating in them; +4. merge those documents, [in order](ytt-data-values.md#splitting-data-values-overlays-into-multiple-files). That is, start with the first document and then overlay the second one onto it; then overlay the third document on top of _that_, and so on... +5. finally, override values with the "Custom Values" input, as described in [How to Use Data Values > Configuring Data Values](how-to-use-data-values.md#configuring-data-values). \ + The result of all this is the final set of values that will be available to templates: the dark grey "final Data Values". +6. lastly, if there are any [validations declared in the schema](how-to-write-validations.md), all such rules are evaluated over this final result. + +_(Note the data in-flow arrows into this pipeline step are deliberately ordered, left-to-right, reinforcing the sequence in which values are set: defaults from schema, data values documents, and custom values; last source wins.)_ + + +### Step 2: Evaluate Templates + +Next, evaluate the remaining templates (all the other kinds of "Configuration" documents): +1. "evaluate" means executing all of the [Starlark code](lang.md): loops are run, conditionals decided, expressions evaluated. +1. one notable exception is overlay annotations (i.e. those that start with `@overlay/...`), these are deferred until the next step. +1. a template accesses input variables (i.e. the Data Values calculated in the previous step) via the [`@ytt:data` module](lang-ref-ytt.md#data); + +The result of all this evaluation is a set of YAML documents, configured with the Data Values (shown as "Evaluated Document Set" in the diagram, [above](#the-ytt-pipeline)). + +### Step 3: Apply Overlays + +Note that the "Evaluated Document Set" (see the output from the second step in [the diagram](#the-ytt-pipeline)) contains two groups: +- "Evaluated Documents" — these are the pile of "Plain Documents" and evaluated "Template Documents" (bright yellow) from the previous step. +- "Overlay Documents" — these are the configuration file "Overlay Documents" (bright blue) wherein everything except the `@overlay/...` annotations have been evaluated. + +With these in hand: +1. apply each "Overlay Document" on top of the full set of "Evaluated Documents".\ + You can think of each overlay as like a SQL `update` command: + - the value of it's `by` argument is like a `where` clause that selects over the whole collection of "Evaluated Documents". For example, + ```yaml + #@overlay/match by=overlay.subset({"kind": "Deployment"}), ... + --- + ``` + selects all of the documents which contain a key `"kind"` whose value is `"Deployment"` + - for each of the documents selected, apply the overlay on top of it. This is like a series of `set` clauses, each updating a portion of the document. For example, + ```yaml + #@overlay/match by=overlay.subset({"kind": "Deployment"}), ... + --- + #@overlay/match-child-defaults missing_ok=True + metadata: + labels: + app: frontend + ``` + sets each "Deployment"'s `metadata.labels.app` to be `"frontend"`. +1. repeat that process for each "Overlay Document", [in order](lang-ref-ytt-overlay.md#overlay-order). + +The result is (shown as "Output Document Set" in the diagram, [above](#the-ytt-pipeline)) — the finalized set of YAML documents, in memory. Which leaves one last step... + +### Step 4: Serialize + +This is simply iterating over the "Output Document Set", rendering each YAML Document ("Output Files", [above](#the-ytt-pipeline)). + +The result is sent to standard out (suitable for piping into other tools). If desired, the output can be sent instead to disk using the [`--output...` flags](outputs.md). + +## Further Reading + +We've scratched the surface: an end-to-end flow from pre-processing inputs, processing templates, post-processing overlays, and finally rendering the resulting output. + +To learn more about... +- **Data Values Schema** + - learn about [writing Schema](how-to-write-schema.md) for Data Values + - read-up on the details in the "[Data Values Schema Referce](lang-ref-ytt-schema.md)" material + - work with a complete example from the source: \ + [carvel-dev/ytt/../examples/schema](https://github.com/carvel-dev/ytt/tree/develop/examples/schema) +- **Data Values**... + - poke at a working example in the ytt Playground: [Load Data Values](/ytt/#example:example-load-data-values) example + - read-up on the details in "[Using Data Values](how-to-use-data-values.md)" + - work with a complete example from the source: \ + [carvel-dev/ytt/../examples/data-values](https://github.com/carvel-dev/ytt/tree/develop/examples/data-values) +- **Templates**... + - learn Starlark by example in the first bunch of ["Basics" examples](/ytt/#example:example-plain-yaml). + - read-up on [`ytt`'s built-in libraries](lang-ref-ytt.md) to encode/decode, hash, regex match over data. + - get an helpfully abridged tour of Starlark in "[Language](lang.md)" +- **Overlays**... + - walk through the "Overlays" group of examples: + 1. go to the [ytt Playground](/ytt/#example:example-match-all-docs) + 2. find the "Overlays" group header, click on it to reveal the group _(you can close the "Basics" group by clicking on it)_. + - read an introduction at "[Using Overlays](ytt-overlays.md)" + - watch [Primer on `ytt` Overlays](/blog/primer-on-ytt-overlays/) for a screencast-formatted in-depth introduction to writing and using overlays. + +This overview has meant to provide an end-to-end tour of the core functionality of `ytt`. + +There's more to it: +[modularizing configuration files](how-to-modularize/#extract-functionality-into-custom-library) into reusable Libraries, [text templating](ytt-text-templating.md), and +overriding file processing with [file marks](file-marks.md), and more. Use the left navigation to discover more. diff --git a/site/content/ytt/docs/v0.49.x/how-to-export-schema.md b/site/content/ytt/docs/v0.49.x/how-to-export-schema.md new file mode 100644 index 000000000..e6fae01d3 --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/how-to-export-schema.md @@ -0,0 +1,233 @@ +--- +aliases: [/ytt/docs/latest/how-to-export-schema] +title: Export Schema in OpenAPI Format +--- + +The primary use of schema is to declare (and type-check) Data Values. + +`ytt` _also_ supports the ability to export schema definitions in [OpenAPI v3.0.x](https://swagger.io/specification/) format. This is useful for tools that require schema definition in this industry-standard format. For example, `kapp-controller`, requires [Package](/kapp-controller/docs/latest/packaging/#package) 's `spec.valuesSchema.openAPIv3` to contain an [OpenAPI Schema map](https://swagger.io/specification/#schema-object). + +In this way, one can write their schema once and reuse it in situations that require the OpenAPI v3 format. + +## Working Example + +Given a `ytt` schema: + +```yaml +#! schema.yml + +#@data/values-schema +--- +#@schema/title "LoadBalancer type service" +#@schema/desc "Whether to include a LoadBalancer type service and if so, what its IP address is." +load_balancer: + enabled: true + #@schema/nullable + static_ip: "" + +#@schema/title "DNS domains" +#@schema/desc "DNS domains to accept traffic for." +#@schema/default ["apps.example.com", "mobile.example.com"] +app_domains: + #@schema/examples ("Example app domain", "web.myapp.com"), ("","localhost:8080") + - "" + +#@schema/title "Database connections" +#@schema/desc "Connection information for databases used by the system." +#@schema/examples ("Example for local db", [{"name": "default", "adapter": "postgresql", "host": "localhost", "port": 8080}]) +databases: +- name: "" + adapter: postgresql + host: "" + port: 5432 + +#@schema/title "Additional configuration" +#@schema/desc "Configuration for experimental/optional components; see documentation for more details." +#@schema/examples ("Example of additional config", {"username": "default", "password": "password", "insecureFlag": True}) +#@schema/type any=True +#@schema/deprecated "This data value will be removed in a future release" +additional_config: {} +``` +One can generate the corresponding OpenAPI Document: + +```bash +$ ytt -f schema.yml --data-values-schema-inspect --output openapi-v3 +``` +where: +- `--data-values-schema-inspect` — after all Data Value Schema files have been processed, print the effective schema (instead of rendering any templates). +- `--ouput=openapi-v3` — the schema format to use when rendering is OpenAPI v3.0.x (as opposed to `ytt` formatted schema). + +... which yields: + +```yaml +openapi: 3.0.0 +info: + version: 0.1.0 + title: Schema for data values, generated by ytt +paths: {} +components: + schemas: + dataValues: + type: object + additionalProperties: false + properties: + load_balancer: + title: LoadBalancer type service + type: object + additionalProperties: false + description: Whether to include a LoadBalancer type service and if so, what its IP address is. + properties: + enabled: + type: boolean + default: true + static_ip: + type: string + nullable: true + default: null + app_domains: + title: DNS domains + type: array + description: DNS domains to accept traffic for. + items: + type: string + x-example-description: Example app domain + example: web.myapp.com + default: "" + default: + - apps.example.com + - mobile.example.com + databases: + title: Database connections + type: array + description: Connection information for databases used by the system. + x-example-description: Example for local db + example: + - name: default + adapter: postgresql + host: localhost + port: 8080 + items: + type: object + additionalProperties: false + properties: + name: + type: string + default: "" + adapter: + type: string + default: postgresql + host: + type: string + default: "" + port: + type: integer + default: 5432 + default: [] + additional_config: + title: Additional configuration + nullable: true + deprecated: true + description: Configuration for experimental/optional components; see documentation for more details. + x-example-description: Example of additional config + example: + username: default + password: password + insecureFlag: true + default: {} +``` + +## Exported Properties + +Of the properties declared in the [OpenAPI Schema specification](https://swagger.io/specification/#schema-object), the following are generated. + +### `title` + +(As of v0.39.0+) + +Sets the user-friendly name or title of the node + +- when a data value is annotated `@schema/title`, the value of that title is the value of this property, verbatim; +- otherwise, this property is omitted. + +### `type` + +Names the type of the schema. + +The value of this property depends on the type of data value implied in the `ytt` schema: +- map ==> `object` +- array ==> `array` +- boolean ==> `bool` +- string ==> `string` +- integer ==> `integer` +- float ==> `type: number; format: float` + +### `additionalProperties` + +Indicates whether other keys are allowed in a mapping/object. + +- whenever inspecting a data value that is a map, `ytt` includes this property, setting it to `false`; +- when a data value is annotated `@schema/type any=True`, this property is omitted. + +### `nullable` + +Indicates whether `null` is also allowed. + +- when a data value is annotated `@schema/nullable` or `@schema/type any=True`, this property is included and set to `true`; +- otherwise, this property is omitted. + +### `deprecated` + +(As of v0.39.0+) + +Indicates if a key is deprecated. + +- when a data value is annotated `@schema/deprecated ""`, this property is included and set to `true`; +- requires a string argument intended to explain the deprecation, this string is omitted from the openapi document. +- otherwise, this property is omitted. + +### `description` + +Explains the contents and/or consequences of certain values of the property. + +- when a data value is annotated `@schema/desc`, the value of that description is the value of this property, verbatim; +- otherwise, this property is omitted. + +### `x-example-description` + +(As of v0.39.0+) + +Explains the contents of the `example` property. + +- when a data value is annotated `@schema/examples`, examples are provided via 2-tuples, only the first tuple will be exported to the OpenAPI Document. +- the example description is the first argument of 2-tuple, and must be a string. +- otherwise, this property is omitted. + +### `example` + +(As of v0.39.0+) + +Presents an example value for the data value. + +- when a data value is annotated `@schema/examples`, examples are provided via 2-tuples, only the first tuple will be exported to the OpenAPI Document. +- a 2-tuple contains string description, and an example value: `("first example", exampleYAML())`. +- example values should conform to the type of the same property. +- otherwise, this property is omitted. + +### `default` + +Declares a default value. + +The value of this property: +- is always included; +- is typically the value of the data value in the `ytt` schema; +- if the data value is annotated `@schema/default`, _that_ value is used instead. + +## Known Limitations + +The following are not yet supported in `ytt`: +- `ytt` schema does not yet support declarative validations and thus does not produce such validations in an OpenAPI export. +- there is no means yet to modify the [`info` section of the OpenAPI document](https://swagger.io/specification/#info-object) from within a `--data-values-schema-inspect`. There are defaults supplied, however we recommend that these fields are updated manually. +- inspecting output in `ytt` Schema format is not yet supported. + + + diff --git a/site/content/ytt/docs/v0.49.x/how-to-modularize.md b/site/content/ytt/docs/v0.49.x/how-to-modularize.md new file mode 100644 index 000000000..780c748fa --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/how-to-modularize.md @@ -0,0 +1,477 @@ +--- +aliases: [/ytt/docs/latest/how-to-modularize] +title: Getting started +--- + +## Overview + +Configuration authors looking for examples of how to use functions and variables, [modules](/ytt/docs/develop/lang-ref-load/#files), [data values schema](/ytt/docs/develop/how-to-write-schema/), or a [custom library](/ytt/docs/develop/lang-ref-ytt-library/), will see concrete examples in this guide. Language reference introduces concepts of basic syntax like ytt directives and ytt annotations [definitions](/ytt/docs/develop/faq/#when-should-i-include-a-space-in-my-ytt-comment-does-it-matter-is-it-load-or--load-overlaymatch-or--overlaymatch) (ie:`#@`). See the ytt playground ['getting started'](/ytt/#example:example-hello-world) section for additional examples. + +## Variable and function reuse + +A foundational concept in ytt is using Starlark code to create variables or functions. Inside a YAML file, prefix Starlark code with a ytt annotation `#@ ` (including a space afterwards) to use it inline. + +### Starlark variables +In the code block below there are duplicated values for `name: frontend`, and other values that we may want to modify often. + +```yaml +#! config.yml + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: frontend-deployment + namespace: default + labels: + app.kubernetes.io/version: 0.1.0 + app.kubernetes.io/name: frontend +spec: + selector: + matchLabels: + app: frontend + replicas: 1 + template: + spec: + containers: + - name: frontend + image: index.docker.io/k14s/image@sha256:6ab29951e0207fde6760f6db227f218f20e875f45b22e8ca0ee06c0c8cab32cd +--- +apiVersion: v1 +kind: Service +metadata: + name: frontend-service + labels: + app.kubernetes.io/version: 0.1.0 + app.kubernetes.io/name: frontend +spec: + type: ClusterIP + ports: + - port: 80 +``` + +_(This is a ytt comment `#!`, use these instead of YAML comments `#` which are [discouraged](/ytt/docs/develop/faq/#why-is-ytt-complaining-about-unknown-comment-syntax-cant-i-write-standard-yaml-comments-) to ensure that all comments are intentional. Comments will be consumed during execution.)_ + +Using Starlark's Python-like syntax, extract these values into Starlark variables. All the code defined here can be used in the same file. + +```yaml +#! config.yml + +#@ name = "frontend" +#@ namespace = "default" +#@ version = "0.1.0" +#@ replicas = 1 + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: #@ name + "-deployment" + namespace: #@ namespace + labels: + app.kubernetes.io/version: #@ version + app.kubernetes.io/name: #@ name +spec: + selector: + matchLabels: + app: #@ name + replicas: #@ replicas + template: + spec: + containers: + - name: #@ name + image: index.docker.io/k14s/image@sha256:6ab29951e0207fde6760f6db227f218f20e875f45b22e8ca0ee06c0c8cab32cd +--- +apiVersion: v1 +kind: Service +metadata: + name: #@ name + "-service" + labels: + app.kubernetes.io/version: #@ version + app.kubernetes.io/name: #@ name +spec: + type: ClusterIP + ports: + - port: 80 +``` +Execute this template by running `ytt -f config.yml`. + +The result is identical to our original template, and now we can be sure all our repeated values will be consistent and easier to modify. + +### Functions +[Functions](lang-ref-def.md) provide a way to extract common code into a separate fragment or code snippet. + +There are two ways to define a function in ytt: as a Starlark function; as a YAML fragment function. + +[Starlark functions](https://github.com/google/starlark-go/blob/master/doc/spec.md#functions) make use of a `return` statement. Because of this they can be great for returning a value that must be transformed in some way. + +[YAML fragment functions](lang-ref-yaml-fragment/#docs) differ in that they are YAML structure wrapped in a Starlark function definition. Everything inside the function will be the return value. They can be great when needing to return nested YAML structure, or key and value pairs. + +Going back to the previous solution, we can see each `labels` key is duplicated YAML, like `app.kubernetes.io/version: #@ version`. There is also some duplicated string manipulation in the `metadata.name` key. +```yaml +#! config.yml + +#@ name = "frontend" +#@ namespace = "default" +#@ version = "0.1.0" +#@ replicas = 1 + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: #@ name + "-deployment" + namespace: #@ namespace + labels: + app.kubernetes.io/version: #@ version + app.kubernetes.io/name: #@ name +spec: + selector: + matchLabels: + app: #@ name + replicas: #@ replicas + template: + spec: + containers: + - name: #@ name + image: index.docker.io/k14s/image@sha256:6ab29951e0207fde6760f6db227f218f20e875f45b22e8ca0ee06c0c8cab32cd +--- +apiVersion: v1 +kind: Service +metadata: + name: #@ name + "-service" + labels: + app.kubernetes.io/version: #@ version + app.kubernetes.io/name: #@ name +spec: + type: ClusterIP + ports: + - port: 80 +``` + +Move the duplicated `labels` keys into a YAML fragment function, and move name formatting into a Starlark function. + +```yaml +#! config.yml + +#@ name = "frontend" +#@ namespace = "default" +#@ version = "0.1.0" +#@ replicas = 1 + +#! Starlark function +#@ def fmt(name, type): +#@ return "{}-{}".format(name, type) +#@ end + +#! YAML fragment function +#@ def labels(name, version): +app.kubernetes.io/version: #@ version +app.kubernetes.io/name: #@ name +#@ end + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: #@ fmt(name, "deployment") + namespace: #@ namespace + labels: #@ labels(name, version) +spec: + selector: + matchLabels: + app: #@ name + replicas: #@ replicas + template: + spec: + containers: + - name: #@ name + image: index.docker.io/k14s/image@sha256:6ab29951e0207fde6760f6db227f218f20e875f45b22e8ca0ee06c0c8cab32cd +--- +apiVersion: v1 +kind: Service +metadata: + name: #@ fmt(name, "service") + labels: #@ labels(name, version) +spec: + type: ClusterIP + ports: + - port: 80 +``` +Execute this template by running `ytt -f config.yml`. +Again, the result is identical to our original template, and we can be sure all our repeated sections of YAML will be consistent. + +--- +## Externalize a value with data values schema + +Use [Data values schema](how-to-write-schema.md) to externalize a configuration value. When externalizing a value, you can also set a default value for it, and provide implicit type validation. Data values schema are used by configuration authors to declare a data value as an input to templates by naming it in a schema file. It can then be used in templates, and configuration consumers can modify it by providing a separate [Data Value](how-to-use-data-values.md) file. + +Building on the previous solution, `name`, `namespace`, `version`, and `replicas` are values we want as data values input, so that we can easily change them for different applications or environments. + +```yaml +#! schema.yml +#@data/values-schema +--- +name: "frontend" #! ensures that any value for 'frontend' must be a string +namespace: "default" #! ensures that any value for 'default' must be a string +version: "0.1.0" #! ensures that any value for 'version' must be a string +replicas: 1 #! ensures that any value for 'replicas' must be a int +``` +```yaml +#! config.yml + +#@ load("@ytt:data", "data") + +#@ def fmt(name, type): +#@ return "{}-{}".format(name, type) +#@ end + +#@ def labels(name, version): +app.kubernetes.io/version: #@ version +app.kubernetes.io/name: #@ name +#@ end + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: #@ fmt(data.values.name, "deployment") + namespace: #@ data.values.namespace + labels: #@ labels(data.values.name, data.values.version) +spec: + selector: + matchLabels: + app: #@ data.values.name + replicas: #@ data.values.replicas + template: + spec: + containers: + - name: #@ data.values.name + image: index.docker.io/k14s/image@sha256:6ab29951e0207fde6760f6db227f218f20e875f45b22e8ca0ee06c0c8cab32cd +--- +apiVersion: v1 +kind: Service +metadata: + name: #@ fmt(data.values.name, "service") + labels: #@ labels(data.values.name, data.values.version) +spec: + type: ClusterIP + ports: + - port: 80 +``` + +Execute ytt via `ytt -f config.yml -f schema.yml`. +The result is identical to our original template. + +## Extract code into modules + +Modules contain code in a file that can be imported and used in templates. + +Starlark modules have the `.star` extension. + +YAML modules have the `.lib.yml` extension. + +These two files are imported identically. The main difference is that Starlark modules contain only Starlark code, and YAML modules are YAML structures with Starlark code contained in `#@` annotations, just like the code we have seen thus far. + +### Starlark module +Following the last solution, move the `fmt()` function to a separate Starlark file. +```python +#! format.star + +def fmt(name, type): + return "{}-{}".format(name, type) +end +``` +Import the module by loading it `#@ load("format.star", "fmt")`. + +### YAML module +Move the `labels()` function to a separate YAML file. + +```yaml +#! labels.lib.yml + +#@ def labels(name, version): +app.kubernetes.io/version: #@ version +app.kubernetes.io/name: #@ name +#@ end +``` +Import the module by loading it `#@ load("labels.lib.yml", "labels")`. + +The load function takes a module file path, and secondly the name of the function or variable to export from the module. For multiple symbols, use a comma separated list of strings. If your module has many symbols that are usually all exported together, consider putting them in a [struct](faq/#can-i-load-multiple-functions-without-having-to-name-each-one), and load that struct. + +```yaml +#! config.yml + +#@ load("@ytt:data", "data") +#@ load("labels.lib.yml", "labels") +#@ load("format.star", "fmt") + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: #@ fmt(data.values.name, "deployment") + namespace: #@ data.values.namespace + labels: #@ labels(data.values.name, data.values.version) +spec: + selector: + matchLabels: + app: #@ data.values.name + replicas: #@ data.values.replicas + template: + spec: + containers: + - name: #@ data.values.name + image: index.docker.io/k14s/image@sha256:6ab29951e0207fde6760f6db227f218f20e875f45b22e8ca0ee06c0c8cab32cd +--- +apiVersion: v1 +kind: Service +metadata: + name: #@ fmt(data.values.name, "service") + labels: #@ labels(data.values.name, data.values.version) +spec: + type: ClusterIP + ports: + - port: 80 +``` + +```yaml +#@data/values-schema +--- +name: "frontend" +namespace: "default" +version: "0.1.0" +replicas: 1 +``` +```shell +$ tree . +. +├── config.yml +├── schema.yml +├── format.star +└── labels.lib.yml +``` + +Execute ytt via `ytt -f .` to include all files in this directory. + +## Extract functionality into custom library +You can extract a whole set of input files (i.e. templates, overlays, data values, etc.) into a "Library" in the `_ytt_lib/` folder. A library can be thought of as a separate self-contained ytt invocation. Libraries are _not_ automatically included in `ytt` output. They must be programmatically imported using the [`library` module](/ytt/docs/develop/lang-ref-ytt-library/), configured, evaluated, and inserted into a template that is part of the output. + +### Uses of a custom library +Libraries are helpful when +* importing 3rd party configuration into one combined piece of configuration. Such as from a division of responsibility or shared configuration. + * For example, having a library that provides helpful functions that needs to be used across multiple teams. Authors may use a tool to ensure these stay in sync. +* A template is needed by two distinct applications like frontend and backend. +* There is a need to update one application with an evaluated value from the other. [Playground example here](/ytt/#example:example-ytt-library-module). + +All the previous section's files have moved to `_ytt_lib/resources`: + +```shell +config/ +$ tree . +├── _ytt_lib/ +│ └── resources/ +│ ├── config.yml +│ ├── schema.yml +│ ├── labels.lib.yml +│ └── format.star +├── config.yml +└── values.yml +``` +In this example we want the resources from the library for a frontend application, and also for a backend application. We will use this library to create both of these. + +Focusing on only the two top level files, `config.yml`, and `values.yml`, we import the custom library, provide it data values for a frontend and separately for a backend, and use `#@ template.replace()` to insert it inline so it shows in the output. + +```yaml +#! config.yml +#@ load("@ytt:data", "data") +#@ load("@ytt:library", "library") +#@ load("@ytt:template", "template") + +#@ resources_lib = library.get("resources") +#@ backend = resources_lib.with_data_values(data.values.backend) +#@ frontend = resources_lib.with_data_values(data.values.frontend) + +--- #@ template.replace(backend.eval()) +--- #@ template.replace(frontend.eval()) +``` +Provide the values that we pass to the library. +```yaml +#@data/values-schema +--- +frontend: + name: "frontend" + namespace: "dev" + replicas: 1 + version: "0.5.0" +backend: + name: "backend" + namespace: "dev" + replicas: 1 + version: "0.2.0" +``` + +Run ytt with `.` to include all files in this directory. + +```shell +$ ytt -f . +``` +The result is the similar to our original template with resources configured for two different applications. +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: backend-deployment + namespace: dev + labels: + app.kubernetes.io/version: 0.2.0 + app.kubernetes.io/name: backend +spec: + selector: + matchLabels: + app: backend + replicas: 1 + template: + spec: + containers: + - name: backend + image: index.docker.io/k14s/image@sha256:6ab29951e0207fde6760f6db227f218f20e875f45b22e8ca0ee06c0c8cab32cd +--- +apiVersion: v1 +kind: Service +metadata: + name: backend-service + labels: + app.kubernetes.io/version: 0.2.0 + app.kubernetes.io/name: backend +spec: + type: ClusterIP + ports: + - port: 80 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: frontend-deployment + namespace: dev + labels: + app.kubernetes.io/version: 0.5.0 + app.kubernetes.io/name: frontend +spec: + selector: + matchLabels: + app: frontend + replicas: 1 + template: + spec: + containers: + - name: frontend + image: index.docker.io/k14s/image@sha256:6ab29951e0207fde6760f6db227f218f20e875f45b22e8ca0ee06c0c8cab32cd +--- +apiVersion: v1 +kind: Service +metadata: + name: frontend-service + labels: + app.kubernetes.io/version: 0.5.0 + app.kubernetes.io/name: frontend +spec: + type: ClusterIP + ports: + - port: 80 +``` + diff --git a/site/content/ytt/docs/v0.49.x/how-to-use-data-values.md b/site/content/ytt/docs/v0.49.x/how-to-use-data-values.md new file mode 100644 index 000000000..99c243ae1 --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/how-to-use-data-values.md @@ -0,0 +1,141 @@ +--- +aliases: [/ytt/docs/latest/how-to-use-data-values] +title: Using Data Values +--- + +## Overview + +A Configuration Author introduces variables in `ytt` (i.e. to externalize configuration values) by: +1. declaring them as "Data Values" by naming it in a schema file and then, +2. referencing them in templates. + +Configuration Consumers then set values for those variables in any combination of: +- one or more of the `--data-value...` flag(s) and/or +- Data Values Overlay(s) through the `--file` flag + +This guide illustrates how to declare and configure data values. + +_(for a higher-level overview of `ytt`, see [How it works](how-it-works.md).)_ + +### Declaring Data Values + +In `ytt`, before a Data Value is used, it is declared. This is typically done in a schema file. + +For example: + +`schema.yml` +```yaml +#@data/values-schema +--- +name: monitor +ingress: + virtual_host_fqdn: "monitor.system.example" + service_port: 80 + enable_tls: false +``` + +declares five Data Values: +- `name` contains a string; the default name is "monitor". + - `ingress` is a map that contains three map items: `virtual_host_fqdn`, `service_port`, and `enable_tls`. +- `ingress.virtual_host_fqdn` is a string; by default, the fully-qualified host name is the value given. +- `ingress.service_port` is an integer; by default, the service is listening on the standard HTTP port. +- `ingress.enable_tls` is a boolean; by default, transport layer security is off. + +_(see the [How To Write Schema](how-to-write-schema.md) guide, for details.)_ + + +### Referencing Data Values + +Those Data Values can then be referred to in template(s): + +`config.yml` +```yaml +#@ load("@ytt:data", "data") +--- +name: #@ data.values.name +spec: + virtualhost: #@ data.values.ingress.virtual_host_fqdn + services: + - port: #@ data.values.ingress.service_port + #@ if/end data.values.ingress.enable_tls: + - port: 443 +``` +where: +- `load("@ytt:data", "data")` imports the the `data` struct from the `@ytt:data` module +- `data.values` contains all of the declared data values +- `#@ if/end` only includes the annotated array item if the data value `ingress.enable_tls` is true. + +Using the defaults given in the schema, `ytt` produces: +```console +$ ytt -f schema.yml -f config.yml +name: monitor +spec: + virtualhost: monitor.system.example + services: + - port: 80 +``` + +_(For details on using the Data module, refer to [`@ytt:data`](lang-ref-ytt.md#data).)_ + +### Configuring Data Values + +Those Data Values can be configured by a Consumer: + +This is done, typically, via a Data Values File: + +`values.yml` +```yaml +--- +name: observer +ingress: + virtual_host_fqdn: "observer.system.example" + enable_tls: true +``` + +which is a plain YAML file (i.e. _cannot_ contain any `ytt` templating). This file is specified through the `--data-values-file` flag. + +Using the example files from above, `ytt` produces this output: + +```console +$ ytt -f schema.yml -f config.yml --data-values-file values.yml +name: observer +spec: + virtualhost: observer.system.example + services: + - port: 80 + - port: 443 +``` + +Supplied Data Values are automatically checked against the schema. If any value is of the wrong type, `ytt` reports the discrepancies and stops processing. + +_(For details on how to configure Data Values, consult the [Data Values](ytt-data-values.md) reference.)_ + + +## Resources + +Documentation: +- [How To Write Schema](how-to-write-schema.md) guide — step-by-step writing schema in `ytt`. +- [Data Values Reference](ytt-data-values.md) — details of how Data Values are specified in all scenarios. +- [Data Values Schema Reference](lang-ref-ytt-schema.md) — the anatomy of a `ytt` Schema file all elements within. +- [Schema Migration Guide](data-values-schema-migration-guide.md) — migrating existing `ytt` code from pre-Schema versions. + +Examples: +- Declaring and using Data Values in schema: \ + https://github.com/carvel-dev/ytt/tree/develop/examples/schema +- Setting a value for an _array_ in schema: \ + https://github.com/carvel-dev/ytt/tree/develop/examples/schema-arrays +- Using most of the `--data-value...` flags:\ + https://github.com/carvel-dev/ytt/tree/develop/examples/data-values/ +- Marking a data value as "required":\ + https://github.com/carvel-dev/ytt/tree/develop/examples/data-values-required/ +- Maintaining per-environment data value overrides:\ + https://github.com/carvel-dev/ytt/tree/develop/examples/data-values-multiple-envs +- Wrapping an upstream set of templates to expose a simplified set of data values:\ + https://github.com/carvel-dev/ytt/tree/develop/examples/data-values-wrap-library +- Using a directory full of YAML files for data values input:\ + https://github.com/carvel-dev/ytt/tree/develop/examples/data-values-directory + +Blog Articles: +- [Parameterizing Project Configuration with ytt](https://carvel.dev/blog/parameterizing-project-config-with-ytt/), by Garrett Cheadle +- [Deploying to multiple environments with ytt and kapp](https://carvel.dev/blog/multi-env-deployment-ytt-kapp/), by Yash Sethiya + diff --git a/site/content/ytt/docs/v0.49.x/how-to-write-schema.md b/site/content/ytt/docs/v0.49.x/how-to-write-schema.md new file mode 100644 index 000000000..2f29ba188 --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/how-to-write-schema.md @@ -0,0 +1,283 @@ +--- +aliases: [/ytt/docs/latest/how-to-write-schema] +title: Writing Schema +--- + +## Overview + +In `ytt`, before a Data Value can be used in a template, it must be declared. This is typically done via Data Values Schema. + +This guide shows how to write such schema. + +_(For a broader overview of Data Values, see [Using Data Values](how-to-use-data-values.md))._ + +--- +## Starting a Schema Document + +One writes Data Values Schema in a YAML document annotated with `#@data/values-schema`: + +```yaml +#@data/values-schema +--- +#! Schema contents +``` + +Files containing Schema documents are included via the `--file`/`-f` flag. + +```bash +$ ytt ... -f schema.yml ... +``` + +The contents of such a document _are_ the Data Values being declared. + +## Declaring Data Values + +Each item in the schema document declares a Data Value. + +A Data Value declaration has three (3) parts: a **name**, a **default value**, and a **type**. + +For example, +```yaml +#@data/values-schema +--- +system_domain: "" +``` + +declares the Data Value, with the name `system_domain` to be of type **string** with the default value of an **empty string** (i.e. `""`). + +--- +## Implying Types + +In `ytt` Schema types of Data Values are _inferred_ from the values given. So, when one writes schema, they are _implying_ the type of each Data Value through the default value they give. + +For example: +```yaml +#@data/values-schema +--- +system_domain: "" + +load_balancer: + enabled: true + static_ip: "" + +app_domains: +- "" + +databases: +- name: "" + adapter: postgresql + host: "" + port: 5432 + user: admin + secretRef: + name: "" +``` + +effectively declares the following data values: +- `system_domain` — a string +- `load_balancer` — a map containing two items: + - `enabled` — a boolean + - `static_ip` — a string +- `app_domains` — an array of strings (and only strings) +- `databases` — an array where each element is a map. Each map has exactly six items: + - `name` — a string + - `adapter` — a string + - `host` — a string + - `port` — an integer + - `user` — a string + - `secretRef` — a map having exactly one item: + - `name` — a string + +_(see [Data Values Schema Reference: Types](lang-ref-ytt-schema.md#types) for details.)_ + +--- +## Setting Default Values + +In `ytt` Schema, the default value for a Data Value is almost always simply the value specified. + +From the example, [above](#implying-types), the corresponding Data Values have the following defaults: +- `system_domain` is empty string (i.e. `[]`), +- `load_balancer.enabled` is `true`, and +- `load_balancer.static_ip` initializes to empty string. + +For details on how to set individual default values, see [Data Values Schema Reference: Default Values](lang-ref-ytt-schema.md#default-values). + +**Special Case: Arrays** + +There is one exception: arrays. As described in [Data Values Schema Reference: Defaults for Arrays](lang-ref-ytt-schema.md#defaults-for-arrays), the default value for arrays, by default, is an empty list (i.e. `[]`). That said, when an item is added to the array, _that item's_ value is defaulted as defined in the schema. + +In the example, [above](#implying-types), the definition of `databases` is an array. Each item in _that_ array is a map with six keys including `adapter`, `port`, etc. + +`database` starts out as an empty list. Then, as each item added to it, they will be defaulted with the values given in the schema. + +Continuing with our example schema, [above](#implying-types), if a Data Values overlay gives an actual value for the array: + +```yaml +#@data/values +--- +databases: +- name: core + host: localhost +``` + +That item will be filled-in with defaults: + +```yaml +name: core +adapter: postgresql +host: localhost +port: 5432 +user: admin +secretRef: + name: "" +``` + +In order to override the default of the array, _itself_, within schema see [Setting a Default Value for Arrays](#setting-a-default-value-for-an-array). + +## Constraining values with Validations + +To learn about writing schema validations, please refer to [How to Write Schema Validations](how-to-write-validations.md)\ +To get started quickly - [Schema validations Cheat Sheet](schema-validations-cheat-sheet.md) + +## Specific Use-Cases + +A few less common, but real-world scenarios: + +- [setting a default value for arrays](#setting-a-default-value-for-an-array) +- [marking a Data Value as optional](#marking-a-data-value-as-optional) +- [allowing multiple types of maps or arrays](#allowing-multiple-types-of-maps-or-arrays) +- [declaring "pass-through" Data Values](#declaring-pass-through-data-values) + +### Setting a Default Value for an Array + +As explained in [Data Values Schema Reference: Defaults for Arrays](lang-ref-ytt-schema.md#defaults-for-arrays), unlike all other types, the default value for an array is an empty list (i.e. `[]`). + +In some cases, it is useful to provide a non-empty default value for an array. To do so, one uses the `@schema/default` annotation. + +For example, with this schema: + +```yaml +#@data/values-schema +--- +#@schema/default ["apps.cf-apps.io", "mobile.cf-apps.io"] +app_domains: +- "" +``` + +The default value for `app_domains` will be `["apps.cf-apps.io", "mobile.cf-apps.io"]`. + +See also: [@schema/default](lang-ref-ytt-schema.md#schemadefault). + +### Marking a Data Value as Optional + +Sometimes, it can be useful to define a section of Data Values as optional. This typically means that templates that rely on those values conditionally include output that use the contained value. + +For example, the following template: +```yaml +#@ load("@ytt:data", "data") +--- +... +spec: + #@ if data.values.load_balancer: + loadBalancerIP: #@ data.values.load_balancer.static_ip + #@ end +... +``` +will only include `spec.loadBalancerIP` if a value is provided for the `load_balancer` Data Value. + +One notes this in Schema using the `@schema/nullable` annotation: + +```yaml +#@data/values-schema +--- +#@schema/nullable +load_balancer: + static_ip: "" +``` + +which indicates that `load_balancer` is `null`, by default. However, if a value _is_ provided for `load_balancer`, it must be the `static_ip` and have a value that is a **string**. + +For more details see [Data Values Schema Reference: `@schema/nullable`](lang-ref-ytt-schema.md#schemanullable). + +### Marking a Data Value as Required + +In `ytt`, a data value can be marked as "Required" using schema validations. +Please refer to +["Required" Data Values](/ytt/docs/develop/how-to-write-validations/#required-data-values) + +### Allowing Multiple Types of Maps or Arrays + +In rare cases, a given Data Value needs allow more than one type. + +Currently, `ytt` Schema does not explicitly support specifying more than one type for a Data Value. + +In the meantime, one can mark such Data Values as having [`any` Type](lang-ref-ytt-schema.md#any-type): + +```yaml +#@schema/type any=True +int_or_string: "" +``` +so that: +- `int_or_string` is, by default, an empty string +- it can accept an **integer** or a **string** ... or any other type, +- and the value of `int_or_string` and its children is not checked by schema. + +If it is critical to ensure that the type of `int_or_string` to be _only_ an integer or string, one can include a validating library that does so explicitly: + +```python +load("@ytt:assert", "assert") +load("@ytt:data", "data") + +if type(data.value.int_or_string) not in ("int", "string"): + assert.fail("'int_or_string' must be either an integer or a string.") +end +``` + +### Declaring "Pass-through" Data Values + +In certain cases, one designs a Data Value to carry a chunk of YAML whose exact type or shape is unimportant to templates. In these situations, it is undesirable for that chunk of YAML to be type-checked. + +One can effectively disable schema type checking and defaulting by marking the Data Value as of type "any": + +```yaml +#@data/values-schema +--- +honeycomb: + enabled: false + api_key: so124me14v4al1i5da5p5i180key + #@schema/type any=True + optional_config: null +``` + +Here, `additional_config` can contain any valid YAML. + +For example: + +```yaml +#@data/values +--- +honeycomb: + optional_config: + default_series: + id: 1001 + description: Administrative Actions +``` + +In a template, the Data Value can be referenced and its contents will be inserted: + +```yaml +#@ load("@ytt:data", "data") +--- +#@ if/end data.values.honeycomb.enabled: +honeycomb: + config: + api_key: #@ data.values.honeycomb.api_key + #@ if/end data.values.honeycomb.optional_config: + optional: #@ data.values.honeycomb.optional_config +``` + + +--- +## Next Steps + +Once you've declared your Data Values, they can be referenced in `ytt` templates as described in [Using Data Values > Referencing Data Values](how-to-use-data-values.md#referencing-data-values) diff --git a/site/content/ytt/docs/v0.49.x/how-to-write-validations.md b/site/content/ytt/docs/v0.49.x/how-to-write-validations.md new file mode 100644 index 000000000..2419e7503 --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/how-to-write-validations.md @@ -0,0 +1,573 @@ +--- +aliases: [/ytt/docs/latest/how-to-write-validations] +title: Writing Schema Validations +--- + +## Overview + +_(Looking for a quick start? see the [Validations Cheat Sheet](schema-validations-cheat-sheet.md))_ + +A Configuration Author can constraint their users' Data Value inputs via `ytt` Validations. + +One might do this for a number of reasons: +- **catch configuration errors early** — help the user of the `ytt` library from wasting time _discovering_ errors in their configuration when they use it... by catching and _reporting_ those errors right away + - e.g. in Kubernetes, instead of sifting through statuses and logs troubleshooting a failed deploy, present the user with an error message at configuration time — _before_ the deployment begins. +- **avoid impractical configuration** — guide them away from setting values that won't work in practice (e.g. too many `replicas:` of a Kubernetes Deployment, when the system won't actually scale that large); +- **make a Data Values "required"** — force the user to supply values for Data Values that you — as the author — can't possibly know (e.g. credentials, connection info to services, etc.). + +This guide explains how to do all that with Validations. + +## What Validations Look Like + +A validation is an annotation on a Data Value in a schema file: + +```yaml +#@data/values-schema +--- +dex: + #@schema/validation min_len=1 + namespace: "" +``` + +Here: +- the Data Value `dex.namespace` is a string +- to be valid, that string must be set to a value at least one character long. + +One can specify multiple "rules": + +```yaml +#@data/values-schema +--- +dex: + #@schema/validation min_len=1, max_len=63 + namespace: "" +``` + +Here, `dex.namespace` must ultimately be: +- at least 1 character, _and_ +- no more than 63 characters in length. + +And one can declare validations for _each_ Data Value; `ytt` will validate _all_ Data Values, together. + +```yaml +#@data/values-schema +--- +dex: + #@schema/validation min_len=1, max_len=63 + namespace: "" + #@schema/validation min_len=1 + username: "" +``` + +Here: +- additionally, `dex.username` is also a string; and to be valid must be at least one character in length. + +> _There can only be one `@schema/validation` annotation on a Data Value: all rules required to define validity must be combined into that one annotation._ + +Finally, one can write their own custom rules, as well: + +```yaml +#@data/values-schema +--- +dex: + #@schema/validation ("not 'default'", lambda v: v != "default"), min_len=1, max_len=63 + namespace: "" +``` + +Here: +- For `dex.namespace` to be valid it must: + - _not_ be the string "default", _and_ + - be at least one character long, _and_ + - be no more than 63 characters in length. + +_(For a list of the most common rules, see [Validations Cheat Sheet](schema-validations-cheat-sheet.md))_ + +_(For details on the use and shape of individual rules, see [About Rules](#about-rules), below)_ + +## How Validations Work + +At a high level, validations fit into the flow like so: + +1. In schema, the Author declares validations on Data Values (described [above](#what-a-validations-look-like)) +2. The Consumer configures data values (described in [How To Use Data Values](how-to-use-data-values.md#configuring-data-values)) +3. All of those values are merged into a single set (the first step of the [ytt Pipeline](how-it-works.md#step-1-calculate-data-values)) +4. All validations are run on that final Data Values; if any fail, those are collected as "violations" +5. If there were violations, processing stops and those are reported; \ + ... otherwise, processing continues normally. + +For example, given this schema: + +```yaml +#@data/values-schema +--- +dex: + #@schema/validation min_len=1, max_len=63 + namespace: "" + #@schema/validation min_len=1 + username: "" +``` + +If the Consumer supplies no data values, then the final Data Values are the defaults from the schema. +When the validations are run, instead of rendering templates, `ytt` reports the violations: + +```console +$ ytt -f schema.yaml +ytt: Error: Validating final data values: + dex.namespace + from: schema.yaml:5 + - must be: length >= 1 (by: schema.yaml:4) + found: length = 0 + + dex.username + from: schema.yaml:7 + - must be: length >= 1 (by: schema.yaml:6) + found: length = 0 +``` + +And if the Consumer supplies data values: + +```yaml +dex: + namespace: the-longest-namespace-you-ever-did-see-in-fact-probably-too-long + username: alice +``` + +Only _after_ those are merged with the default values are validations run: + +```console +$ ytt -f schema.yaml --data-values-file values.yaml +ytt: Error: Validating final data values: + dex.namespace + from: alues.yaml:2 + - must be: length <= 63 (by: schema.yaml:4) + found: length = 64 +``` + +Until, finally, all Data Values have valid values: + +```console +$ ytt -f schema.yaml --data-values-file values.yaml --data-value dex.namespace=ident-system --data-values-inspect +dex: + namespace: ident-system + username: alice +``` + +Next, we cover typical situations where validations are useful... + +## Common Use Cases + +There are a variety of ways you can put validations to use: + +- ["Required" Data Values](#required-data-values) — ensure the user supplies their own value +- [Enumerations](#enumerations) — limit the value to a finite, specific set. +- [Mutually Exclusive Sections](#mutually-exclusive-sections) — when there are multiple way to configure a feature, but the Consumer should only choose one. +- [Conditional Validations](#conditional-validations) — trigger validations only in certain situations. + +### "Required" Data Values + +Sometimes, there are configuration values that you — as the Author — either can't possibly know (e.g. IP addresses, domain names) or do not want to default (e.g. passwords, tokens, certificates, other credentials). Instead, you want to force the Consumer to supply these values. + +> ℹ️ _The way to mark a Data Value as "required" is by declaring a validation rule that is not satisfied by that Data Value's default._ + +There are three general tactics: + +- ideally, [using natural constraints](#using-natural-constraints) +- otherwise, [using the empty/zero value](#using-the-emptyzero-value) +- if all else fails, [mark as 'nullable' _and_ 'not_null'](#mark-as-nullable-and-not_null) + +#### Using Natural Constraints + +The most concise (and maintainable) way to make a data value "required" is to set a default outside of its natural constraints. + +For example, if `port:`, means any "registered" (ports 1024 - 49151) or "dynamic" (49152 - 65535) port, then + +```yaml +#@data/values-schema +--- +#@schema/validation min=1024 +port: 0 +``` + +out of the box, the Consumer receives this message: + +```console +$ ytt -f schema.yaml + port + from: schema.yaml:4 + - must be: a value >= 1024 (by: schema.yaml:3) + found: value < 1024 +``` + +Where there are not "natural" limits, one might be able to use the zero or empty value... + +#### Using the empty/zero value + +For strings, an empty value is often not valid. One can specifically require a non-zero length: + +```yaml +#@schema/validation min_len=1 +username: "" +``` + +For integers and floating-point values, non-positive numbers are often not valid. One can require a non-negative number + +```yaml +#@schema/validation min=1 +replicas: 0 +``` + +For array values, note that [the default value is always an empty list](how-to-write-schema.md#setting-a-default-value-for-an-array). One can require that the array not be empty: + +```yaml +#@data/values-schema +--- +dex: + oauth2: + #@schema/validation min_len=1 + responseTypes: + - "" +``` +Here, +- `dex.oauth2.responseTypes` is an array of strings. +- by default no response types are configured. +- however, the rule requires that _at least one_ be specified. + +#### Mark as 'nullable' and 'not_null' + +In some cases, there simply is no invalid value and/or there is no zero value (e.g. maps). + +What's left is to specify no value at all (i.e. `null`) and then require a non-null value. + +```yaml +#@data/values-schema +--- +#@schema/nullable +#@schema/validation not_null=True +tlsCertificate: + tls.crt: "" + tls.key: "" + ca.crt: "" +``` +Here: +- `tlsCertificate:` is a map, containing three items. +- `@schema/nullable` changes `tlsCertificate:` in two ways (details at [`@schema/nullable`](lang-ref-ytt-schema.md#schemanullable)) + - now, `tlsCertificate` can be set to `null` + - and, `tlsCertificate` is `null` by default. +- the `not_null=` rule requires that `tlsCertificate:` _not_ be `null` + +out of the box, the Consumer receives this message: + +```console +$ ytt -f schema.yaml +ytt: Error: Validating final data values: + tlsCertificate + from: schema.yaml:5 + - must be: not null (by: schema.yaml:4) + found: value is null +``` + +### Enumerations + +Some values must be from a discrete and specific set. + +```yaml +#@data/values-schema +--- +#@schema/validation one_of=["aws", "azure", "vsphere"] +provider: vsphere +``` + +### Conditional Validations + +Sometimes, a Data Value should be validated only when some _other_ configuration has been set. + +In `ytt` Validations, this is achieved through the `when=` keyword. + +For example: + +```yaml +#@data/values-schema +--- +#@schema/validation ("at least 1 instance", lambda v: v["instances"] >= 1), when=lambda v: v["enabled"] +service: + enabled: true + instances: 1 +``` +Here: +- if `service.enabled` is false, the validation is _not_ run; +- when `service.enabled` is true, `service.instances` is required to be non-negative. + +_(For more details, see [Reference for `@schema/validation`](lang-ref-ytt-schema.md#schemavalidation).)_ + +#### Making Validations Dependent on Other Data Values + +In some situations, a Data Values's final value is only relevant (i.e. worth validating) if some _other_ data value has a specific setting. +For these situations, the `@schema/validation ... when=` can accept an optional second parameter. + +For example, the previous example could be rewritten as: + +```yaml +#@data/values-schema +--- +service: + enabled: true + #@schema/validation min=1, when=lambda _, ctx: ctx.parent["enabled"] + instances: 6 +``` +where: +- the `when=` now has a function value that accepts two (2) arguments; the second of which is named `ctx`. + - see [Reference for `@schema/validation`](lang-ref-ytt-schema.md#schemavalidation) for details of the value assigned to `ctx`. +- `instances` will only be validated if `enabled` is `true` + +### Mutually Exclusive Sections + +One pattern found in configuration files is the "mutually exclusive" structure. + +This is typically done with a discriminator field: + +```yaml +--- +dex: + config: + type: "oidc" + oidc: + CLIENT_ID: null #! required if oidc enabled + CLIENT_SECRET: null #! required if oidc enabled + issuer: null #! is required if oidc enabled + ldap: + host: null #! is required if ldap enabed + bindDN: null + bindPW: null +``` +Here: +- there are two kinds of identity systems one could configure: OIDC _or_ LDAP. +- which one being used is named in `dex.config.type`; in this case, OIDC. +- both structures are present and `null` values are used. + +Essential is that the Consumer can configure _either_ OIDC _or_ LDAP _but not both._ + +There are at least a couple of approaches possible: +- [Using `one_not_null=`](#using-one_not_null) to enforce that only one section can be populated. +- [Using a Discriminator as the Condition](#using-a-discriminator-as-the-condition) to trigger validations only on the currently selected section. + +#### Using `one_not_null=` + +With `ytt` the Author can more clearly enforce this structure and validate only the active configuration: + +```yaml +#@data/values-schema +--- +dex: + #@schema/validation one_not_null=["oidc", "ldap"] + config: + #@schema/nullable + oidc: + CLIENT_ID: "" + CLIENT_SECRET: "" + issuer: "" + #@schema/nullable + ldap: + host: "" + bindDN: "" + bindPW: "" +``` +Here: +- each option (i.e. `oidc:` and `ldap:`) are made "optional" by marking them as `@schema/nullable` + - `@schema/nullable` makes a Data Value _able_ to be `null` _and_ sets it to `null`, by default. (for details [`@schema/nullable`](lang-ref-ytt-schema.md#schemanullable)) +- however, `config:` requires that _exactly one (1)_ of the listed keys contain a **not-null** value. + +By default, then, neither `oidc:` nor `ldap:` are configured: + +```console +$ ytt -f schema.yaml --inspect-data-values --dangerous-data-values-disable-validation +dex: + config: + oidc: null + ldap: null +``` + +When validations run, the Consumer is prompted to configure one: + +```console +$ ytt -f schema.yaml --data-values-inspect +ytt: Error: Validating final data values: + dex.config + from: schema.yaml:5 + - must be: exactly one of ["oidc", "ldap"] to be not null (by: schema.yaml:4) + found: all values are null +``` + +Once a value _is_ provided for one or the other, the configuration becomes valid: + +```console +$ ytt -f schema.yaml --data-values-inspect --data-value dex.config.oidc.CLIENT_ID=admin +dex: + config: + oidc: + CLIENT_ID: admin + CLIENT_SECRET: "" + issuer: "" + ldap: null +``` + +#### Using a Discriminator as the Condition + +In some cases, it may be desirable to keep the discriminator. + +Reworking the example from above... + +```yaml +#@data/values-schema +--- +dex: + config: + #@schema/validation one_of=["oidc", "ldap"] + type: "oidc" + oidc: + #@schema/validation min_len=1, when=lambda _, ctx: ctx.root["dex"]["config"]["type"] == "oidc" + CLIENT_ID: "" + #@schema/validation min_len=1, when=lambda _, ctx: ctx.root["dex"]["config"]["type"] == "oidc" + CLIENT_SECRET: "" + #@schema/validation min_len=1, when=lambda _, ctx: ctx.root["dex"]["config"]["type"] == "oidc" + issuer: "" + ldap: + #@schema/validation min_len=1, when=lambda _, ctx: ctx.root["dex"]["config"]["type"] == "ldap" + host: "" + #@schema/validation min_len=1, when=lambda _, ctx: ctx.root["dex"]["config"]["type"] == "ldap" + bindDN: "" + #@schema/validation min_len=1, when=lambda _, ctx: ctx.root["dex"]["config"]["type"] == "ldap" + bindPW: "" +``` +where: +- `dex.config.type` can only be _either_ "oidc" or "ldap" +- `dex.config.oidc` values will only be validated if `type` is "oidc" +- `dex.config.ldap` values, likewise, will only be validated if `type` is "ldap" + +_(See also [Making Validations Dependent on Other Data Values](#making-validations-dependent-on-other-data-values).)_ + +## About Rules + +A validation is made up of one or more rules. + +A rule comes in one of two forms: +- a [named rule](#using-named-rules) — a set of pre-built commonly used rules +- a [custom rule](#writing-your-own-custom-rules) — a rule an Author writes for a specific purpose + +### Using "Named" Rules + +`ytt` comes with a library of built-in rules known as "named" rules. + +These rules are primarily used as a keyword on the `@schema/validation` annotation; refer to the [Data Values Schema Reference](lang-ref-ytt-schema.md#schemavalidation) for the current complete list. Most of the examples we see use named rules. + +For example: +```yaml +#@data/values-schema +--- +#@schema/validation one_of=["INFO", "WARN", "ERROR", "FATAL"] +logLevel: INFO +``` +Here: +- `logLevel` is a string, defaulting to "INFO" +- a valid `logLevel` must be one of the four values given. + +Authors are encouraged to use named rules whenever possible: +- there's no code to maintain: these rules are [unit-tested](https://github.com/carvel-dev/ytt/tree/develop/pkg/validations/filetests) +- they more succinctly document the constraints, making the schema easier to read/maintain +- when rules are included in [OpenAPI v3 schema exports](how-to-export-schema.md), these are the first batch of such rules likely to be included. + + +### Writing Custom Rules + +The ["Named" rules](#using-named-rules) will not cover _all_ possible validation cases. One might opt to write a custom rule for a number of reasons: +- the desired constraint can't be expressed through a named rule +- the description supplied by a named rule is inadequate + + +- [Complex Custom Rules](#complex-custom-rules) +- [About `null` values](#about-null-values) + +A validation rule has two parts: +- a description of a valid value; +- a function that implements that definition in Starlark code. + +For example: +```yaml +#@data/values-schema +--- +#@schema/validation ("a multiple of 1024", lambda v: v % 1024 == 0) +quota: 1023 +``` +Here: +- the rule is a two-value "[tuple](https://github.com/google/starlark-go/blob/master/doc/spec.md#tuples)" +- the first value is a string; + - it describes what a valid value looks like; + - this string is used in violation messages (see below) to help the user provide a valid input. +- the second value is a function (here's a [lambda](https://github.com/google/starlark-go/blob/master/doc/spec.md#lambda-expressions) expression); + - the function will be passed one (1) argument: the value being validated. + - the function must return a boolean value (either `True` or `False`) _or_ `fail()` with a message describing the failure. + +Has the initial result: + +```console +$ ytt -f schema.yaml +ytt: Error: Validating final data values: + quota + from: schema.yaml:4 + - must be: a multiple of 1024 (by: schema.yaml:3) +``` + +And quietly reports nothing when the value _is_ valid. + +#### Complex Custom Rules + +Occasionally, a validation rule requires pre-processing of a value or requires multiple checks. In these cases, a lambda expression is often not enough: a function needs to be written. + +To keep the schema itself readable/maintainable, Authors will typically extract these functions to a separate file: + +`rules.star` +```python +def one_registry_if_pvc_is_filesystem(val): + if val["persistence"]["imageChartStorage"]["type"] == "filesystem" and \ + val["persistence"]["persistentVolumeClaim"]["registry"]["accessMode"] == "ReadWriteOnce": + return val["registry"]["replicas"] == 1 \ + or fail("{} replicas are configured".format(val["registry"]["replicas"])) + end +end +``` + +`schema.yaml` +```yaml +#@ load("rules.star", "one_registry_if_pvc_is_filesystem") + +#@data/values-schema +#@schema/validation ("exactly one (1) registry replica if Helm Charts are stored on the filesystem.", one_registry_if_pvc_is_filesystem) +--- +registry: + replicas: 2 +persistence: + imageChartStorage: + type: "filesystem" + persistentVolumeClaim: + registry: + accessMode: "ReadWriteOnce" +``` + +Here: +- the `@schema/validation` annotates the _document_ because the validation applies across two top-level keys (`registry` and `persistence`); +- the assertion itself is defined in `rules.star`, so as to not clutter the schema; it is _loaded_ into `schema.yaml` +- in `rules.star`, the parameter `val` is expected to receive the value of the document: + - that value is a [YAML Fragment](lang-ref-yaml-fragment.md) containing a map with those two top-level keys (and their contents, recursively) + - and so, contained items are accessed through bracket notation. + +_(See also using a `struct` to export multiple functions through a single `load()` in [Load Statements > Usage](lang-ref-load.md#usage))_ + +#### About `null` values + +`ytt` attempts to gracefully handle `null` values in validations: +- when a Data Value is marked `@schema/nullable`, and the value remains `null`, validations are skipped automatically. +- if the same Data Value has the `not_null=True` rule, _that_ rule is run. +- the `not_null=` rule, if present, is checked _first_. + +The upshot of these policies are: +- no other rules need handling the `null` value. + diff --git a/site/content/ytt/docs/v0.49.x/index-playground-examples.md b/site/content/ytt/docs/v0.49.x/index-playground-examples.md new file mode 100644 index 000000000..7d04cbfca --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/index-playground-examples.md @@ -0,0 +1,160 @@ +--- +aliases: [/ytt/docs/latest/index-playground-examples] +title: Playground Examples Index +--- + +## Basics + +### How Plain YAML is parsed + +https://carvel.dev/ytt/#example:example-plain-yaml + +### Datatypes in Starlark are encoded in YAML + +https://carvel.dev/ytt/#example:example-datatypes + +### Local variables within a template + +https://carvel.dev/ytt/#example:example-variable + +### Surrounding YAML with an if statement + +https://carvel.dev/ytt/#example:example-if + +### Looping or Generating YAML with a for loop + +https://carvel.dev/ytt/#example:example-for + +### Making a mini-template or extracting a chunk of YAML using a function + +https://carvel.dev/ytt/#example:example-function + +### Including helper functions from another file using load() + +https://carvel.dev/ytt/#example:example-load + +### Encoding or hashing a string + +https://carvel.dev/ytt/#example:example-load-ytt-library-module + +### Including a function from a library + +https://carvel.dev/ytt/#example:example-load-custom-library-module + +### Asserting on a particular value or condition + +https://carvel.dev/ytt/#example:example-assert + +### Declaring and using Data Values + +https://carvel.dev/ytt/#example:example-load-data-values + +### Loading and including data from a text or CSV file + +https://carvel.dev/ytt/#example:example-load-data-files + +### Templating strings and key names using text templating + +https://carvel.dev/ytt/#example:example-text-template + +### Inserting a YAML fragment or a value using template.replace() + +https://carvel.dev/ytt/#example:example-replace + +### Modifying or editing a YAML fragment with an overlay + +https://carvel.dev/ytt/#example:example-overlay + +### Writing an overlay that applies over all the other documents + +https://carvel.dev/ytt/#example:example-overlay-files + +### Splitting data values into multiple files + +https://carvel.dev/ytt/#example:example-multiple-data-values + +### Grouping and using multiple templates as a set using libraries + +https://carvel.dev/ytt/#example:example-ytt-library-module + +### Simple Kubernetes example with a Pod and a Service + +https://carvel.dev/ytt/#example:example-k8s-ingress-single + +### Kubernetes example producing multiple applications from data values + +https://carvel.dev/ytt/#example:example-k8s-ingress-multiple + +### Helm Chart like output from ytt + +https://carvel.dev/ytt/#example:example-k8s-helm-ish + +### A complete working example of many ytt features + +https://carvel.dev/ytt/#example:example-demo + +## Overlays + +### Overlay matching on all documents + +https://carvel.dev/ytt/#example:example-match-all-docs + +### Overlay matching on specific documents + +https://carvel.dev/ytt/#example:example-match-subset-docs + +### Overlay matching on specific documents using a YAML Fragment + +https://carvel.dev/ytt/#example:example-match-subset-by-fragment + +### Overlay matching on a specific document by index + +https://carvel.dev/ytt/#example:example-match-by-index + +### Overlay matching on an array item by a specific field or key + +https://carvel.dev/ytt/#example:example-match-by-key + +### Overlay that inserts an array item at a specific spot + +https://carvel.dev/ytt/#example:example-insert-array-item + +### Overlay that replaces an array item + +https://carvel.dev/ytt/#example:example-replace-array-item + +### Overlay that removes or deletes an array item + +https://carvel.dev/ytt/#example:example-remove-array-item + +### Overlay that edits or modifies an array item in place + +https://carvel.dev/ytt/#example:example-edit-array-item + +### Overlay that appends an array item (inserts at the end) + +https://carvel.dev/ytt/#example:example-append-array-item + +### Overlay that completely replaces the contents of an array + +https://carvel.dev/ytt/#example:example-replace-array + +### Overlay that adds a new map item or field + +https://carvel.dev/ytt/#example:example-add-map-item + +### Overlay that edits the value of a map item or field + +https://carvel.dev/ytt/#example:example-edit-map-value + +### Overlay that removes or deletes a map item or field + +https://carvel.dev/ytt/#example:example-remove-map-item + +### Overlay that renames the key or field of a map item + +https://carvel.dev/ytt/#example:example-rename-key-in-map + +### Overlay that add or appends a map into an array item + +https://carvel.dev/ytt/#example:example-append-map-to-array diff --git a/site/content/ytt/docs/v0.49.x/injecting-secrets.md b/site/content/ytt/docs/v0.49.x/injecting-secrets.md new file mode 100644 index 000000000..9e0d095ff --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/injecting-secrets.md @@ -0,0 +1,160 @@ +--- +aliases: [/ytt/docs/latest/injecting-secrets] +title: Injecting Secrets +--- + +## Overview + +**This document is work in progress.** + +Unlike most configuration, many organizations disallow storing of plain secret values next to other code/configuration. + +This document: +- presents several common approaches used to make secrets available to your templates +- does _not_ cover injection of secrets directly into an application at runtime (overall may be the best approach) +- does _not_ recommend one approach over another (though it does state pros and cons) +- does _not_ talk about where resulting templates are forwarded + +One common question that's asked is why not to extend ytt to allow it to shell out to other programs or why not include builtin library that can fetch secrets from outside (e.g. imagine having builtin `vault` function). We are _not_ planning to support either of these features because we want to maintain strong sandbox boundary between templating language and ytt's external environment. Our expectation is that if ytt user wants to provide specific data to templates, it should be injected into ytt rather than templates (or ytt itself) fetching it. We believe this is a good security posture and as a side effect makes templates more portable (some users may store secrets in Vault and some other in Lastpass). + +- [Via command line args](#via-command-line-args) +- [Via environment variables](#via-environment-variables) +- [Via secret references, before templating](#via-secret-references-before-templating) +- [Via encrypted secrets, before templating](#via-encrypted-secrets-before-templating) +- [Via secret references, after templating](#via-secret-references-after-templating) +- [Via encrypted resources, later decrypted inside the k8s cluster](#via-encrypted-resources-later-decrypted-inside-the-k8s-cluster) + +There might be other ways to inject secrets into ytt that are not listed here. + +--- +## Via command line args + +```bash +ytt -f . --data-value username=user --data-value password=pass +``` + +Cons: + +- depending on a computing environment secret values are visible in a process tree (as part of full command line) **which typically is considered unwanted** + +--- +## Via environment variables + +```bash +export CFG_secrets__username=user +export CFG_secrets__password=pass +ytt -f . --data-values-env CFG +``` + +Cons: + +- depending on a computing environment secret values are visible in process metadata (e.g. typically accessible to root user under `/proc//environ`) + +--- +## Via secret references, before templating + +Given following two files: + +`config.yml`: + +```yaml +#@ load("@ytt:data", "data") + +users: +- username: #@ data.values.username + password: #@ data.values.password +``` + +`secrets`: + +```yaml +username: (( vault "secret/my/credentials/admin:username" )) +password: (( vault "secret/my/credentials/admin:password" )) +``` + +(Note that `secrets` file does not have `yaml` extension to make ytt exclude it from template result. Alternatively `--file-mark` flag can be used to exclude that file.) + +One can compose ytt with external tooling that knows how to resolve referenced values. For example, [spruce](https://starkandwayne.com/blog/simple-secure-credentials-into-yaml-with-vault-and-spruce/) can resolve secret references and then forward that information onto ytt: + +```bash +echo -e "#@data/values\n---\n$(spruce merge ./secrets)" | ytt -f . -f - +``` + +Pros: + +- able to commit mapping of data values to secrets in your preferred secret storage (in this example Vault) +- actual secret values are available to templates, hence, can be inserted in the middle of a string or base64 encoded (as sometimes required by k8s) + +--- +## Via encrypted secrets, before templating + +Similar to above "Via secret references, before templating" approach, instead of storing secret references in `secrets` file one can store encrypted secret values in `secrets` next to other configuration. Various tools like [sops](https://github.com/mozilla/sops) make encrypting configuration files fairly easy: + +```bash +echo -e "#@data/values\n---\n$(sops -d ./secrets)" | ytt -f . -f - +``` + +Pros: + +- provides a way to version (backup, etc) secrets next to other configuration +- actual secret values are available to templates, hence, can be inserted in the middle of a string or base64 encoded (as sometimes required by k8s) + +Cons: + +- depending on organization requirements even encrypted secrets may not be allowed to be stored next to other configuration + +--- +## Via secret references, after templating + +Given following two files: + +`config.yml`: + +```yaml +#@ load("@ytt:data", "data") + +users: +- username: #@ data.values.username + password: #@ data.values.password +``` + +`values.yml`: + +```yaml +#@data/values +--- +username: (( vault "secret/my/credentials/admin:username" )) +password: (( vault "secret/my/credentials/admin:password" )) +``` + +One can compose ytt with external tooling that knows how to resolve referenced values. Such tools typically look for a particular patterned map or array items. One such tool is [spruce](https://starkandwayne.com/blog/simple-secure-credentials-into-yaml-with-vault-and-spruce/): + +```bash +ytt -f . | spruce merge - +``` + +Pros: + +- provides a way to commit mapping of data values and their associated secret locations (e.g. `username` comes from `vault` under `secret/my/credentials/admin:username`) + +Cons: + +- secret resolution tools may not be able to find secret references inside strings (e.g. `http://(( ... )):(( ... ))@website`) +- secret resolution tools will not understand if secret reference was encoded (for example with `base64` encoding as wanted sometimes by k8s) + +--- +## Via encrypted resources, later decrypted inside the k8s cluster + +This approach is Kubernetes specific. + +[Bitnami's `sealed-secrets` project](https://github.com/bitnami-labs/sealed-secrets) provides a way to encrypt secrets hence allowing to commit them next to your other configuration. With this approach, secret values are not accessible to templates directly; however, they can be referenced by k8s resources (for example via `secretRef` in `Pod` spec). At the time of the deploy of such resources, sealed secrets controller will create appropriate `Secret` objects with decrypted secret values. + +Pros: + +- provides a way to version (backup, etc) secrets next to other configuration +- no way to access decrypted secret values in templates directly (though, this may not be necessary in many cases) + +Cons: + +- Kubernetes specific +- depending on organization requirements even encrypted secrets may not be allowed to be stored next to other configuration diff --git a/site/content/ytt/docs/v0.49.x/inputs.md b/site/content/ytt/docs/v0.49.x/inputs.md new file mode 100644 index 000000000..f58b6f99d --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/inputs.md @@ -0,0 +1,94 @@ +--- +aliases: [/ytt/docs/latest/inputs] +title: Inputs +--- + +ytt supports different input sources: + +- Files & Directories + - Those are provided via the `-f`/`--file` flag + + - ytt uses the file's name for its internal representation + + If you have a tree like + ```terminal + $ tree . + . + ├── dir1 + │   └── some.yaml + └── dir2 + └── sub + ├── another.yaml + └── someother.yaml + + 4 directories, 3 files + ``` + and you call `ytt --file dir1/some.yaml --file dir2/ ...` then ytt loads + - `dir1/some.yaml` as `some.yaml` + - `dir2/sub/another.yaml` as `sub/another.yaml` + - `dir2/sub/someother.yaml` as `sub/someother.yaml` + + - ytt uses a file's extension to determine its type, e.g. a extension like + `yaml` flags that file as "yaml-template"; you can read more about that in + [File Marks](file-marks/) + + - You can change a file's name, location, and also "type" by explicitly + setting the file's name to be used by ytt, e.g. `ytt --file + a/different/file.foo=dir1/some.yaml`, which would mean that ytt + + - loads that file as `a/different/file.foo` + - would not consider it as "yaml-template"/"yaml-plain", but as "data", because of its extension + + Note: this only works for files, not for directories + + - Explicitly setting file's names can be especially useful when consuming + files where you have no control over their name, like process substitutions: + + Running `ytt --file <(echo 'some: yaml')` (on Linux) would have the shell + produce a file like `/dev/fd/63` and pass that on to ytt. This file, based + on it's name "63", would not be considered yaml and thus interpreted as + "data". To change that, you need to run `ytt --file subst.yaml=<(echo 'some: + yaml')` to have ytt treat it as yaml. + + - ytt can also consume stdin by using `-`, like: `ytt --file -` + + Note: When using `-`, ytt automatically treats data on stdin as yaml, as it + will use stdin as `stdin.yaml`, thus having an extension which flags it as + "yaml-template". If you use some other means to consume stdin, e.g. `ytt + --file /dev/stdin`, this does not happen and ytt treats stdin as a file + `stdin` and thus as "data", because it has no extension marking it + differently. You can still set a different file name explicitly, e.g. with + `ytt --file my-stdin.yaml=/dev/stdin`. + + - ytt can also consume files via http/s, e.g. `ytt --file + https://raw.githubusercontent.com/carvel-dev/ytt/develop/.golangci.yml` + + - ytt can also consume symlinks, however if a symlink's target is not a file + you have already included into the set of files ytt should consider + (`--file ...`), ytt will not allow that and print an error. You can + explicitly allow additional symlink targets via the + `--allow-symlink-destination ...` flag. + + - To debug / inspect which files ytt considers and how it handles those, the + flags `--files-inspect` & `--debug` can be helpful + + +- Data Values & Data Values Schemas + + You can read about how to define data-values schemas and how to consume and + set Data Values and Schemas here: + + - [Write Schema](how-to-write-schema/) + - [Data Values Schema](lang-ref-ytt-schema/) + - [Use Data Values](how-to-use-data-values/) + - [Data Values](ytt-data-values/) + + Generally you can provide Data Values + - as strings + - on the command line via the flags `--data-value`/`-v` + - from the environment via the flag `--data-values-env` + - from files via the flag `--data-value-file` + - as structured data / yaml + - on the command line via the flag `--data-value-yaml` + - from the environment via the flag `--data-values-env-yaml` + - from files via the flag `--data-values-file` diff --git a/site/content/ytt/docs/v0.49.x/install.md b/site/content/ytt/docs/v0.49.x/install.md new file mode 100644 index 000000000..61deecb21 --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/install.md @@ -0,0 +1,66 @@ +--- +aliases: [/ytt/docs/latest/install] +title: Install +--- + +## Via script (macOS or Linux) + +(Note that `install.sh` script installs other Carvel tools as well.) + +Install binaries into specific directory: + +```bash +$ mkdir local-bin/ +$ curl -L https://carvel.dev/install.sh | K14SIO_INSTALL_BIN_DIR=local-bin bash + +$ export PATH=$PWD/local-bin/:$PATH +$ ytt version +``` + +Or system wide: + +```bash +$ wget -O- https://carvel.dev/install.sh > install.sh + +# Inspect install.sh before running... +$ sudo bash install.sh +$ ytt version +``` + +## Via Homebrew (macOS or Linux) + +Based on [github.com/carvel-dev/homebrew](https://github.com/carvel-dev/homebrew). + +```bash +$ brew tap carvel-dev/carvel +$ brew install ytt +$ ytt version +``` + +## Specific version from a GitHub release + +To download, click on one of the assets in a [chosen GitHub release](https://github.com/carvel-dev/ytt/releases), for example for 'ytt-darwin-amd64'. + +```bash +# **Compare binary checksum** against what's specified in the release notes +# (if checksums do not match, binary was not successfully downloaded) +$ shasum -a 256 ~/Downloads/ytt-darwin-amd64 +08b25d21675fdc77d4281c9bb74b5b36710cc091f30552830604459512f5744c /Users/pivotal/Downloads/ytt-darwin-amd64 + +# Move binary next to your other executables +$ mv ~/Downloads/ytt-darwin-amd64 /usr/local/bin/ytt + +# Make binary executable +$ chmod +x /usr/local/bin/ytt + +# Check its version +$ ytt version +``` +## Shell Completion + +The `ytt completion` command generates an autocompletion script for the specified shell. + +See `ytt completion --help` for information and instructions. + +For detailed instructions on enabling shell completion, specify the type of shell in the help command. For example: +`ytt completion zsh --help`. diff --git a/site/content/ytt/docs/v0.49.x/known-limitations.md b/site/content/ytt/docs/v0.49.x/known-limitations.md new file mode 100644 index 000000000..a02a65476 --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/known-limitations.md @@ -0,0 +1,12 @@ +--- +aliases: [/ytt/docs/latest/known-limitations] +title: Known Limitations +--- + +- YAML anchors and templating directive for the same YAML node are not supported. + + ```yaml + first: &content #@ 123 + second: *content + ``` + `second` key-value pair will _not_ contain 123 since YAML anchors are resolved before ytt evaluates templating directives. diff --git a/site/content/ytt/docs/v0.49.x/lang-ref-annotation.md b/site/content/ytt/docs/v0.49.x/lang-ref-annotation.md new file mode 100644 index 000000000..bfe6a4a12 --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/lang-ref-annotation.md @@ -0,0 +1,54 @@ +--- +aliases: [/ytt/docs/latest/lang-ref-annotation] +title: Annotations +--- + +## Format + +``` +@ann1-name [ann-arg1, ann-arg2, ..., keyword-ann-arg1=val1] +``` + +- content between brackets is optional. + +- annotation names are typically namespaced, for example, `overlay/merge` is an annotation within an `overlay` namespace. Annotation namespaces are there for general organization, they are not associated with loaded packages (from `load` keyword). + +- annotation arguments (positional and keyword) is just plain code + +## Shared templating annotations + +- `@template/code [code]` or just `@ [code]` (on its own line) represents plain code line + +```yaml +#@ a = calculate(100) +key: value +``` + +- `@template/value [code]` or just `@ [code]` (at the end of line) represents a value associated structure + +```yaml +key: #@ value +array: +- #@ value +``` + +## YAML templating annotations + +- `@yaml/map-key-override` (no args) + - necessary to indicate that map key is being intentionally overriden + +- `@yaml/text-templated-strings` (no args) + - necessary to indicate that node contents (including map key and map value) should be text templated (ie `(@` indicates start of text templating) (see [text templating](ytt-text-templating.md) for more details.) + +## Text templating annotations + +- `@text/trim-left` trims space to the left of code node +- `@text/trim-right` trims space to the right of code node + +## Data module annotations + +- `@data/values` (no args) specifies values accessible via `data.values` from `@ytt:data` package (see [ytt @data/values](ytt-data-values.md) for more details) + +## Overlay module annotations + +See [Overlay module](lang-ref-ytt-overlay.md) for list of annotations. diff --git a/site/content/ytt/docs/v0.49.x/lang-ref-def.md b/site/content/ytt/docs/v0.49.x/lang-ref-def.md new file mode 100644 index 000000000..7b418f365 --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/lang-ref-def.md @@ -0,0 +1,73 @@ +--- +aliases: [/ytt/docs/latest/lang-ref-def] +title: Functions +--- + +Refer to [Starlark function specification](https://github.com/google/starlark-go/blob/master/doc/spec.md#functions) for details about various types of function arguments. Note that ytt's Starlark use requires functions to be closed with an `end`. + +- function definition within YAML + +Labels returns map with two keys: test1, and test2: + +```yaml +#@ def my_labels(): +test1: 123 +test2: 124 +#@ end +``` + +Above is _almost_ equivalent to (differnce is that return type in one case is a YAML fragment and in another it's a dict): + +```yaml +#@ def my_labels(): +#@ return {"test1": 123, "test2": 124} +#@ end +``` + +- function definition within Starlark (.star files) + +```python +def my_labels(): + return {"test1": 123, "test2": 124} +end +``` + +- function arguments (positional and keyword arguments) + +```yaml +#@ def my_deployment(name, replicas=1, labels={}): +kind: Deployment +metadata: + name: #@ name + labels: #@ labels +spec: + replicas: #@ replicas +#@ end + +--- +kind: List +items: +- #@ my_deployment("dep1", replicas=3) +``` + +- common function usage + +To set `labels` key to return value of `my_labels()`: + +```yaml +labels: #@ my_labels() +labels_as_array: +- #@ my_labels() +``` + +To merge return value of `my_labels()` into `labels` map: + +```yaml +#@ load("@ytt:template", "template") + +labels: + another-label: true + _: #@ template.replace(my_labels()) +``` + +Note that in most cases `template.replace` is not necessary since it's only helps replacing one item (array item, map item or document) with multiple items of that type. diff --git a/site/content/ytt/docs/v0.49.x/lang-ref-dict.md b/site/content/ytt/docs/v0.49.x/lang-ref-dict.md new file mode 100644 index 000000000..e04eaa843 --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/lang-ref-dict.md @@ -0,0 +1,80 @@ +--- +aliases: [/ytt/docs/latest/lang-ref-dict] +title: Dictionaries +--- + +```yaml +#@ color = {"red": 123, "yellow": 100, "blue": "245"} +red: #@ color["red"] +``` + +Copied here for convenience from [Starlark specification](https://github.com/google/starlark-go/blob/master/doc/spec.md#dictclear). + +- [dict·clear](https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·clear) (`D.clear()`) +```python +x = {"one": 1, "two": 2} +x.clear() # None +print(x) # {} +``` + +- [dict·get](https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·get) (`D.get(key[, default])`) +```python +x = {"one": 1, "two": 2} +x.get("one") # 1 +x.get("three") # None +x.get("three", 0) # 0 +``` + +- [dict·items](https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·items) (`D.items()`) +```python +x = {"one": 1, "two": 2} +x.items() # [("one", 1), ("two", 2)] +``` + +- [dict·keys](https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·keys) (`D.keys()`) +```python +x = {"one": 1, "two": 2} +x.keys() # ["one", "two"] +``` + +- [dict·pop](https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·pop) (`D.pop(key[, default])`) +```python +x = {"one": 1, "two": 2} +x.pop("one") # 1 +x # {"two": 2} +x.pop("three", 0) # 0 +x.pop("four") # error: missing key +``` + +- [dict·popitem](https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·popitem) (`D.popitem()`) +```python +x = {"one": 1, "two": 2} +x.popitem() # ("one", 1) +x.popitem() # ("two", 2) +x.popitem() # error: empty dict +``` + +- [dict·setdefault](https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·setdefault) (`D.setdefault(key[, default])`) +```python +x = {"one": 1, "two": 2} +x.setdefault("one") # 1 +x.setdefault("three", 0) # 0 +x # {"one": 1, "two": 2, "three": 0} +x.setdefault("four") # None +x # {"one": 1, "two": 2, "three": None} +``` + +- [dict·update](https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·update) (`D.update([pairs][, name=value[, ...])`) +```python +x = {} +x.update([("a", 1), ("b", 2)], c=3) +x.update({"d": 4}) +x.update(e=5) +x # {"a": 1, "b": "2", "c": 3, "d": 4, "e": 5} +``` + +- [dict·values](https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·values) (`D.values()`) +```python +x = {"one": 1, "two": 2} +x.values() # [1, 2] +``` diff --git a/site/content/ytt/docs/v0.49.x/lang-ref-for.md b/site/content/ytt/docs/v0.49.x/lang-ref-for.md new file mode 100644 index 000000000..d73222ef5 --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/lang-ref-for.md @@ -0,0 +1,40 @@ +--- +aliases: [/ytt/docs/latest/lang-ref-for] +title: For loop +--- + +Refer to [Starlark for loop specification](https://github.com/google/starlark-go/blob/master/doc/spec.md#for-loops) for details. + +- iterating with values + +```yaml +array: +#@ for i in range(0,3): +- #@ i +- #@ i+1 +#@ end +``` + +- iterating with index + +```yaml +array: +#@ arr = [1,5,{"key":"val"}] +#@ for i in range(len(arr)): +- val: #@ arr[i] + index: #@ i +#@ end +``` + +- use of `continue/break` + +```yaml +array: +#@ for i in range(0,3): +#@ if i == 1: +#@ continue +#@ end +- #@ i +- #@ i+1 +#@ end +``` diff --git a/site/content/ytt/docs/v0.49.x/lang-ref-if.md b/site/content/ytt/docs/v0.49.x/lang-ref-if.md new file mode 100644 index 000000000..914115d3a --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/lang-ref-if.md @@ -0,0 +1,82 @@ +--- +aliases: [/ytt/docs/latest/lang-ref-if] +title: If Statements +--- + +Refer to [Starlark if statement specification](https://github.com/google/starlark-go/blob/master/doc/spec.md#if-statements) for details. + +- if + +```yaml +#@ if True: +test1: 123 +test2: 124 +#@ end +``` + +- if (negative) + +```yaml +#@ if not True: +test1: 123 +#@ end +``` + +- single-node if + +```yaml +#@ if/end True: +test1: 123 +``` + +- if-else conditional + +```yaml +#@ if True: +test1: 123 +#@ else: +test2: 124 +#@ end +``` + +- if-elif-else conditional + +```yaml +#@ if True: +test2: 123 +#@ elif False: +test2: 124 +#@ else: +test2: 125 +#@ end +``` + +- if-elif-else conditional boolean (and/or) \ + See [Starlark or/and operators](https://github.com/google/starlark-go/blob/master/doc/spec.md#or-and-and) for more details. + + +```yaml +#@ test = 123 +#@ if test > 100 and test < 200: +test1: 123 +#@ elif test == 100 or test == 200: +test2: 124 +#@ else: +test3: 125 +#@ end +``` + + +- single line if + +```yaml +#@ passwd = "..." +test1: #@ passwd if passwd else assert.fail("password must be set") +``` + +- implicit if + +```yaml +#@ passwd = "..." +test1: #@ passwd or assert.fail("password must be set") +``` diff --git a/site/content/ytt/docs/v0.49.x/lang-ref-list.md b/site/content/ytt/docs/v0.49.x/lang-ref-list.md new file mode 100644 index 000000000..33661f57b --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/lang-ref-list.md @@ -0,0 +1,60 @@ +--- +aliases: [/ytt/docs/latest/lang-ref-list] +title: Lists +--- + +```yaml +#@ nums = [123, 374, 490] +first: #@ nums[0] +``` + +Copied here for convenience from [Starlark specification](https://github.com/google/starlark-go/blob/master/doc/spec.md#listappend). + +- [list·append](https://github.com/google/starlark-go/blob/master/doc/spec.md#list·append) (`L.append(x)`) +```python +x = [] +x.append(1) # None +x.append(2) # None +x.append(3) # None +x # [1, 2, 3] +``` + +- [list·clear](https://github.com/google/starlark-go/blob/master/doc/spec.md#list·clear) (`L.clear()`) +```python +x = [1, 2, 3] +x.clear() # None +x # [] +``` + +- [list·extend](https://github.com/google/starlark-go/blob/master/doc/spec.md#list·extend) (`L.extend(x)`) +```python +x = [] +x.extend([1, 2, 3]) # None +x.extend(["foo"]) # None +x # [1, 2, 3, "foo"] +``` + +- [list·index](https://github.com/google/starlark-go/blob/master/doc/spec.md#list·index) (`L.index(x[, start[, end]])`) +```python +x = list("banana".codepoints()) +x.index("a") # 1 (bAnana) +x.index("a", 2) # 3 (banAna) +x.index("a", -2) # 5 (bananA) +``` + +- [list·insert](https://github.com/google/starlark-go/blob/master/doc/spec.md#list·insert) (`L.insert(i, x)`) +```python +x = ["b", "c", "e"] +x.insert(0, "a") # None +x.insert(-1, "d") # None +x # ["a", "b", "c", "d", "e"] +``` + +- [list·pop](https://github.com/google/starlark-go/blob/master/doc/spec.md#list·pop) (`L.pop([index])`) +- [list·remove](https://github.com/google/starlark-go/blob/master/doc/spec.md#list·remove) (`L.remove(x)`) +```python +x = [1, 2, 3, 2] +x.remove(2) # None (x == [1, 3, 2]) +x.remove(2) # None (x == [1, 3]) +x.remove(2) # error: element not found +``` diff --git a/site/content/ytt/docs/v0.49.x/lang-ref-load.md b/site/content/ytt/docs/v0.49.x/lang-ref-load.md new file mode 100644 index 000000000..a2c764088 --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/lang-ref-load.md @@ -0,0 +1,115 @@ +--- +aliases: [/ytt/docs/latest/lang-ref-load] +title: Load Statements +--- + +## Terminology + +- `module`: single file; can export variables, functions, or be templated => some type of result e.g. yaml structure, or string, or None) +- `package`: single directory; contains modules +- `library`: collection of packages + +## Usage + +Load statement allows to load functions from other modules (such as ones from [builtin `ytt` library](lang-ref-ytt.md)). + +- [load](https://github.com/google/starlark-go/blob/master/doc/spec.md#load-statements) +```python +load("@ytt:overlay", "overlay") # load overlay module from builtin ytt library +load("@ytt:overlay", ov="overlay") # load overlay symbol under a different alias +load("helpers.star", "func1", "func2") # load func1, func2 from Starlark file +load("helpers.lib.yml", "func1", "func2") # load func1, func2 from YAML file +load("helpers.lib.txt", "func1", "func2") # load func1, func2 from text file +load("/dir/helpers.lib.yml", "func1") # load func1 from file relative to root of library +load("sub-dir/helpers.lib.txt", "func1") # load func1 from a sub-directory +load("@project:dir/helpers.lib.txt", "func1") # load func1 from a project located under _ytt_lib +``` + +`load` arguments are as follows: + +1. location which takes following shape `[@[library]:][package/]{0,n}module`, where, + - `library` could be `ytt` or local path under `_ytt_lib` directory + - examples: [`ytt`](lang-ref-ytt.md), `github.com/k14s/k8s-lib`, `common` + - `package` could be a directory path + - examples: `overlay`, `regexp`, `app/`, `/app/something` + - `module` is a file name or predefined name (included in `ytt` library) + - examples: `module.lib.yml` +1. one or more symbols to import with optional aliases + - examples: `func1`, `func1="as_func1"` + +Files can be loaded from current or child directories. As of ytt v0.24.0, `/` package prefix can be used to load files relative to the root of the current library. + +Note that there is a distinction between using `load("@project:dir/helpers.lib.txt", "func1")` or `load("@project/dir:helpers.lib.txt", "func1")`, in that, `:` signifies what ytt considers a self-contained library (i.e. is `dir` simply a package in `project` or `project/dir` a standalone library). This allows files to load other files relative to the library root. + +To load a set of functions from a single file, you can create a `struct` that contains references to the functions. For example: +`funcs.star`: +``` +load("@ytt:struct", "struct") + +def testfunc(): + return 123 +end + +def otherfunc(): + return 456 +end + +mod = struct.make(testfunc=testfunc, otherfunc=otherfunc) +``` + +`config.yml`: +``` +#@ load("funcs.star", "mod") + +result: #@ mod.testfunc() +other_result: #@ mod.otherfunc() +``` + +## _ytt_lib directory + +`_ytt_lib` directory allows to keep private dependencies from consumers of libraries. + +For example given following directory structure: + +``` +app1.yml +_ytt_lib/big-corp/sre.lib.yml +_ytt_lib/big-corp/_ytt_lib/big-corp/common/deployments.lib.yml +_ytt_lib/big-corp/_ytt_lib/big-corp/common/services.lib.yml +``` + +- `app1.yml` _can_ load `big-corp/sre.lib.yml` via `@big-corp:sre.lib.yml` +- `app1.yml` _cannot_ load `big-corp/_ytt_lib/big-corp/common/services.lib.yml` as it is a private dependency of anything inside `_ytt_lib/big-corp/` directory (e.g. `sre.lib.yml`) + +hence making it possible for `big-corp/sre.lib.yml` module to keep its `big-corp/common` library dependency private. +## Files + +To make files available to `load` statement they have to be given to ytt CLI via `--file` (`-f`) option. The argument of that option can be a path to either of: + +- a **file**: in which case the file can be loaded by its name. +- a **directory**: in which case all the files found can be loaded by using paths relative to the directory. If the directory contains a `_ytt_lib` folder, then libraries in it can also be loaded. + +For example, given following directory structure: + +``` +app1.yml +helpers.lib.yml +_ytt_lib/apps/apps.lib.yml +sub-dir/more-helpers.lib.yml +sub-dir/_ytt_lib/weird-lib/funcs.lib.yml +``` + +- `ytt -f .` will make it possible for `app1.yml` to load: + - `helpers.lib.yml` + - `@apps:apps.lib.yml` + - `sub-dir/more-helpers.lib.yml` +- `ytt -f helpers.lib.yml -f sub-dir -f app1.yml` will make it possible for `app1.yml` to load: + - `helpers.lib.yml` + - `more-helpers.lib.yml` (not `sub-dir/more-helpers.lib.yml`) + - `@weird-lib:funcs.lib.yml` + +## Examples + +- [Load](/ytt/#example:example-load) +- [Load ytt library](/ytt/#example:example-load-ytt-library-module) +- [Load custom library](/ytt/#example:example-load-custom-library-module) diff --git a/site/content/ytt/docs/v0.49.x/lang-ref-string.md b/site/content/ytt/docs/v0.49.x/lang-ref-string.md new file mode 100644 index 000000000..4be6bbf5b --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/lang-ref-string.md @@ -0,0 +1,130 @@ +--- +aliases: [/ytt/docs/latest/lang-ref-string] +title: Strings +--- + +```yaml +name1: #@ name + "-deployment" +name2: #@ "{}-deployment".format("name") +``` + +Copied here for convenience from [Starlark specification](https://github.com/google/starlark-go/blob/master/doc/spec.md#stringelem_ords). + +- [string·elem_ords](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·elem_ords) +- [string·capitalize](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·capitalize) (`S.capitalize()`) +```python +"hello, world!".capitalize() # "Hello, world!"` +``` + +- [string·codepoint_ords](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·codepoint_ords) +- [string·count](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·count) (`S.count(sub[, start[, end]])`) +```python +"hello, world!".count("o") # 2 +"hello, world!".count("o", 7, 12) # 1 (in "world") +``` + +- [string·endswith](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·endswith) (`S.endswith(suffix[, start[, end]])`) +```python +"filename.star".endswith(".star") # True +'foo.cc'.endswith(('.cc', '.h')) # True +``` + +- [string·find](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·find) (`S.find(sub[, start[, end]])`) +```python +"bonbon".find("on") # 1 +"bonbon".find("on", 2) # 4 +"bonbon".find("on", 2, 5) # -1 +``` + +- [string·format](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·format) (`S.format(*args, **kwargs)`) +```python +"a{x}b{y}c{}".format(1, x=2, y=3) # "a2b3c1" +"a{}b{}c".format(1, 2) # "a1b2c" +"({1}, {0})".format("zero", "one") # "(one, zero)" +``` + +- [string·index](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·index) (`S.index(sub[, start[, end]])`) +```python +"bonbon".index("on") # 1 +"bonbon".index("on", 2) # 4 +"bonbon".index("on", 2, 5) # error: substring not found (in "nbo") +``` + +- [string·isalnum](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·isalnum) +```python +"base64".isalnum() # True +"Catch-22".isalnum() # False +``` + +- [string·isalpha](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·isalpha) +- [string·isdigit](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·isdigit) +- [string·islower](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·islower) +- [string·isspace](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·isspace) +- [string·istitle](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·istitle) +- [string·isupper](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·isupper) +- [string·join](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·join) (`S.join(iterable)`) +```python +", ".join(["one", "two", "three"]) # "one, two, three" +"a".join("ctmrn".codepoints()) # "catamaran" +``` + +- [string·lower](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·lower) +- [string·lstrip](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·lstrip) +```python +" hello ".lstrip() # "hello " +``` + +- [string·partition](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·partition) +- [string·replace](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·replace) (`S.replace(old, new[, count])`) +```python +"banana".replace("a", "o") # "bonono" +"banana".replace("a", "o", 2) # "bonona" +``` + +- [string·rfind](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·rfind) (`S.rfind(sub[, start[, end]])`) +```python +"bonbon".rfind("on") # 4 +"bonbon".rfind("on", None, 5) # 1 +"bonbon".rfind("on", 2, 5) # -1 +``` + +- [string·rindex](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·rindex) (`S.rindex(sub[, start[, end]])`) +- [string·rpartition](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·rpartition) +- [string·rsplit](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·rsplit) +- [string·rstrip](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·rstrip) (`S.rstrip()`) +```python +" hello ".rstrip() # " hello" +``` + +- [string·split](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·split) (`S.split([sep [, maxsplit]])`) +```python +"one two three".split() # ["one", "two", "three"] +"one two three".split(" ") # ["one", "two", "", "three"] +"one two three".split(None, 1) # ["one", "two three"] +"banana".split("n") # ["ba", "a", "a"] +"banana".split("n", 1) # ["ba", "ana"] +``` + +- [string·elems](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·elems) +- [string·codepoints](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·codepoints) +- [string·splitlines](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·splitlines) (`S.splitlines([keepends])`) +```python +"one\n\ntwo".splitlines() # ["one", "", "two"] +"one\n\ntwo".splitlines(True) # ["one\n", "\n", "two"] +``` + +- [string·startswith](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·startswith) (`S.startswith(prefix[, start[, end]])`) +```python +"filename.star".startswith("filename") # True` +``` + +- [string·strip](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·strip) (`S.strip()`) +```python +" hello ".strip() # "hello" +``` + +- [string·title](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·title) +- [string·upper](https://github.com/google/starlark-go/blob/master/doc/spec.md#string·upper) (`S.upper()`) +```python +"Hello, World!".upper() # "HELLO, WORLD!" +``` diff --git a/site/content/ytt/docs/v0.49.x/lang-ref-structs.md b/site/content/ytt/docs/v0.49.x/lang-ref-structs.md new file mode 100644 index 000000000..aa4ca708c --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/lang-ref-structs.md @@ -0,0 +1,94 @@ +--- +aliases: [/ytt/docs/latest/lang-ref-structs] +title: Structs +--- + +## Overview + +Structs are well-defined data objects, comprised of key/value pairs known as "attributes". They are a way to store and refer to data of a known structure. + +The most commonly used `struct` is `data.values`, supplied by the [`@ytt:data`](ytt-data-values.md) module. For example, a data values defined by: + +```yaml +#@data/values +--- +db_conn: + host: acme.example.com +``` + +is automatically processed into a `struct` (named `values`): the keys in the `@data/values` file are defined one-for-one as attributes on the `struct`. + +Those attribues can be referenced by name: + +```yaml +#@ load("@ytt:data", "data") +--- +persistence: + db_url: #@ data.values.db_conn.host +``` + +--- +## Attributes + +Attributes are a key/value pair, where the key is a `string` and the value can be of any type. + +Attributes can be referenced: +- by field (using "[dot notation](https://github.com/google/starlark-go/blob/master/doc/spec.md#dot-expressions)") + ```yaml + db_url: #@ data.values.db_conn.host + ``` +- by string (using "[index notation](https://github.com/google/starlark-go/blob/master/doc/spec.md#index-expressions)") _(as of v0.31.0)_: + ```yaml + db_url: #@ data.values["db_conn"]["host"] + ``` + useful when the name of the attribute is not known statically. + +Referencing an attribute that is not defined on the `struct` results in an error: +```yaml +db_url: #@ data.values.bd_conn # <== struct has no .bd_conn field or method +db_host: #@ data.values["db_conn"]["hast"] # <== key "hast" not in struct +``` + +--- +## Built-in Functions + +The following built-ins can be useful with `struct` values: + +- [`dir()`](https://github.com/google/starlark-go/blob/master/doc/spec.md#dir) enumerates all its attributes. + ``` + load("@ytt:struct", "struct") + + foo = struct.encode({"a": 1, "b": 2, "c": 3}) + keys = dir(foo) # <== ["a", "b", "c"] + ``` + +- [`getattr()`](https://github.com/google/starlark-go/blob/master/doc/spec.md#getattr) can be used to select an attribute. + ```yaml + #@ for vpc_config in getattr(getattr(data.values.accounts, foundation_name), vpc_name): + ... + #@ end + ``` + - as of v0.31.0, `struct`s support [index notation](https://github.com/google/starlark-go/blob/master/doc/spec.md#index-expressions) behaving identically, more succinctly/readably: + ```yaml + #@ for vpc_config in data.values.accounts[foundation_name][vpc_name]: + ... + #@ end + ``` + +- [`hasattr()`](https://github.com/google/starlark-go/blob/master/doc/spec.md#hasattr) reports whether a value has a specific attribute + ```python + # `value` is a struct that _might_ have a field, "additional_ports" + + def ports(value): + if hasattr(value, "additional_ports"): + ... + end + ``` + +--- +## Creating structs + +`struct` instances can be made using the `@ytt:struct` module. + +See [ytt Library: struct module](lang-ref-ytt-struct.md) for details. + diff --git a/site/content/ytt/docs/v0.49.x/lang-ref-yaml-fragment.md b/site/content/ytt/docs/v0.49.x/lang-ref-yaml-fragment.md new file mode 100644 index 000000000..547c8a7b8 --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/lang-ref-yaml-fragment.md @@ -0,0 +1,104 @@ +--- +aliases: [/ytt/docs/latest/lang-ref-yaml-fragment] +title: YAMLFragments +--- + +## Overview + +YAMLFragment is a type of value that is defined directly in YAML (instead of plain Starlark). For example, function `val()` returns a value of type `yamlfragment`. + +```yaml +#@ def vals(): +key1: val1 +key2: + subkey: val2 +#@ end +``` + +YAMLFragment may contain: + +- YAML document set (array of YAML documents) +- YAML array +- YAML map +- null + +Given various contents it wraps, YAMLFragment currently exposes limited ways of accessing its contents directly. Following accessors are available in v0.26.0+. + +## YAML Document Set + +```yaml +#@ def docs(): +--- +doc1 +--- +doc2 +--- +doc3 +#@ end +``` + +- access contents of a document at particular index +```python +docs()[1] # returns "doc2" +``` + +- loop over each document, setting `val` to its contents +```python +for val in docs(): + val # ... +end +``` + +## YAML Array + +```yaml +#@ def vals(): +- val1 +- val2 +#@ end +``` + +- access contents of an array item at particular index +```python +vals()[1] # returns "val2" +``` + +- loop over each array item, setting `val` to its contents +```python +for val in vals(): + val # ... +end +``` + +## YAML Map + +```yaml +#@ def vals(): +key1: val1 +key2: + subkey: val2 +#@ end +``` + +- access contents of a map item with particular key +```python +vals()["key1"] # returns "val1" +``` + +- check if map contains particular key +```python +"key1" in vals() # returns True +"key6" in vals() # returns False +``` + +- loop over each map item, setting `val` to its contents +```python +for key in vals(): + val = vals()[key] # ... +end +``` + +- convert to a dictionary +```python +dict(**vals()) # returns {"key1": "val1", "key2": yamlfragment({"subkey": "val2"})} +``` diff --git a/site/content/ytt/docs/v0.49.x/lang-ref-ytt-assert.md b/site/content/ytt/docs/v0.49.x/lang-ref-ytt-assert.md new file mode 100644 index 000000000..a5f3c3ae3 --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/lang-ref-ytt-assert.md @@ -0,0 +1,160 @@ +--- +aliases: [/ytt/docs/latest/lang-ref-ytt-assert] +title: Assert Module +--- + +## Overview + +`ytt`'s Assert module allows users to make assertions about their templates and stop execution if desired. + +## Functions + +The `@ytt:assert` module provides several built-in assertion functions. +To use these functions, include the `@ytt:assert` module: + +```python +#@ load("@ytt:assert", "assert") +``` + +### assert.equals() + - Checks equality of the two arguments provided, stops execution if values are not equal. +```python +load("@ytt:assert", "assert") + +assert.equals("not", "equal") # stops execution +``` + +### assert.fail() +- Stops execution and reports a failure. +- Takes a single string argument used as the failure message, this can be formatted with available values. +```python +load("@ytt:assert", "assert") + +assert.fail("custom failure message") +assert.fail("expected value foo, but was {}".format(value)) +x = data.values.env.mysql_password or assert.fail("missing env.mysql_password") +``` + +### assert.max() + +- Checks that values are less than or equal to the maximum value. +- `x = assert.max(4)` returns an object, `x`, that can `x.check()` if values are less than or equal to 4. +- Is able to compare numbers, strings, lists, dictionaries, and [YAML fragments](lang-ref-yaml-fragment). +```python +load("@ytt:assert", "assert") + +assert.max(4).check(3) + +assert.max(4).check(5) # stops execution +``` + +### assert.max_len() + +- Checks that values have length less than or equal to the maximum length. +- Maximum length argument is an integer. +- `x = assert.max_len(4)` creates an object, `x` that can `check()` if the length of values are less than or equal to 4. +- Checks the length of strings, lists, dictionaries, and [YAML fragments](lang-ref-yaml-fragment). +```python +load("@ytt:assert", "assert") + +assert.max_len(4).check({'foo': 0, 'bar': 1}) + +assert.max(4)_len.check("123.45.67.89") # stops execution +``` + +### assert.min() + +- Checks that values are greater than or equal to the minimum value. +- `x = assert.min(2)` returns an object, `x`, that can `x.check()` if values are greater than or equal to 2. +- Is able to compare numbers, strings, lists, dictionaries, and [YAML fragments](lang-ref-yaml-fragment). +```python +load("@ytt:assert", "assert") + +assert.min(2).check(5) + +assert.min(2).check(1) # stops execution +``` + +### assert.min_len() + + - Checks that values have length greater than or equal to the minimum length. + - Minimum length argument is an integer. + - `x = assert.min_len(1)` creates an object, `x` that can `check()` if the length of values are greater than or equal to 1. + - Checks the length of strings, lists, dictionaries, and [YAML fragments](lang-ref-yaml-fragment). +```python +load("@ytt:assert", "assert") + +assert.min_len(1).check(["some","list","of","values"]) + +assert.min_len(1).check("") # stops execution +``` + +### assert.not_null() + + - Checks that a value is not null or none. +```python +load("@ytt:assert", "assert") + +v = None +assert.not_null(v) # stops execution +# is syntactic sugar for +assert.not_null().check(v) # stops execution +``` + +### assert.one_not_null() + +- Checks that a map (or dictionary)'s value has one and only one not-null item. +```python +load("@ytt:assert", "assert") + +# passes +assert.one_not_null().check({"foo": 1, "bar": None}) + +# fails: two values are not null (stops execution) +assert.one_not_null().check({"foo": 1, "bar": 2}) + +# passes: one of named values is not null +assert.one_not_null(["foo", "bar"]).check({"foo": 1, "bar": None, "baz": 3}) + +# passes: missing keys are ok +assert.one_not_null(["foo", "not-present"]).check({"foo": 1, "bar": 2}) +``` + +### assert.one_of() + +- Checks that the value is one in the specified list +```python +load("@ytt:assert", "assert") + +# passes +assert.one_of(["debug", "info", "warn"]).check("warn") + +# fails: value not in the list. +assert.one_of(["aws", "azure", "gcp"]).check("digitalocean") + +# An assertion can be used multiple times against different values +valid_ports = assert.one_of([1433, 1434, 1521, 1830, 3306, 5432]) + +# all pass +valid_ports.check(3306) +valid_ports.check(5432) + +# fails: items in enumeration are integers, value is a string +valid_ports.check("5432") +``` + +### assert.try_to() +- Invokes a function, catching failure if one occurs. +- Takes single function as argument. +- Returns the return value of function, or the error if one occurs. +```python +load("@ytt:assert", "assert") + +x, err = assert.try_to(lambda : json.decode('{"key": "value"}')) +x # { "key" = "value" } (i.e. dict with one entry) +err # None + +x, err = assert.try_to(lambda : json.decode("(not JSON)")) +x # None +err # "json.decode: invalid character '(' looking for beginning of value" +``` diff --git a/site/content/ytt/docs/v0.49.x/lang-ref-ytt-library.md b/site/content/ytt/docs/v0.49.x/lang-ref-ytt-library.md new file mode 100644 index 000000000..3a7a312fb --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/lang-ref-ytt-library.md @@ -0,0 +1,400 @@ +--- +aliases: [/ytt/docs/latest/lang-ref-ytt-library] +title: Library Module +--- + +## Overview + +You can extract a whole set of input files (i.e. templates, overlays, data values, etc.) into a "Library". + +For example: + +```yaml +config/ +├── _ytt_lib/ +│ └── frontend/ +│ ├── schema.yml +│ └── store.yml +└── config.yml +``` +where: +- `config/_ytt_lib/frontend/` and its contents is a library named `"frontend"` + +Libraries are _not_ automatically included in `ytt` output; one must programmatically load, configure, evaluate, and insert those results into a template that is part of the output. + +```yaml +#! config/config.yml -- example of using a library + +#@ load("@ytt:library", "library") +#@ load("@ytt:template", "template") + +#! 1. Load an instance of the library +#@ app = library.get("frontend") + +#! 2. Create a configured copy of the library (does not mutate original) +#@ app_with_vals = app.with_data_values({"apiDomain": "gateway.example.com"}) + +#! 3. Evaluate the library and include results (a document set) in the output +--- #@ template.replace(app_with_vals.eval()) +``` + +For a complete working example, see [ytt-library-module example](/ytt/#example:example-ytt-library-module). + +## What is a Library? + +A `ytt` library is a directory tree contained within a specially-named directory: [`_ytt_lib/`](lang-ref-load.md#_ytt_lib-directory). +- The library's _name_ is the path relative from the `_ytt_lib/` directory. +- The library's _contents_ are those of the directory along with subdirectories, recursively. +- A library may contain libraries as well, if one of its subdirectories is `_ytt_lib/`. + +The root directory of a `ytt` invocation is itself a library known as the "root library". + +Libraries are evaluated in isolation: each a separate execution of the pipeline described in [How it works](how-it-works.md). +- Each library has its own data values schema. +- Overlays within a library only apply over _its_ evaluated document set. +- The final evaluated result is returned as a [YAML Fragment wrapping a document set.](lang-ref-yaml-fragment.md#yaml-document-set). + +--- + +## Functions + +There's but one function in the `@ytt:library` module: [`library.get()`](#libraryget) + +### library.get() + +Contructs a new [`@ytt:library.instance`](#library-instances) based on the contents from the named library. + +```python +instance = library.get(name, []) +``` + +- **`name`** (`string`) — path to the base directory of the desired library: `./_ytt_lib/`. Can contain slashes `/` for sub-directories (e.g. `github.com/carvel-dev/ytt-library-for-kubernetes/app`) +- keyword arguments (optional): + - **`alias=`** (`string`) — unique name for this library instance. See [Aliases](#aliases), below. + - **`ignore_unknown_comments=`** (`bool`) — equivalent to `ytt --ignore-unknown-comments`; see [File Marks > type detection for YAML files](file-marks.md#type-detection-for-yaml-files) for more details (default: `False`). (as of v0.31.0) + - **`implicit_map_key_overrides=`** (`bool`) — equivalent to `ytt --implicit-map-key-overrides`; see [@yaml/map-key-override](lang-ref-annotation.md#yaml-templating-annotations) for more details. (default: `False`). (as of v0.31.0) + - **`strict=`** (`bool`) — equivalent to `ytt --strict` (default: `False`). (as of v0.31.0) +- **`instance`** ([`@ytt:library.instance`](#library-instances)) — a new library instance backed by the contents of the named library. + +The file containing this method invocation must be a sibling of the [`_ytt_lib` directory](lang-ref-load.md#_ytt_lib-directory). + +--- + +## Library Instances + +Each library returned from a function within this module is a copy: a separate instance. + +A library instance (a value of type `@ytt:library.instance`) is created from source with [`library.get()`](#libraryget). + +With a library instance: +- create configured copies using: + - [`instance.with_data_values_schema()`](#instancewith_data_values_schema) + - [`instance.with_data_values()`](#instancewith_data_values) +- evaluate its contents via [`instance.eval()`](#instanceeval) +- fetch values from it using: + - [`instance.data_values()`](#instancedata_values) for the final data values for the library + - [`instance.export()`](#instanceexport) to access its functions and variables + +### instance.data_values() + +Calculates and returns just the Data Values configured on this library instance. + +```python +dvs = instance.data_values() +``` + +- **`dvs`** ([`struct`](lang-ref-structs.md)) — the final data values (i.e. the net result of [all configured data values](ytt-data-values.md#library-data-values)). + +### instance.eval() + +Calculates the library's final data values (i.e. the net result of [all configured data values](ytt-data-values.md#library-data-values)), evaluates its templates into a document set, and applies its overlays on that document set (i.e. executes the pipeline described in [How it works](how-it-works.md) for this library instance's inputs and contents. The output of that execution — rather than rendered automatically — is returned in a variable). + +```python +document_set = instance.eval() +``` + +- **`document_set`** ([`yamlfragment`](lang-ref-yaml-fragment.md#yaml-document-set)) — the YAML document set resulting from the evaluation of this instance. + +Note: the resulting Document Set is _**not**_ automatically included in output. A common way to include this result in the output is to use [`template.replace()`](lang-ref-ytt-template.md#templatereplace): + +```yaml +#@ load("@ytt:template", "template") +#@ load("@ytt:library", "library") + +--- #@ template.replace(library.get("cert-manager").eval()) +``` + +See also, [Playground: ytt library module](/ytt/#example:example-ytt-library-module) example. + +### instance.export() + +(As of v0.28.0) + +Returns the value of an identifier declared within the library instance. + +```python +value = instance.export(name, [path=]) +``` + +- **`name`** (`string`) — the name of a function or a variable declared within some [module](lang-ref-load.md#terminology)/file in the library. (i.e. a file with the extension `.lib.yml` or `.star`). +- **`path=`** (`string`) — the path to the module/file that contains the declaration. Only required when `name` is not unique within the library. +- **`value`** (any) — a copy of the specified value. + - if `value` is a function, it is executed within the context of _its_ library instance. For example, if the function depends on values from the [`@ytt:data`](lang-ref-ytt.md#data) module, the values provided are those of this library instance. + +**Examples:** + +_Example 1: Exporting a function from a library._ + +Assuming some module/file in the "helpers" library contains the definition: + +```python +... +def wrap_name(name): + ... +end +... +``` + +Can be exported and used from another library: + +```python +helpers = library.get("helpers") +wrap_name = helpers.export("wrap_name") + +full_name = wrap_name("app") +``` + +_Example 2: Disambiguating between multiple declarations of function._ + +Assuming two modules/files in the "helpers" library have the same name: + +```python +# main/funcs.star +def wrap_name(name): ... +``` +and +```python +# lib/funcs.star +def wrap_name(name): ... +``` + +One of which can be unambiguously referenced: + +```python +helpers = library.get("helpers") +wrap_name = helpers.export("wrap_name", path="lib/funcs.star") + +full_name = wrap_name("app") +``` + +Note: without the `path=` keyword argument, `helpers.export()` would report an error. + +### instance.with_data_values() + +Returns a copy of the library instance with data values overlayed with those given. + +```python +new_instance = instance.with_data_values(dvs, [plain=]) +``` + +- **`dvs`** (`struct` | [`yamlfragment`](lang-ref-yaml-fragment.md)) — data values with which to overlay (or set, if none exist). + - only `yamlfragment`s wrapping a map or an array are supported (i.e. `yamlfragment`s wrapping document sets are not supported). + - `yamlfragment` values _can_ contain [overlay annotations](lang-ref-ytt-overlay.md#overlay-annotations) for fine-grained overlay control. +- **`plain=`** (`bool`) — when `True` indicates that `dvs` should be "plain merged" over existing data values (i.e. the exact same behavior as [`--data-values-file`](ytt-data-values.md#configuring-data-values-via-command-line-flags)). + - `dvs` must be plain YAML (i.e. a `struct` or a `yamlfragment` with no annotations). +- **`new_instance`** ([`@ytt:library.instance`](#library-instances)) — a copy of `instance` with `dvs` overlayed on its data values; `instance` remains unchanged. + +### instance.with_data_values_schema() + +(As of v0.35.0) + +Returns a copy of the library instance with data values schema overlayed with that given. + +```python +new_instance = instance.with_data_values_schema(schema) +``` + +- **`schema`** (`struct` | [`yamlfragment`](lang-ref-yaml-fragment.md)) — schema for data values with which to overlay on existing schema (or set if none exist). + - only `yamlfragment`s wrapping a map or an array are supported (i.e. `yamlfragment`s wrapping document sets are not supported) + - `yamlfragment` values _can_ contain [overlay annotations](lang-ref-ytt-overlay.md#overlay-annotations) for fine-grained overlay control. +- **`new_instance`** ([`@ytt:library.instance`](#library-instances)) — a copy of `instance` with a schema updated with `schema`; `instance` remains unchanged. + +**Examples:** + +_Example 1: Declaring a new data value (and setting it)._ + +```yaml +#@ def app_schema(): +name: "" +#@overlay/match missing_ok=True +env_vars: + custom_key: "" +#@ end + +#@ app1_with_schema = app1.with_data_values_schema(app_schema()) +--- +#@ def app_vals(): +name: app1 +env_vars: + custom_key: some_val +#@ end + +#@ app1_with_vals = app1.with_data_values(app_vals()) +``` + +--- + +## Annotations + +### @library/ref + +(As of v0.28.0) + +Attaches a YAML document to the specified library. When the library is evaluated, the annotated document is included. +Only supported on documents annotated with `@data/values` and `@data/values-schema`. + +``` +@library/ref library_name +``` +- **`library_name`** (`string`) — `@`-prefixed path to the base directory of the desired library: `./_ytt_lib/`. Can contain slashes `/` for sub-directories (e.g. `github.com/carvel-dev/ytt-library-for-kubernetes/app`). Can also be an [alias](#aliases) for specific library instance(s). + +**Examples:** + +_Example 1: Change schema default for a data value in a library._ + +```yaml +#@data/values-schema +#@library/ref "@frontend" +--- +name: "custom" +``` + +Overlays the default value for `name` in the "frontend" library to be "custom". + +_Example 2: Target a data value overlay to a library._ + +```yaml +#@data/values +#@library/ref "@backend" +--- +#@overlay/replace +domains: +- internal.example.com +- internal-backup.example.com +``` + +Sets the "backend" library's `domains` data value to be exactly the values given. + +See also: [Data Values > Setting Library Values via Files](ytt-data-values.md#setting-library-values-via-files). + +Note: data values may also be attached to libraries via [command line flags](ytt-data-values.md#setting-library-values-via-command-line-flags). + +--- + +## Aliases + +To facilitate configuring specific library instances, one can mark them with an alias. + +An alias: +- is defined in a [`library.get()`](#libraryget) call, using the optional `alias=` keyword argument. +- is added to a library reference by prefixing it with a tilde, `~`: + - `@~` refers to _any_ library instance with the alias. + - `@~` refers to any instance of the named library that _also_ has the alias. + +For example, given a library known as "fruit": + +``` +├── apple-values.yml +├── config.yml +├── orange-values.yml +└── _ytt_lib + └── fruit + ├── doc.yml + └── values.yml +``` + +where: +```yaml +#! _ytt_lib/fruit/doc.yml + +#@ load("@ytt:data", "data") +--- #@ data.values +``` +the template in the library simply returns its data values as a document, and ... +```yaml +#! _ytt_lib/fruit/values.yml + +#@data/values +--- +variety: ordinary +poisoned: false +``` +... those are the data values in the library. + +The root library can assign aliases to library instances: + +```yaml +#! ./config.yml + +#@ load("@ytt:library", "library") + +#@ apple1 = library.get("fruit", alias="apple") +#@ apple2 = apple1.with_data_values({"variety": "jonamac"}) + +#@ orange = library.get("fruit", alias="orange") + +--- +apple: + 1: #@ apple1.eval()[0] + 2: #@ apple2.eval()[0] +orange: #@ orange.eval()[0] +``` +where: +- `apple1` has the alias "apple" +- `apple2` also has the alias "apple" (part of being a copy of `apple1`) +- `orange` has the alias "orange" + +These aliases can be used to target changes to specific library instance(s). + +For example, our root library has these two data values overlays: + +```yaml +#! ./apple-values.yml + +#@data/values +#@library/ref "@~apple" +--- +variety: red delicious +poisoned: true +``` +... which will affect all library instances with the alias "apple", and ... + +```yaml +#! ./orange-values.yml + +#@data/values +#@library/ref "@~orange" +--- +variety: valencia +``` + +... overlays on top of library instance with the alias "orange". + +When the whole fileset is evaluated, the result is: + +```yaml +apple: + 1: + variety: red delicious + poisoned: true + 2: + variety: jonamac + poisoned: true +orange: + variety: valencia + poisoned: false +``` + +notice: +- only the "@~orange" instance has the variety = "valencia" +- both "@~apple" library instances are poisoned; while the "orange" instance is not. diff --git a/site/content/ytt/docs/v0.49.x/lang-ref-ytt-overlay.md b/site/content/ytt/docs/v0.49.x/lang-ref-ytt-overlay.md new file mode 100644 index 000000000..7dff6289d --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/lang-ref-ytt-overlay.md @@ -0,0 +1,759 @@ +--- +aliases: [/ytt/docs/latest/lang-ref-ytt-overlay] +title: Overlay module +--- + +## Overview + +`ytt`'s Overlay feature provides a way to combine YAML structures together with the help of annotations. + +There are two (2) structures involved in an overlay operation: +- the "left" — the YAML document(s) (and/or contained maps and arrays) being modified, and +- the "right" — the YAML document (and/or contained maps and arrays) that is the overlay, describing the modification. + +Each modification is composed of: +- a matcher (via an [`@overlay/(match)`](#matching-annotations) annotation), identifying which node(s) on the "left" are the target(s) of the edit, and +- an action (via an [`@overlay/(action)`](#action-annotations) annotation), describing the edit. + +Once written, an overlay can be applied in one of two ways: +- on all rendered templates, [**declaratively**, via YAML documents annotated with `@overlay/match`](#overlays-as-files); this is the most common approach. +- on selected documents, [**programmatically**, via `overlay.apply()`](#programmatic-access). + +Programmatic overlays are applied immediately (while the contained template is being evaluated). +Declarative overlays are applied in the "Apply Overlays" step as described in [How it works](how-it-works.md). + +_(For a step-by-step primer on writing and using overlays, watch [Primer on `ytt` Overlays](/blog/primer-on-ytt-overlays/).)_ + +--- +## Overlays as files + +As `ytt` scans input files, it pulls aside any YAML Document that is annotated with `@overlay/match`, and considers it an overlay. + +After YAML templates are rendered, the collection of identified overlays are applied. Each overlay executes, one-at-a-time over the entire set of the rendered YAML documents. + +Order matters: modifications from earlier overlays are seen by later overlays. Overlays are applied in the order detailed in [Overlay order](#overlay-order), below. + +In the example below, the last YAML document is an overlay (it has the `@overlay/match` annotation). +That overlay matcher's selects the first YAML document *only*: it's the only one that has a `metadata.name` of `example-ingress`. + +```yaml +#@ load("@ytt:overlay", "overlay") + +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: example-ingress + annotations: + ingress.kubernetes.io/rewrite-target: / +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: another-example-ingress + annotations: + ingress.kubernetes.io/rewrite-target: / + +#@overlay/match by=overlay.subset({"metadata":{"name":"example-ingress"}}) +--- +metadata: + annotations: + #@overlay/remove + ingress.kubernetes.io/rewrite-target: +``` + +yields: + +```yaml +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: example-ingress + annotations: {} +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: another-example-ingress + annotations: + ingress.kubernetes.io/rewrite-target: / +``` + +See also: [Overlay files example](/ytt/#example:example-overlay-files) in online playground. + +_(For a high-level overview of `ytt`, see [How it works](how-it-works.md).)_ + +### Overlay order + +(as of v0.13.0) + +Overlays are applied, in sequence, by: + +1. left-to-right for file flags + - e.g. in `-f overlay1.yml -f overlay2.yml`, `overlay1.yml` will be applied first +1. if file flag is set to a directory, files are alphanumerically sorted + - e.g. in `aaa/z.yml xxx/c.yml d.yml`, will be applied in following order `aaa/z.yml d.yml xxx/c.yml` +1. top-to-bottom order for overlay YAML documents within a single file + +### Next Steps + +Familiarize yourself with the [overlay annotations](#overlay-annotations). + +--- +## Programmatic access + +Overlays need not apply to the entire set of rendered YAML documents (as is the case with [the declarative approach](#overlays-as-files)). + +Instead, the declared modifications can be captured in a function and applied to a specific set of documents via [`overlay.apply()`](#overlayapply) in Starlark code. + +In this example we have `left()` function that returns target structure and `right()` that returns the overlay, specifying the modifications. + +`overlay.apply(...)` will execute the execute the overlay and return a new structure. + +```yaml +#@ load("@ytt:overlay", "overlay") + +#@ def left(): +key1: val1 +key2: + key3: + key4: val4 + key5: + - name: item1 + key6: val6 + - name: item2 + key7: val7 +#@ end + +#@ def right(): +#@overlay/remove +key1: val1 +key2: + key3: + key4: val4 + key5: + #@overlay/match by="name" + - name: item2 + #@overlay/match missing_ok=True + key8: new-val8 +#@ end + +result: #@ overlay.apply(left(), right()) +``` + +yields: + +```yaml +result: + key2: + key3: + key4: val4 + key5: + - name: item1 + key6: val6 + - name: item2 + key7: val7 + key8: new-val8 +``` + +### Next Steps + +Familiarize yourself with the two kinds of overlay annotations: +- matchers (via an [`@overlay/(match)`](#matching-annotations) annotation), and +- actions (via an [`@overlay/(action)`](#action-annotations) annotation). + +--- +## `@overlay` Annotations + +There are two groups of overlay annotations: + +- [Matching Annotations](#matching-annotations) +- [Action Annotations](#action-annotations) + +--- +## Matching Annotations + +These annotations are used to select which structure(s) will be modified: + +- [@overlay/match](#overlaymatch) +- [@overlay/match-child-defaults](#overlaymatch-child-defaults) + +### @overlay/match + +Specifies which nodes on the "left" to modify. + +**Valid on:** Document, Map Item, Array Item. + +``` +@overlay/match [by=Function|String, expects=Int|String|List|Function, missing_ok=Bool, when=Int|String|List] +``` +- **`by=`**`Function|String` — criteria for matching nodes on the "left" + - `Function` — predicate of whether a given node is a match + - provided matcher functions (supplied by the `@ytt:overlay` module): + - [`overlay.all()`](#overlayall) + - [`overlay.subset()`](#overlaysubset) + - [`overlay.index()`](#overlayindex) + - [`overlay.map_key()`](#overlaymap_key) + - [Custom matcher function](#custom-overlay-matcher-functions) can also be used + - `String` — short-hand for [`overlay.map_key()`](#overlaymap_key) with the same argument + - Defaults (depends on the type of the annotated node): + - document or array item: none (i.e. `by` is required) + - map item: key equality (i.e. [`overlay.map_key()`](#overlaymap_key)) +- **`expects=`**`Int|String|List|Function` — (optional) expected number of nodes to be found in the "left." If not satisfied, raises an error. + - `Int` — must match this number, exactly + - `String` (e.g. `"1+"`) — must match _at least_ the number + - `Function(found):Bool` — predicate of whether the expected number of matches were found + - `found` (`Int`) — number of actual matches + - `List[Int|String|Function]` — must match one of the given criteria + - Default: `1` (`Int`) (i.e. expecting to match exactly one (1) node on the "left"). +- **`missing_ok=`**`Bool` (optional) shorthand syntax for `expects=[0,1]` +- **`when=`**`Int|String|List` (optional) criteria for when the overlay should apply. If the criteria is met, the overlay applies; otherwise, nothing happens. + - `Int` — must equal this number, exactly + - `String` (e.g. `"1+"`) — must match _at least_ the number + - `List[Int|String]` — must match one of the given criteria + +**Notes:** +- `expects`, `missing_ok`, and `when` are mutually-exclusive parameters. +- take care when `expects` includes zero (0); matching none is indistinguishable from a mistakenly written match (e.g. a misspelling of a key name) + +**Examples:** + +- `#@overlay/match by="id"`: expects to find one (1) node on the "left" that has the key `id` and value matching the same-named item on the "right." +- `#@overlay/match by=`[`overlay.map_key("name")`](#overlaymap_key): (same as above) +- `#@overlay/match by=`[`overlay.all`](#overlayall)`,expects="0+"`: has no effective matching expectations +- `#@overlay/match missing_ok=True`: expects to find 0 or 1 matching nodes on the "left" +- `#@overlay/match expects=2`: expects to find exactly two (2) matching nodes on the "left" +- `#@overlay/match expects="2+"`: expects to find two (2) or more matching nodes on the "left" +- `#@overlay/match expects=[0,1,4]`: expects to find 0, 1 or 4 matching nodes on the "left" +- `#@overlay/match expects=lambda x: return x < 10`: expects 9 or fewer matching nodes on the "left" +- `#@overlay/match when=2`: applies changes only if two (2) nodes are found on the "left" and will not error otherwise +- `#@overlay/match when=2+`: applies changes only if two (2) or more nodes are found on the "left" and will not error otherwise +- `#@overlay/match when=[0,1,4]`: applies changes only if there were exactly 0, 1 or 4 nodes found on the "left" and will not error otherwise + +**History:** +- v0.28.0+ — added `when` keyword argument. + +#### Custom Overlay Matcher Functions + +The matcher functions from `@ytt:overlay` cover many use-cases. From time-to-time, more precise matching is required. + +A matcher function has the following signature: + +`Function(indexOrKey,left,right):Boolean` + - `indexOrKey` + - when `left` is an array item: (`Int`) — the potential match's _zero-based index_ in the list that is the left-side + - when `left` is a map item: (`any`) — the potential match's _key_ in the map that is the left-side + - `left` ([`yamlfragment`](lang-ref-yaml-fragment.md) or scalar) — the potential match/target node + - `right` ([`yamlfragment`](lang-ref-yaml-fragment.md) or scalar) — the value of the annotated node in the overlay + - returns `True` if `left` should be considered a match; `False` otherwise. + +Most custom matchers can be written as a Lambda expression. + +Lambda expressions start with the keyword `lambda`, followed by a parameter list, then a `:`, and a single expression that is the body of the function. + +**Examples:** + +_Example 1: Key presence or partial string match_ + +`left` contains `right`: +```python +lambda indexOrKey, left, right: right in left +``` +Returns `True` when `right` is "a member of" `left` + +(see also: [Starlark Spec: Membership tests](https://github.com/google/starlark-go/blob/master/doc/spec.md#membership-tests) for more details) + +_Example 2: Precise string matching_ + +`left` contains a key of the same name as the value of `right`: +```python +lambda indexOrKey, left, right: left["metadata"]["name"].endswith("server") +``` +See also: +- [Language: String](lang-ref-string.md) for more built-in functions on strings. +- [@ytt:regexp Library](lang-ref-ytt.md#regexp) for regular expression matching. + +_Example 3: Match on both key and value_ + +Is the map item with key/value of: `type: LoadBalancer`. + +```python +lambda indexOrKey, left, right: indexOrKey == "type" and left == "LoadBalancer" +``` + +### @overlay/match-child-defaults + +Sets default values for `expects`, `missing_ok`, or `when` for the children of the annotated node. +Does not set these values for the annotated node, itself. + +Commonly used to avoid repeating `@overlay/match missing_ok=True` on each child node in maps. + +**Valid on:** Document, Map Item, Array Item. + +``` +@overlay/match-child-defaults [expects=Int|String|List|Function, missing_ok=Bool, when=Int|String|List] +``` + +_(see [@overlay/match](#overlaymatch) for parameter specifications.)_ + +**Examples:** + +Without the `#@overlay/match-child-defaults`, _each_ of the four new annotation would have needed an `@overlay/match missing_ok=True` to apply successfully: +```yaml +--- +metadata: + annotations: + ingress.kubernetes.io/rewrite-target: / + +#@overlay/match by=overlay.all +--- +metadata: + #@overlay/match-child-defaults missing_ok=True + annotations: + nginx.ingress.kubernetes.io/limit-rps: 2000 + nginx.ingress.kubernetes.io/enable-access-log: "true" + nginx.ingress.kubernetes.io/canary: "true" + nginx.ingress.kubernetes.io/client-body-buffer-size: 1M +``` + +--- +## Action Annotations + +The following annotations describe how to modify the matched "left" node. + +They are: +- [`@overlay/merge`](#overlaymerge) — (default) combine left and right nodes +- [`@overlay/remove`](#overlayremove) — delete nodes from left +- [`@overlay/replace`](#overlayreplace) — replace the left node +- [`@overlay/insert`](#overlayinsert) — insert right node into left +- [`@overlay/append`](#overlayappend) — add right node at end of collection on left +- [`@overlay/assert`](#overlayassert) — declare an invariant on the left node + +### @overlay/merge + +Merge the value of "right" node with the corresponding "left" node. + +**Valid on:** Map Item, Array Item. + +``` +@overlay/merge +``` +_(this annotation has no parameters.)_ + +**Note:** This is the default action; for each node in an overlay, either the action is explicitly specified or it is `merge`. + +### @overlay/remove + +Deletes the matched "left" node. + +**Valid on:** Document, Map Item, Array Item. + +``` +@overlay/remove +``` +_(this annotation has no parameters.)_ + +### @overlay/replace + +Substitutes matched "left" node with the value of the "right" node (or by that of a provided function). + +**Valid on:** Document, Map Item, Array Item. + +``` +@overlay/replace [or_add=Bool, via=Function] +``` + +- **`or_add`**`Bool` _(optional)_ determines what should be done in case matched node is not found + - `or_add=True` indicates that node should be added. Can be used in combination with `@overlay/match missing_ok=True`. +- **`via=`**`Function(left, right): (any)` _(optional)_ determines the value to substitute in. If omitted, the value is `right`. + - `left` ([`yamlfragment`](lang-ref-yaml-fragment.md) or scalar) — the matched node's value + - `right` ([`yamlfragment`](lang-ref-yaml-fragment.md) or scalar) — the value of the annotated node + +**History:** +- v0.26.0 — works with [`yamlfragment`](lang-ref-yaml-fragment.md) values. + +**Examples:** + +_Example 1: Use value from "right"_ + +Replaces the corresponding "left" with the value `"v1"` +```yaml +#@overlay/replace +apiVersion: v1 +``` + +_Example 2: Edit string value_ + +```yaml +#@overlay/replace via=lambda left, right: "prefix-"+left +``` + +See also: +- `ytt` modules that export functions useful for manipulating values: + - [base64 module](lang-ref-ytt.md#base64) + - [json module](lang-ref-ytt.md#json) + - [md5 module](lang-ref-ytt.md#md5) + - [sha256 module](lang-ref-ytt.md#sha256) + - [url module](lang-ref-ytt.md#url) + - [yaml module](lang-ref-ytt.md#yaml) +- [Language: String](lang-ref-string.md) for built-in string functions. +- Other Starlark language features that manipulate values: + - [string interpolation](https://github.com/google/starlark-go/blob/master/doc/spec.md#string-interpolation) + - [conditional expressions](https://github.com/google/starlark-go/blob/master/doc/spec.md#conditional-expressions) + - [index expressions](https://github.com/google/starlark-go/blob/master/doc/spec.md#index-expressions) + - [slice expressions](https://github.com/google/starlark-go/blob/master/doc/spec.md#slice-expressions) + +### @overlay/insert + +Inserts "right" node before/after the matched "left" node. The inserted node is either the "right" node or that provided by a function. + +**Valid on:** Document, Array Item. + +``` +@overlay/insert [before=Bool, after=Bool] +``` +- **`before=`**`Bool` whether to insert the "right" node immediately in front of the matched "left" node. +- **`after=`**`Bool` whether to insert the "right" node immediately following the matched "left" node. +- **`via=`**`Function(left, right): (any)` _(optional)_ determines the value to substitute in. If omitted, the value is `right`. + - `left` ([`yamlfragment`](lang-ref-yaml-fragment.md) or scalar) — the matched node's value + - `right` ([`yamlfragment`](lang-ref-yaml-fragment.md) or scalar) — the value of the annotated node + +**Examples:** + +Add a `ConfigMap` into each `Namespace`: +```yaml +#@ def configMap(namespace): +apiVersion: v1 +kind: ConfigMap +metadata: + name: insert + namespace: #@ namespace.metadata.name +#@ end + +#@overlay/match overlay.subset({"kind": "Namespace"}) +#@overlay/insert after=True, via=lambda namespace, _: configMap(namespace) +--- +``` + +### @overlay/append + +Inserts the "right" node after the last "left" node. + +**Valid on:** Document, Array Item. + +``` +@overlay/append +``` +_(this annotation has no parameters.)_ + +**Note:** This action implies an `@overlay/match` selecting the last node. Any other `@overlay/match` annotation is ignored. + +**History:** +- v0.32.0 — omitting `@overlay/append` annotation implies a merge operation and 0 matches, defaulting to insert after the last item. + +### @overlay/assert + +Checks assertion that value of "left" matched node equals that of the annotated "right" node (_or_ a provided predicate). + +**Valid on:** Document, Map Item, Array Item. + +``` +@overlay/assert [via=Function] +``` + +- Default: checks that the value of the matched "left" node equals the value of the annotated "right" node. +- **`via`**`=Function(left, right):(Bool|Tuple(Bool|String)|None)` _(optional)_ predicate indicating whether "left" passes the check + - `left` ([`yamlfragment`](lang-ref-yaml-fragment.md) or scalar) — the matched node's value + - `right` ([`yamlfragment`](lang-ref-yaml-fragment.md) or scalar) — the value of the annotated node + - Return types: + - `Bool` — if `False`, the assertion fails; otherwise, nothing happens. + - `Tuple(Bool|String)` — if `False`, the assertion fails and specified string is appended to the resulting error message; otherwise nothing happens. + - `None` — the assertion assumes to succeed. In these situations, the function makes use of the [`@ytt:assert`](lang-ref-ytt.md#assert) module to effect the assertion. + +**History:** +- v0.26.0 — works with [`yamlfragment`](lang-ref-yaml-fragment.md) values. +- v0.24.0 — introduced + +**Examples:** + +_Example 1: Range check_ + +Fails the execution if `left` not between 0 and 1000, exclusively. + +```yaml +#@overlay/assert via=lambda left, right: left > 0 and left < 1000 +``` + +_Example 2: Well-formedness check_ + +Fails the execution if `left` contains anything other than lowercase letters or numbers. + +```yaml +#@overlay/assert via=lambda left, right: regexp.match("[a-z0-9]+", left) +``` + +See also: +- `ytt` hashing functions from: + - [md5 module](lang-ref-ytt.md#md5) + - [sha256 module](lang-ref-ytt.md#sha256) +- Boolean expression operators and built-in functions, including: + - [`in`](https://github.com/google/starlark-go/blob/master/doc/spec.md#membership-tests) (aka "membership test") + - [`and` and `or`](https://github.com/google/starlark-go/blob/master/doc/spec.md#or-and-and) + - [`any()`](https://github.com/google/starlark-go/blob/master/doc/spec.md#any) or [`all()`](https://github.com/google/starlark-go/blob/master/doc/spec.md#all) + - [`hasattr()`](https://github.com/google/starlark-go/blob/master/doc/spec.md#hasattr) + - [`len()`](https://github.com/google/starlark-go/blob/master/doc/spec.md#len) + - [`type()`](https://github.com/google/starlark-go/blob/master/doc/spec.md#type) +- [Language: String](lang-ref-string.md) functions + +--- +## Functions + +The `@ytt:overlay` module provides several functions that support overlay use. + +To use these functions, include the `@ytt:overlay` module: + +```python +#@ load("@ytt:overlay", "overlay") +``` + +The functions exported by this module are: +- [overlay.apply()](#overlayapply) +- [overlay.map_key()](#overlaymap_key) +- [overlay.index()](#overlayindex) +- [overlay.all()](#overlayall) +- [overlay.subset()](#overlaysubset) +- [overlay.and_op()](#overlayand_op) +- [overlay.or_op()](#overlayor_op) +- [overlay.not_op()](#overlaynot_op) + +### overlay.apply() + +Executes the supplied overlays on top of the given structure. + +```python +overlay.apply(left, right1[, rightN...]) +``` + +- `left` ([`yamlfragment`](lang-ref-yaml-fragment.md)) — the target of the overlays +- `right1` ([`yamlfragment`](lang-ref-yaml-fragment.md) annotated with [`@overlay/(action)`](#action-annotations)) — the (first) overlay to apply on `left`. +- `rightN` ([`yamlfragment`](lang-ref-yaml-fragment.md) annotated with [`@overlay/(action)`](#action-annotations)) — the Nth overlay to apply on the result so far (which reflects the changes made by prior overlays) + +**Notes:** +- For details on how to use `apply()`, see [Programmatic access](#programmatic-access). + +**Examples:** + +```python +overlay.apply(left(), right()) +overlay.apply(left(), one(), two()) +``` + +See also: [Overlay example](/ytt/#example:example-overlay) in the ytt Playground. + +### overlay.map_key() + +An [Overlay matcher function](#overlaymatch) that matches when the collection (i.e. Map or Array) in the "left" contains a map item with the key of `name` and value equal to the corresponding map item from the "right." + +```python +overlay.map_key(name) +``` +- `name` (`String`) — the key of the contained map item on which to match + +**Note:** this matcher requires that _all_ items in the target collection have a map item with the key `name`; if this requirement cannot be guaranteed, consider using [`overlay.subset()`](#overlaysubset), instead. + +**Examples:** + +_Example 1: Over an Array_ + +With "left" similar to: +```yaml +clients: +- id: 1 +- id: 2 +``` +the following matches on the second array item: +```yaml +clients: +#@overlay/match by=overlay.map_key("id") +- id: 2 +``` + +_Example 2: Over a Map_ + +(as of v0.26.0+) + +With "left" similar to: +```yaml +clients: + clientA: + id: 1 + clientB: + id: 2 +``` +the following matches on the second map item: +```yaml +--- +clients: + #@overlay/match by=overlay.map_key("id") + _: + id: 2 +``` +(note: the key name `_` is arbitrary and ignored). + +### overlay.index() + +An [Overlay matcher function](#overlaymatch) that matches the array item at the given index + +```python +overlay.index(i) +``` +- `i` (`Int`) — the ordinal of the item in the array on the "left" to match (zero-based index) + +**Example:** + +```yaml +#@overlay/match by=overlay.index(0) +- item10 +``` + +### overlay.all() + +An [Overlay matcher function](#overlaymatch) that matches all contained nodes from the "left", unconditionally. + +```python +overlay.all() +``` +_(this function has no parameters.)_ + +**Examples:** + +_Example 1: Documents_ + +Matches each and every document: +```yaml +#@overlay/match by=overlay.all +--- +metadata: + annotations: ... +``` + +_Example 2: Array Items_ + +Matches each and every item in the array contained in `items` on the "left": +```yaml +items: +#@overlay/match by=overlay.all +- item10 +``` + +_Example 3: Map Items_ + +(as of v0.26.0+) + +Matches each and every item in the map contained in `items` on the "left": +```yaml +items: + #@overlay/match by=overlay.all + _: + name: item10 +``` +(note: the key name `_` is arbitrary and ignored) + +### overlay.subset() + +An [Overlay matcher function](#overlaymatch) that matches when the "left" node's structure and value equals the given `target`. + +```python +overlay.subset(target) +``` +- `target` (`any`) — value that the "left" node must equal. + +**Examples** + +_Example 1: Scalar_ + +To match, scalar values must be equal. + +```yaml +#@overlay/match by=overlay.subset(1) +#@overlay/match by=overlay.subset("Entire string must match") +#@overlay/match by=overlay.subset(True) +``` +(if a partial match is required, consider writing a [Custom Overlay Matcher function](#custom-overlay-matcher-functions)) + +_Example 2: Dict (aka "map")_ + +To match, dictionary literals must match the structure and value of `left`. + +```yaml +#@overlay/match by=overlay.subset({"kind": "Deployment"}) +#@overlay/match by=overlay.subset({"metadata": {"name": "istio-system"}}) +``` + +_Example 3: YAML Fragment_ + +To match, [`yamlfragment`](lang-ref-yaml-fragment.md)'s must match structure and value of `left`. + +```yaml +#@ def resource(kind, name): +kind: #@ kind +metadata: + name: #@ name +#@ end + +#@overlay/match by=overlay.subset(resource("Deployment", "istio-system")) +``` + +### overlay.and_op() + +(as of v0.26.0+) + +An [Overlay matcher function](#overlaymatch) that matches when all given matchers return `True`. + +```python +overlay.and_op(matcher1, matcher2, ...) +``` +- `matcher1`, `matcher2`, ... — one or more other [Overlay matcher function](#overlaymatch)s. + +**Examples:** + +```yaml +#@ not_sa = overlay.not_op(overlay.subset({"kind": "ServiceAccount"})) +#@ inside_ns = overlay.subset({"metadata": {"namespace": "some-ns"}}) + +#@overlay/match by=overlay.and_op(not_sa, inside_ns),expects="1+" +``` + +### overlay.or_op() + +(as of v0.26.0+) + +An [Overlay matcher function](#overlaymatch) that matches when at least one of the given matchers return `True`. + +```python +overlay.or_op(matcher1, matcher2, ...) +``` +- `matcher1`, `matcher2`, ... — one or more other [Overlay matcher function](#overlaymatch)s. + +**Examples:** + +```yaml +#@ config_maps = overlay.subset({"kind": "ConfigMap"}) +#@ secrets = overlay.subset({"kind": "Secret"}) + +#@overlay/match by=overlay.or_op(config_maps, secrets) +``` + +### overlay.not_op() + +(as of v0.26.0+) + +An [Overlay matcher function](#overlaymatch) that matches when the given matcher does not. + +```pythons +not_op(matcher) +``` +- `matcher` — another [Overlay matcher function](#overlaymatch). + +```yaml +#@overlay/match by=overlay.not_op(overlay.subset({"metadata": {"namespace": "app"}})) +``` diff --git a/site/content/ytt/docs/v0.49.x/lang-ref-ytt-schema.md b/site/content/ytt/docs/v0.49.x/lang-ref-ytt-schema.md new file mode 100644 index 000000000..42160d800 --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/lang-ref-ytt-schema.md @@ -0,0 +1,701 @@ +--- +aliases: [/ytt/docs/latest/lang-ref-ytt-schema] +title: Data Values Schema Reference +--- + +## Overview + +This reference covers details of Data Values Schema: supported types and annotations. + +For an introduction of Data Values, see [Using Data Values](how-to-use-data-values.md). \ +For details on writing Schema, see [Writing Schema](how-to-write-schema.md). + +## The Schema Document + +Schema is written in YAML. + +```yaml +#@data/values-schema +--- +... +``` +where: +- the document must be annotated as `@data/values-schema` +- each item in the document [declares a Data Value](#data-value-declarations) (i.e. an item in the [data.values](lang-ref-ytt.md#data) struct). +- (optionally) types and default values can be explicitly specified through [annotations](#annotations). +- a file containing a Schema document must not contain any other kind of document. + +### Multiple Schema Documents + +In some cases, it is useful to separate Schema into multiple YAML documents (typically in separate files). + +When doing so, it becomes relevant to know that Schema Documents are [`ytt` Overlays](ytt-overlays.md): +- they merge in [the same order as overlays](lang-ref-ytt-overlay.md#overlay-order), and +- one controls that merge via [overlay annotations](lang-ref-ytt-overlay.md#overlay-annotations). + +--- +## Data Value Declarations + +Each item in a Schema Document declares a Data Value. + +A Data Value declaration has three (3) parts: +- the **name** of the data value, +- its **default** value, and +- the **type** of the value. + +--- +### Names + +A Data Value is referred to by its name (aka "key" or "attribute"). \ +A Data Value name must be a string. + +When using multi-word names, it is recommended to employ snake-case (e.g. `database_connection`). This principally because: +- the underlying programming language in `ytt` — Starlark — is Pythonic in which identifiers are snake-cased, by convention +- as in most modern languages, the dash (i.e. `-`) is not allowed for identifier names in Starlark (allow characters are: Unicode letters, decimal digits, and underscores `_`, per the [Starlark spec](https://github.com/google/starlark-go/blob/master/doc/spec.md#lexical-elements)). + +Where disallowed characters in names cannot be avoided, references will need to employ either: +- the Starlark built-in [`getattr()`](https://github.com/google/starlark-go/blob/master/doc/spec.md#getattr) + ```python + secure: #@ getattr(data.values, "db-conn").secure + ``` +- _(as of v0.31.0)_ or [index notation](lang-ref-structs.md#attributes): + ```python + secure: #@ data.values["db-conn"].secure + ``` + +--- +### Default Values + +The default value for a Data Value is specified in schema, directly: + +- the default value for a **scalar** is the value given; +- the default value for a **map** are all of the items specified in the schema (with _their_ defaults, recursively); +- the default value for an **array** is an empty list (i.e. `[]`). + - the default value for _an item_ in an array are the contents of the item specified in schema (with _their_ defaults, recursively). + +Default values can be overridden using the [@schema/default](#schemadefault) annotation. + +#### Defaults for Scalars + +When a [scalar value](#types-of-scalars) is specified, the default is merely that value. + +For example, +```yaml +#@data/values-schema +--- +system_domain: "" + +load_balancer: + enabled: true + +databases: +- name: "" + adapter: postgresql +``` + +- `system_domain` is `""` (i.e. an empty string), by default. +- `load_balancer.enabled` is `true` by default. +- `databases[].adapter` is the string `"postgres"`, by default. + +#### Defaults for Mappings + +The set of items of a map are its default: any missing item will be automatically added with _its_ defaults. + +For example, +```yaml +#@data/values-schema +--- +load_balancer: + enabled: true + static_ip: "" +``` + +if `load_balancer` were omitted from supplied Data Values, entirely, it would default to: +- `load_balancer.enabled` is `true` +- `load_balancer.static_ip` is `""` + +if `load_balancer` is _partially_ specified... + +```yaml +#@data/values +--- +load_balancer: + static_ip: 10.0.101.1 +``` + +the missing item (here, `enabled`) is defaulted: +- `load_balancer.static_ip` is `"10.0.101.1"` +- `load_balancer.enabled` is `true` + + +#### Defaults for Arrays + +The default value for all arrays is, by default, an empty array. + +This is different from all other types where the default value is literally what is specified in schema. For arrays, it is `[]` (i.e. an empty array). + +This means that the value given for the element is _only_ used to infer the type of the array's _elements_. + +```yaml +#@data/values-schema +--- +app_domains: +- "" + +databases: +- name: "" + adapter: postgresql + host: "" + port: 5432 + user: admin + secretRef: + name: "" +``` + +- `app_domains` is `[]` by default. Each item in the array must be a **string**. +- `databases` is `[]` by default. Each item in the array must be a **map**. When an item is added to the array: + - its key must be one of those named in this schema: `name`, `adapter`, `host`, etc. + - if it lacks any of the six keys, they will be added with their default values. + +The default value of an array _itself_ can be overridden using the [@schema/default](#schemadefault) annotation. + +--- +### Types + +`ytt` Schema can infer the type of the Data Value from the following... + +#### Types of Scalars + +`ytt` recognizes the following scalar types: + +- **strings** — e.g. `""`, `"ConfigMap"`, `"0xbeadcafe"` +- **integers** — e.g. `42` +- **booleans** — `true`, `false` (and when not strict, `yes`, `no`, `Y`, `N`, etc.) +- **floats** — e.g. `0.4` + +#### Types of Maps + +A map is a collection of map items, each a key/value pair. + +The schema of a map is inferred from that collection. Each item declares a nested Data Value of the type inferred from the given map item's value. + +For example, +```yaml +load_balancer: + enabled: true + static_ip: "" +``` + +where: +- `load_balancer` has a type of a map that has two items: + - one item has a key `enabled` whose type is a **boolean**. + - the other item has a key of `static_ip` and is a **string**. + +#### Types of Arrays + +An array is a sequence of array items. + +The schema of an array must contain _exactly_ one (1) item. The type inferred from that item becomes the type of _all_ items in that array. That is, arrays in `ytt` are homogenous. + +For example, +```yaml +app_domains: +- "" +``` +where: +- `app_domains` has a type of an array. Each element in that array will be a **string**. +- note that the default value for `app_domains` is an empty list as explained in [Defaults for Arrays](#defaults-for-arrays), above. + +#### `null` Type + +The `null` value means the absence of a value. + +In `ytt` schema, a default value is not permitted to be `null` (with one exception described in [`any` Type](#any-type), below). This is because no useful type can be inferred from the value `null`. + +Instead, one provides a non-null default value and annotates the Data Value as "nullable". + +This results in a Data Value whose default value is `null`, but when set to a non-null value has an explicit type. See [`@schema/nullable`](#schemanullable) for details. + +#### `any` Type + +In certain cases, it may be necessary to relax all restrictions on the type or shape of a Data Value: + +- the Data Value is a pass-through, where template(s) using it merely insert its value, but care not about the actual contents; +- a heterogeneous array is required; +- there are multiple possible allowed types for a given Data Value. + +This is done by annotating the Data Value as having "any" type. See [`@schema/type`](#schematype) for details. + +--- +## Annotations + +`ytt` determines the type of each Data Value by _inferring_ it from the value specified in the schema file (as described in [Types](#types), above). Currently, there is no way to _explicitly_ set the type of a Data Value. + +Configuration Authors can explicit specify the type of a Data Value in two cases that are **not** inferrable: +- overriding the default value (via the [@schema/default](#schemadefault) annotation); +- also allowing null (via the [@schema/nullable](#schemanullable) annotation); +- allowing any type (via the [@schema/type](#schematype) annotation). + +### @schema/default + +Overrides the default value for the annotated node. + +```yaml +#@schema/default default_value +``` +- `default_value` — the value to set as the default for the annotated node. + - this value must be of the same type as the value given on the node. + +_(as of v0.38.0)_ + +_Example 1: Default value for an array of scalars_ + +```yaml +#@data/values-schema +--- +#@schema/default ["apps.example.com", "gateway.example.com"] +app_domains: +- "" +``` + +... yields the default: + +```yaml +app_domains: +- apps.example.com +- gateway.example.com +``` + +_Example 2: Default value for an array of maps_ + +When specifying values for an array of maps, it can quickly become unwieldy to keep on a single line. + +To handle these situations, enclose those values in a [Fragment function](lang-ref-yaml-fragment.md) and invoke that function as the value for `@schema/default`: + +```yaml +#! For best results, declare functions *before* the schema document. +#@ def default_dbs(): +- name: core + host: coredb + user: app1 +- name: audit + host: metrics.svc.local + user: observer +#@ end + +#@data/values-schema +--- +#@schema/default default_dbs() +databases: +- name: "" + adapter: postgresql + host: "" + port: 5432 + user: admin + secretRef: + name: "" +``` + +Yields the default: + +```yaml +databases: +- name: core + adapter: postgresql + host: coredb + port: 5432 + user: app1 + secretRef: + name: "" +- name: audit + adapter: postgresql + host: metrics.svc.local + port: 5432 + user: observer + secretRef: + name: "" +``` + +Note: as the comment in the example schema indicates, it is best to declare the function prior to starting the schema document itself (see https://github.com/carvel-dev/ytt/issues/526 for details). + +### @schema/nullable + +Extends the type of the Data Value to also allow `null` _and_ sets the default value to be `null`. + +```yaml +#@schema/nullable +``` + +**Unset value for strings** + +The preferred way to express "unset value" for a string-type is _not_ to mark it as "nullable" but to provide the empty value: `""`. Empty values in Starlark are falsey (details in the [Starlark Spec > Booleans](https://github.com/google/starlark-go/blob/master/doc/spec.md#booleans)). + +When empty string is a useful/valid value for a given Data Value, _then_ marking it as "nullable" is appropriate. In this case, one must take care to explicitly check if that Data Value is not [`None`](lang.md#types). + +_Example: Nullable map_ + +```yaml +#@data/values-schema +--- +#@schema/nullable +aws: + username: admin + password: "1234" + +name: "" +``` + +Without other Data Value settings, `aws` is `null` by default: +```yaml +aws: null +name: "" +``` + +However, if a Data Value is set: + +```bash +$ ytt ... --data-value aws.username=sa ... +``` + +That effectively sets `aws` to be non-null: `username` is set to the custom value and `password` is defaulted. + +```yaml +aws: + username: sa + password: "1234" + +name: "" +``` + +### @schema/type + +Explicitly configures the type of the annotated node. Currently, the only supported configuration is whether to allow the "any" type or not. + +```yaml +#@schema/type any=True +``` + +where: +- `any` (`bool`) — whether or not any and all types are permitted on this node and its children. + +The annotated node and its nested children are not checked by schema, and has no schema defaulting behavior. +However, _(as of v0.39.0)_ any nested `@schema` annotation that alters type or value of a child would conflict with the fragment's "any" type, resulting in an error. +Otherwise, the annotated node and its children are simply passed-through as a data value. + +_Example: Using any=True to avoid schema restrictions on an array_ + +```yaml +#@data/values-schema +--- +#@schema/type any=True +app_domains: + - "carvel.dev" + - 8080 +``` + +_Example: Error case when setting schema within an any type fragment_ + +```yaml +#@data/values-schema +--- +#@schema/type any=True +app_domains: + #@schema/default "localhost" + #@schema/type any=False + - "carvel.dev" +``` +``` +ytt: Error: + Invalid schema + ============== + + Schema was specified within an "any type" fragment + schema.yml: + | + 5 | #@schema/default "localhost" + 6 | #@schema/type any=False + 7 | - "carvel.dev" + | + + = found: @schema/type, @schema/default annotation(s) + = expected: no '@schema/...' on nodes within a node annotated '@schema/type any=True' +``` + +### @schema/validation + +Attaches a validation to the type being declared by the annotated node. + +``` +@schema/validation rule0, rule1, ... [,] [,when=] +``` + +where: +- `ruleX` — any number of custom rules, each a 2-item tuple `(description, assertion)` + - `description` (`string`) — a description of what a valid value is. + - `assertion` (`function(value) : bool`) — returns `True` is the value is valid; returns `False` or `fail()`s if the value is invalid. + - `value` (`string` | `number` | `bool` | [`yamlfragment`](lang-ref-yaml-fragment.md)) — the value of the annotated node. + - the message given in the `fail()` will be incorporated in the violation message. +- `` — a combination of one or more built-in keywords that provide assertion functions for common scenarios. + - for a quick reference, see [Validations Cheat Sheet](quick-ref-validations.md). + - for the complete list, see [Named Validation Rules](#named-validation-rules), below. +- `when=` (`function(value[, context]) : bool`) — criteria for when the validation rules should be checked. + - `value` (`string` | `int` | `float` | `bool` | [`yamlfragment`](lang-ref-yaml-fragment.md)) — the value of the annotated node + - `context` (_optional_) — a struct with two attributes (both of the same _type_ as `value`). + - `parent` — the node directly containing the annotated node (in other words, its parent) + - `root` — the document in which the annotated node is contained (that is, the root of document resulting from the merge of all data values) + +When present, the predicate given for `when=` is run. If it evaluates to `True`, the validation is run. For guidance on writing these conditions, see [How To Write Validations](how-to-write-validations.md#conditional-validations). + +For a quick reference of various rules and idioms, see [Schema Validation Cheat Sheet](schema-validations-cheat-sheet.md). + +Below: +- [Example 1: Using "named" rules](#example-1-using-named-rules) +- [Example 2: Using `when=`](#example-2-using-when) +- [Example 3: Accessing other data values within `when=`](#example-3-accessing-other-data-values-within-when) +- [Validation Rule Evaluation](#validation-rule-evaluation) +- [Named Validation Rules](#named-validation-rules) + + +#### Example 1: Using "named" rules + +```yaml +#@data/values-schema +--- +#@schema/validation min_len=1 +namespace: "" + +#@schema/validation min_len=1 +hostname: "" + +port: + #@schema/validation min=1, max=32767 + https: 443 + +#@schema/validation one_of=["debug", "info", "warning", "error", "fatal"] +logLevel: info + +#@schema/nullable +tlsCertificate: + #@schema/validation min_len=1 + tls.crt: "" + #@schema/validation min_len=1 + tls.key: "" + #@schema/nullable + ca.crt: "" +``` +where: +- `namespace` and `hostname` are "required" — they will require overrides from the user to pass validation. + - see [How to Write Validations: "Required" Data Values](how-to-write-validations.md#required-data-values) +- `port.https` must be between 1 and 32767, inclusive. +- `logLevel` can _only_ be one of the values given +- `tlsCertificate` is optional, by default (and is `null`) + - however, if one or more of its values are set, _then_ both `tls.crt` and `tls.key` are "required". + - `ca.crt` is optional (and defaults to `null`) + +#### Example 2: Using `when=` + +```yaml +#@data/values-schema +--- +#@schema/validation ("have 1+ response type", lambda v: len(v["responseTypes"]) > 0), when=lambda v: v["enabled"] == True +oauth2: + enabled: true + responseTypes: + - "" +``` +where: +- validation on `oauth2` will only be run if `oauth2.enabled` is true. +- `v` is a [YAML Fragment](lang-ref-yaml-fragment.md) (hence the need for index notation to traverse the tree). + +#### Example 3: Accessing other data values within `when=` + +```yaml +#@data/values-schema +--- +credential: + useDefaultSecret: true + #@schema/nullable + #@schema/validation not_null=True, when=lambda _, ctx: ctx.parent["useDefaultSecret"] + secretContents: + cloud: "" +backupStorageLocation: + spec: + #@schema/nullable + #@schema/validation not_null=True, when=lambda _, ctx: not ctx.root["credential"]["useDefaultSecret"] + existingSecret: "" +#@schema/validation ("have 1+ response type", lambda v: len(v["responseTypes"]) > 0), when=lambda v: v["enabled"] == True +oauth2: + enabled: true + responseTypes: + - "" +``` +where: +- `secretContents` validation is dependent on its sibling `useDefaultSecret` value. + - the value of `when=` is a lambda expression making it possible to define a function value in-place. + - Lambda expressions start with the keyword `lambda`, followed by a parameter list, then a `:`, and a single expression that is the body of the function. For more detail see [Starlark Spec: lambda expression](https://github.com/google/starlark-go/blob/master/doc/spec.md#lambda-expressions). + - the `_` is an idiom for an ignored parameter. Here, the value of `secretContents` is not being used. + - the optional second parameter `ctx` where the `.parent` attribute refers to the value of `credential` +- `existingSecret` validation is dependent on `secretContents` which resides in a whole different subset of the data values. + - `ctx.root` refers to the top-most node of the document resulting from merging of all data values (as described in [How It Works: Calculating Data Values](how-it-works.md#step-1-calculate-data-values)). + +#### Validation Rule Evaluation + +When a validation runs, _all_ rules are evaluated. Rules are evaluated in the order they appear in the annotation (left-to-right). + +The one exception is the `not_null=` rule: +- when present, this rule is evaluated _first_ — regardless of its position on the annotation. +- if this rule is not satisfied, no other rules are evaluated. + +`not_null=` behaves this way so that all other rules can assume they are validating a non-null value. This simplifies all other rules as they need not perform a null-check. + +Validity: +- if _all_ rules pass (i.e. returns `True`), then the value is valid. +- if _any_ rule returns `False` or `fail()`'s, then the value is invalid. + + +#### Named Validation Rules + +There are seven (7) built-in (so-called "named") rules: + +- [`max=`](#max) — the node's value must be <= the maximum given +- [`max_len=`](#max_len) — the length of node's value must be <= the maximum given +- [`min=`](#min) — the node's value must be >= the minimum given +- [`min_len=`](#min_len) — the length of node's value must be >= the minimum given +- [`not_null=`](#not_null) — the node's value must not be `null` +- [`one_not_null=`](#one_not_null) — _exactly_ one (1) item in the map is not `null`. +- [`one_of=`](#one_of) — the node's value must be one from the given set. + +Every named rule is also available as a function in the [`@ytt:assert` module](lang-ref-ytt-assert.md). Having a function that behaves identically to the named rule makes it easier to transition to more customized experience. + + +What follows is a reference for each named rule. + +--- + +##### max= + +``` +@schema/validation max=maximum +``` + +Where: +- `maximum` (`int` | `float` | `string` | `bool` | `list` ) — the largest allowable valid value (inclusive); see [Starlark: Comparisons](https://github.com/google/starlark-go/blob/master/doc/spec.md#comparisons) for how different values compare. + +This keyword is equivalent to: + +``` +@schema/validation ("a value less than or equal to", lambda v: v <= maximum) +``` + +##### max_len= + +``` +@schema/validation max_len=maximum +``` + +Where: +- `maximum` (`int`) — the longest allowable length (inclusive) + +This keyword is equivalent to: + +``` +@schema/validation ("length less than or equal to", lambda v: len(v) <= maximum) +``` + +##### min= + +``` +@schema/validation min=minimum +``` + +Where: +- `minimum` (`int` | `float` | `string` | `bool` | `list` ) — the smallest allowable value (inclusive); see [Starlark: Comparisons](https://github.com/google/starlark-go/blob/master/doc/spec.md#comparisons) for how different values compare. + +This keyword is equivalent to: + +``` +@schema/validation ("a value greater than or equal to", lambda v: v >= minimum) +``` + +##### min_len= + +``` +@schema/validation min_len=(minimum) +``` + +Where: +- `minimum` (`int`) — the shortest allowable length (inclusive) + +This keyword is equivalent to: + +``` +@schema/validation ("length greater than or equal to", lambda v: len(v) >= minimum) +``` + +##### not_null= + +``` +@schema/validation not_null=(nullable) +``` + +Where: +- `nullable` (`bool`) — whether `null` is a valid value. + +When present, this rule is checked before any other; this allows other rules (including custom rules) to assume the value is not null. + + +This keyword is equivalent to: + +``` +@schema/validation ("not null", lambda v: v != None) +``` + +##### one_not_null= + +Requires a map to contain exactly one (1) item that has a non-null value. + +``` +@schema/validation one_not_null=([key0, key1,...] | True) +``` + +Where: +- `keyX` (`string`) — keys within the annotated map to check for null/non-null; other map items are ignored. +- `True` — check all contained map items for null/non-null. + +Each item that is referenced by this rule is almost always annotated as nullable: + +```yaml +#@schema/validation one_not_null=["item1", "item2", "item3"] +map: + #@schema/nullable + item1: "" + #@schema/nullable + item2: "" + #@schema/nullable + item3: "" + otherConfig: true +``` + +This keyword is equivalent to: + +``` +@schema/validation ("exactly one child not null", lambda v: assert.one_not_null([key0, key1,...])) +``` +(see also [@ytt:assert.one_not_null()](lang-ref-ytt-assert.md#assertone_not_null)) + +##### one_of= + +Requires value to be exactly one of the given enumeration. + +``` +@schema/validation one_of=[val0, val1, ...] +``` + +Where: +- `[val1, val2, ...]` (list/tuple of any type) — the exhaustive set of valid values. + +This keyword is equivalent to: + +``` +@schema/validation ("one of [val0, val1, ...]", lambda v: v in [val0, val1, ...]) +``` diff --git a/site/content/ytt/docs/v0.49.x/lang-ref-ytt-struct.md b/site/content/ytt/docs/v0.49.x/lang-ref-ytt-struct.md new file mode 100644 index 000000000..a85bc898e --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/lang-ref-ytt-struct.md @@ -0,0 +1,203 @@ +--- +aliases: [/ytt/docs/latest/lang-ref-ytt-struct] +title: Struct module +--- + +## Overview + +The `@ytt:struct` module provides functions for constructing and deconstructing [`struct`](lang-ref-structs.md) values. + +To use these functions, include the `@ytt:struct` module: + +```python +load("@ytt:struct", "struct") +``` + +--- +## struct.decode() + +Deconstructs a given value into plain/Starlark values, recursively. + +```python +struct.decode(struct_val) +``` + +- `struct_val` ([`struct`](lang-ref-structs.md)) — the value to decompose. + - `struct` values are converted into `dict` values where each attribute in the `struct` becomes a + key on the `dict`. + - if the value of an attribute is a `struct`, it is likewise converted to a `dict`. + - all other values are copied, as is. + +### Example + +```python +load("@ytt:struct", "struct") + +foo = struct.encode({"a": [1,2,3,{"c":456}], "b": "str"}) +bar = struct.decode(foo) + +bar["a"] # <== [1, 2, 3, {"c": 456}] +``` + +--- +## struct.encode() + +Makes a `struct` of a given value, recursively. + +```python +struct.encode(value) +``` + +- `value` ([`dict`](lang-ref-dict.md) | [`list`](lang-ref-list.md) | scalar) — the value to encode. + - `dict` values are converted into `struct`s where each key in the `dict` becomes an attribute on the `struct`. + Keys of the items in `dict` values must be strings. + - if a `dict` or `list` contains a value that is a `dict`, it is likewise converted to a `struct`. + +Notes: + +- `encode()` cannot encode functions nor [YAML Fragments](lang-ref-yaml-fragment.md). If you wish to make a struct that + contains attributes that hold these types, consider [`make()`](#structmake). + +### Example: Data structure from a dictionary + +```python +load("@ytt:struct", "struct") + +d = struct.encode({"a": [1,2,3,{"c":456}], "b": "str"}) +d.a # <== [1, 2, 3, c: (struct...)] +d.a[3].c # <== 456 +bar["b"] # <== "str" +``` + +## struct.make() + +Instantiates a `struct` based on the key/value pairs provided. + +```python +struct.make(key1=value1, key2=value2, ...) +``` + +- `keyN` (keyword argument name) — becomes the name of the Nth attribute in the constructed + `struct`. +- `=valueN`(`any`) — becomes the value of the Nth attribute in the constructed `struct`. + +Notes: + +- `make()` does not modify `values` in any way (e.g. if `valueN` is a dictionary, it is + _not_ converted into a `struct`). To recursively build a hierarchy of `struct`s from `dict`, + `list`, and scalars, see [`struct.encode()`](#structencode). + +### Example 1: Scalar values + +For visually pleasing collections of fields +```python +load("@ytt:struct", "struct") + +consts = struct.make(version="0.39.0", service_name="prometheus") + +consts.version # <== "0.39.0" +consts.service_name # <== "prometheus" +``` + +### Example 2: Data structures + +Dictionaries values remain instances of `dict`. +```python +load("@ytt:struct", "struct") + +consts = struct.make(service={"version": "0.39.0", "name": "prometheus"}) + +consts.service["version"] # <== "0.39.0" +consts.service["name"] # <== "prometheus" +# const.service.version # Error! "dict has no .version field or method" +``` + +### Example 3: Nested structs + +Nested invocations of `make()` to retain dot expression access. +```python +load("@ytt:struct", "struct") + +consts = struct.make(service=struct.make(version="0.39.0", name="prometheus")) + +consts.service.version # <== "0.39.0" +consts.service.name # <== "prometheus" +``` +See also: [`struct.encode()`](#structencode) to convert all `dict` values to `struct`s, recursively. + +### Example 4: Collection of functions + +"Export" a set of functions from a library file. + +`urls.star` +```python +load("@ytt:struct", "struct") + +def _valid_port(port): +... +end + +def _url_encode(url): +... +end + +urls = struct.make(valid_port= _valid_port, encode= _url_encode, ...) +``` +```yaml +#@ load("urls.star", "urls") + +#@ if/end urls.valid_port(...): +encoded: #@ urls.encode("encode_url") +``` +--- +## struct.make_and_bind() + +Binds one or more function(s) to a `struct`, making them method(s) on that struct. +This allows `struct`s to carry both data and behavior related to that data. + +```python +struct.make_and_bind(receiver, method_name1=function1, ...) +``` + +- `receiver` ([`struct`](lang-ref-structs.md)) — "object" to attach the function(s) to. +- `method_nameN` (keyword argument name) — the name that callers will specify to invoke the method. +- `functionN` ([`function`](lang-ref-def.md)) — the function value (either the name of a `function` or a `lambda` expression) + that will be bound to `receiver` by the name `method_nameN`. + - the first parameter of `functionN` is `receiver`, implicitly. + - the remaining parameters of `functionN` become the parameters of `receiver.method_nameN()` + +Notes: + +- Binding is useful for cases where a commonly desired value is a calculation of two or more + values on `receiver`. + +### Example 1: Binding a function value + +```python +load("@ytt:struct", "struct") + +conn = struct.make(hostname="svc.example.com", default_port=1022, protocol="https") + +def _url(self, port=None): + port = port or self.default_port + return "{}://{}:{}".format(self.protocol, self.hostname, port) +end + +conn = struct.make_and_bind(conn, url=_url) + +conn.url() # ==> https://svc.example.com:1022 +conn.url(8080) # ==> https://svc.example.com:8080 +``` + +### Example 2: Binding a lambda expression + +```python +load("@ytt:struct", "struct") + +_conn_data = struct.make(hostname="svc.example.com", default_port=1022, protocol="https") + +conn = struct.make_and_bind(_conn_data, url=lambda self, port=None: "{}://{}:{}".format(self.protocol, self.hostname, port or self.default_port)) + +conn.url() # ==> https://svc.example.com:1022 +conn.url(8080) # ==> https://svc.example.com:8080 +``` diff --git a/site/content/ytt/docs/v0.49.x/lang-ref-ytt-template.md b/site/content/ytt/docs/v0.49.x/lang-ref-ytt-template.md new file mode 100644 index 000000000..b813ee923 --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/lang-ref-ytt-template.md @@ -0,0 +1,71 @@ +--- +aliases: [/ytt/docs/latest/lang-ref-ytt-template] +title: Template Module +--- + +## `@template` Functions + +The `@ytt:template` module provides a function that can be used to update templates. + +To use these functions, include the `@ytt:template` module: + +```python +#@ load("@ytt:template", "template") +``` + +The functions exported by this module are: +- [template.replace()](#templatereplace) + +### template.replace() +Replaces the existing yaml node with the yaml node(s) provided or returned from a function call, of the same type. +Underscore (`_`) is the conventional replacement key, though any key can be used. + +``` +template.replace(node) +``` + +* `node` ([`yamlfragment`](lang-ref-yaml-fragment.md)) — yaml fragment that will replace the existing node + +**Examples:** + +##### Add a new item to the `labels` mapping +```yaml +#@ load("@ytt:template", "template") + +labels: + another-label: true + _: #@ template.replace({"key2": "value2"}) +``` +results in: +```yaml +labels: + another-label: true + key2: value2 +``` + +Notice that the argument to the function entirely replaces the `_` map item. + +##### Use a function instead of providing the item(s) inline +```yaml +#@ load("@ytt:template", "template") + +#@ def my_labels(): +#@ return {"key2": "value2"} +#@ end + +labels: + another-label: true + key-will-disappear: #@ template.replace(my_labels()) +``` +results in: +```yaml +labels: + another-label: true + key2: value2 +``` + +Notice that the argument to the `replace` function entirely replaces the `key-will-disappear` map item. + +So, regardless of the node's key or value, `template.replace` will overwrite it with the argument provided. + +See also: [Replace example](/ytt/#example:example-replace) in the ytt Playground. diff --git a/site/content/ytt/docs/v0.49.x/lang-ref-ytt-version.md b/site/content/ytt/docs/v0.49.x/lang-ref-ytt-version.md new file mode 100644 index 000000000..0b70659f2 --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/lang-ref-ytt-version.md @@ -0,0 +1,25 @@ +--- +aliases: [/ytt/docs/latest/lang-ref-ytt-version] +title: Version module +--- + +Available in v0.26.0+ + +Version module provides a way to assert on minimum ytt binary version in your configuration. It could be placed into a conditional or just at the top level within `*.star`, `*.yml` or any other template file. + +Example configuration directory may look like this: + +- `config/` + - `0-min-version.star`: contents below + - `deployment.yml` + - `other.yml` + +```python +# filename starts with '0-' to make sure this file gets +# processed first, consequently forcing version check run first +load("@ytt:version", "version") + +version.require_at_least("0.26.0") +``` + +Note that ytt sorts files alphanumerically and executes templates in order. Most of the time it's best to check version first before processing other templates, hence, in the above example we've named file `0-min-version.star` so that it's first alphanumerically. diff --git a/site/content/ytt/docs/v0.49.x/lang-ref-ytt.md b/site/content/ytt/docs/v0.49.x/lang-ref-ytt.md new file mode 100644 index 000000000..fb8777d63 --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/lang-ref-ytt.md @@ -0,0 +1,272 @@ +--- +aliases: [/ytt/docs/latest/lang-ref-ytt] +title: Built-in ytt Library +--- + +## General modules + +### assert + +See [@ytt:assert module docs](lang-ref-ytt-assert.md). + +### data + +See [Data Values](ytt-data-values.md) reference for more details + +```python +load("@ytt:data", "data") + +data.values # struct that has input values + +# relative to current package +data.list() # ["template.yml", "data/data.txt"] +data.read("data/data.txt") # "data-txt contents" + +# relative to library root (available in v0.27.1+) +data.list("/") # list files +data.read("/data/data.txt") # read file +``` + +### ip + +Parse and inspect Internet Protocol values. + +(available in v0.37.0+) + +```python +load("@ytt:ip", "ip") + +# Parse IP addresses... +addr = ip.parse_addr("192.0.2.1") +addr.is_ipv4() # True +addr.is_ipv6() # False +addr.string() # "192.0.2.1" + +addr = ip.parse_addr("2001:db8::1") +addr.is_ipv4() # False +addr.is_ipv6() # True +addr.string() # "2001:db8::1" + +# Parse CIDR notation into an IP Address and IP Network... +addr, net = ip.parse_cidr("192.0.2.1/24") +addr.string() # "192.0.2.1" +addr.is_ipv4() # True +addr.is_ipv6() # False +net.string() # "192.0.2.0/24" +net.addr().string() # "192.0.2.1" +net.addr().is_ipv4() # True +net.addr().is_ipv6() # False + +addr, net = ip.parse_cidr("2001:db8::1/96") +addr.string() # "2001:db8::1" +addr.is_ipv4() # False +addr.is_ipv6() # True +net.string() # "2001:db8::/96" +net.addr().string() # "2001:db8::" +net.addr().is_ipv4() # False +net.addr().is_ipv6() # True +``` + +### math + +Module math is a Starlark module of math-related functions and constants. + +> ⚠️ **Non-Deterministic Results** \ +> The functions in this module do not guarantee bit-identical results across CPU architectures. +> Using one or more of these functions may produce different output on different machines. + + +The module defines the following functions: + +_(All functions accept both int and float values as arguments.)_ + +```python +math.ceil(x) # the ceiling of x, the smallest integer greater than or equal to x. +math.copysign(x, y) # a value with the magnitude of x and the sign of y. +math.fabs(x) # the absolute value of x as float. +math.floor(x) # the floor of x, the largest integer less than or equal to x. +math.mod(x, y) # the floating-point remainder of x/y. The magnitude of the result is less than y and its sign agrees with that of x. +math.pow(x, y) # x**y, the base-x exponential of y. +math.remainder(x, y) # the IEEE 754 floating-point remainder of x/y. +math.round(x) # the nearest integer, rounding half away from zero. + +math.exp(x) # e raised to the power x, where e = 2.718281… is the base of natural logarithms. +math.sqrt(x) # the square root of x. + +math.acos(x) # the arc cosine of x, in radians. +math.asin(x) # the arc sine of x, in radians. +math.atan(x) # the arc tangent of x, in radians. +math.atan2(y, x) # atan(y / x), in radians. + # The result is between -pi and pi. + # The vector in the plane from the origin to point (x, y) makes this angle with the positive X axis. + # The point of atan2() is that the signs of both inputs are known to it, so it can compute the correct + # quadrant for the angle. + # For example, atan(1) and atan2(1, 1) are both pi/4, but atan2(-1, -1) is -3*pi/4. + +math.cos(x) # the cosine of x, in radians. +math.hypot(x, y) # the Euclidean norm, sqrt(x*x + y*y). This is the length of the vector from the origin to point (x, y). +math.sin(x) # the sine of x, in radians. +math.tan(x) # the tangent of x, in radians. + +math.degrees(x) # Converts angle x from radians to degrees. +math.radians(x) # Converts angle x from degrees to radians. + +math.acosh(x) # the inverse hyperbolic cosine of x. +math.asinh(x) # the inverse hyperbolic sine of x. +math.atanh(x) # the inverse hyperbolic tangent of x. +math.cosh(x) # the hyperbolic cosine of x. +math.sinh(x) # the hyperbolic sine of x. +math.tanh(x) # the hyperbolic tangent of x. + +math.log(x, base) # the logarithm of x in the given base, or natural logarithm by default. + +math.gamma(x) # the Gamma function of x. + +math.e # The base of natural logarithms, approximately 2.71828. +math.pi # The ratio of a circle's circumference to its diameter, approximately 3.14159. +``` + +### regexp + +```python +load("@ytt:regexp", "regexp") + +regexp.match("[a-z]+[0-9]+", "__hello123__") # True + +regexp.replace("[a-z]+[0-9]+", "__hello123__", "foo") # __foo__ +regexp.replace("(?i)[a-z]+[0-9]+", "__hello123__HI456__", "bye") # __bye__bye__ +regexp.replace("([a-z]+)[0-9]+", "__hello123__bye123__", "$1") # __hello__bye__ +regexp.replace("[a-z]+[0-9]+", "__hello123__", lambda s: str(len(s))) # __8__ + +# example of passing the "dot matches newline" flag and using replace to extract a single match from a multiline input string +input_str = "\\ multline string\n\nconst (\n\t// Value is what we want to scrape\n\tValue = 12\n)\n\nfunc main() {..." +regexp.replace("(?s).*Value = ([0-9]+).*", input_str, "$1") # 12 +``` + +See the [RE2 docs](https://github.com/google/re2/wiki/Syntax) for more on regex syntax. Note that flags such as multiline mode are passed in the pattern string as in the [golang regexp library](https://pkg.go.dev/regexp/syntax). + +When calling `replace` you can pass either a string or a lambda function as the third parameter. When given a string, `$` symbols are expanded, so that `$1` expands to the first submatch. When given a lambda function, the match is directly replaced by the result of the function. + +While `match` and `replace` are currently the only regexp verbs supported, it is possible to mimic `find` by using `replace` to replace all its input with a capture group (see example above). + +### struct + +See [@ytt:struct module docs](lang-ref-ytt-struct.md). + +### url + +```python +load("@ytt:url", "url") + +url.path_segment_encode("part part") # "part%20part" +url.path_segment_decode("part%20part") # "part part" + +url.query_param_value_encode("part part") # "part+part" +url.query_param_value_decode("part+part") # "part part" + +url.query_params_encode({"x":["1"],"y":["2","3"],"z":[""]}) # "x=1&y=2&y=3&z=" +url.query_params_decode("x=1&y=2&y=3;z") # (DEPRECATED) # {"x":["1"],"y":["2","3"],"z":[""]} +url.query_params_decode("x=1&y=2&y=3&z") # {"x":["1"],"y":["2","3"],"z":[""]} + +u = url.parse("http://alice:secret@example.com") +u.string() # "http://alice:secret@example.com" +u.user.name # "alice" +u.user.password # "secret" +u.user.string() # "alice:secret" +u.without_user().string() # "http://example.com" +``` + +As of v0.38.0, including semicolons in query strings is deprecated behavior. +Allowing semicolons in query strings can [lead to cache poisoning attacks](https://snyk.io/blog/cache-poisoning-in-popular-open-source-packages/). Authors should use ampersands (i.e. `&`) exclusively to separate parameters. + +### version + +`load("@ytt:version", "version")` (see [version module doc](lang-ref-ytt-version.md)) + +--- +## Hashing modules + +### md5 + +```python +load("@ytt:md5", "md5") + +md5.sum("data") # "8d777f385d3dfec8815d20f7496026dc" +``` + +### sha256 + +```python +load("@ytt:sha256", "sha256") + +sha256.sum("data") # "3a6eb0790f39ac87c94f3856b2dd2c5d110e6811602261a9a923d3bb23adc8b7" +``` + +--- +## Serialization modules + +### base64 + +```python +load("@ytt:base64", "base64") + +base64.encode("regular") # "cmVndWxhcg==" +base64.decode("cmVndWxhcg==") # "regular" +``` + +### json + +```python +load("@ytt:json", "json") + +json.encode({"a": [1,2,3,{"c":456}], "b": "str"}) +json.encode({"a": [1,2,3,{"c":456}], "b": "str"}, indent=3) + +json.decode('{"a":[1,2,3,{"c":456}],"b":"str"}') +``` +As of v0.35.0, `json.encode()` with `indent` argument encodes result in multi-line string. + +### toml + +As of v0.38.0. + +```python +load("@ytt:toml", "toml") + +toml.encode({"a": [1,2,3,456], "b": "str"}) # 'a = [1, 2, 3, 456]\nb = "str"' +toml.encode({"metrics": {"address":"", "grpc_histogram": False}}, indent=4) + # '[metrics]\n address = ""\n grpc_histogram = false\n' + +toml.decode("[plugins]\n [plugins.cgroups]\n no_prometheus = false") + # {"plugins": {"cgroups": {"no_prometheus": False}}} +``` + + +### yaml + +```python +load("@ytt:yaml", "yaml") + +yaml.encode({"a": [1,2,3,{"c":456}], "b": "str"}) +yaml.decode('{"a":[1,2,3,{"c":456}],"b":"str"}') +``` + +--- +## Library module + +See [Library specific docs](lang-ref-ytt-library.md). + +--- +## Overlay module + +See [Overlay specific docs](lang-ref-ytt-overlay.md). + +--- +## Schema Module + +See [Schema specific docs](lang-ref-ytt-schema.md). + +--- +## Template module + +See [Template specific docs](lang-ref-ytt-template.md). diff --git a/site/content/ytt/docs/v0.49.x/lang.md b/site/content/ytt/docs/v0.49.x/lang.md new file mode 100644 index 000000000..d46f0a34c --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/lang.md @@ -0,0 +1,34 @@ +--- +aliases: [/ytt/docs/latest/lang] +title: Language +--- + +## Overview + +Templating language used in `ytt` is a slightly modified version of [Starlark](https://github.com/google/starlark-go/blob/master/doc/spec.md). Following modifications were made: + +- requires `end` keyword for block closing + - hence no longer whitespace sensitive (except new line breaks) +- does not allow use of `pass` keyword + +See [full Starlark specification](https://github.com/google/starlark-go/blob/master/doc/spec.md#contents) for detailed reference. + +## Types + +- NoneType: `None` (equivalent to null in other languages) +- Bool: `True` or `False` +- Integer: `1` +- Float: `1.1` +- [String](lang-ref-string.md): `"string"` +- [List](lang-ref-list.md): `[1, 2, {"a":3}]` +- Tuple: `(1, 2, "a")` +- [Dictionary](lang-ref-dict.md): `{"a": 1, "b": "b"}` +- [Struct](lang-ref-structs.md): `struct.make(field1=123, field2="val2")` +- [YAML fragment](lang-ref-yaml-fragment.md) +- [Annotation](lang-ref-annotation.md): `@name arg1,arg2,keyword_arg3=123` + +## Control flow + +- [If conditional](lang-ref-if.md) +- [For loop](lang-ref-for.md) +- [Function](lang-ref-def.md) diff --git a/site/content/ytt/docs/v0.49.x/outputs.md b/site/content/ytt/docs/v0.49.x/outputs.md new file mode 100644 index 000000000..6c9548312 --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/outputs.md @@ -0,0 +1,17 @@ +--- +aliases: [/ytt/docs/latest/outputs] +title: Outputs +--- + +ytt supports three different output destinations: + +- stdout, which is default + - All YAML documents are combined into one document set. Non-YAML files are not printed anywhere. +- output files, controlled via `--output-files` flag (v0.28.0+) + - Output files will be added to given directory, preserving file names. + - Example: `ytt -f config.yml --output-files tmp/`. +- output directory, controlled via `--dangerous-emptied-output-directory` flag + - Given directory will be _emptied out_ beforehand and output files will be added preserving file names. + - Example: `ytt -f config.yml --dangerous-emptied-output-directory tmp/ytt/`. + +If you want to control which files are included in the output use `--file-mark 'something.yml:exclusive-for-output=true'` flag to mark one or more files. diff --git a/site/content/ytt/docs/v0.49.x/schema-validations-cheat-sheet.md b/site/content/ytt/docs/v0.49.x/schema-validations-cheat-sheet.md new file mode 100644 index 000000000..e20254858 --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/schema-validations-cheat-sheet.md @@ -0,0 +1,214 @@ +--- +aliases: [/ytt/docs/latest/schema-validations-cheat-sheet] +title: "Schema Validations Cheat Sheet" +--- + +_(For a more detailed guide, see [Writing Schema Validations](how-to-write-validations.md).)_ + +{{< table class="cheat-sheet" >}} + +(th)**Use Case**(/th) +(th)**Syntax**(/th) +(tr) +(td) +##### Required string +_usage: [Using the empty/zero value](../how-to-write-validations#using-the-emptyzero-value)_\ +_reference: [`min_len=`](../lang-ref-ytt-schema#min_len)_ +(/td) +(td) +```yaml +#@schema/validation min_len=1 +username: "" +``` +(/td) +(/tr) + +(tr) +(td) +##### Required integer +_usage: [Using the empty/zero value](../how-to-write-validations#using-the-emptyzero-value)_\ +_reference: [`min=`](../lang-ref-ytt-schema#min)_ +(/td) +(td) +```yaml +#@schema/validation min=1 +replicas: 0 +``` +(/td) +(/tr) + +(tr) +(td) +##### Required array +_usage: [Using the empty/zero value](../how-to-write-validations#using-the-emptyzero-value)_\ +_reference: [`min_len=`](../lang-ref-ytt-schema#min_len)_ +(/td) +(td) +```yaml +#@schema/validation min_len=1 +responseTypes: +- "" +``` +(/td) +(/tr) + +(tr) +(td) +##### Required map +_usage: [mark as "nullable" and "not_null"](../how-to-write-validations#mark-as-nullable-and-not_null)_ \ +_reference: [`@schema/nullable`](../lang-ref-ytt-schema#schemanullable) and [`not_null=`](../lang-ref-ytt-schema#not_null)_ + +(/td) +(td) +```yaml +#@schema/nullable +#@schema/validation not_null=True +credential: + name: "" + cloud: "" +``` +(/td) +(/tr) + +(tr) +(td) +##### Ensure string minimum length +_reference: [`min_len=`](../lang-ref-ytt-schema#min_len)_ +(/td) +(td) +```yaml +#@schema/validation min_len=8 +password: "" +``` +(/td) +(/tr) + +(tr) +(td) +##### Ensure string exact length +(/td) +(td) +```yaml +#@schema/validation min_len=8, max_len=8 +password: "" +``` +(/td) +(/tr) + +(tr) +(td) +##### Ensure a min value +(/td) +(td) +```yaml +#@schema/validation min=3 +replicas: 5 +``` +(/td) +(/tr) + +(tr) +(td) +##### Ensure a max value +(/td) +(td) +```yaml +#@schema/validation max=5 +replicas: 3 +``` +(/td) +(/tr) + +(tr) +(td) +##### Ensure a value between min and max +(/td) +(td) +```yaml +#@schema/validation min=1, max=65535 +port: 1024 +``` +(/td) +(/tr) + +(tr) +(td) +##### Enumeration +_usage: [enumerations](../how-to-write-validations#enumerations)_\ +_reference: [`one_of=`](../lang-ref-ytt-schema#one_of)_ +(/td) +(td) +```yaml +#@schema/validation one_of=["aws", "azure", "vsphere"] +provider: "" +``` +(/td) +(/tr) + +(tr) +(td) +##### Exactly one is specified +_usage: [mutually exclusive config](../how-to-write-validations#mutually-exclusive-sections)_\ +_reference: [`one_not_null=`](../lang-ref-ytt-schema#one_not_null)_ +(/td) +(td) +```yaml +#@schema/validation one_not_null=["oidc", "ldap"] +config: + #@schema/nullable + oidc: + client_id: “” + #@schema/nullable + ldap: + host: “” +``` +(/td) +(/tr) + +(tr) +(td) +##### Conditionally run validations +_usage: [conditional validations](../how-to-write-validations#conditional-validations)_\ +_reference: [`@schema/validation ... when=`](../lang-ref-ytt-schema#schemavalidation)_ +(/td) +(td) +```yaml +#@data/values-schema +--- +service: + type: LoadBalancer + #@schema/validation min_len=1, when=lambda _, ctx: ctx.parent["type"] == "LoadBalancer" + name: "" +``` +(/td) +(/tr) + + +(tr) +(td) +##### Custom description of valid value +_usage: [writing custom rules](../how-to-write-validations#writing-custom-rules)_\ +_reference: [@ytt:assert](../lang-ref-ytt-assert)_ +(/td) +(td) +```yaml +#@ load("@ytt:assert", "assert") + +#@schema/validation ("a non-blank name", assert.min_len(1)) +username: "" +``` +(/td) +(/tr) + +(tr) +(td) +##### Disable validations flag +(/td) +(td) +```yaml +$ ytt ... --dangerous-data-values-disable-validation +``` +(/td) +(/tr) + +{{< /table >}} diff --git a/site/content/ytt/docs/v0.49.x/security.md b/site/content/ytt/docs/v0.49.x/security.md new file mode 100644 index 000000000..c8868cf88 --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/security.md @@ -0,0 +1,34 @@ +--- +aliases: [/ytt/docs/latest/security] +title: Security +--- + +## Vulnerability Disclosure + +If you believe you have found a security issue in `ytt`, please privately and responsibly disclose it by following the directions in our [security policy](/shared/docs/latest/security-policy). + +## Attack Vectors + +This section is a work-in-progress... + +- malicious template input + - input tries to exhaust cpu/mem/disk resources + - A: how does it affect go-yaml? ... https://en.wikipedia.org/wiki/Billion_laughs_attack + - input tries to use YAML tagging to initialize custom objects (std yaml concern) + - A: TBD + +- malicious template code + - code tries to load file contents from sensitive locations + - A: templating is constrained to seeing only files explicitly specified by the user via -f flag, and does not follow symlinks. Unless user is tricked to provide sensitive files as input, template code is not able to access it. In other words, template runtime does not have facilities to access arbitrary filesystem locations. + - code tries to exfiltrate data over network + - A: template runtime does not have facilities to access network. + - code tries to exhaust cpu/mem/disk resources + - A: there are currently no resource constraints set by ytt itself for cpu/mem/disk. currently cpu can be pegged at 100% via an infinite loop. function recursion is also possible; however, it will be contstrained by Go stack space (and will exit the program). + - code tries to produce YAML that exhausts resources + - A: TBD + - meltdown/spectre style attacks + - A: TBD + +- CLI output directory + - user is tricked to set --output-files flag to a sensitive filesystem location + - A: template output is constrained to stdout or specified output directory via --output-files flag. if user is tricked to point --output-files flag to a sensitive filesystem location such as ~/.ssh/, attacker may be able to write templates (for example ~/.ssh/authorized_keys) that can be intepreted by the system as configuration/executable files. diff --git a/site/content/ytt/docs/v0.49.x/strict.md b/site/content/ytt/docs/v0.49.x/strict.md new file mode 100644 index 000000000..43a8b7e39 --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/strict.md @@ -0,0 +1,48 @@ +--- +aliases: [/ytt/docs/latest/strict] +title: Strict YAML +--- + +## Overview + +ytt includes strict YAML subset mode that tries to remove any kind of ambiguity in user's intent when parsing YAML. + +Unlike full YAML, strict subset: + +- only supports specifying nulls as "" or `null` +- only supports specifying bools as `false` or `true` +- only support basic int and float declarations + - prefix, suffix, octal notation, etc are not supported +- requires strings with whitespace to be explicitly quoted +- requires strings with colon to be explicitly quoted +- requires strings with triple-dash (document start) to be explicitly quoted + +## Example + +Non-strict: + +```bash +$ echo 'key: yes' | ytt -f- +key: true +``` + +Strict: + +```bash +$ echo 'key: yes' | ytt -f- -s +Error: Unmarshaling YAML template 'stdin.yml': yaml: Strict parsing: Found 'yes' ambigious (could be !!str or !!bool) +``` + +To fix error, explicitly make it a string: + +```bash +$ echo 'key: "yes"' | ytt -f- -s +key: "yes" +``` + +or via YAML tag `!!str`: + +```bash +$ echo 'key: !!str yes' | ytt -f- -s +key: "yes" +``` diff --git a/site/content/ytt/docs/v0.49.x/yaml-primer.md b/site/content/ytt/docs/v0.49.x/yaml-primer.md new file mode 100644 index 000000000..cf95a8a6d --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/yaml-primer.md @@ -0,0 +1,228 @@ +--- +aliases: [/ytt/docs/latest/yaml-primer] +title: "YAML and Annotations" +--- + +## Overview + +Templates in `ytt` — rather than being YAML-like text files with templating injected _in_ — are well-formed YAML files with templating annotated _on_. + +It's reasonable to have expectations of how to write templates, especially if we have prior experience. But in the shift from a free-form text file to a structured YAML document set, some of those expectations are foiled. And when they are, it can be rather frustrating. + +If we take a few minutes to cover: + +- [what YAML is](#what-is-yaml) — its format and constituent parts; and +- [how to annotate YAML in `ytt`](#annotating-yaml), + +it can mitigate much of that frustration. + +## What is YAML? + +YAML is a tree of nodes. Each node is of one of these six types: + +- a **Document Set** (which is list of **Documents**) +- a **Map** that has **Map Items** +- an **Array** that has **Array Items** + +We'll start from the top of the tree... + +### Documents and Document Sets + +A file containing text in YAML format is parsed into a **Document Set**: a collection of **Document**s. + +![a file containing two YAML documents](/images/ytt/yaml-primer/two-docs.jpg) + +_Figure 1: A file, parsed into a **document set** (dotted blue) containing two **documents** (solid blue)._ + +Note: `---` marks the start of a new document. + +The corresponding tree of nodes looks something like this: + +![](/images/ytt/yaml-primer/two-docs-ast.jpg) + +_Figure 2: Corresponding tree: this document set has two documents._ + +A given document has exactly one (1) value. It is usually either: +- a **Map**, +- an **Array**, or +- a **Scalar** + +We'll look at each of these, in turn. Maps are most common... + +### Maps and Map Items + +A "map" is collection of key-value pairs. Each pair is referred to as a "map item". + +![](/images/ytt/yaml-primer/two-maps-and-items.jpg) + +_Figure 3: Each document has a **map** (dotted green) each containing two **map items** (solid green)._ + +The complete tree of nodes looks like this: + +![](/images/ytt/yaml-primer/two-maps-and-items-ast.jpg) + +_Figure 4: Corresponding tree: each document has a map for its value; each map has two items._ + +Let's zoom in on the first document, and explicitly reveal the contents of a map item: + +![](/images/ytt/yaml-primer/map-items-ast.jpg) + +_Figure 5: Each map item has a **key** and a **value**; here, both map items' key is a string and their value is an integer._ + +Like documents, a map item has exactly one (1) value. And just like documents, it's either: +- a **Map**, +- an **Array**, or +- a **Scalar** + +Let's see what it looks like for a **map item** to have a **map**, itself. + +![](/images/ytt/yaml-primer/map-item-has-map-ast.jpg) + +_Figure 6: The document's value is a map with two map items; the second map item has a key of "metadata" and a value that is another map._ + + +### Arrays and Array Items + +An "array" is a list of "array items" (zero-indexed). + +Like documents (and map items), an array item has exactly one (1) value. Once more, it's either: +- a **Map** +- an **Array**, or +- a **Scalar** + +![](/images/ytt/yaml-primer/map-item-has-array-ast.jpg) + +_Figure 7: The **map item** "foo" has an **array** (dotted gold) for a value; that array has three **array items** (solid gold)._ + +Let's see what it looks like when an **array item**'s value is a **map**... + +![](/images/ytt/yaml-primer/array-item-has-map-ast.jpg) + +_Figure 8: "foo" has an **array** with one **array item** whose value is a **map** containing two **map item**s: `name` and `factor`._ + +We've seen scalars in use above. Let's cover them explicitly. + +### Scalars + +A "scalar" is a fundamental value. YAML supports: +- integers (e.g. 13, 42, 137, 32767) +- floating point numbers (e.g. 1.0, 3.14, 137.03599913) +- strings (e.g. "", "fine structure", "$ecr3t") +- boolean values: `true` or `false` +- `null` (when a value is omitted, that implies `null`; `~` == `null`) + +### Summary + +In short: + +- a YAML file is parsed into a **Document Set** which is merely a list of **Document**s. +- a given **Document** has one _value_. +- Both **Map**s and **Array**s are nothing more than collections of items. + - a **Map Item** has a _key_ and a _value_ + - an **Array Item** has a _value_ +- a _value_ (whether held by a document, map item, or array item) is either a **Map**, an **Array**, or a **Scalar**. + +With this structure in mind, we can now look into how `ytt` annotates it. + +## Annotating YAML + +A `ytt` "annotation" is a YAML comment of a specific format; it attaches behavior to a node. + +For example: + +![](/images/ytt/yaml-primer/map-item-with-value.jpg) + +_Figure 9: A YAML file with a `ytt` annotation._ + +Can be understood to mean that the value of `foo` is the result of evaluating the expression `13 + 23 + 6`. + +The diagram reveals how the annotation is attached to the YAML tree: + +![](/images/ytt/yaml-primer/map-item-ann-with-value-ast.jpg) + +_Figure 10: The annotation (dotted black) is attached to the **map item** (solid green)._ + +This is a document, whose value is a map, that contains a single map item. To the right of the map item is an annotation that contains an expression that, when evaluated, becomes a _value_. The annotation is attached to the map item. + +### How an Annotation Finds its Node + +When attaching annotations, `ytt` follows these two rules (in order): +1. the annotation is attached to the value-holding node **on its left**, if there is one; otherwise, +2. the annotation is attached to the value-holding node **directly below** it. + +In the example above, the value-holding node to the left of the annotation is the map item. + +As noted in the summary [above](#summary), "value-holding" nodes are: +- **Document**s, +- **Map Item**s, and +- **Array Item**s. + +#### Example: Annotating Map Items + +Let's see these rules at play: + +![](/images/ytt/yaml-primer/overlay-ann-on-doc-and-map.jpg) + +_Figure 11: A templated document with three annotations._ + +We'll ignore _what_ these annotations mean, for now, and focus on _where_ they attach. There are three annotations in total. + +Visualizing the YAML tree can help: + +![](/images/ytt/yaml-primer/overlay-ann-on-doc-and-map-ast.jpg) + +_Figure 12: Each annotation attaches to the value-holding node to its left or bottom._ + +Taking each annotation in turn: +- `@overlay/match by=overlay.all`: + - is in the document set, but that is not a value-holding node + - has a **document** node just below it and so is attached to _that_ node. +- `@overlay/replace`: + - has no node to its left; + - has a **map** just below it but maps are _not_ value-holding; + - the next node is a **map item**, and so attaches to _that_ node. +- `@ 13 + 23 + 6`: + - has a **map item** to its left, and so attaches to _that_ node. + - there _is_ another **map item** below it (i.e. `bar: true`), but a home has already been found for this annotation. + +#### Example: Annotating an Array + +When an **array item** contains a **map**, it can be tricky to know which item a particular annotation attaches to. + +In this example, we reinforce the value of knowing the two rules that determine [how an annotation finds its node](#how-an-annotation-finds-its-node). + +![](/images/ytt/yaml-primer/overlay-ann-on-array-and-map.jpg) + +_Figure 13: An overlay ensuring each book has been marked as reviewed. The blank line after the array item is intentional._ + +There are three annotations in this example. The first one clearly annotates the document. But what about the next two? + +Visualizing the layer, it can start to become more clear... + +![](/images/ytt/yaml-primer/overlay-ann-on-array-and-map-ast.jpg) + +_Figure 14: The newline after the **array item** ensures the last annotation attaches to the **map item**._ + +Taking each annotation in turn: +- the first `@overlay/match by=overlay.all`: + - has a **document** node just below it and so is attached to _that_ node. +- the second `@overlay/match by=overlay.all`: + - has a **map item** (`books:`), but that is _above_ this annotation; + - `books:` contains an **array**, but arrays are _not_ value-holding nodes; + - that array's **array item** (denoted by `-`) is just below the annotation and so is attached to _that_ node. +- finally, `@overlay/match missing_ok=True`: + - if this _had_ been the same line as the **array item**, it _would_ have also attached to the same node as the previous annotation (but it is not, and is it won't); + - the next node is a **map**, but again, maps are _not_ value-holding; + - the next node is a **map item** (`reviewed:`), so the annotation attaches to _that_ node. + + +## Further Exploration + +While we've thoroughly covered the fundamentals here, these concepts only become real with use: + +- put your understanding to the test by picking an example from [the Playground](https://carvel.dev/ytt/#example:example-demo) and tweaking it in various ways to see what _actually_ happens. +- take a simple example and run it through `ytt --debug` and study the `### ast` section of the output. Note: + - in this output, annotations are referred to as `meta`. + - "AST" is short for "Abstract Syntax Tree" which is the technical name for the trees we've been visualizing in this guide. + - we recommend making small changes and study how it modifies the "AST" +- if you'd like to obsess over YAML itself, look no further than the [YAML spec](https://yaml.org/spec/1.2/spec.html). diff --git a/site/content/ytt/docs/v0.49.x/ytt-data-values.md b/site/content/ytt/docs/v0.49.x/ytt-data-values.md new file mode 100644 index 000000000..123d1e62f --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/ytt-data-values.md @@ -0,0 +1,446 @@ +--- +aliases: [/ytt/docs/latest/ytt-data-values] +title: Data Values +--- + +## Overview + +A `ytt` run can be configured by supplying custom Data Values. + +_(For a high-level overview of `ytt`, see [How it works](how-it-works.md).)_ + +## Declaring Data Values + +Typically, Data Values are declared in a schema file. See the [Using Data Values](how-to-use-data-values.md) guide for more details. + +Note: `ytt` continues to support declaring Data Values without schema for backwards-compatibility. However, due to the significantly improved support for catching configuration errors that schema brings, it is the recommended method for doing so. + +## Configuring Data Values + +Data Values can be configured in one of two ways: + +- on the command-line via the family of [command-line `--data-value...` flags](#configuring-data-values-via-command-line-flags), +- in a ["Data Values Overlay" document](#configuring-data-values-via-data-values-overlays) and included via the `--file` flag, + +### Configuring Data Values via command line flags + +The `--data-value...` family of command-line flags provides a means of configuring Data Values from: + +- the command-line, multiple values: + - [`--data-values-file`](#--data-values-file) — multiple values from a file, directory, URL, or standard in. +- the command-line, directly, one at a time: + - [`--data-value`](#--data-value) — single value as a string. + - [`--data-value-yaml`](#--data-value-yaml) — single value decoded a YAML. + - [`--data-value-file`](#--data-value-file) — a single value to the contents of a file, URL, or standard in. +- OS environment variables: + - [`--data-values-env`](#--data-values-env) — all OS variables with a named prefix, all as strings. + - [`--data-values-env-yaml`](#--data-values-env-yaml) — all OS variables with a named prefix, decoded as YAML. + +**Quick Example** + +```bash +export STR_VALS_key6=true +export YAML_VALS_key7=true + +$ cat dev/values.yml +key1: values.yml-key1 +key2: + original: from values.yml + +$ ytt \ + --data-value key1=val1-arg \ # overrides key1 from dev/values.yml + --data-value-yaml key2.nested=123 \ # merges into key2 from dev/values.yml + --data-value-yaml 'key3.other={"nested": true}' \ # decoded to a map + --data-value-file key4=client.crt \ # contains contents of client.crt + --data-values-env STR_VALS \ # decodes STR_VALS_key6 to a string + --data-values-env-yaml YAML_VALS \ # decodes YAML_VALS_key7 to a boolean + --data-values-file dev/ \ # finds and uses dev/values.yml + --data-values-inspect +``` + +yields: +```yaml +key2: + original: from values.yml + nested: 123 +key6: "true" +key7: true +key1: val1-arg +key3: + other: + nested: true +key4: +``` + +**Notes** +- the `--data-value...` flags can be repeated multiple times and used in any combination. The do not necessarily combine in the order supplied on the command-line; see [Data Values merge order](#data-values-merge-order) for details. +- Where schema is used, `--data-value...` flags can only _override_ values allowed by schema. Once schema is in use, _all_ Data Values must be declared in schema files. +- Where schema is _not_ used, (as of v0.34.0) Data Values can be _declared_ via `--data-value...` flags (previously, these flags could only _override_ previously declared values). This is useful for ad-hoc templating situations (usually a shell script one-liner) involving one or two Data Values — where type checks and validations are less useful. + + +#### `--data-values-file` + +Sets one or more Data Values from a plain YAML file, a directory containing YAML files, an HTTP URL, or standard input. + +``` +--data-values-file [@lib:]path +``` + +- `path` — one of: a filesystem path; an HTTP URL; or the dash character `-`. + - filesystem path can either be the path to a single YAML file _or_ a directory that (recursively) contains one or more files that have either the `.yaml` or `.yml` extension. If a directory is specified, all non-YAML files are ignored. + - HTTP URL is expected to resolve to a stream of one or more YAML document(s). + - `-` is the UNIX convention for referring to standard input. When specified, stdin is expected to contain a plain YAML document. + - The `-` file can only be used once in a `ytt` invocation. + - this input is given the name `stdin.yml`. +- `@lib:` — (optional) names the library whose data values to configure (details [below](#setting-library-values-via-command-line-flags)) rather than the root library. + +Not to be confused with [`--data-value-file`](#--data-value-file) which sets the value of exactly one Data Value. + +**Plain YAML Data Values** + +Regardless the source, the inputs for this flag must be plain YAML. It must not contain `ytt` templating (i.e. comments that start with `#@`. + +When multiple YAML documents are given, they are merged: +- each document is merged into the previous; +- map values are merged (values for entries that were previously defined are merged within the same key, recursively; values for new keys are added to the map); +- array values are replaced (last wins) + +**Examples:** + +_Example 1: Single File_ + +`prod-values.yml` +```yaml +domain: example.com +client_opts: + timeout: 10 + retry: 5 +``` + +```console +$ ytt ... --data-values-file prod-values.yml +``` + +sets all three Data Values: +- `domain=example.com` (a string value) +- `client_opts.timeout=10` (an integer value) +- `client_opts.retry=5` (an integer value) + +_Example 2: Directory_ + +See https://github.com/carvel-dev/ytt/tree/develop/examples/data-values-directory for a complete example and explanation. + +_Example 3: HTTP URL_ + +Given https://raw.githubusercontent.com/carvel-dev/ytt/develop/examples/data-values/values-file.yml + +```console +$ ytt --data-values-file https://raw.githubusercontent.com/carvel-dev/ytt/develop/examples/data-values/values-file.yml --data-values-inspect +``` +yields +```yaml +nothing: something +string: str +bool: true +int: 124 +new_thing: new +``` + +(Note the merged value of `int:`) + +#### `--data-value` + +Sets a single Data Value to a _string_ value. + +``` +--data-value [@lib:]key=value +``` + +- `key` — name of Data Value. Use dot notation for nested values (e.g. `key2.nested=val`) +- `value` — value to set (always interpreted as a string) +- `@lib:` — (optional) specify library whose data values to configure (details [below](#setting-library-values-via-command-line-flags)) +- examples: `instance.count=123`, `key=string`, `input=true`, all set to strings + +#### `--data-value-yaml` + +Sets a single Data Value to a YAML-parsed value. + +``` +--data-value-yaml [@lib:]key=value +``` +- `key` — name of Data Value. +- `value` — value to set (decoded as a YAML value) +- `@lib:` — (optional) specify library whose data values to configure (details [below](#setting-library-values-via-command-line-flags)) +- examples: `instance.count=123` sets as integer, `key=string` as string, `input=true` as bool + +#### `--data-value-file` + +Sets a single Data Value to the _contents_ of: a given file, an HTTP URL, or standard input. + +``` +--data-value-file [@lib:]key=path +``` +- `key` — name of Data Value. +- `path` — one of: a file path; an HTTP URL; or the dash character `-`. + - `-` is the UNIX convention for referring to standard input. The `-` file can only be used once in a `ytt` invocation. +- `@lib:` — (optional) specify library whose data values to configure (details [below](#setting-library-values-via-command-line-flags)) + +This flag is particularly useful for loading multi-line string values from files such as private and public key files, certificates, etc. + +Not to be confused with [`--data-values-file`](#--data-values-file) which sets the values of multiple Data Values. + + +#### `--data-values-env` + +Sets one or more Data Values to _string_ values from OS environment variables that start with the given prefix. + +``` +--data-values-env [@lib:]PREFIX +``` +- `PREFIX` — the literal prefix used to select the set of environment variables from which to configure Data Values. +- `@lib:` — (optional) specify library whose data values to configure (details [below](#setting-library-values-via-command-line-flags)) + +**Setting Environment Variables** +- for nested values, use double-underscore (i.e. `__`) in environment variable names to denote a "dot". + +**Examples** + +_Example: With Nested Values_ + +```shell +$ env +... +DVAL_key1=blue +DVAL_key2__nested=1337 +... +``` + +```console +$ ytt ... --data-values-env DVAL +``` + +would set two Data Values: +- `key1=blue` (the string "blue") +- `key2.nested="1337"` (the string "1337") + +#### `--data-values-env-yaml` + + +Sets one or more Data Values to the YAML-decoded values from OS environment variables that start with the given prefix. + +``` +--data-values-env-yaml [@lib:]PREFIX +``` + +- `PREFIX` — the literal prefix used to select the set of environment variables from which to configure Data Values. +- `@lib:` — (optional) specify library whose data values to configure (details [below](#setting-library-values-via-command-line-flags)) +- for nested values, use double-underscore (i.e. `__`) in environment variable names to denote a "dot". + +**Examples** + +_Example: With Nested Values_ + +```shell +$ env +... +DVAL_key1=blue +DVAL_key2__nested=1337 +... +``` + +```console +$ ytt ... --data-values-env DVAL +``` + +would set two Data Values: +- `key1=blue` (a string value) +- `key2.nested=1337` (an integer value) + + +### Configuring Data Values via Data Values Overlays + +Data Values can also be configured via a specific kind of `ytt` Overlay. + +A Data Values Overlay is a YAML document annotated with `@data/values`. + +```yaml +#@data/values +--- +key1: val1 +key2: + nested: val2 +key3: val3 +key4: +``` + +Note: +- `data.values` is a [`struct`](lang-ref-structs.md). + +#### Splitting Data Values Overlays into multiple files + +Available in v0.13.0. + +It's possible to split data values into multiple files (or specify multiple data values in the same file). `@ytt:data` library provides access to the _merged_ result. Merging is controlled via [overlay annotations](lang-ref-ytt-overlay.md) and follows same ordering as [overlays](lang-ref-ytt-overlay.md#overlay-order). Example: + +`values-default.yml`: + +```yaml +#@data/values +--- +key1: val1 +key2: + nested: val2 +key3: +key4: +``` + +`values-production.yml`: + +```yaml +#@data/values +--- +key3: new-val3 +#@overlay/remove +key4: +#@overlay/match missing_ok=True +key5: new-val5 +``` + +Note that `key4` is being removed, and `key5` is marked as `missing_ok=True` because it doesn't exist in `values-default.yml` (this is a safety feature to prevent accidental typos in keys). + +`config.yml`: + +```yaml +#@ load("@ytt:data", "data") + +first: #@ data.values.key1 +third: #@ data.values.key3 +fifth: #@ data.values.key5 +``` + +Running `ytt -f .` (or `ytt -f config.yml -f values-default.yml -f values-production.yml`) results in: + +```yaml +first: val1 +third: new-val3 +fifth: new-val5 +``` + +See [Multiple data values example](/ytt/#example:example-multiple-data-values) in the online playground. + + +### Data Values merge order + +Data values are merged in following order (latter one wins): + +1. default values from `@data/values-schema` files +2. `@data/values` overlays (same ordering as [overlays](lang-ref-ytt-overlay.md#overlay-order)) +3. `--data-values-file` (same ordering as [overlays](lang-ref-ytt-overlay.md#overlay-order)) +4. `--data-values-env` specified values (left to right) +5. `--data-values-env-yaml` specified values (left to right) +6. `--data-value` specified value (left to right) +7. `--data-value-yaml` specified value (left to right) +8. `--data-value-file` specified value (left to right) + +_(When configuring libraries, the [data values merge order](#library-data-values-merge-order), is the same, even if through different mechanisms.)_ + +--- +## Library data values + +Available in v0.28.0+ + +Each library may specify data values which will be evaluated separately from the root level library. + +### Setting library values via files + +To override library data values, add [`@library/ref`](lang-ref-ytt-library.md#libraryref) annotation to data values YAML document, like so: + +```yaml +#@library/ref "@lib1" +#@data/values +--- +key1: val1 +key2: val2 + +#@library/ref "@lib1" +#@data/values after_library_module=True +--- +key3: val3 +``` + +The `@data/values` annotation also supports a keyword argument `after_library_module`. If this keyword argument is specified, given data values will take precedence over data values passed to the `.with_data_values(...)` function when evaluating via the [library module](./lang-ref-ytt-library.md). + +### Setting library values via command line flags + +Data value flags support attaching values to libraries for use during [library module](./lang-ref-ytt-library.md) evaluation: + +```bash +export STR_VALS_key6=true # will be string 'true' +export YAML_VALS_key6=true # will be boolean true + +$ ytt -f . \ + --data-value @lib1:key1=val1-arg \ + --data-value-yaml @lib2:key2.nested=123 \ # will be int 123 + --data-value-yaml '@lib3:key3.other={"nested": true}' \ + --data-value-file @lib4:key4=/path \ + --data-values-env @lib5:STR_VALS \ + --data-values-env-yaml @lib6:YAML_VALS + --data-values-file @lib6:/path +``` + +```console +export STR_VALS_key6=true +export YAML_VALS_key7=true + +$ cat dev/values.yml +key1: values.yml-key1 +key2: + original: from values.yml + +$ ytt \ + --data-value @lib1:key1=val1-arg \ # overrides key1 from dev/values.yml + --data-value-yaml @lib1:key2.nested=123 \ # merges into key2 from dev/values.yml + --data-value-yaml '@lib2:key3.other={"nested": true}' \ # decoded to a map + --data-value-file @lib2:key4=client.crt \ # contains contents of client.crt + --data-values-env @lib2:STR_VALS \ # decodes STR_VALS_key6 to a string + --data-values-env-yaml @lib1:YAML_VALS \ # decodes YAML_VALS_key7 to a boolean + --data-values-file @lib1:dev/ # finds and uses dev/values.yml +``` +sends the following Data Values to library `lib1`: +```yaml +key2: + original: from values.yml + nested: 123 +key7: true +key1: val1-arg +``` +and the following Data Values to library `lib2`: +```yaml +key6: "true" +key3: + other: + nested: true +key4: +``` + + +### Library Data Values merge order + +For a given library instance, data values are merged in following order (latter one wins): + +1. default values from schema: + 1. `@data/values-schema` files within the library + 2. `@data/values-schema` files [externally referenced in](#setting-library-values-via-files). + 3. given through [`instance.with_data_values_schema()`](lang-ref-ytt-library.md#instancewith_data_values_schema) + 4. `@data/values-schema` files [externally referenced in `after_library_module=True`](#setting-library-values-via-files). +2. values from data value sources: + 1. `@data/values` overlays within the library (same ordering as [overlays](lang-ref-ytt-overlay.md#overlay-order)) + 2. `@data/values` overlays [externally referenced in](#setting-library-values-via-files) + 3. specified using [`instance.with_data_values()`](lang-ref-ytt-library.md#instancewith_data_values) + 4. `@data/values` overlays [externally referenced in `after_library_module=True`](#setting-library-values-via-files) + 5. `--data-values-file` specified files [referenced in](#setting-library-values-via-command-line-flags) (same ordering as [overlays](lang-ref-ytt-overlay.md#overlay-order)) + 6. `--data-values-env` specified values [referenced in](#setting-library-values-via-command-line-flags) (left to right) + 7. `--data-values-env-yaml` specified values [referenced in](#setting-library-values-via-command-line-flags) (left to right) + 8. `--data-value` specified value [referenced in](#setting-library-values-via-command-line-flags) (left to right) + 9. `--data-value-yaml` specified value [referenced in](#setting-library-values-via-command-line-flags) (left to right) + diff --git a/site/content/ytt/docs/v0.49.x/ytt-overlays.md b/site/content/ytt/docs/v0.49.x/ytt-overlays.md new file mode 100644 index 000000000..e3bec98f7 --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/ytt-overlays.md @@ -0,0 +1,77 @@ +--- +aliases: [/ytt/docs/latest/ytt-overlays] +title: Overlays +--- + +## Overview + +Configure patch-like edits in `ytt` via "Overlays". + +_(For a high-level overview of `ytt`, see [How it works](how-it-works.md).)_ + +Sometimes it makes more sense to patch some YAML rather than template it. + +For example, when: +- the file should not be edited directly (e.g. from a third party); +- the edit will apply to most or all documents; or +- the specific variable is less commonly configured. + +## Example +Given a sample target YAML file: + +`config.yml` +```yaml +--- +id: 1 +contents: +- apple +--- +id: 2 +contents: +- pineapple +``` +... this overlay ... + +`add-content.yml` +```yaml +#@ load("@ytt:overlay", "overlay") + +#@overlay/match by=overlay.all, expects="1+" +--- +contents: +- pen +``` + +_read as..._ +1. _"match all YAML documents, expecting to match _at least_ one;"_ +2. _"within _each_ document, merge the key named `contents`;"_ +3. _"append an array item with the content `"pen"`"_ + + +... when processed by `ytt` ... + +```console +$ ytt -f config.yml -f add-content.yml +``` + +... produces ... + +`config.yml` _(edited)_ +```yaml +id: 1 +contents: +- apple +- pen +--- +id: 2 +contents: +- pineapple +- pen +``` + +## Next Steps + +- [Overlay example](/ytt/#example:example-overlay-files) in the ytt Playground to try it out, yourself. +- [ytt Library: Overlay module](lang-ref-ytt-overlay.md) for reference of all overlay annotations and functions. +- [Data Values vs Overlays](data-values-vs-overlays.md) for when to use one mechanism over the other. +- [Primer on `ytt` Overlays](/blog/primer-on-ytt-overlays/) for a screencast-formatted in-depth introduction to writing and using overlays. diff --git a/site/content/ytt/docs/v0.49.x/ytt-playground-screenshot.png b/site/content/ytt/docs/v0.49.x/ytt-playground-screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..92882a6a988872731881e277e62a5f810a1d4616 GIT binary patch literal 363691 zcmd41V|b-cwmuxI!;Y;EI%dbVZQI$glijh=vDvY0+qP}nZ_k`LGryVhzs_|&zWdYK zRduhbx~rb5weIH$m6sJmfWv_U0Rcgf5EoVi0fAu!0fBIaf%;RD&15YJ0)k<0AtWR( zAtXd3?*K5hur>h!Q4dT=hE_(^tL?h9#es#Ty(5r3YJc4L{YU~ve7BJXDJ%k+FAhB-l*mpH{@_A+Lom>;&z3}eFxHp@XnGisRI(m=b}$eO6P!~;e@Y(^yO|I0neFXg zolUU=I~||Ba$B8Gck2%lIS{tDe2+I;M-l!;c|DZFpOb^vKq)u%w=64|=^tM55zXK> z^n#wIn1ZCVjT}9T0}n{hukQ_?;{w+Q6ToB~tG(;scglf8AUp5e@)nYk;YTJep4s+Y zun`Gu;;~j9@;H{j#-7s0i&}!rP$xv(KZSA-F z&}?4^`VcHc$#wSeQoZWK+Xa^lVnhTJ!Osu^@#c%0#QUj|F4d)jd>pRgsWeX< zbc#-f+FuIjOi}_el>|D)C>I$m@gx@fcJB|y-UXu$in#^d!;d!vB9aYP_mh|Q1-}b} z9n^ITvWO@I0|+k;;o0RA16spRM+uR?#lj9w_j5EGMH`%rpREa+;3oX95F@X-odW?@kU@8_^i)F16|q+RYi6Nap?!XZO#tK3uD$xX2aa8sY@%Gmg0o%oqI0OH5RT~XC_M<;!@BjG7!JI2n-`za%?zdT@erif}4%BDKJ@IJJSASWO!DE(%FZ6fp3EXQ#*P+ zGCS_v*0_pwBW~`JG+TufnfnXBI+7bvMHAc0BZG@eKJ%tyB(t!E`#TTR; z#1y0&6byffT^*4d0TrPe@jXH$LMEa&VpLk4oGA_kHPC`k347ML0JcEIl%y7{me!H| zmhm8b>@h{JR$qOHhMdNS23ljHYRxo-f+VCzn3N>J?WbF!TT)Y3m?^qZy*`4$xMBMk zXJ1RtKu^KgK~G|z>VQj}O%Q&!tZAb@lF_6Az!*5z(cL|0(6v2jAD@@GOFW1}fmFy_ z$Y(}w27btX2$e*X1gl1=MzF|QVPX5N@mnL0E6L;ci`KPK?lNvoZsknrOwr83HipV? zmEf1Q8_aFsZKYmsUZ!3OUKG!^x3sslx2LzkSD06uJ9=M<;Ev!RzM#Q4!L^|nqp~2w zqtc+Xkv0kLkfp@!NT~cKo+*e)j;WPzk@ugfV~|zXVdrn+ZxVA85$5mHG>#U}7*bLw zD{3*1F|RhSJi^h4IFsa+?hy5%9r$iaoJgWd@gSW}R6#*Nt4sSK4n~_pheD-5%t!(& zRU!^0ktl&E)vZ{f*sX}Jm^VXdfp83SY2IOw;+d3b^tt=#^0|y@(qo0YlKX?Z!29>R$y0h9ajXC= zZ6-GWB>>B4$*{@vw?*Az(I}RIwQ0Hqw{ef*j|Y z_oP>k-=v?Vf>m{mkj>x*zAMdjVpL*&qN8f8Dx>Q8Le)LuDeA(cwXh9|b;UB6)|XY( zhFKRwmuVOL2XjxZSEg4rL^woGJP-WPsEsI0{7z16&V6Z7PpqUma=A? z)`QlGrs3vuXV0yPbAq#t1K(@AYxc|PmRV!7?FmRZ^hx;1!PY((vh$Y9{&VyL=h?LB zw4L=m{~P=J=A+vTL#g6|vKP&##McV>$fEwaMxPfSNFS*W&X4Dha8ORrXV6M;XD~Sk zI>=*4bVyogT*xFSAUq=y2YwBPC9WoF3;F@h0mI;D%6wH6+=%{DVKJpqu?=Z64W4vhiNG+FicP>$~;7DO6M&*nFV zVHP9J>qF~v>pY&~o)4EP>sD2bmV|Yau6lAMb|>9)fh@IkUtaBBg&vU#Mhg!MfgD>N z+3!S~-F5!A!DEQ?CJe@X#?p8ToL(<5H{w^na`Gg_-npQYPY0vT({pz_H$?Zn|7Xd zQofePHpG!oILi6ZBT(VfGt*d69@6q@cBpQvU0=n26u-=R71qiEl%dpQRZ~>zRpUy- zC$cBMrMo*X>|0-Q+&k7EHcy&md@7(SFgtc{_he|+udIZ(VLF~{wLG=tOJydB(&6ga zmL?TfTIvIaTEHs$^xPfi4^MVQM+Y*=?+ac`rw?v045dt|=Tg^N9B7YLq|-cCkM8jo zJy+^iv^b5qENx?Keu2*+5J1ySW zZt~1}HhLm>aJ@Rc*sd+M0o(Q3KUcu#q07Okz-j%d{AqQ@NYC?D@)`+F zc+Loz30fbAcBcL9)2^onC!#4RxG7A@-^um(U_EcHEqZ3hi|fpa%u;5hXGJF6v(!3c z+@=}wI2|1q++6f0>}@SGIX@pv&PFf4KV5zO zrZ5N@Y%alu1p(V|0ho$lfdbyb^akWdy834JR&Hp%WBS8bgUZ9)0>91XO{5F}Tk3nU zdj(f$w}`iS5Xunm63c>-JxC%OaR!V~_M)&JD`}T$b6m$7DU8dj4zxFB=nm+QRDqPO zMMtHQB`EV9rS#_T=0B_wtO%!Ir=6!}=aYUQGZ?cOG7Y2>$@nB!q?9LjHY6`IT5uRU zFTMoH#GRY8hCkFJV&cU4OpxT_$`c z{VXSX(SJbBkuPRQuxp127eA!@JnBo{tv=l4+@(O}B{ztJRGM>HJdbfPZ(obPexE$NF>eH-Tf2|PWA9%7lCCA&lZk)^Ydj*^n2 zrl$0`UW*2mPM6AA%91Sr^7{b2W}r-0LRUj} z#a2hm_SAKi*)~0CBI#n@G$UU1jD66xEi6hXbZl50eZJF$Pt)Q4#=J5+LSq&&J`luv z>#&1U-xLJC}xzrj|>7 zTmH^Kapo)%6`@*tnJ!N_@1VPGjp(bNO!;GfQA?dva}xC_E{?74aVO`-Q|__zt>mc3 zsVeRZXHG`F|7*~e%nj|aY(8Da1wPM3WADZ94IcZ-g2r~h)ra(FA?O~_E3&(~NuS-^ zNzQKaDQUF*e=MD5SdULd49ZJ=``-9Y@<^H0#Rpx2xCVQv%I@W62&HGbyOjdzb&u8N2r}n3i zFka~oEt>KVzNwR}5v76GkSwBXS}aK_j21F(ikrAG8qu#w@i8%IaU>FsqYT4_`#;yr zo<#yB`g!wJ_iw2JOQ7d+=Upw-ElVt5t(eU#PsdK9j+~F#u(cV=Sg4rkScZS_SSOlh z8l%*cPR)#t46pQvE~?JUPU!ado>A_6+mm)cvDUk-ijJM(jSzj`@nKd6;m z{@vr_^brAi=G!LlTi+qXxJWzMut_pN^tW!jMxA$rtF^MWrkAdu%Fg`nioNal<8{De z*`>ddy~We}l2gc!@DA>$BY8Q|-t92b6tP|A?-~n(bJ1VSm`y*2+>^MH9%+C#eZt+w z+i|(WJHxS|anVwQ36TlWs_}w6eeM%=-$A8qHN}fC5E3yr-1=?P_-Z))5-9Cn_B{t`|EM8?gS6XFV zzE^z&Ko^6HLEQ#P2FXP#N2>Cb;ZXyXxicTxt#-EdHYh^nNDKWkpQT&*T)hm~jQCDD z54i~6nY?qZ<^%hq7X77|0lc)No7Ekd&)H46x6@B?$7ZwiqNN^s^$W_2=p7O^F>H{X zj_*Vt->h?h`=42t3)gM+w0fTXj4SSN$)laYap zW-Vv(5ozv*LQ4NcjroZtpLaj_(1k1Pr>rTbfuPyt&49ow#2x}+At)>VEgp8B|1ld9 z3n9E$wNAG(U}2M%v7Q@opJ*U7)d0--rqG~m;Nj28W}b0n1MO-8R83}6sh~kjNp8!}GEidq_=eZyD z(7DhnT?%J$j=(33PEPaD>$8Mlona_qx8PNv|H5y^viYinDi$Iq?Uu3^zt*Z{CU?r) zKS%wFK7)%yh2P?#elYYb{ZSUX+jrKcebDBIx&}Ef!qksXQ7&~jsw4-SMNrJ?G_ZxY z^Y;Gb8MB>!c##w*Q`g3J#45DI6sXhT|1q#uo>&n<(8x9OxV2IIT8;zy6mbyEh?^-~p>%-Yb)%ajd`Wk$F`8b&M=J+emq$maa6IX~cpQ=9G8ui~g&cVbeLDv~4;Srd#Bv|<2CL#lUb zGinDa8_FrF?J{YTB`lJxqje?qr_Q!B8)lVvjqvUfERj4i4`~a35+&ejH=x*T-==-B zpywfRJso^OVS0G?NJhw_EPGmCavR(JJHg1tu<~nNw^J#&fAAR-0f9X`XR3)~8Q&^*A=@Mg zu&Ar~%H7_4SoFBsK~ZGT;&CKv-r1~u!f}qfgtqLqNxDR@!=atAcDYogInvI%I`7t0 zpb*%2V`FD_rR%Qe*7*T4xHWRsWxlTd;MOC&71vc9f1HX5IE2?#rQb>1*`#ZGRCj+` zc5yadH`(ovz5)(^0xLcbJ5TkFT;IjsDn485Uc|Z$tH|&mF?%zva3Q#QAby1u35^jy z#}EuD?lGgmqKCBjy+m#J(g^{(lJ%(=PMs#4Xw?zV2+p`Ca293hW%c|D^CEwm__1xH z_@npohzbqZk_#1hj;W6A(x4?G^?8@U|H*p8tRI(nmOJhC>?9o#?L_QQo4MTH+%?=< zpK_ldpT!^^zEHt+kR-rzz%j${ArF78Cufe!3Q-9UmNFO5{U(-A_1oWM&X}=AZtr9t z>ATx^ODg;CHk5p7$0{}orAnRI3YE(_tl2!JwK;*#NV7s{flU((&ZDyjzZRPT09@;hE;)najV(&jbYSGG<^@ln}Y zxvW~4Z9O+tjsjmqU#OuCgT_Q5L^~sS2ml$B-Zelf!PIH% z6#Bzt+fZ5=t?cu4-`q{nOLg}TiBS`I{xW{mx+;q zk^Ap}|55d?F8>16_!pFsk>%f@f2sNhl$-uEOEpgq)2_2zWwB^hFJ!g6031xK6|@L&g!q=MiMZ9^dTC zL@87{mbPR&BXJR}CBNm)TR<~4L!%I82Cxz_ZIHL2Q}TvTATwPl2;e@5=6?dp+ZQ^j z*%rq|-YeXVEAJ~F8+fg{>lRj@p`}1sl{0b|7Y7jFbdGTX&CxK7F)&t;ps0< z|IPH3CGN@&lC{TKg8lDK|E>O?L-|W-IwaQI7EO=M{~5p;2;z^!*>9mw@fR=uyJ?4^ zFYjLq)A;>gWc=@dBQ!90%OkWx-;95!@P9#dY5WvGm>^jz_&3h~7eKZW3`0?lQaY52 z-~WUC|2G|Gpir#r;dW{NSHSSp@OtZVJ}*HuBhW+0eLqaZeOdrpQ=0<4q-!pWYjAXsi(_%ucB+a9Cv@ zy@;|>P$lqYvc|S$rDue*EE)F1euznr%iIK1%tVQBk>5H`Hrk`kxgF1N zq;t8#CQS?<=1Z}4qg*5Z$JTSQ1-+D=ZC|0Lq^9ov%=qAo&)`B*%0Mw9kl_brxf~hv z8_)bWtLx64erE*|5{KRfZ!f6Q9q+&^Wn~ccMDZkm$>D`vy<~F-StOTbEnHewC8_GR5sO?_H#j-=#Oc-^3bT74UubC>5CfK@$F@xA6mxc$)>C=-Z_{JibxB8C~M^F51X zWRdO|N{3Z%qVcY6z~Srpr)d$86zADn(XaaLvgx|=68G+|di9@YWVSolxqVhn1)Nsa zX3(>qg-)EBm?Cdh)SzH1Yh|V8MN18{dR{pY*xVrET-PM8gU~>3rtecx9yApHYsigd zEwPI=8E$cVg1Gklo1xv-4hHc-jT&Kf0E|Ha#W75oN6f9Vx@@mr6q8ZbY-unfQ|j(B@wSMabDg zsFu^cK(}WQg)xPUeM9_%2S0dZjTNhLSEZRKQ^;i-O&a#`4IfTv+Z6uEE=t>zaOcji z$yA3;B#S599-58B#|?2K<=_RU+MsbgbymY>fsv(LjVX3yMt-arrYu!sjr_3NgbxSK z@7hHp*9etoNiVO_5hfZG;p3xjYe>uSyfM4^MWpOd{xfZR^NCUQtoLqz)^|!~>HV(g z#hEGaFHgZNjMJPqlnMKo>U(2tG=_vW^jyOuCG3p5P!S$~g$s@e*V0se@8ogyor${p z5%k>;_R&;>L<2_n_E&?Ys%k7QYX%Ru4=d01T{fJIPI~OsEl<04)0F?X0=5z{s(2TU z#RI7i>6@%OygNA6h^Ya2D?XBU;`g_9i6+VcM?>FXoGvuR8`XWNR%3y&5G+M`Oi!f8 z$1>vXY6LDi!vo8ydc<|$v(%7D;aw;s>Ab`;mRE850(8< ztu2ci3^ySpgYv+HVuARdP!bGL!kbXwI3S(8LcVPov94icm6blV#25-W7ninZ=js-) zjaRx6GLE}*3fOOh6vRAweo&IPzueEtxTTS5A4Zg@&7w^-OJ+8303Pk+ds3V6`7NZC zffWYoZ>k&^qu=mtTyz%FMG7ETD5Z?@Y}6gx4+44!W0BQN?>!M9!3%(2F-Uae6B5mdXmnI6^(O@$PSse zRvme6{7IjM>Tcn31$~Ww-dc9lFmlI1Y!xXkIoMfGu?r66Z0bKbW zZnqV~Ma9LxFo~jwa}Hsw@9pl(qW-mI{wq#7`Qo-HXZaal^$LXiEYW;|9s)m##d~9Y z{amR;C3~%w_ip0-Vo&7h?%TX~gc_uA zQ>{vHk$V2c%|XXIn;szT8dke-1J3Nm)v2@HQ4v2-Gd9s+4zErtJ-om9$GXC9&pb$<{_XVa+wYzCt?3Y3ZIUzUk^5uaK&~zOC|z zD>=oc1VYAVL+u~MlYuCVz|^eV7m+&`5ZX-Z>4<_lH9_gM$xDFCAm_QkrUH8R_wR79z*Y0>jdp*;i;8+xQ`&;@p_=h z)pd)9-@j08CY@ct&99C_{Bx=Q@jzXr^B=m!Q}t3qL8$BgDsSFsaFkU>!^pWw-Jf^y zZ$uGUwon~Enav;KY7p=SLEP*0s)iB4Bm+Dm2i(_ssg!dkaFbpI6om$Y^yX z`gHgO!J?j6z*p1wW4(b|Z9nIILHcZL)7RU9-FM?=1p39$_=z+mJX|PQx_R5<*EEPj z?XRS);4_Eo7_nB+4bwfnFTyh+8p&^0JP3%@MN4OVF~KvViFRPX6wzI?T`}Um#rF`{ zsQ03-75v0d{+}YrxeysFHOUnmfpkOulKbWf{pUiDsfxy>Ny03m#iW2_`S^NpuA5th zyY`Kae8l&{;%`V=HQ$zUO3$s+DsUJqVps(38r_AB4e)*#^xeC*Jm%_l3^k2|mmuXB8nm@ObGkpmyJc0E6eOq=(&-O`+4 zcIh*-deKtyZ9DTGQmcT}i0Jq_#mIuA0B*h5I4abl?dz@aYS+Y$8%(d7t$d4c#tT{d zjo;i$8C>p7%j>*xM^h1TM>Eia@@@=xmPp&G^TRIsp`q^>TzL2RWpQbixe=C#~bV@`vvs2j zsmAea$xS+o1-7@>bE$^2GG$_0e?k}U^CGroa4C-s3L))hw=3(F_e?XQt zAgH8r7CEJ(D@WRFwdl!%nLsXU*KQ2*r1#3pm~b_8&WQi!N=Kcg{G$l%^@AJsy8mWt zqyI+xL6rOPo+{nvS&a@1S9E!R^TIwg_)uq zE#bA0>vjgjYV-H?4{}jsDcbNG*vR&9yC^Qq0yr|g}Qy2 zh}U2`c;TS3!G-2sopd=Vf!Skk^(v1Rj5{N>6m1~}(NV^we2(&lJkRLT%ni~LtwYa z^J{E_aD`q~qL2^0rpNhdLfxL`%qUfsfqmt6oNYb~w#{+U^#%Ly%}XR9<+@d? z%?|Dt5u?B+N+l3#{*p;HsX$s%gTw+3SAioFu|vr z6qj-Ylif~i|IW&50lmAjJO&lEmq{HFC+uI zF!{-)`#MsLHS%pc<}I!Z=Ge-AGy(N#7nr}3R_akv;r5omNBrZ=g{M0&YoRi|Cbw)A zNPWtvE(!<%{hII#&!@;?4R}H1!$*cg9ldxtalPJJePcpos*5wT5fbcZ-s{R?#lY~8 z@&ZXlCc2^o9c9;?6#AEI=I`c~gzrE{u18IIROKieM0jvTS6DZ7mE{o?O&u?Plt_tn zWz+0J|DHHmbgb)l)&59_r<$@orn$XcZ`y2PPGfY$F|IK52G6ezVVrj3@tvTL%aAO6 zL1kSPeg5oBT+mtYvJDSs@BXM)X9Fas4JQ2owPY{7dz9bADq?FmmYAJ+Opk5V4)l#W ze$4!=uY=XN4B&+L=}ylP(Uf*Wx{pA?ZXn`6RAV4NS3n zF?m(5e^Aokb)bR&Mwm6h#uj`~2gfOEj@O4+LRS)nHP(`GM5t&}L#lI)!22eaGwk zNi0%utyc(NY}HP$ZT*p@u`^#w*B-`PK3(xbH^2=bwOCv3ilBoR7I9IvnMwl`3I-^8 zHwx3&e^eA6wv!;em#1htpKDoKEY};K`j)Mu1YO4su8I~NpF}If?E?ltUbS)&$DE4fg0woh1$BFs6JngF%N8KMxk2d3-4_s- zKoC7i1I5ZC2jfMENSq@51`p_T4<@V3a8#7U`tduE)S`~8TJ~Vl^L3Qjg~PbM0s^pc zh&2^M>7ADJThvhs3fA1b$D!J;Qs)nkTU5N8S~CH$=gIv}@UZXaU^wLMQDfj@_6K7U z#%2{kePl(W7yF@2%)~kfK~tWO>xr9O5G?SiEJ&a4o0UcbMCI_LI8IbP|BRHmM6o0vFFy~gyy+h9?I*KTfk8s5}eM`m1RlG|0qpZ=u1<3B6YH(e=;4*9VS47~jy zFxh#2J~&cYS!jP+1TA;avM%3O3lh8nq|=Hq`+8m-8C+{zXv75_3@{G7p4`9zca3M7 zVY0koq-wvHM}zIN7JM9>5KR#t;)oEfT>B<|@?$OrE7)i9NFHdx3S|4}{#I9ivSP34 z|M?oPKqbv(*KmOHbozL4R?>c~Mh^^!{8TT+H)Y`!eJlG{yAzP?&;_w-ij7G#9{`i=v)S29G;NGyqqAjCsK+x9@aZoXW9-o0#`6v8w4B5h+c3S8~A5;AcXX) zFByezC6h^NSm=AXlGfg*Gtx|5>L3qckV&nWP=`tJ=vm>!*6y=s)i*UM^Q~Py6*|Gb z)*td+O0C}rmN~vXD{iaO%=*LckwJR{Cq|Z+O6s{R?m?}+46Euf1q9^urDWh2zTFKn zOBR%esyi}3b>h9sHg?olH+ zFgkK=A9+guTqa9F9i>w_O^}pO%R!1!nPB2L{5p#NN#%g})*pX}ie9k3ZTgJj^Gh5o z$Gz(tKB0g3w%M1uo#`b*%qe||yTPYIzP>Ouq4}KEdlzk=6`r~%_-Bo{f=oLNIcC)R z-B1Ta7`xjvCZ83Lt@pwF1Z(LJC&H0~5g{j=;a8_n6U{H*^rw}1VEC6UgtLF-3MqF& zY`sRt&S84LLTCcoj-2OS@9xF6K*(5;6-I`LZl?Hz-OUM)u-@b=wc88M-%9_Oq@CBG%=d z+1FDpdh1x`<8Nf$Hk3(!eIlm!WM059%{y{Hf7`=(xWs@I%J+ek+6oDeZ4^|TzNmMY z*G!yqd;JL|rMc8AvE*z|NNx_Ks4qh#{CEeGchcu$;v4Px2+uE}j{pt1Q7wS9udmx# zSuC*{uHP%6D3|;KQ>`0UWFJOusnSm){VyTjF7M0SJm@4})368DFA0Muw@g~|u9n_& z$?{d6d)K9#D0h=&)@*_CL-c^xtk|RT%~5j{7d?-YlL`HGtHfVeZ39poe*}?zkGnzC z%lVxx?^ojLuLyMBJz_&20K6M`nA*tHqpIFGT&>w(nX|a<37j%~TT2(tPrZ^nxuD`{ z`Eq2MAX(J8=+!p7l8E2I6B@S;N3@z{!*fbq)V4-MjSr#)tvfB_OC}q}ief#Xj)Va| zY%I?HpIZtcNrn9*;JY)_Okh7d=FklGXR5`W@7peNGmCvEx#G@5y{jh8gwJ}JX#JT; zM8U(0_q1q<_}$Gn*PtH|l-IWCRx>gaR@sd9lcuNx*M#1fqq&ly~kwOK%EIL3s z!o2re8B3z~+G2ts3v1>{u6s@D;Mf4A>S9t=Uv})4W!r^YQD@I;5=TVGX$LbaA9ISd z3yP06Fi8hDcO5h#TN4?mMPWfoIlb5n998LCSzJp+R@Fwt!KF?v zZAb34I_Jid40+UsLtdLtaibON9Z_fn5SHnpHokS!$u(o=2t9kwEE)17E%>mg3O;yV zX<7+ta8cU9R3G349-o8%6I*WWv<9Htv7KChh1}sW`t?n|Q&4KerrW5>uey7IPHQ~_<>nUNe8Lh-^j!vm{aVSmK#2f)GUE9b%N1qM-0byD^z*guq!fZzjy24dJgnEN1)&qE$1S z3Y2?%O|Q}Xk#;*ZYXT`FMbmT=e9c%J6MKyZCwaKlfu_}brbPWq?-oBQO;AEBRmYVskJi%U^LEY1W*0?5}6M z$UT>cgu^*))zeoysK`N#)sfz%)=g0dm-qpTpDa=cBWx+s3{a2;Q`rVscs@z9?YQdT zkDjk=)0>-H>Vw%=P=3T7>Z0N1AD~qb|B6BX>wacY>S|4S2CSXw^j=M9?YoI_fTc)% zT(glE0SJLH+EGnDD(cUI0h~I$JrZIjoN%tB;8-ZEL>Mt_NsD_@XrEHh3iWT0zqQ9i ze=}nEF78E|x`W`FA|M{|S6DTuG2Td(Fmz<4(O+TigH|rcoQfl`npI!tk}T3X8j8eeFohhQdUuq8_EKM~}x7Mc3|p&I0lmXqj7E6!Jsm;|};3#MDv^#=$zy z6|&n^<1p9iC1eLs3cG&ce@L)i#s%YZ?WARj47RBYVjX(HQFoR64$i_N>0h&{KW9qk ztkxJiJZ@;m{NkT>Mi*Ne(`W*XsHWPxh3ltaZx7>WFE{#a7EZn3>yW6f4|=Y4`#eyi zz_HksM?8+UBk`<-`p3&}$|%6t(!RYWk0*nYRH(=c@V%eOBPfwXjiIMSt~>p4m9wZj z;O(60ykBM1yz;;bO1M8FsUd@N)gWz!OKOqH{^f==#9^9#w%YrIl5RqWC1a#lla^8C z!<3x!Z5&=$J)UoetSBc31vl0E+;d0MffxDTB$TdMu&=ZXmwBPd@3%NK;cr|E;`+Ve zpDCcdhqH`~b;rKYW-N#cn>K0*){^aeCNb0m%QK5HjxvBEQPFyNWVLDac58BQrF1^&37%f}! zgGW#!ugjfKFIPgbfWU^}UeINC&)D)GQ99@I6KJYABM8j+$tjR`e{IG5ggo7(89a`6 zpvf%b^Rw~RJ6+U&lxgvW-X8F|;n$^3r*agaLQ*}YCpY$C*YVUxkj7qFj1@VVC#WZN zsfuXZs!P6MGiv?-SwuBKm_VT7`IT``cNfqvpecZ5dEw;u5#N<^x9%2ZjVIp-Me%!I;AIx7?y= zb@KcXB?9p_U#*iS7{x>jyI8>&bV@23XPghk?}oza=6;$khsOX~XZH6ps_!Dx7Vj8Q zv$D%$nmE40pE2J_W!u972Tcvvv?{y$vHcY`78a8Nu}|esa0g<Jc{>V9^W1(Jmzfz437N0s-_jhf?BjK9O)>~*E*5n@xe_AH_%_r!O;c*jYzDxzTH50p zc}t{Nf)L9;*=o(DQdzURw>_%n$-5QC+a7rAX_xCaTU4;CMLX9blo-1DVd%V%?}n>@s?MYx z?beU1%h(H(D}ky8;H6e@%x7 zSK|ix>I*Q6O})9Ajl5so0T0N5q1EB2Q^N3W^0wVXw)vX^s+g4Z>2)(-r5(3+bI5*r z%vGJ^6!_~d%Ftys`EqB39TMM?6{qF*7Zj`ZCvWpK{pAHmhM z1hzUAf+FsLFO?;^FwdI&0>y0tdiLE7mATrDaRknt(BV+jyGWFyuUL;WwM$8W)5_vE ztul8WG{-s=>?s>`!+Up%%H^O}z2s_PTC)MdeCPfAR9|Vv@_Z$r71cMdie34#I$!(kT_RFfuEA(Cge>bqD1kj~FCD`>BQQbA zh+(Uo80&G{&U&nIf6l|?wTTJZLPJD;QXw#+t=bzr)4LjpWRoz;zO-<(4!fbOpr`Inf1=QgGe`mYi}d?z9NWH2!4Q8zg_7~{p$weH|#Zf1$9CIZJ!5S<-R z?t$nSh2UHaxB1)R3Abh%VB4!khbOh*lSgFb+8Gj3uI=t$2)%4u- z4!(?Mx%ZjQhbp=7eOgr0dgNoW!&}%xTj^j+oiJGRgvE+|3QaWos+(oZp(lWPU|5mp z;pI-_g}Tw(-Q&oRLYH&3h4JkPv2S#tmu;KNIjjDA#eaTS^)ndKwzRnTdAQg4M{Ns^ zv3G2g;=Y!fkksY2ctpKdvr|HO+uF?!esgnxTC0r(<^%Bot%;MLxwZSY5`24}U_mBU zZq!^ANi`O%LL^_;Mq>@qPQBWS8d+u+8n&g5M#;CUuCw#tuC9U8D(9kKtSPNN2U14$ zY@fKp)U9gcbc6Ar9vxK1BdbpfU64G@8;3M!?#Hab2G_SAc>crvvPdSU3AJRxNE6*J zdcno8BDh)oO>L148IfhnN%bbQ{jr!qQtUxB-}EPOFIjMi^r=_<>ThTuI$xXe^(R#* z)vu^W*V1mX8*;!_d2&wcKH6h zc*7NMQ@#`TNp-0#fFL;-ub>uBls}LtKAbHpu+t7!xalSAgGp6&&`&IG-{gZ+L8kK2 z@h2Mbw)v>P47DoW*8O2q`5oxr#8YS*1-~A!E*EX7DR7YcPi4o3%FZ)wVIzG+hFlBA z%5lVI&VZJEjCFU zdI6_C3q72=(k@VHf4zkoxyegx-!e{V=?6Px%dLJpj!8iQqsU5$olS+{>ZS z!~@*Q#iXALv~(wWXJ{@TaoAuTe&M_-z|~GaR{GpPrn-C#rw#U4$ZYwmG3`i2nT;4s zfv~7m8m|45GVUhYDKi1|#p1+CKSxOXZE^bSRy+(gq(007D~qDvhDF>m=SVLNc7D$F zPpEu-qJxeY|9D#k588!EaU(FrU_j5WB<(-?hKQJf(oS$l5S&JEys?18@EFCyb z{Mtu|=h8La08-{5XSTpF9VxIa&HSwF7D6RJ(Deh=zYL`Gt{LWlOh{ zF51XpF4{DTEX8_(X^|M+o9;2g-5a&10{<3$*PJSctH}`#)g@-&zB8Y4s{JK);0)*I z+AxdZ_tWjgi(Z|a>Zi;30<~iEy;PS;McB@Dln1d>e^lzLtj3!43rW|c;>6UbO0zA) zkeVa?)0~x3@zsa^`9ZiEo3vy`RP@H&!E>#Z3~YqB3~rdoNUayGywLJF#jGkjJVCV^wUIsYUeqhDlD#D=bc@r)H*Y}4?;)MXHXXIZcC=b?Tp>sjzI9A zWy)scCcu=thp_rad(#M_OhT*0A6A@F65BEqTK{{ApbqA#l z;p$7w15V0DZ%)aLHkO#`Db}x`b1s0Bu6Va95@W8n_WdBk^!atX6u}mV#B$y8cY{ew;H!?6`Y)tj{z$2^QG#Yc~Vl{~u>>9ah(} zqz?xtK{oDAu;A{l!6mp`aCZ;xk_0v$G`PFF6N0Hg}lL8S#CZhRTh_5fU8}>nZUI= z${m+Ljv~!tsjax`w|TrL8p3|-+TI#Eo%Y_lw=nLU_&G`G=W1S($DzlLhW(E4SN3;C zXo=hz!lCGIlC;;RVQw@R3Mb#X zirw&jB%n|)G^p`n+T~n z*tcR-<5{nbaf)NHU#y;d`g`yJ34~fLdEu`{NuT@QD#?DJ27H->aUb$DU5;?Jo-E=9 zUbQP>#%0Ctg6Vw8M@|uoHARgtJR5oPsBg}QA|eL-Oh)O`XZPyvcu&FL;&^TYS>0b= zHin37n?RSf9Zac-G)?@dm-P)ZkE;y&Gd8d-SAA5x{6;^B#Z@Pf^)FOtgvs~kxj>>I z0xh>cjp+Y)MSl^>1iZk=@&uP!x5se63<f)-rh-eY)fBAD;|9j>i^QAE z&8wVJso5I@(3xt@N$Xwdd(Poa>qg}$@8$j+$A6z*Xz6VTL?Avk?#kQ6G3^89H!w$O zfymEl;iZMJze|HLa7B=)k17zZZ~${+VWt8L;3tAp1;KKx7Yvr>05)QCoinIS799u z8zAkC&}cMuw+RKdmj_FbUlkXJbss6Tms)A7ft}yJTifT^9g(+_R$T#w(fS!G;xUNC ztX((8k(3bf{z$0pQzr{PGb?-+6O$b8mC!d|9>hrY5aJ zWmD5**%@x<(3hk!$`K%VZLRUdAKw`9!$|yae3;dBy_1xQZYe zI{Sv1Vea29urIgNZ_E|;RuG@w`pMx3Y4(e?`=6oz{9L~xs%F?$)_{~M9w??2my=t0V!{ET-F`yWp|``6U_#(QM{ z7PNdZ^uNF26_m9%h=ITGu$>?-@t;ne7>Y|*zVmWFuednS)Al8b_OD)wQR_sHm?s`% z!cTk&iAl~#EvXC5QfFl}8!$Mf_!Lt+Ft<89#|bC^5x?tg+Ys5xA}3-lwCFR>Cqq%= zn^W`DYANlvDo*zQGlVFJA{tP{YM@W@#Xn{eZ@Z;6GI zn={Gv1pGWVlwy$gYUA}n|0F1_Hw&59*zn#!Orh9$>W}flNFNO5_jozn2^g?w+$L9o zb-->G5A05q29yapJZ$3I77F3uaY|;j!dp6nR{MkR8aG9Wr<+vNb;&H7sMmk!6XC@Gy-@O zVXb4BFpFc**G|5_tKLcu^3F**bh42g&&|wRNDF3}1@0&QQRD6BwSB@8Ng$zZK@kQR zUU7R*_a6WlNM~`7>bj4ZZvqYHzOGvHHhJC~cIU?3EKi5U{Z1z61$|FtF+k7=88#`Ugx7lEV)a=pK6Gz8WbkGEaR z6E+RMAzcz*%m!?LfwVNiN$TJk;d7V`{H0k)DY719>} zqhqJAl6WK5;TTK?KW^j(6lTm-GMuC|#2tVjbam<$>uozy#Ol~`QfBL554x+HQ~N?a zW5~#Mro6}ZhrEta`s?*l>^P3oxQGJ=`S<&N`bzh!<@uz@8l|n%3Z9ghFyR)Pu4IMk z0~dSIW&kQdtT>K3S^&1LgtoC{1Att%8B>ur)@rc3H!RLw=0S*inDw>adq@*!t8Iljz7o2$iN;mJf@njE#Gx5v=Z6|ka6JT~s2$F%1` z7Ja}T6fOFo*7yp?5Fo+h-@pKDUu#SI?M(V)0tMP(U@d}m*Zf*Op)}lHKi#YNw5y2m zim3kJ4*bg`$b#eBrez$B5yRsstxO;fZuNgP?t9RK0DAG^qY5{wqwJfPo4A1~wp5_M zf6r++UslrZg2BW zMm$!I1XpM(XDZhbOhahZRb{(M!x+43j>P2Pl@Ny0F>t{Gfc{h&crRPrm8?$tQYj6o zB&9NDTB#HAM0%C>+Dy$Cw-t7d7S251Hb0G&nHs3pB$Gm2svLrXIcoxpHOi%*A)Q^Y zyiD29x_r7JXslnq+~X;3aq41Lt1(?80i~Nit>|m+%4A8X+K07wnJwkBY!;!8~Vt z5(yq1tC`n+WcwrjvguAr>c`T5a13^0!L3}=+C5ta-1{I1FM8+AI?EozM{mxlfo*`9r+6`wRHHf*y&Mh_>S*%dMDocNPEc-(io3pZ0YayF7JFVPSbf3bmb<4&ChRl zb;$TmyD1jUh2zH>zaAE^>hqSSs)@t?tQZ`P`$$aNiJ0x9MCON!fe4ArP@vsXAh0Lq50aX)*Il6RWJN+1G zhm1p|z49nNg>ZVM%TGacpbKZH4?%L24?$Ra(55?s?@~pfT=8M7UN#*d2tG+){o+h;sD9T4c%(mI{p>3FxEN$Ra2~OvFIrCvFV}D7e;JJ$7c0l6< zn&AzkrG!*xlVA<}*R4nPG3O{_0sqJVi{G}805_~_tuQXFX(o#9lU3SfxJF;z{pzxx zhFZYyr`dn`X_QKMRhwSW%Vnc|j|GZ3vamv*+Sx=waYihAcVoQ+W|WH<%}^%hmE1^A zJy`r8vr4~{%Xyz*6ybClMyk(hPdg0z^Y?IJSB9hXaFd_mWn1Ectqc#u=;hzs^j~EG z*{}EaBu||7mOtlbvsb{orSr#V1aS5Bfundbc&Noxcg;2fUxCxtqn^h|W14GwH~3kF z!%Ys&TSEYS*p$U6%MJkvGk`#pYzC7V;_6ll6o=ED{@tBI;Evyj{yI`Y5n$e*%*;7U zQ5r$aS~8+8AN$luF*Jo1M64OXZzOUpD>CV;%FgO!v`Sz*^v%Exl7<({sA3nLQ-&4? z!O)MQ79I)4!|Ij-8YPg#bw3K@fS-vmXg=f!Dr4wcawP9JsVSrGHE4EH$^U_SkbwgsSw? z!y739Pqobs!oizq!rwe2_iMOl0<3px0TO3Q6fIx)bHCQr7gs)=7j$xyq{q__AojyT zNwMk4p5>m&dqON}wIJ-X%@Ig(4{ZgI{(sKJ+f~r?W1nuPY3+eGhNFwaF^@$|ATJw&EoA{awG0$T7urVBYZ{Ll}4Y z!s@;zsarb%ct4(+X3hmX)cG0GiIFfQm+oUTFi>gb=#@dSP;$eAJ-$tE$Dql;LifqX zpTA|Eskuf98yt|=*3N%ozDMZpXcs)%XxMkQHh>swcFWlauC3>c9A`Aa~=txU=S|c zPGqkYB4k&VX^8bVnfL^)XT1dv&EndYto(PE=@a-6=8h35>%>}`=5vtJ)G3ySe#hI~ zdi-Rc2pYjZ-Ox5#su8|x?651#_O4v|I;5#wGTZV&x_&7gPPg+>VrQxv<PDa;bLI zI!tT(wK9{raH}sCUmp`~fKKB!Unir_A) zZJduXznPv_P)Tn`LdDGM{Cq5*sAaV#f=y346K-?`?hz-m{q@QJ!B3;qLpDvjhiCon z@h|h)QFa>+6VrGNW;q=5DPTL5(6Nz^|5Dt}Hg#AjAFPuk10GABqE?^pwm_ozYRB11 zBV_cEVS($>|GQ}A-AD^(@jX$UDnu{A!F&LS0AAQx~q67G=7J#%1EHo_atEa~(xP<2# z44Rc5MYra4(^)8Kc;%%B7oB$sd4SI=Xr?PEm7McBE&8rP;9xBpq@)5(Dj@?@Ei+Fw z6GWx0Jgn3eI&Hogsz^QQ@E$f0D9hpP;eh%*3ubpcYxkuA)O))fdnBMW0f=Z`g&J=R z2~W70qd@yJji$rnJy7ATY)+pY&mM=B*tll-pn4$;^@j{KF;s+;%6XGo4X>v#r$PAU z2-f^b>gxEgn9Exo&ue<@{MC|-8{>nKn&hW;4lMc>KG;TGzNp3FqAr*B7J4%k7-@Op z#M|88h!KkUGKzi+CZL&?3s7L?XMhec5H-8=@Hxti*%AzamFZ%Y|LIgN#bPcbk~Tv` zh61z~FQdk-)4V~6S)88U!N$W)L6?mcG0Dl{+WX7h%u-Ku`gm5g@M)(FDz_9fUmTEj zEmRBU{dLl!+j~GsC<_lmMc-zBi(~j|N~L#-n^WGMNySo^zBQGSXw{*--NJpK%wofe zBv0Y82j?GXp>8HYLR%VhgT0))eg6+8Z;0ksQXq}Z&NO=L>SQZHM^pZF2S8Qcx#(44 ze2QStd+kPOlknjT5Q+Ncb{DXE={3NDR4svbO||vNhyphaAWTV2He&-b8W}0 zB&dKSR#PlVuTUhUN{8Dk__^A2Aa7lcM@!e4ptGnqMR0=qa1>*Pv?r&tg6mF?fk4xa zvb+AiI1M80jj%{F%xRld?WOowg>YYju78?$K}SM9_KoYUDLf!$wmU|q#gw?gM{u%Oz7C}MgZf=?*fQo_O=7%=^rbqu$3ijl- z!_=)$(tE-`qV}S;{{?F1w)^KdI>IW6qfrhoBv<{q|C)$@lZSxW+Y2kBWWvObjJ^0` z$F_X$$wlyH@+f>_Qg8p5O|RwYL-Xj_Qy*2rn?X{K-XCO|>_l7F!1Pz&Q`z}_AjgHY zd5l!U-ZOM=hchvhmLpQT8CUrq;*u8v?uWV?8Fw&&W?Zq_*73fNww~t@vRMifdYWg@ zJjaej5aRblYJOxbV}5+GT&SviMB@fCP~NeJf;awm64=(7)khLqq~kOZ3M7<9pY=ka zItz%{gdod%&@3g{G0@FzPXHE3V@KoX9*N~1nRuAb3KIe<4{P5w4%JpuPMldLI zlmKCk*{Caj~f6xX?V?I8Xlv$)^e2FPog~F~a*i^57|H>+#)q z`fL!mzABBVS)P?ynyXy~QW0(({g}rF=$$7KNjvpX1NJb3h`0M}K$cWefga?E7hzw& zC%ABVgH~3(Y_rCoMyMZ+2y{*%D4DMqutj*fG2SEXQ^WY=d$sGlZ>%W6*(m`n?b z=g|2_jEuw@uQ2FNKUO6x3fWv6m4FhD0E*)&;5f0+jmy3;$zx?e3L&xDGCJ>7HFtZG zsqC5W>MjEV0)$PpRu~!}&@+_}Hpb9ar>K_<(f!%V&C}=t5p()A0PXz0b4>qj#@dD`Mu5lG>~e*Mpa*nxaR-W*O-wDbp*8Ub(BR@23dcztebc0k zSI`>u1`CRJNm-4iaEVtNfSpX;Ld|{za(SQg-<3$6_$I^L(9J95DCWiH8XMz0q?&EDTX|LxQH4u%SaV3SU89jR9An5jcz_;s^fdO- zp-5w?agNYfajK+EIcWx=7l}d3 z*xxkh-$CYu58!+#ed#jVBOB`#%^5cG{=^r4hv+}jYbi+IvS9dTm{fZ^edK*h#F^OMw6XWeBW5A?-PrTHBb-)8B#iLFb_QRxmIGp% zbN|2#TqQtV68IUh{xm>`Ab9bze?c$?}Bg*Y`#HDEg7rb7!MfhcyrtTKX~{{;l6mcZxqtWQHU6><@ueH zmgvzbekUKPAnj+{efn%Tk&U_U_a%yygC5h zvezPS715sl_vQ7))lC;ZVqUnh9O4Cca3U)hn%0k@uLI@m-x3h&-ImNRge9c?VaHb9 zj=cB7F}K@F_F0<=Wj^~<|8q{1-jM#m^1xLDy886%U0A1DK4L#oWZA2%DqUacU%6v- zCoOxq>(TGBn{j*t5j!~!x>BlO6Hw)dGPJ>Q-y#{B1>l5aT+=eyV>!OP19fd&S{zUBZi_heJg02?zPU(7uI}Gegqj2F z&}HeL&||67d4WYPLX7M8ceKS4G(d~IW_NvyWyf~Nb$l69)z_Ac*sIHnD!|`Ebg!Ve z;sszp$Bs+H{+K~yGq&RKj`v+YY=5VH`Lwm1WRD{c#^?}TH)6T>83DZ`ECO zY11N1F#+-C{5Qp*(mjEIw;>q^-|@0dhyBv-X$lu}iWn{&dHP~XWL$|3)w0brSw&r) zZ`I;K4g-{hTpU~RH}Vap3rGCA^eCf|fU}{YwT9}gsj-R&Z(c)9m67%D(tR4({$Msk zB08e$ajKie!W+0=WlV*y6LCwUIDb1gK!h5#^z~zA*PBl`MJ1;D&;jBlotai9I6tO= z+%{i7g+0k4P?EB_I_J071nwj8oq*8Hl2gmcqR5x3!d3k*^zd7I`FYakc*3KnTP&W3 z6G)MO1?#~3)@F4Mlf|{wt~1Ek?0Tn026hwHz83eegfND<}KZ4O0&R+uWZD! z*l`&iSYN&^8TGr7;hM~EHdZ6s4Fj_ditP_@7HR;!QnjTb4O=lhj;L>R-u3LpqW;P$ zZ$xphee2d#Q+rAHM2Z4p#d!#G)}X45irL|q%QXh+m#<;Y)_5O%2;#fN94wb49MG8w zGetGPnes)OB+G< zpll%^uS8jlaVLGV=E?PR!$#iK{+*(Dtpec>;~AiS{(J-?&lDRMkBZdG6Y2PBQgO2JYjJu*!nZ-Oi~akW?TSu-k0MYD<(M6es7wloGM08jhnXuf;n$z z^7vLFJBkwSlsG?Z5(aUB`Cl8njQ~E}jZsLO$0)$bLk*JZdWp~b;4~fHjY1D4Dih|< zcu;+l!}=~WtQTPS$>%X0AXmMtoZt6Km|H6Ydb#G(0_D96DR@%sXlLTUwSEf51gh6H zj|i)N4(9XdYqdJ3*NWe%9r(0@KiiO1FFLKPb{aeVX4TthiAF}Rv8}-ju7h_VhNDN) ztI0|EZ|ZFlJYMLU{0cdSX|Q-tMY7?M2xI2_|^b9j81ath!}R?#O*}e?VZ#Rq1UwpB~{KD$-zQ1#BF>y zD3DpvcYVO7{_f3r89zOtvTEEO0xTX<;ODGk-?Z}2rI)IpR2p8?{iyf+r5_}1{S^6L zWeK64bY<%j7cJSM4S1tU!VQDBK%*&B+MEvB7sKXu_sK0yOhd3gxL=9t>1iV7>X|i| z@yqlL4}mgo2r33QA3_yA)neaHgMVf*z#!-MjK+vr&0*6Ww2R$3?p%FPOqfJ|{V?Kk zA|Da<#!jj4iC z{b4tvKfMslRqsg%5f)C9s2vmq;(Jim@z@pm!~o+JlUDERO29Wydsy~6Zx*&~D@~|mTjMAJT{3;1pd^QqSJWus z$eNYNpLgL>2D%L726U08MqGSyQ6jCUVo9uKZE0ei_vuH!#1@nHBK~X-Zbej@z9dw> zh$E+)htJo%Hpog;AsP=AYowo4z z61Awowb?G^Te@flj?+IE=;#TL_LvlBq33}S-u0nfIk^sZ05_%?s-nFzi*zYF23&ec zCQ&HXtHvv^f0ch5id6E~)A}fnVjK`-qKi`qJwZ9GBJ@GnmjqqH!wD9HZ6rt0xJnDK zpIQP#w%DNcvvb`>xnEieGgV88D6JfW=mlCZ{cpfACZ1si-~Lq{dW^FQ3D!x!p;y>=0A${sRKlk4r0*W|y{N&HD5G$0JsH-dV= zFFqKVtR-bDvWGd&IF#|=gs0Vv7-}<*X{+H=#vj$n|C6pP0|SPCDWL1aYcx9z=sChX zq7)H!OFlMy0k^W#&?+=`44dwCMa{ zZdZGg;)QpWEc-8Nb&hk6=n_1KQiu;hU;EZGh!yf_UKpAkYh+}D^aP=OErYtu;N(pa zPQx3x=F2bT5^;yZle{E#OsE{qW&5X)%~UD z&3;V_N`5KJ#jL{{@VU3?85v^Qhs>*%zap_&h#~{!a_x1G5cH0MEG8<4G7e~#2iyJ(=&ldW_*~%f40~oPCMK%V! z++~WNie_5Ia7Gv`PRQ}xGO%)WIf#4BEtJfX8qW}vO+S_qc7l*J0?!sgHXmgFg=WACX)^&; z<<@n*n)6Od_0rrqu$BM>b;LFgbb2-VD5A}W@B09ro(~q?w3MBm_)jonXmUPzi^3`* z-$L!YHT{}duSdp}6c&|aY|fg`Y*xsYGZbr_#cDimz-AntmncO_XLj^PgHk3mi3VZK zkH!yBQ{>Sev%GD2{jG5o3QSNpxA*efIcM4-5B;MZ-?kTDS{U3`)rp7a$yAumjk_kl z=S?P4nVmXX%A-cb&=1(jZA219!V#pSKy`;l2B)eyR#4(TZ^d1K$Pt^v*u-sWcQsc=0Q;?Eyex0)3ylFvj$^ZB{;8rHL5PSKwCRWXgvHkPz~)!UIy=1W-?Dzp zp2~GPPfo+t0HJM@9N53^6I>x|Ao&mw|7!TVt8>^P?qiOhf!}iqYQH>2WqL296GwCq{-^}>543YWe43+Udq)uyt8C_eIu)AGFmU)R)}_~kDYbFl=~YNmEY?- ztqTE|a54m;Q~L{j*B`r)*P^=4c(ffxss2{iT@mndL#WJ5t^>I_1{f{6%}Autl2Wt> zy>$Tgq>mnbD6Nqna;qX64Jno|e1zwwTDi1xqynUmV^M=A@GlHW102f+hwv+R0HBRFe%heh0lE(HD8%Xf z2ng|`l~N}Z21B`hauOeRDhg-{ve&we%uWYGHBv2w8}Y{vhTk z;{MF?BSN;F0NLoIzY~U$*owv)olNK$H`IT0?c;e)cnD!agfU6+{se<3i!zNBG0=>` z#`@$6j)AbiLnq_W);1ao!VL2jKfq=DHYP^?ZM9U}Ckj!$_I8pDm5@M)>X6$#Cd-7h z?;B2FADgEE<43H~)6&UF5hlT*WVF)N>c_77H(~8NWWc1l$zeH{3TB-dEXe4S<};^* zeo7Q(byH@kx~%8S3=YaWu<`?wR(;8PrTEKEG-P@G`f9(lv|EiT2Lj@#(#!O^DR1UO ztwI&GN4BAnbSWE@W0#$~PVE=*o9RjcR5 zkhHrmbU8lnXTw|?B+tsZV_9`Ym5K`Z&k&#jf>OOl=Jr&OM~JLs<6|7SFQa$XE2M&En`yKW$!ghgm@mJkO60r{F;10b^By^6XdM;CzYn- zs_+^&cLh)!xt^dG(aZMKrJ%NTJ8|4P;5AgUt%>BEU6W$Cw@r9-FUPdQX2Fs(SbgIQ zxqUPw!B{ioG+Q2}L7vbfIAPo@+>c7&U0ZiLWX#jd!>T(~`S|g~JUY=wsyqGIVP$a= z&q%=mH491OJ^{L_wK%!v?Dat880>glEh2H|54n3M?8eY`8g3tl2E2vuzlT5MU3`NG zO=7m8kbNsV2Fz0ZVdqAxBvb z#dA!A?yfNH-1i(c*4!ZwRM4^EY|X1dF}CcS>4F1h_`nt7lJ|(dC(L>*6&uF%Bz}Hf zUS&@5{`L5S&2zei#%?RXkSLPA^*XwGD6Y^`YU{tdIlrQTBOKr|G_KKTb`9)n+Xr-8A;~}0mLwCr5_`yEn zo4o?+f?OwK;d>d;>0_6W)@4++i${^uQC5=U9zE*~>4;;i)DmvC9X}lry{Ouw*zrbu zpI|$ZbNAE!+gW>ed2y#I!VB;*aB*VJ^OU}|NNa=Ha6}+;CD*$F#}^yb3CXn04tjs- z!=8zRyxbJpb80diZO*E_2Hg?7u9bGykX>Lm4g8pycJkF+ZNA|9KFNiM)$#DdlsIUX z0~>U6O*#W$(ECoI!;fbnAstO8S~Ne(92(Zp_>w+{bi*8`IK1okEY{FXp#pm8F+VX> zJ5QTy+vq)Sm)!sjZ&r<3I3`h6n?16=mO9Z9ytpc*3kbhnn2<-;b!yfT#A3;0Z&7nl z)2S`H@kF*|6+|$w?-jbuXyQz0Ul4Z?@CO3P!Ev%_4-Eo&j9WGvHz-y}mjn`$Uh^0( zME9zSn00qudpA|{!lm(>K11uqkLGJ1>N^Za&3&_COk=AloyAS{bXd}wM_QQcIeRy> zopkpNdA^$oFT9Ef0y*WRe??(uN}&#GKq29gbZPmyj=Rxp2)5^r!~(VR^>e^vmusU zu+n+lO~50faCb27i}ObeJ@48ssBqs)G?1rPnD(8WxIFj18@#!K){uGM`zlz|1TKEG zP7f!M#wmlqU^rtAyA_U)t;X~0(No1pbbEbh%+xn-+n6_QPS{nSUnv@f_&H!6J?7Jbc>mXVw(8&IB3b%j%qP3$Ll4)pLdO z^i|wJAprIl%7bR=={v{MWA(69UmJLVa_x3%cjR0hL{|gbJzu)LYDG0T%x|z(SWfaF zQ1qUY6d=qE)bCG$r9tU+5YGF5>;p|$dJ*EujCQ`O zTXvR31=ZXGN>>rY{=_Vnf%UxK@Uvs^5zbsV8LJTEQ!7bcyFG@SHi6Ui+Ur$(lk0X* z{UuH%ZyPz}ht8r_1&cY1MGdKGeec3iYtF zr%2Dr=}q!9g%!pmOYz`5-W~+}DX9^H20-a#nd@3(hZ5P>Kc9z8@E4+Nm>ooHnn#&G zQXcIXupeZ~HtP|;kR$7xiER$yNCfTMZ1Kid7vgk?A>XOVTe-357pO%cCcMuXM2omp z+1N$Z2|7S7#~^BNW^Ed$K>IKN)>@Os4@Y+Pg9`D-9VG+wf|w-jpb%K@YkLE>AV{mb z*Io$d^)oT)Z!gk3VEDe-H{KSRo=?SqJ|k_yc{M{_xOrjgPj19*7zpe?(`-~RQwT?g zu`FfH)-H!NFKbpA)X2_~G{TX;o4Q+c-yPo4cS!&|hsg)Q5ZYYf54F!beOhB$ z6yIk*RuLN0a_Q}z7k;MpBXe$kz~*}vCy@O)j0y&czrmyW2&(>7m2~xd0e_WRUCg+> zA7@Yl%c@P7Ii^umnosWYFvNs5hoP(#LCJGUD*K@7KECv{l`&c9H9tJ{D!(C-)ZAP% zWxU5WNF>ZyxQJ~%G*Ybyie=(Mv|7r>t!r{M-lsLf(}$}-^Wx~xHVm_An8WI$P5P?) zdDBHo?qo7gACc8z4auQpavPZ0pZHoKy?|)Z=5zKwsj;*d8HtOHLXf?V0WpfTG5;iW(y&y)wc44+(y-nw#{8^){3TvZTUrZLANb?TGYL-hHlm3k5Ub2zcD5$|N{!dHkl6Y9*;+M6t$8 zn=ZJYCR6fCWHIY4K_6uqCv}dwh{6$73iUMfD-n!hI`Qd-j;Cr3)o-=yGEEg14hP(> zNHBn-E^Z$m6z(%ilgcu>Uc(pakCsR1Yk@bRCotB4FmroJ?%861|0T*K8580#=CQ6RBDjz3qv4iDYo^Ey8z!aBo;4xQRyQc}pRWa@(aS@$`CskJs`0&|=b_%Crs)dh zVWbVL!X92V`X=#pY*H?9eUmMYsrpfshmt;a4k?X^alh@g6Ael3TXC)ATP^)>w;Jim zPI}fILY^L(NMimBD9kvPcN=Do>ZcrI6W*qmrmE&>hnr{x(-db161cwYrNwQp8NUjw z&f=xqxI;m=5?E+77%XTj$@r2B_gJ(;jMJ4qlpF()TFMjZ zR>k2EvdWZ8l(kfyF{UiMe(+J0XmeAvgwuwBu~)02oKYC81<%v^lhMWL-%ro|z+Xh# z+oqgo!^PLgztmZXc8D*Uvt1hjT`C}9LYvqiED#5_(TrzPs9exo^}l|7{lY0Lc{@_K z4v&s5N;!ZDs1=nL{0@sp`EGi#P*QtQDQLmI5O;ea+&-z$!i484h$+PO)*c%=;(gny zCGAc68}!Y3iN$*S=)h9ZR((+2v~@vCiD}-l+RVD9ZL}5%&cLuY5CfvK7Z8mno(qx- zvp)1efTb|U@kq@$tuv6XqN73TM1Hr}vw#TgB*4qjJ;hDqL5O!1t3`htKc$syYk8z}zF z)`ljq!jWbFakq*mVlVT+=Xq5x;s+ZHg;u_$v=Is)A%b)YzknM<>AAy)8jHT|878lo z?g&VVDc;DL<5XKA_M)U3DF22$AKgS;!f4^rmPkbNHW}cJ6|}}P8+*52 zNS=EztUQC2sv3sj^1QFB1b)2wohh4Gp!TiJ@`u`)evL<1=& zzald-jIX5>x;kwTHXijJ`^-eN4^sgyw6zz@Sgla3=PFdeV!` z_=_5=z?5ju8|-6}B4;M^3J%!Uvp{8KjY|uHsdR4$tEv(nNa99nrv4}`?dd1QsIJb1l(vzL-mHg8QAQGhdSuQ=Fs_uDyAM7lvxWpO zko!7sNFH8ZyGRpr+ul^WETq-r8}c&k_@xBfz)wMQ4{tf|r>SfXYZGa}aYXum*i3$> z_(;?f`UyI}xMln+gRs@bDLOK&J28@=Ll^Gtu}DVmn$uS8(`bg;G6`q~@)xFK=6#>(r#odWut`Dim{Kw%HQ*b)B^FoZF)mdp_GqG0MIPE30(6 z^n`y7n+>f}bx8!p5nk6rShGt*eJ1aE|Ba@6WXI$~UTN;#o-Ve2Bggu4G_@Z$;+$1bJ4 zkyfOrX=sZU$a8`?mecc9mf8leFjq+0AydCNQc}%L7u3+z}gqVMt;K~nj^FXxordP*BuR3tXdWWiwVao8kaJ` z%k0SbPOoXXde)BUqIxSF)$9L&Q!j~ZL25RlEf@y|Wf0aLK6ZvmGCS0a4$cLg5tn*z zq)jzmQpwDUcBC0ZFt6Lu+VkD;;kes>rt+TQ^fC0Lk@U4qG&}Da+vL7&Z;ZdO%~g+Q zVIrZ)g=|L?V|8AyK7_NZ7K+q6w74uG_gBZe#2*7}j|3}JzE*7n)Kp~$Qyvo!=(lA)962^L-N?joesI5i4osqG0lWB3AoP7%`@E6`QbT6Ih}R-aTe>tq?#CHX`e zm_^tlyJkYiN=R8fX!*X$a$L+@6fp6b7NS9`jBTQKF(!y=;zQl*z)(HXTH=FjW>)w) z&AK6ld@_1HVC={lu?L@xu%2ZR()jmtu}&W{=_kaL1-Fv_5lm^W9ke(z>yAc2uC`f! zC`q}Ie}$1>a4;2kYI~wc}GD&d3O>5gY`Lby+L64&86Y{&x=4zsW>6N^RAQ zxP~AOnt&3jX|&ropE8%RCmy$~PQ8z@8NXg@iWLE0ns;v;90Sb%-5V&ShznGBAS23_ zM$C6#EPYTuTjmLEU4u7BQQcII@te-^gxTB zuKtT;r~d4D!1EurSh+(7(H1Z7=g$WIBZ-1WI-~)j{tex28*cvt9rFdV@W0e%x)h|T zCCke|Rk1%b^dB3bnNESGZaT$J$o&WQ{dZ&Vx5jnezbZF8KGifay}S$k?Ww>26sQD! zZBe-L>2}8LKWx!w^a;GI=XXK|!-XPnmBm zEvD<6Zra*}^g%DFM!yz;14X_^G~&!mZkY`a(+;xx_WK4-w3kTO7jLw`wH{ zkW6+y^OTmOb9+z7l8AC^bs<~rHou8vB?!aReNgU0YPYphy5OC0*dRRn1;BA#tBF1{ zOKW9qEmO;$p%dFWW%-+`@uxbpJXbp(C$(CbKN82rigsc)F_X%%9VByJX?bQ^A~`lajz{NX;3TJZN` zbLpZHp8+MZ&HM{bk@4X8_i?Tev%rn_SaBV z)l{YjrH^gDDdVyuB~u1U8h*J|0gfn17^KxEQ0ELNkqL0^e%cx6b4$g?9E}^F8(R*uFpHX--i|xd zqH4`@$DvrXw-nkf{hB4tK2jHQsTRS~F;(R=-E+II|2r|eZ4!-Igj1SPS-Q; zA~`zW;9%T$?=*;<*Irf8^M@NL^!AgGc`kJ}B4omdW%@@q(lKPdQ4ekrw4WkVsCk1< z;OlDIbu90EM1jZk#Xs}xT|NFW9Xn7bpWIkUb$F;3A#Kgba||iDrvBk`?8bVnWn&d)S^Sf5Z||fP{1unuy_F zd&;OKHoW(-opI&fi3dxr`2s%Z$~5bI5);E% zl1bF3wPQ{O#!kar$(-YcZGnF?IwX95v##~dyzUS4F{GQ`0KR-=(_|MU21PyGr8+O0 z(XogS7iYb58Ejhh^9k{E|L2-s1ItgMF)^2(iTnGE=G@0`*l^5tM4M=cK1vbeV|9H{ z#-1dZK1k$pd%-1lcF5Wud1$v9^~4^UV0=rOfhXR@3@%RsH=Ewk-QjOyl8NWOj%^`| zI>xK8MXJW9b{tvdOUx(*@nU|mr4r|+j|kn5s9SAbarkbB{JD9}fS67z23l;6xXgmt_QC7+$~v4;ZBtLaAYk%-CAixxTVe&pURsUdvx#b)jy-ou~|-;2Z!Rr?(Kdbb-RYZR3lq6md{OLJy@0n48NP zN-n!}nzI+fo87#*P0fuSEMkG??8|X8j8?Qc$1JU<=@`}bmpiz;Cu?=f`RtR%YW&8` zA1UUB%)j3B1hr!=j~^L&f4)|=OnpXM8~0|83r{&?Z(?W)->rLcF8KVd3`@KBM|@#b z%C5lwW9+S?;(E4i;o$DU9fE7)EjvdY_ox_>bv2(fm&E8sd|^!uRx9#RjLa3@ zGFgs2N!`IIAo9jm6L7W%X@PonP)#6SqIzy)4PQKtFn)G8bI14q5QNF~N)x&38PI!U zOCQQ}J4C~tB61L4GIo`0f$8ZZ+XT@=z+Ri>Z`r+k5}$>})>!)pX=gnD30_Wh3?mfp z3+NiCy*>qwmPe>ce~gkKUUYD^4X%*sqXPAOj~Fgs2n$o@5hbT zBa!GB-<=4Hnxbk^s>Mpb;HYw*&WJ6zbG2Lz{{2QWWudZrP+u2t7;Zw-qHe%sRoagh z*%$-z)%U66{QL}48eg-gc#(3NJC(HT7}6CciX97H z*W5%k(KDM~Ei&rf(6<(?Cqx-BC0>9lmu2Z{Vqb2O-SMgmPb<$%>Zs_Kval5j)(V}E zA>A&jl+ep&;44g=8ci^=bJz6Yz>A9ISk$dk4+T!W@mS^nsZR*`nb~nZ$%eCY7;f%> zSpde7JesDA*6x-&q4%fbgOk?&@-RXswv@4y-Bz({h{|fCQcVDNrOWF zzqRH+Z>Y4)fY+ETc53O}ngPCNVlZojB+>5yw_;zS>gp5h2OG>e*y; z=p^R(hb{ld1Szqe?%Bn-q~ze%L;Gs4bwtOCsovv0pnj;aVf#<RvwE%C{?mXTM+|ElNgrMVABW z4w=o0j=GA*F?g;UEfIX;4NS{4xzFQXMlNZYVy+@rWt*r5U2ViZ8Q*GreFTF{z@iJe z9hJs=JiZGL@5{q6dS?8F!iSr}dRvtDoETbg@rSm&Kb0te+C;QTR#6rku6jb*!arb0 zE|>#Sw-2QT>9yn=Fgi#h@zWN+oGE;uzdJ1_=$y30`U0%pQY62Ga=~pV+)5k6qqXIM zQSaq;$W((R zoLeG`F1qSm2{j82{H%ohrQ2ysMr9}GZjJ`|oFwZ?4o(s~DnmJ*KTjzgem=T!=#of^ zmYdth4u4l`X*x9^`(-*|4~Y4AOvzov<~17A|2cH)mSd*QRPH{W8sL3}LqNzdOZ^BS z?YR)A^x&-leJ6c^T8*mayc?Nu&{QC$8y$M(v6&U_i0A~Pbfg;!!;i+L27^j3Lf z9>*)s#-T*)Pb*n91iz#*O-}T%1uoU>`K1J(Z%io2B94oHzDX*UT?+RmZ_jDJTniUY z-Ndw>Y#7k-AW*Y>(#8KoIpqVtS!<5*^Mn|w&ou0OKT=WkM2~hu791LO_7#0Z_v1ce z-5%JI#&`(7*g)j%3Y+4icD^XfNlTMZBzc5k4YRHn>UdonihWsCv=6O?wTCu-MOAhQ z426?t{Ig?q#FVb3bD1 z{l{M3Gw#Ivu^I&!N|fimXEk z$POjI2ejQ7M0e{G1J(vPREh+7NIWNmrr)~awx=~?*Iq%Jt{jF+LDYua!?E$A0-}i<1T0gFm79XT6uZ`;xp^i;5>TDI1Lifdtn}9ugpzVPhK76 zpXtX1-Aja+)Gpb%AC8Mgt!U1fedzo}O^rMjwoMC~B6=b#6n&#I54YR+fR=e;$b2QU z_I^-6(i+CfY1Wc`mWiZn%NHvMT#0y>BGAdzr?2ggo|2(knffhh_>3| zOi&acfP_NGlUC@CBKBCa@Wr{#$ev%?00tf?niuR!U#RK^W zoBb6zZAac~yjlXE)ckf2YSkzjnt}0g_kA_0wl8Kip4}a7yh?PHJ*&?GXxFR$qJ@g- zAstVT`pmc5$X4%p7Qy7sQmmZ3CKdl&Xq^}@vOeL9j-Pn~E%TiXYecHecNWOvI5e(N zrtlqAsNZ6IW4@1ET=MWD07>ptoNH*&xUswT|0Z;74;1unkkTklq_zi=R_|n#I;E;{gPIP$u)%eN+&Pkue zW_YI9#+VQR-HJs7hZb^OCrJBfXZ3podJRNr)QBk+pW;S_;i~bol56(|99#JVooa`0 z%+i#66F(n`Z=OJ!^4zq6xOGHHVP5PC+?eEzBrVU9OVEm@hp1_IdV8$jNB~yybDr5>_^`;d#Vx})}3hazxK)w z2R{JLn+9t3d?%c|M%%txl*f_RMLNcxGxR5pP+R0B=Q+kJQODCI)eJCSO4-Y(Ax4D- zdmckM-;3<%w|H;D2mMk^#`dK~5YxF3Pa|DdNWIEg4ZD_0AP~LH^p}bF8v8Urj>Sbqgxk)q#QfSKhezJ-tI00?%kikfkS%X z9DlRfnS|&)qJ^n2!+e9pO1ww@e%!Fsxq_YJ#Pn>8GCCWv9viLfGIwlmd*7w{FMl^(L*5j5m-li;I=R z)sH@8`4`ra4veTFy~IM`X3C`|8WwU)nVUzUs#l=Y@(9#xorug6KM|{4hqYePZ_D?Q zv6)VniDq0`fZ45?rh&Dm)2eMTpnZ|-MFW0)PQ6}{BNiaCIM$hTFMoJ{7xmz2j3ko zxL_9f$6Hwc4p-^%%8mZQn}xSryiP&U+QC;gVs)C`nhk)|AI@4(M^2CV25Z_olyuc- z?Rq3z1dnj2Wvs+)t!8@s)@zUn(aKb$V3lc5)K;K;b3UL%=%pa$eNH$sANSZAbcYv_ zr4beM7G#**80^#>7JqzKLU9t&d-K7cEJ%fKk z=cU9@eG1^m-c*?Ks&BwDDT#I^$RQR*M*JPb{_Lm($DN;nr)qp99f$0o9vObxh`;?E zx#&E(%Wm>v`Yh_TDuOGR*xLX0Pp4!q-hb}nTJUdCP_Dc=?nvzE@*3;0lrs4O3)%a~ zV1|XIka?>b5fj~`%2UShZ6hc^_v9>wiYD*m?5z1bxCF9>yv$Dg-q}7)hhi92BS!o# z!%!-&1T|)pbs|`2)xZd9D@GP&c-%BU%6=e2!9uiOC@pga5B5BBlJ-j@q+N{)TGlw+ zmB~(HV|R2loY0!iK8P2(GS;hUogWdeu_@!zk7D1PExrrWj`7w@?wQ0TP%5?_}?DJ%6jlkm6p>dXMeUN^CUVsJnf&mB?hRwPuu+P=Q*11 zz<%6YEB1zl^5_I*{)L`ZCz73vc8lrZBGitgpv*O4@$+bl~fO|lGbRWr~;<^58X6Iq&7?J(A9jfPB)s|IhX zZf;j1)R#ERl7)_Q#u<;~H#vUoH5Z>+MONy&0O1OwzRurY7gaBDb!~|sF5U{`m8V#` zm%yvl=p5g`!>05}J=MIyqQ*n5BU+4ORPU4Gh|P-cm&)xf2tCURBca6e(57KIttL>e zAc(C0l3P9`w@DQWp4(G49%S|ZBvAhWmQwbTA^l@rcU@j)&BM>^tHT*mU@zYQD1Z)IdDb8q^eUVBJ1!ghBu11ns(jlU}! ze+ZDD^!Ap}=(FegXI3h)jI;fLvga@$%GSO%m>wNwdqoQTjG6BT?bG^duW-(#>JXl{ zO4CX8(M8?Yo00{?U-y!9j2MlbAo8?h>&*yRJK=PF7%bLy))$R{DGngef^9v}z^2CK z-2KLI?8Cze$X)yHR7a><4AvxUw6_<>Km5q5ruU<_3S~|>zH$?w(lhW!`)0Sn&UJcA ziuE`K)*%!WSc|K)>TJCm7p+itx#LpWP9A_Ellx4c?FU~n1f@Ey++!5i%?k2$oYD3X zP;UeK?uys>n59bjpT{0TxQ-5)){I3s$FgiG4>aCsC!VMOQQYxg0OF`>=P{oI3gBVV z2=dnXT!P$pRy%+Lgq@<7JPy`N3+Pgzd`MgK)@g>Ioc<&7g+19OLDh+w>;0Trrv_$eSMM zFfJ5e{ci3>uJ*xJ?~P;c>LiC*9HJGbGi}o$UVG9e6(L;F*GN8RosqXj+^h+X3tZFP zDm7Knk)52_C?X^!@Da??u}RZ$De?A1{eZ2ajph4AlZe+glsw7tU;->nav19Uk<|Xe z&B0jgc(y)Q)DUd5HOUsWdqn17-G62O`o~ko zY9E?)qflz$#_@!F>uaUk z_ezdqJ`1>u9CW$qq3ZqbMv$L04XECtyh^s~tl-^S3T=@wmBn}VkEXk(J|x>7BKi7t zy2Z=%I^-VH+Wt1&FvfI)+WrGP;0CqNa+nM>`ZKd>VROpVBmYRB3R4W(%;A0N?`oGw za(+vg1bj3exMgDHlegJ%06s?K1-8=iSw8DMvNsRS$vU328+yl`v}J`MK3BpGq9h?v z+m2x#W{tDI`W;K}vJsP1Gk;gL&h)Bfho_SCrQKO;P-47@ce&I8y*il6Yk^!+zSF-d z{UC7e#EL@1+!gXTonJWAG@FhKM6`@X9Ha17JCa%9zzY-M!ct11e~lO3WbkyXNssX} z{9Qg}&l6gUP+5QTRqx>zyP)lX$-%o4UBRiDHVs-z zFtij3i#t;oIm(QJovZ{FZp-D@#Gm$eSG#GF3*uNMZPbbihx;a9;O_#P$y-Ci?w$;D zF6ay4uDTdQO^H>>6T#8PwLB)zpS7p8f#4A(kR)h5rRTd`GuFvr&q7JtY~&!wql(^j z66O37?TH>Yoi3Qtd0SReQH|^@eSAzZWF5^lDMGb88;>4=)k@wk?YzYMjBiYVgyD&9 zpQi8|7t&;)#5R~by+NJ$!MnnxF(*GgYgk)=O zPb6{BJx1ca8jjZTOx_osZnh`=;hB)52uC#r9aILmyU;w1gCSyO@^?QF`=oyy>xkzW zC}W5!F4y-4a(-txf!EY#NQ~KkmKdEg8XRDCAT1boC{^KQ`Bc$=5;;Nk}FkF}sv75MgtmO4Q`? zoZ98K-iZ}bddOxzJt+^xDvnq(D#f--ujBhFwV0=K=QQugPKLZ5+l`!5f#R_}dIL+Q zak}!p^9*A7$~`qJXO1{{<4>RiQZxV)s$~;(L!@SbqPqafRo7RR5Z#nVNoT zbSb$~mDuh#w|H_#2ZoM3ns=zN%H4Zr&!58?tECvGS!i#&kJsm|AoxZp2wwuk7v^`E zqjuQyd&7wN9iz(bC@(hoV_Gyf3hjqFOFKdAFL*v-TNgqkQ;^9x@^p*n{kGu(8po@p zOX*V?F(s=}ZjCvTIdg%4uMFfDvD~|pLsEBTn^Yp}4X)9LG;$x)+4_7hm=bu~f!cz$ zKbj;>+#cMv9(9x<4QktAs-Fj>6~0fe48Va0#^n12@#fwn#Z_~POSYyAs_$RFsR0GK zEvx!xd-$S#kHq)@%Vnii<})tou}olQo*ks56E?|SeqL^r3Anq!1t<>6u_Bp7@D8IwxPYdTWCh9 zXQ0E5DxjyYz~rqUGZBz?>>yq{2&!$1uuj(0@iMSeww|7zLx38)YIGZAe<2PCq@|j` zREE^f`lx6F>8(CcT@Iozis0m)c`Uu4VN$W^GG|K~^VMrd#BIj9$eWiFVJzq-pBJKC8qSRu%04)2#qn=uZ+CBBa^#+b^{6h0~h# zbicWQoBa-t^)APbY^Rq`Dq_lY1 z>Q5H$J1UA!ftvMLU*D=~)OS7Zfh>PZaMG#+T0FY)8aAmeUHJ`iZI7hl1!fouSK~|x ziEND|iSSD{*ZR1L@WB_jn*i6G3Y++a`;`7vA)DK~NY4g@b0J*UBi`XZ(c@8xHF!vd zzKmhUEEG+S{ustKYhrjT6y&C2K(*F}Otu2`dy#WqJXnKX=oTlzeWzqMdcOG<6Zvsz z>-$Qh^jggOl_363)4!^{{#WliUEdgujCZcX=vK?M6@E>h`{v8$5BL(CGTB{(11}va z*t+m}ME#AeK~mo)f4vMxIo->_4S;_z#4Eeg$|T-!`y;#t1JPj**%I8cwN z`Otj&2g@|7zXosUm&+rXE$H=707G`k5F#TuyiElW+c}hp+SLFT-!PGEI*4 z{P;v;7k%8FUa|1-dgL!z<$rC>iBE7|qY?c$?WYkThS9r2qNPfBiBlET5W$@3X9@=MYCi zU|`^-KKH>(Bm^Rq>3j$Eu@GL1?wsSrS+ze!G=GZQtknNek}>iU|1UrI=QsabmelzH z(t?kv*XMsek^cL`|FsEMDMYeG^ff*D&-MHNXunntX_4{OMC6a0=70V1|FhSKG$2B< z=%LZRKUUrUYvc}^kQQTd0P;T+J%0)X{@lpXj1a|^^tOfR|D)%Bw&#Oryx=VhQeywQ zsegX&|6V6h8X}>S$aAU%{Kq5DfV2SCs*e1n9r|~@6w5}R5()8%9P_v(8AxyV+;j`+C}#Mc(T#O>`mx2)B*B8S}^1LbR* z?sk5zS_BR$y->kgNkj7v@bzr#F~=+fn`?GJU?gH5lGGqX{+rJIEe(>arBj-aQ5+zb z362D;cGIO_6cYGH1d)wO9X(|Ji`frHrvtf z3`Rj0p}{|${zh)pRH(pfw@GO8gsL_7)59z}vkOQ&Ftv2I2#fTK=Edz9)$L4m4C$Hc z;R6an-!nkLtHuRhsPHtxdk&l&uNT-zSieJZcu#CQ&jz>$we52c&6U>U<&P}nke;Q# zTpN|jfi|LQz;xpLaOmib>F#q2k7nJKI86eaHI(;C?hIyALB zRfp;>9mn@I*GC?rZDo6AcWJX>>1>}F85Q0w{Y~0vuPq>Or#R?R+{OA!S`MecPyd2n z#FA%{En`aIVK`fLc|T0*tFNV|%#v<9^{aOQ7q30HmUU-N#`@5!zV$dbNs8H@QsFC? zxuIaspVGj8Hh4fqCpVF0^^q)05j_k88QByQwd?$aDWNJjt&dZZ_-KD3sErD*@jl%?n4iX%9SU3t(FAeWhBI@icycy{o zdW+lk`UEA`@(IH0@SVIo7zYUS{F`v!5@+eX+nL}erGLb2F4X!o28gG9kjm4iC6%CO zbkc7Br!S#(b@I7nS}vqmK^-DQF#cu1q-AAn5pIxbor8v3_{c)7ZIje@?ueJHeLHNN z?V^w3yavasNa(FqD{ zW*}-Jb_~qxQk8_P&)nc_lj$qdi4|NV#N${7W#I@UfH69d7?p-_zG;W4eEEVrBMBM) zhMKRHT>A1#W+&#^NU+c3{wx&JXy3*6Vosg#FO+so0LyB`F=R*Df_VfKn5f40pa@XH z*ZB7P^Y;u%$!7|MtQidb#W^9dO-hS}a#q2}9vNlI>$e3$N$bmF2=Q&X6J7maZ7J7Z z42LU85kbP{^UhF+$!iOq#tM3tAhZ*>Y$kZ#8G{AwB$@?}+c$4%1)IlI2S&ia&@6 zgS@NgqudV>BE!f9@|$EOkLxO!wX&9XEDB3O1{lf1YJ&7uS@T|XyfH$lVI4H3uHkHG zTvu&R+B{U+{FG1JTRx$5_m)OQ482`3R@2)jZH{-7i{%^-5f$gzZ+#Z*3oiy3{CiM( zT2f*TOm{D?@F#CE>?u{%-P8?@w~n0B5?n}RVpn3=Vtk^JRNC+KpmSi6s)RoS&fSLh zAZH&`9fA^xZkhe&uaUj#YM6BrT48PPMeKEz5~uu#*F;DF<>HA&s#m4=HoxFr$T419 zg+TN2riu_p>$p5{@2d*Oa>ByH8|0ys=+}mnw}t&UVb;@MuDKilBGz~fzBOFjo_Z}Q zSq7xq2>Pfexl}EGeOOu9nQj=}dx|*>NZsFO9*O1)gY`9cyQ1s(g>rs;V14;r7FX$b zA;MVsH4{yK4Ms)z0_$2xudd8SJFCb7Vaw_--MI~nT95+Tqrk>|zk6PYywq)cMbkOd z9yi{ys~kchPXUbkC!sqEDiZYz`Jd=Qw?n&4Z_K|o;5Bacx@SvRP2a96?cqPau+wlL zMfULWije(GTdOa9_ms)!aMK~Hteh;0aP_G(BKjRB8|uueCK#viQWe4)6aS{Pt@g1o z%+E&!g=`0gso0m|i1Gmk_TEv=cei>j5ry#@;AZ33w`fv_$S^|q>Q|j{GK=f8{8?v4 z0WODmyDZP>rgadxQnX!G%DD+~RAkMM5^0py8*7A51e0A$)eTx{fwMm#3aH!PeLe5r ziq$D-J=-$#v5x0DjBu4w&N3wV@Lf^Iq>4;oV~U#CN~bCU?|0nZL%JqJUF~(kJ1{)_ z)@^~O6}O0$)bWDl;?q6t8H-$Y*RYP5#LVXWy#$`$V?AQ8=X39Vqx3+fjvIHDxJc{5 zleO#r@}i&%l!g#2A!h$lKJ7J#ZWxd=$D9P)>fW#SB>tkqzv7r!Vge35^qRCh!@CYynRFri z2}AF_1yvXtD4Q1j{qP504Fl{iB+w1Uu$s4(XY4-RmIIMkHbx#<{fmtky$8AN1N_9* zkGIFwVP}|o?Is@)A1y|-gIfxqO`KED2qThXgFliX#9!|PWnP?tN&)FEWt!J1N>7Wi=U2z1BE~8$U@88#gEd5f;?P z`;+d>Jm+l?gM9&ovx+E-#Bv7{#6AtY5ignv) z)m@6fu?So5y?es+jjHcjOrS|0oW2;ad5l8nLK@?6$q_c3%CT#gPEj0fM`&m9L$aN% zgVBL+b&6J(;Hz(i>CP&pR0!GrrHL`Vd5sV&8zm=+(BGm)*DVK*<2Y1tc^ls!It@f| z0!x;!NU@j=EcvVrLZMh2T?2XSgBVt4k3ArXyml#wTqO@QM@zw1N)7G90z;5b@^f)d zok3gt*NXo>*$n|ICeGV^9_1i3PjwQe+YMf_o(`$^K%ce^a?fTAzzz6jSRPZzlD!-F zxPDL)m2-pYgV+2K82|ACz#>ek<%9b;>l*dPA+&!|CkJ7~;!V_!Fvk!wG?B3hy{^`Y z^ywUEo^duKEd)PICD44&T(QHw9V`1hdm7FJs64$M# zCMO@hoiP1|h-A$K&AlY~_1MaH_x&5&(e9E6P4wifxP^9_TxvaZGOeWPL~Ok!h&M}H zY@aN*^Is{WAoQF_aTm)gi9S)I#d^(50P|P?8_$DQ)<#viY^!)c8Jcy#$9+(ZX-n8P zCCR51eRdvASnk8uyTj2jKvd8fHgS73vQ@QK0Rh~EU9QYUHKITJc6+^zZcVnOKtqQ! zih=g6a=xE8NpnH6gR=wQU4#)ZWO3#fq_CVzOUxoCm1zrq@j5t*^t5SnWGEH-u@%So z=Dhn@8*oBU$~)T64z5|DDjMm}Eh77_q#5g)Iddd1Fk<#1%6Q#G21n$X@$D8%lbXp9 zB}6&dg8n_v`Tm!UdRTC7*H<2f=PkbB0C+XR4tj+aI-Kzc`Vg2FXB7Y?s?^?21IG>x zwVsdl3TOmrY6gj0N@GSZ(5r!yRwz9z`WZX9zKCG@Dt|r^nS2>o$QOUn*OIE>)|$y$gq;e98Er@5`Q9AwtcC6xXfnzYatb-}muLjy!j$A!Gdc8Relh zGlTy9R;{4T=OuhBu;DQAr~YX|IeR3&n&w6c^$`lx*ht)AfXnL>T&{{yZKLDZNP_LA zDLT7i0ePSf^WAORjoamL*mqq#J|ecyq+TH#z!_C%i5H@f|2k1bJ@IN2b&P8<@r=MJ zK>>nxI&{}1OT<@S+L6fqAYSBI3gpdpwafp;(?j|7>sMOnl2InVyZan{TO#&ZyA~X7 zTjrmwzwofo7e-|V9y-Zs8#X?HNMA9xs!b>MSeM52>5ou6)#*ZpFPj;cMTnp#|L{ zIbx~r&qT5GBB!QYFlE|TkR;|MNt%UF3JP|6`w^9ul?mkKDOqg67cgw=#p?HGC#T7c z2#YfYEl&%tEumkc3VL(UKRzPvn`V3?)Y&ei-^8e_Jcg(_@!B3oFn&LRU{kwnBLN2$ zYTO2?`6*et%t=Yryp^gUdbSnem~5WkgWWOIoYa{E-BnM&lcj;qwhj~ONNoI8_zB@SQc>x5Wfax`EKK^gS*gY30zJ7KB} z8>Vp%@@uvE9yLJri?JY(eKpIIu!6rQ`N)Z&_HM_5sd5lTCcW18kDGw>q0(Ns#I0=U zyAtf_=73lJuKo;9kFJ4_ADE>0CEh_n%)cToe3k{U`m)*G=YdVSBcM}PKW-o2M=X*h zB-PBWb>py9c9ij0)v|FudrPUPh3mN)$K(HtwEQ1vsMD40bg|j=QgCb{2qdIX^*Ia> znbyY$g7Ni4xt2)Oa1eKO{g)2ma5gUqcaxA@SsoPsosl@O4$ZqpTalEs1XkGNzMi9P z2@KIB+?&GXD!955Disz`9_8B+$)@AEAaUy#C00=3M;4hIN2G*P*1Ts%+b0ds1qyVJ zvmrKy1m#|zuA7vg*0&!Qf4{p~!liE8p+%*CHiCR_=};1Nm#Ztd}*|6LHskjIqWnaf?sz z)hT5Tlkw#C_GR>dM>`=R{Bn7uio9FFx7DfR zl-Hj<a9TX)-3sH8WWfME|jP(DN2> z9oa*V)v5YQ(zPDyb}tm%#hEwx-Im_#sue{asF@1H5fWB_deZ|m&SmINL|4_g)mj;bWI! zo0~A_zQ0TSN~JrJk@E>Y14kUH(0Rir)uX1nu9^NmdD;m)mG=5I5gI#Dq{Ep5RTg<0 zma<~J?tf;Jv3GnS&u3Sk@LdlrbNvY8IvU{6ASlM&_WMc}MGt^z( z3E{Ha1W=P#Xa3(*{tT1dZLiFan7^3(J4E1qHdPrG$cHZ}5kwNr&Ntnb}82$8%K{erKN9SBeo{Ampb^Y0sr%T=UJU09r6 z$Zk5LnbxStR3jIgZ0hwv$GF6C?!h~{uvdLB*CDehOl-;}G;bu+89E)WaCCo~HX-W8 z*%^`APC4JYyT|qouvR28=UyWN3CaPd^>oz8ZoE!*mo4`0rrl?qjU@Tz{v=tr&&ZFki9!gk$4`DV);el~ zdUD&3KRd6^`PyEeuaRH7cAOljLL(Ac=Pv)IFnjh5%Q(_(MkERwd8VXCK?#`^tZU+Ar8wiFr5F3p z@o;q@D(ZZEHCgTK8Vrk}e)g9nq2TL!BV?ag-uuHI+LjRsnFt!*h*C!i8Gg$8W`zs) zL|6Zcp^OhtM0n)o)pA%pyys8EYHt_DIt0_lz%NkuPT@B>a|gQKh42_cfE!~W?Oh+N zqcw8uStCg0J3l((O%q42em1ab;zdB~mGZ^&QJ^qCn>Iw348J5mF(baRur0i{M-QQB zd(MqpZ^!W+J}JXKT~--xWIrDQ{f`Kuruo%sZrB1<(E;4KXX~fez&7yyZ?ZE9-@UGO zt}h@?PH8SfG2jf+$+!9Rdi0JXx_Zldob$zB+Sl8ZdX30dagi6^=#3E=#(VZMbBxq; zllEK*BFkY9OmpQBmk_4PiG|;Db9!!bKS}3ZrEPUbV8Q14P-u#WRZ|@PFP2YE=Q7*G z1RoIPN4R512sF_#d?@R(K;-?-f+(GpU6nFO0t<>|pc{nDf5QiuAx#Kr?`Ra^~tpLbWUR){fO0nK_|V4!01h; z*0Lq0h4A%iv!aFw0+sP{EY&RvTQQNBN8fO>rUTyfT2TY%|Y`)ytXd%4Rtt^p3+wMZy9E{Bv-8N)! zk4%@*`?Bd3uEj5OEfoVA^Vh zH533S@Ugfk4KT6H(@o2Mcp+)^LQdBdy*_*&U0MdO9{wbElGxPj8SYjAq}V?Y=k~+v z;hDNx1DX^fygVXFKS2dOrGREjTfDAR)ifxM=Dy&7Gh{Q(Cuff(n_jY^JAx(}?k|rJ zjc6VB-SMx?2w59a8px=|VJwSh4B~IZ&DS4Z|1uPTyra~_kgcs&GtjD!7oc%J+ykzG zCSdp=Ca>%B02>uJyr;$=E5(RT9(x6bJa+Iw3mTmNX-ysnWh981LvY8~)E7~cYd?S> za1Z{}-hAb)ZC?oBXYmRD6!VhZ_&5CA+UtK<#b7WGBls{H%odToNZ(!llBxN3 zq}jbn&hn$jWUIx?eCglPk_+t+f^p%=9YpqmYWlY=j^!a8TuL?+WJ~)Ghcs~oagms~ z>Y%>*D;W7Vli*+onb!68hXv2c|8(dL|5T}JcqXk0{ZCumt3r6s>+QL*$d|U}|BfxK z^Mwq(?(rl{_yxuEZ|(=AMr`swZX;|RZU$Wc>54k~$8F@uU*?eVKWwqq{u)AjUez16 zL^@g|Hw-{=!UgMF zrD~}cD|qzHM0@JKty4WWE*ziBZ<~kH>2V%cm!oH{T=L7Bcg_pqgGkI@>~C^0xAO33 zlZ@mwL-c$V9NZB`6RBwcug|3jGjEjd(DJE`HRww>Y61Ks!{Vvl)PpH4WX9R;#;yRB z`L9jr#>blP{!T5B>#zdTx8ZK~%kao7t*Gb5?3U=Dw`H)l$%8rfx33--8Avj*e6sa~ z*j`kIB!^v?Q9zS2EHXVxiqe6A?D#6CpW63`kTD9W6Vu{FnaTJGP3Q zK`15}#><*Ao2UXy)_sm44YXZPO9#*zr(gQ4XTGIn)rWpRc70{=C)e)~i&B@GN`#DV zE8TZnW^v5gc2E7V{>tUbAviLUNl#yYWH+NlEs!m?Xh?%rT52ep?y53TH1924=Uh9W zr&0S%KO`fK&kn|9>jPmc`{q|P*@xf!IUF$fs5n?IRbzS+g7Ek&Y|xdyk;yBh)H_9M zf1ddG=hRDp|?o#6oUzJqX~fak(Q9#Q}h^c{=$EKVndA*P4j z_k!9jH85fXX9)Okg@VZovQ9;Mrci;Y#>Oo!)fh@eqD@iIyd12h)h6X_ve9^I7}n9K zoP%htrXqw#|?!|2zTxSWTFXdGwJJu1;*)T^R>@(2GE3 zzljUR!j$I=X?1f(;&Bdo+lqCDoONfR?XUtmTD`ty3mWd$OVb?4vWJJ1Q*+oDaNpmq zmyqQ|;L;lt4xLILg2u8tx*QnRHZQE;GYl9mnllAvBp{jo;?H2h@f3t#ONJVXHr{jh z-mX=_ka;Tyc%3bAckE)OKa2w@1p1SC7!XE0=`1r{O-YUGU8VnC7uxFvw6Q(uNIKd|3jceNqW4y+Qp7E?q{;>fPR4;b0!%nv(a<=IW6MLL~V% zLr!^Fflvej?5_=Y%$Xd_?v1y!S~@FA;q83yj6ZdZHO2WN7dbOJXyijxBBElvm6Q$0 z+kJ4GvwLg)4&e6#U1*{J#YZ^J99G75I_P^w-hA~A&Zkvh_~hA{O5sprx$faGQan9N z2jHNY*zuFCcC7`PTXwvho77yEK48C@f#8b0`gwIxW!4?pj~w65-FN6)a=O+123EPj zkJsK;&-9JU&hx`5qWsXD;}s!2+@l{VBgS3qN(SIXVZ`YV%alG?#Fsoptmk9qg+k;!SmGR>F8La z=WAr76*@W)Zn76P89q6n>8#y96zSm_uOlcm_yf_C93+83-N1|rD*M_Q3cd~FDVO;j z;z<>$s>47Y?Hm5QxwK7JnbGq-HpDa@JJQ%raXyu zfg&FHPhSnyFNQLZ z-Lw43;+570v%H_S{GJ(ItE;O&uI~L-J^XmP`y9Jogk*($4xW7@xDGKL)A%aCLEH24VJ-T&lHy5OAp;ul)a z&pQ%K?hCek5D^&gIjkd9BWz&K3fWyxtp(1TaX(+d_QaY{Nqp^MmESNwEHH>YktRZY|{LVZw?$+Sh`B=t!(siC{Au$ zl;a?T1LmqlHk6+KF0n&2>5x&s*SsOK9B@cM1o0R`ztD6|H1kH71aTG9D#YXxa!V$r}=KGlD-PXu{Vm4i~yJFs-O$LX7pK+ zNHr#j5n=g`(%g|(Apx4OP7$IU^pucpHkED_e4Fo;@67+z!*AY;q~yJY-^BNH(P8H} zYh1`d>+EGeSkZJ&2*$aZ{V*E<4+zO6j!onOEK2%`d{-`92ZB}8tPV!7CGVc$@NLsZ zyT~8vA!Mx~AD&;v=eoM_)5#_9hIYGZy(HXzBzeMFO!hrs>Z))(Z?3O+6^$Kyq|d`5 zhft)c$OEZ~v%TZjN=R2*@O0eU?=1D9d3iYnZ;uW2NG8r&qaXIq;AQSuW9`4a0HBrd zt39`lE*6MYjO)KGBpmE=&_|M3y_0@89c4{SLhG%{J}jVF=_z0Vrej0-a#$#*Ycs`l>0uhvj|icfQ%2ls3)oQkNNIN>$-@^NZC7~X z3|iaPz)XR)CY5c^FEhiGL!!j?yD?iSEn>xtnHw_L`hr$e(H$$sm83C9`a<$bHnc-M z0dxSHpeh$^0xp6!QBSDNj^S5~jccvORNYJ4oSpUdfRniIDIti!&Y>HB3R(r-WJf$~ zw@NYfKEe8*LF)47z`vjQ*3c{vOxZg5mo?fo!R70#!{QKSBi=9#^bu2-z;lU7v@(E1 z`I)DYGE`|6MhM3TAPO`BdjE)BXM*A?(<9#a4vtz@w>>;OeZMmZ8y8{fbYw);Q!^z0 zqoGN$&>|3@sxJobva-LBAD+KTX*VWO*c5A0(X-a<_yL(j@mJu-$WkdoYj)cw@r-w5 zl?}GNE}QUVn4!3u??!8EM)adoXyitJFfh;?VA*78;Q0A@*gm?V>XL?vb2f%6*l!+H zIBEmhi}cnm&P2sBnV{CI!i_?nzbBINd=fIrUVQaD;IWv>L}CzxPJT4nAB8A-dsrQ2 zjOZ3ER>$?&leT>0*h@gq5;47f4<2GtHOx1PFPYbECBa(Y$~oB9#D*|wVINCPHcQMuub`bvD-|dI7Y+!uJf_i+A*B?a%ZdHgJNc8W|qawU`rM=Gh1jeGm|Z} zm>Df*W@ct)W|-~szBhAipBpiM=HEngC=}6MUAwERv$8VRS_#jzOb80@k8wFjAO|Ts%Y_u?vZ2{#z(rZC!wDzn;jmv{4~2 z5G~8oc8R}!$EftfomzP9Pw^Rx`7-~jHeedXDlQh3NnllZs&zD#EMYfS!IaxfmBD4n z20LKN5sn|&A#Z{$a}ymZ8vQH&^1k}DT?`}2%kxFx!bHH#(UvT%3nnn($$?bJXy5cx zMo51*UVsl;umolynx#j^EZ;6jrFBTfV&yCRbAnCZ2;wXdIPyC1j`t0 zrO+5bdO_jgqv-VtwFnl8@hyYEq*~%kM3Sc;`?M1uRyPZf8=L8b;48v+add17`-YEJ zko#cp6tAPCuqpQ>ZCh^f-gO7~Uo7K}{gF>NVf%XT%Ge-`(q`@GdeGotojt^9aQW#} zUR27c`6(=SN=ffZov7fAX@k+Uw9?P*pg=owSI`+pq#iprf=|oom>3jvZ_o~ zhJ1`1CKqJ&-Plc(8oY_l0U%Sw!vKC}Iv)}PS@m68om^sMd9B#Eix!}lZ|~^Ro#Y#! z{95)D=bLBls`l)+8dKBHQd((+cu@jS#&I**U!l z9lJ0I%kP+G$o;z&05}wxwO|@nsadrQmA6auE3wuHW)p{6mt*VxNXT2kQ6w=N1yfR- zl_Z!_xgh~=93_l}D*TB}{lvK~2qr!g#kQ6s1Fik{uppI?5TU0+2=9KA-mKsjko0b}2lEZ&D?t-8oWdt)D^CG-B z@10i%Yjy4AwuxtsVshR1Hu!!Qo6lkoa>WNr^)8pI%mxX-_$m}=#1xqz!*5uAVp}sU zc7UZOWV-{%JT$a&&0rCnS)?;eTJK&{Lau7(Gac2qV4)_fhh@(QxWQB?5uzhTFwbdC zGa^YZM;yaw>cs?(*26gR=ZZ<*XJNx)xpkC=MsU}8itX5_+F>kN!mgmDLFqpcyTC9t z#e(60edUpIo$z)&9A~S#I*KsIj4T*a&qB+|#&j8G6R#qXA5QKe?eQ`k6#2hqSOft*M1)iLx1TYduZ*{lgbTA3;OJI7p3os>mIM%&*->gAl-;TO%fprh&{#yN zmaTUZ+F=E3`jz9fpE^CVKQg24P5lWwwtWUUwQdh_kZN!~11UpQg~>AqDXSYgmnZWB z%PkXs*)J{I`WjSi<>y76-E$H^#YqRr3S<`&YWvmF%6yU1w$dZ!RvUtb`=%p;V}8pI zDq*2^L$TDg9?CUTOpU26IIjc$)U{K!Li|=&x+@WU>a*qP=cZ(OM$TZHcCPT@d5erh zMZACtHIn1rFF$8p$D=gfSuVtfi0yt_X%-YU0nu`d{v^|guO+kp!w&x1{#}9GMzSQ8 zvyF3#4C@-x))uyPGaDH(rT}1u92ABG3Mv4BFZh;YW{0;#swzTDCUjzBlk&$darmz_ zqGI5C1$ZTVGV=Ct*QUjY`;KQ&hkIt9CkU$^ZUfbtEg2CActf-;1-gzu>SiykWIH&$ zyfhV~(|3p6geQ=5m6l+F(g#(Cb2=WO*)v!WgLGnXh~a~3p!h2n3|S0JB2e?TL$r8y(dXy#)W*8e-ouDb_rs;R2W?vJwm zl>@xjBIqzzy<8>5v!5RSC~y5MaO3LB-G?o$jCs-PG|cH=G2Mr)@rI&VBBmWbb>#~l zd|6@W%7g=7SV+|od&X+ZKbt{8WZOf#KtFaowm0-sMbb5I zRFA4Vk1)=zn9%zH7<0H3!b0>BFE-!GH0Ha*BwkAlX+&Iofk`aedpCeBA@1&b@B)4FJH)`_P2qJlobH&CO@`2pO( zn+5K+7Zwc@bGR1-P(krQqm~Lhr==za!Q}qbMEUq-=W8X7__fPavCz z>D_F#bqKxE!nf_8OvFmbM3q|x(8WvcStT~zjx&=y!Gp;~HbhS@#UT?F&m~hIg`k?4 zO4F;*N!`NlV5cVNAM-SB=~3pf2qCOoDUm8H7(vY%Z{9l|!BKYw5e@fDz{)-q=6r5s zd(4wLOyyHlLPqcH3g6PBkTHfB_ zFwSB9PYB>}wONz4l(P;LdHPc?s12kl5xi80QHq0S)8@$Dq%G>9@}q8cg<87s(vIm{ zc9h1fq~pzH*M3H%MCL3C$|KL*_954hh+9$VXI%TpPida{kovEw#P;qM)w|pKEH01l z40Z4Z7dZa!Z_j9)&$_aYhwvBCsT9sNb8HrTD3~O!ql>ALfrf4tMGtoK1_9C@ng}#Z zlp5!SyZlQj(b&jdL&+=fw&$2OX;N#t3?}YLH~|0fARyWFL`D|*=Z8|P9I}LrKIwN! z-`4U8X%M&8wU#Rp7PiR&i&kM|4q}jWgzEb1 z7x46`67S4Tp(^h0Ff0nY!52+3mjHC5(eUdhZ8s8N`A8_U7#f0?A%3z#7HqOo7_s~M zrmW`_gSvp|r|l~8qCKc*ZXUZ7%C0T`p3nuO9>*AK7t43d{EVnP`NILN1}Q+?;plt@ z-wxo8j*gQWaKRqt5?H4ZRPhvuNDCrfM;7s0kzAQrcYLjS34HY?CHT~rU^fVb>S$VW z8~ph2WB3!j?cYhgnjeCE+i(mz?Sj&!*8l@>ildpOU*T(b`DjlVAlV7&B;bI7*i-eI zg=HL1@L^)|n3`ar=AD{O_NJ>6sl-iKEB4{um$TLU+h;-lQDd2-K+tE>SWXc-X8qMM zDnz8*pS5my~5FH zE-&gX)q%M}=)XGdU3i(aL|C2)jF$MM!Cnqvc2Dj79<{FV6I@Oz>h#!TYf!r2qk)^c z31F~2KM4jRM7&;na>C(J6d7DYccJ4$mdY3wUfDJX3iNADx{9X0aJ3@jkAN+%fL?Y8 z5-wHag@UCi`_>D8TLOBo|Ms(9K3e&*?*ykpdBY-^dp&0jJ{`Yx%whSt1*Ikia(&S?9+8iwZ;z&e zbbM~zx1A=?W`(nm%npNjSN3e`h^_S;Ex25r1@dvUn7erkV3)XF*;Z0kySs@!QzQmX z@XTmCd%N^YUyOn^vmLB{j|?=Ng8?vyOKNf=)9wC!0ODmUct3e$FL}e#8oV_}Z)AEj z6cZ9h-@oKI=>K67x?66;!CBaTvzp`wi+<5j+GFs;q0j_pW7T>GuX^c`G=B_@ZH-Cc zZP^4F`D@L(kMKd8nRrWY{Grl>)GCmuZAR|K{x4)!!4%9wA#*Q=s55E>cZ-hn)8NVZ zez6fSInZ(Itb_ID@?-)y&J0HkT`LqoZs$%6xQ>u7h`~Vhhghb2yVt?#Ax*~C?_xB7 z^|HYkoKvy8^?CL0+5j7E$bk9>V{gS=ivB-EOR7xQxXG-FbNJH;$Bg>b=&AMNE4~sw z!Mzetl#m^3ueM0;NW<32Z2ew92oPsoF$761+9Y@ucIEoh7XM0AW|l#J#a_T3cPj1k zZfIQiT2@?)vkD2p1yFIde*!Zcj8I4|wg>1Sf0kEz^=1pfIx6!2_#SJo zvj5COZM>oU^B8Tv9z#t1_f*rbAFy%vtCYvmsuy#@f6X%gc~d}zgkK5XJq{Dte|x4s zusHx{6VMT>5Fo+(SLC+7KW( z;s53Q@Av=JYU1Tr3opaEzq|k6oBq*A5EX#^m;htN_9hJHdHmnr z-!CCNs2Uq#>;jCUqJi$U*Tq67yP{?Q@x@`_I~LlGvNJ*YzjVIQhPlDn?fFhQ;RGa2 zktAyKm0NrHP_dN{`Bj-aqB*bsRtG`$_sq6NJPkAG zZ|nR&v;7SNaP!yK*ZQ;)4ukFynGjvjQf}p-1wGH@Lk)bs!Br;ISJB{tQtm+Xs!qop zgFkc#d=oIuZZm_XwrGcx>!)+1Oc6DGf6zV@og}2r^|t>!<@%EX_dlc0102P&;CoT& z2o@n=;1BQ`*mJHX`@C`O#7}K|S0^{w&j!zar=gFb0s4W+SZmE>r1u#&oWw%5sBaNH zoL8%5(bTemTc{>AK#eRxGGdCb)cN z6c6sY2wI!XQzUULRpK2MihUy9Xx%JDVTsmQBSU9A8DcSG%t*%-Z*T9J?boNIPd&`{ z$CNH(oH!DK*AbE>k$K@0VB^$Id#l#OQ9B6BD2nqG zX>!h{ZLqr&y54y)AK644`9xX>epH`WQpoK3#1idQwgCY*B#En%%{>Yk<ND}$WK+!z1}8ld%%`f+!b29V$q|XRH-Z>f-o+S$3>PPR!M>m(n~&3W zR=l=i<;^ezSbR=U9w)HF=*e>G3K1FQpom`&L39y=lS@;qy4pT!eWx;E%@n4m2S=Sj zkn@3(Dr&Wx`;@T+2VcLeTe8&f$Fi2m1@;xfzmvPL zjcdtFBaIFGVZ9dnK|Stl=G{EPz2+?$CoDXC*9io)nZ1lJ;di~%U)E=(76h>_IUP50 z>H8%6Gk_>Z zY+#zpiWqNFd77L^;mDIf(DMdTJ2i|px#Q|LwWmj;2>6ai^s-5%(X z^#{ECzy*3c8u6t6P3!Fv{NHK4h_qMlXptMjT)_Yq_SxH0+H9Db(-CYtCyc1LSMW^( zameFQr3C-p>8I_})lbEa8~zwrlg?wNlA4^rX(t9GQ7>P3#b4xU%s6uV(Q-4{KDcoW z)yV|TokTaU31>h{5#5gE_h+{!5QM*_ABHPSOd;c*mCg3Z{xLb}NdQRD$#O9b3t`fD z*F}3l(8P2z5Q2ZRrFx`tuIbxw>_neAnM_VQ<=8TXU`(*se&Vv9y1+o*!F67dFl1V2CmZNS@P@cQC? znm^@RLya=L8Bmw*Ggk0*xZUn2<}2B=UdV00M-0r2W!8$#NbAW*dsJsd((dRz9%g9w z`Tb2M#l7p>clSR`h=y?HEI`V{BbII8O{MiGs+8(Ze9NCl7MPY=w0dd))t~|~o1V?2)X+B0r^~5a9m^#bnK6)k zb*?UJlH)mlRHIL0C=atBN2Ia>wPPmK+ElNE zbEU`{sOaX6Cs-eDnr?po?x5NpMQ#y9O&wfC!oHHaYNJLsr%M<)Ylo=6#~#~427zmW zg|w_VXi!?Ha2?gx|IUW4@v(gas-^CYy9-lRFoN|}#P?L1rn8EAfRJKpg~3q0LdgEv zbKj~jq0t~6V!8C9?!2*nbs8G0X0_y#~}1 zB1uyjiOC4eJL(+hkpu+h=n*|23?{{gzdzyk!;BQvqye18FPOwEesUG+iPef zVE6uKN&#PgFSBDoL4+j z3Z2K!akZh_rBQ&#|9bj(hsYAOE z8O6Ea?}fW9eY3pkKUbyeOU^O1s}derLYja?_MA0GIMJ}b%UTV9bX0mLjS3A#i~GVs z@|x!gfF(2FmRTSWL?ZSiUQ2w3US3P5@UlouyV{SAW!ep0qTE3%X)1L({+qc+Pcs2w zYEdZabfye0MA(r>sc|#z`!A|9XKVm3f%6&nbYU`2UfJnbZG0tVb;X7hfXFUSSI5Gc z#f=@Dh4c*O4?NHnGc1HMD=cu1EUP;6AYt05OK3uR^k$vUN0stmePL-bRds;wz^%|8 z6d&p;iW&|}wOsQ8kM;d-*R)cM<@|>j-wHU$7ly*Dd{*4RdbCEFRs!oa+>z!DSOd&I zF8t*fnl%9dD8JUGnhMY~Orbrg=f|#E-nD3p7M*T@hpaAZ-ORDDTG3VFNjXHh%VlMi z@>eKrI>DQoe?d%O)Qw_0?3Y(*X5Y^Xxh1M`B`T#lm}`-E7iai0>6 zNln7vr4*qoV;U1ndt{L7a{HGu_uiSeuY80f5Ry3<)!QrZp- zS9zzBZ&lW@-OUR*c|*rn)V^<8xg$>;dsYfZQ;{$og@VqtTNX-jTEZX2z{e6*Xw1bR z0P#t&(75t9B`kb;=1KT*n;TRq8miuJCIq4`0|Ly3eFBq|280+MS?rS&5{F`1HRBFd zXh&3Wuh<0yHtxpEce`<&dQ1br(1#^&enN;}#q}h;1A44{FjAPT^~2oKOqrN%K@_5Q zI*9zRa(zI)XTfnv)NLT568L!Qd5emR8*Et|Z-6qh1+>r$Z3MJ$%=CW9OVwKQQPfm1 zjQnA!W-IGh5f%Mqy3!-VO!LTQQ7rY7ZeX&1u9L`w02xFfj&iWh(u|+9LYe zN6(-ZtLqu>^j0^A!%&`1BlW)qvcr=>k8|7(!t@O~Kn5?Iyq{fv%*4D82z0AIf3{o) zjPPK?cRZ2TI6Xd##+wj=-?09xs0?*_=z_jxn=H_nRbAY)j9VDwVL$s>cdBW7KQPraSPTT8__zkt>Ro-{EKbkOyf93S%t726 z;yDaGo)95kcsF%3GS`<#s-L*i;y3djmOYMkV9&z?4gE#|J0bh7v-;i$c$@ImHY|>E zO0ShVc+(wJg%k%Qo{?p(F*iz=8>N8bxvd(g02jV|0iAk&>brQS{*L8*=4NT$KM>gy za3EhS@`JJoYj9Vbpr9`;gNBXc{}{)A99l+@B~E{t{Vu>Dc^q;xMrhxFu+!gvPt1-< zFI#>eQNs%}TpTB!uyBRu;JDX#JQ@DjPYk*`>Wg%@dJBggg`T}O&Ve8}bfrfp3*DO^ zDZ6=-j~R1wFD`xisAn^RHL2*u#K@OAf2hU0S1-FM^c8fDAmerC3xyW5^z#k6 zf2NU;_|%ra{CyrP1*6+Ak@VdbCO=4Xzy2gR+7+JAhCgEKSb{0P;LBIvIdK;kK#WY? zYP}8LbhfU4{Ik$qd|;tNI>foYB+p}VcU`}9T6kPdcY}Rv`W3rs7w8{zVhnX6v}gw> z%-C7S5`I?6P_P%B89u8SvW2&X8U~jpHQqJls|^l@q?MQZl@6aZDmlXvJzY zgL-48E?mnm_I|@Y09vv2tW*cZIpNW#V^mE@o;dE6hOFy^x^t~H%&EfL-92ijY)s`i zqSf#^MA_xrxvHy`StH&QM| z!wZDr?Bc4iRWB%Ci*xv2!VI>AP__Una=Q^`gX>dA0k)>CQmq=ckjA?3lWwUmu#(aF zcTgO>UB|e+r%V)}oudOImi)t3F~P1ca~1RM-RJBSA8zXg>HH|q^wOY;PM<)itgjW4 z(teN2cHl$Tlw}6-G@)VqJJ+Cr1Kc8})ethls#r8p(D~+cH)@L|u~K+sg2;wsXmgPm z_m+{y_Wvj5vN8=qfK^L|rtBUT^R5`#piuQ9hUpw|1KZJY zq4&_xxH7Fe9{$IU+*SZUY1s67yK_DLx1YJefP4Xm7qULmBE>yy{d<@NAcHjxw!~FN zq4GE8@V`WvfWT9!0Ah2L2Gulw%>94s{C`&GR7yaLef4TUsmi~X>wk@J{r`RZe?C^D zS4+mJJi6uhp|YSb_Ke}{w_gCj{Dh1r`>X%A^ei8cddi8nyXtzuvmZE0^FRO;R`kOx zNeZ$i#(jT@_y!^4uAy)xK{7R?p|lD_HtF5=w5h+veV0;HS_XxIxmP}CVkn&`>0=-w zGxJEZ)7suA=D2Npb@Y{`=B%Uqhm#rfwX@OLB&7pc z)#p}^K!U#6YoU7^`rz{NxOTUyzmI9W+Asbdu-(u>F#|}GyFZ?oIIJ+u1 zt+3A}3o>Y<%HbVLBseVi59$lM`%k=3!H9d2T|v0Ar-=#e1-?_{(}|*t{BI7DpFek< zblqs&%3%5u!s3B@%?u3&^g$&R!!yk_)Ted$e%r=sH%f(Ck*}MWc-=Z55z3S`WH>{( z!YYp!Ip2iGCp9}0j1~J&Ki>8oN(*xv4!ewP%=Vq>`qO`&`=76ClwpD*A%lu9B??~G z!w`3ZNc$-=<8pOUTXHpJxfCf7QZXK~E^P~W4x7p3Ossmv03^FBmk4s1{`$M>t`Ecp za%sePS77}ev3v!ud0ML-8YIxkMi@tB0769UG z`l)w@hw4F`iCU=I4Vg1#-%AmPA4WkfaTLwh+D9JCz;f)i-Wz9;$=5Pts>(2Ti@@~C zkG~m+4F)7Ct4H{W}gu4KYWm1@iI1$GQt@mf~0dkS9Ng9U|n8*l=1clUJB9M z;DR3(bi83AVzZ+G8vk%>94Y_c)+k@vIu1Hx+gofkz=y<|7$0B-U{_el*{hgr_;t3) zIp7^5UuTFlTzDZ*b?2ny06U}w@+1m1a^s^wSPAZF?#Cu31){Rt6Z3fy%8*4c?eC7^ zLt%~SuN5#31GCGw;*TQPLprX1lwa>un+EYVy>*&wh0M-%HMXW8wjUhBfW<4Wm*xHV z+hncz49q+hqfcOJYaR%ox(s&d{EL&I@(WAHgogfuQ0_@Wx~Fo&+Sbu7P_-i`i8D)} z1_8qelQr6|Q-7qLTA2^P%@OtKisa43FbUgck|xx4rw#^Ca}2Vet7>(98i-Yfr5_+e zadOK*l&?&OU+pbzW~1GHuq8`ZJ{D0{+!oO+O61n)1}xh7Z_dgiwl-sWxI+@t(LuH0 zmpSlwuQ!cHmaR5L)T}p*4)tqw$p3iEU5!%K8C*KE5(y6Ofg}2KA&F+vKl8R+QsOaP zu3S7?*FJa4bL4*7&QfwU7l^xHqv74kq_5*dG#IivoTy_Cev4 zds@}u@}B9AjZuK>-H&PG!B?NZs#GW&9M#G}b?!On**%ZizPOZD8kjzB4$bsq66Jq{ z;y$Y7qQ|Q@EoSJytf5zP;Komm&m-X!8rt{o^sP$zU`^t6SVFI-ue}!Hr&U!N(2P44 zFDXP|(3~91tH0y=a|o|tbFql0>8Xi!|NO#vZR|6rk^*>Z;VQizQ#RqfsN2yeE2;s( zP^i+2SqX!oY3LBwmJ&6f?n#r5dPo*qEwJL6YHY=ZZWPza^-d;_ULC5)5MJZ91yA07CeqN;zdjl&H1AF4Tqys?pyFp+Tp^F zcl2CvDG#xXEX1ydi_4HGY&lu)yepFT5Ep4#sf715`lUJzF!WLT1p)H@gR8Jpso$Q3_1sNkwj-y^g5^W{- zgV8_c^sY8kV~-Pd=T}s8%+7+(&X!|wxng;gV{9E%oyWd^7oy{UZg4z_pbWYZL1a`d zOV-H94?7oYE~vbOWk$_2tB;P$3Fk>qn}J2mz=o>ew)U3JKOPXcV`H~w=;%Dbksko( zU)5s1N!D@628b7!0vA6A80@J%TK`mOR`45C3Y^-?&_haljed85BCvL3-K%6p{oC+I zqE2AL1*t@Kb9l64;vz^!a{}V8S4}2l!5U_j7!w$^9gf| zpsL4BU8S6d^tJW83(1vHu3O}MeqhpO>?kJpXVR_FbPq@Qh;48G^});INjRpZ;+1B6 zB{V>pv4pF}>ys({{12om#OsXLJQdM5RugOSbLEPqTrPg3`RDQm9b_z9Q%&fE*D3)F zuB7#jx8`JmDni)w+E$sO(+Ylq$OR8JpzfD6HSHpa4`c7<-yR6^%D_Hmw;DCy<<`-C zuorKnpr@z#ia96q^M{xa7zW+587Bs{-3OI2jV1WgOKA2HH=MOkjtJhSURNM}9*M*m z&;3%5Nl2wPQ~D;cF4o5%MDaf<$nretPR#GPN!N`mpiafv1h>@$MAWo0j1Hnj{+d;@qadZN)rc!{q((L_HPH;8`0Y9aM;E_FLfU8sk@QZQlordLKtH<2A7nWJ za&~2x&oRvA@cGVi6`3D^i;|bB!P`Q9TFTDr)S$;lV3ia*V)^FDXAlWh%*4jlilrwD z4%%aXBs2qt@9Hw-S1`Qa40zBSz@j{oKdt^Bnwd)N(&-wNC}6OF$cU}5xv}=3UsW%w0W#QC+$NykM96iAIq2g* zF!C5^26~X~Mjlq76@6zXI?O`e`=&U5S{DDj?8ul5Xkrh&X&J+OO@`2K(>q0=pkAb1 z)Sk7Fs{X!VIuSFt_hf1_%yd5A>_Cnx1t1FNwhZ=1rzEx()zkfv+ILR#*|rNhZlLPJ zW(NS0r(W|F`>@H7+WwY?bklYPB+_jMV7J!^8}9?`}N8^7=0=t!5OOE0T& zKURU?i&_rI$(!A)&evSuZWT)Kfk=b;k(4q6OHD20VjA?t+ZRV^wyMv>bxf!P@M@oM z7rZh%RuKLYGxh-)Ltzq_b5;qg*N&C~^5WaHD_y*T&sC-f`-!NR!yfOak?h?A26yGq zdRlCWdC|4OIV>_clA>8D*>a2jbb;5?&*v9Ly4CrXFUm~r^}j?j1zju{7DN$O4<14( z)6ZzpJCU6FgBvxiBaSx3!x|y|$G#avkMUTpi?Bt$>Z&M!$OTG_S&e(Vu(kFGP3#A; z(_opF+KfPG{R9pckBqcHSb2GuLx1L&HFsGV&7dOEw+I3YXwL=F6F2W{4`WYz*ryiHVTVB=WT@-5RZAViFo#(7Cn`YGq%5K7e{4H=W|!rG}wCo|6vj(R=ds zh3okZeB!u4k_t@9otN4%y=kq|n?P=OWfmS80?N%`5NkQoTh_swT7U9}Lf!rtoAxQT z?xO{yWY{~HodORD5!s_+k*+HhIxzZF7_RG9bL>%E-2(98L&*c~6ua(v=!4M!R4xNt zC+-v+ZeZ1deH#|4npzAA1lLU=Rsdv4n`S1JRrGt@Sw_9L=gNkjfulpy!`oF4Gf!xDRzu8P5|FC2!e)#Tl!AWG z2-=>*vRb6-w|wuz7L^uJW(y-zM#j6ib2mgr&IY4tcm&&2o%XvB%_zfq(==xf)ZEL% z%R9051!XpGB5c!nB4_^yy+z>k)qpBNnt%#JQK`<=HJI5(i=hpv-kggNuRkhF2n+Wh zN;61{tSuYtwBGU_h@Bu*B&0wC%IjfvL=Vo@o`b|oJ3!N*a<`t4K*VVZiA!Y86tYTV z6o>mW7|RGQLYY~Q;6`$iC)Ul%{Z^Ys$@pc#X7SyW&#ddI&IC>9ztHq~(2?p_%G$AM z%7=jA9dCQ&_BO;D%SUeW!g=fu_3K|0JrRnIy5|Cj^+`s3YSKdO4*{&X&HUYx3jV`X z3FlkluZH=ZHiaLWuQ{$NNoYkr&V*x=0?f!x&zMlsrAd?8MOUZOHb|Y>uyT#A-)2@i zlNQUuOA_~OAKLW^8m%2keN_FmCBH)T>)AEd>O=r5^>mLHAcEi(Vb6Mi-w>+RtydH6%?{nvEYb(4ONQ6yi=TgFg5y0P#6 za=BMon~bD2-?dY=c<$gL;|O1dzr^K*LcV{pOzyAMFJa1Uv&IK7>?HOjD)sg#4e&lc zj+d3kP*z1qq~c6uCiEEQeX--(TMDO2r}_VothpVv{=?fq08;!~z?L7C*@| zaP=Wjg;3ycf7Y2sZr*U1r>y>Rmv(H+o25t8L?zX{E?)->Wu{Z89HUyy|O!#ro8J(slv2LIsc+t+i z`WM}fkZ%F-KZx=bvb~d2V5|lex zh~WGQ=l95px_y7oi*>Bh5&>51vuS{$VGn2b#vZn*T;Njeap2(3d;RNR5|fQ^+N8_g zDh(%33dg?GA_JTN?m8zD&dt`rSG@Eu^Iw48K72rQXa_>@QV%HJ_m}_SDKXjj#21VDv9#-Auu0a1Fk~tSf~o*MuMfe@X+<7?g&M$z$l3rE%2vC0nJ^ zAt){C7BDbKwpRclVqyyCP~I7%x)&{Mi7xD_+eSM9ppE8*$B-UuI&T!JjbBY$($j@v zAO@3?z_N*s6^h-+-J(At*j4kGRDI0cT_??>R{OeO zQq6d6zI>3Tv$NbqaBc982H(h{k*u}AwP`d6myP5zU3Zlv!Uv@F!Z+noJU(E3gGLyv z@@%qqO)}FK3+OAehOQuo5F#jMS)Zm*_A&#i8l~rPsv-e+D|qk(CG0O6@oh^_9QBzZ!LtRlUrtO6Y|jIMUgc9U$LVe2FuHv*^JRS|h@lAEbippS zz-&)7TOQ7^E+v(M#fh7xDE&&&10);JWrkw9~ zL0w#?i{t2!yS#^-XYhy-{6L&7?XxM&=&N^TyS~vY4>9i z?$BZ2l`pPb;U>2UA=e3;-~~*!1SpFT*2Ed7Ts411F;yn^$~!tAia4TuDYk;>l9uUR zOa(pBA~j)z`~>)m&~GA_DEV!tuJ4v2);%bIbY$JNNAK*m?o)UkM2#feRjV5%w%NUg+ia% zS-$qPjOs7y^w=AgZ}B5BiAXSwV$T>=^X=&jz}LPqsC#PilTlI55GN zSC`DPW0gk7#Mcp|b+B?@0hMOT{ANSKQ5Oq#q9LYKeIpxU|6O}v!NjL|93l*bny zP8v{$Jzdja!^S>z+DZ%XA;Pvv^_+-oBj3%pA{?8L{I)B%y@#FQ7}NU@&*Pc8nUDn~ zWxBUnKc1)G-k^PXb0czZ4SaEV7QEDoyFdJ7~ z=9{yxY;`5G6s)M~bS|PDq5ETx^$64>rLcFaa)1oyT5;Ia#ZylXKYw{t5#e6hC(fn1SEIC%Dh^@#R znk74dTIbR_A%Ox3hTAtrI42te7wtvh*nJuEZjS=$$nb751QmRBNG$!+)%ccLdPAOE zrxQ-H>$m)=$a!$*=9#9iwd)nHY5}N}vd6ntj{}3vJ|vEJAs9WJ8#~Zg^g>62cv=sW zl`}{-(D@SKiys}TRN1e>Mdt%gDyr^Qv7xjHXoI5;i1$6}m{3~ni)It|6*7nmymPmD zE@5S;V=xL?l^v9%Te=r`!)mua=L~GPpMJwa$6KI+0OO}jNW~o;oB#&Lpm68EtabG0%X8*)>HA&F)fayXEVLSg4`Az)i zD@mVTmhsCan6R$q-ptz1qUFn;)K1BnomQk(OKxG>7lg%__%s8viY*B*gb&(cYK6 z=niyTcMQk#UK-h-gE2hxiHY1E9m9q*q;z`GAi@?YyudR=w>7`wyNmlPm}0Qg1ez=4 zecww~(^w6;c~YGoPc_VJYgTbRSm0ywLdy~1!3sGvVuV0Qus9{Dwz{GVN9RO=2-`Vq zaqCa7*nmgeHVlzyk_D^8JS~W~str&$H!{rT_$M2y3%!i!b6Kx4j*iV%rt!$w01^Hi zGC`0(4>?p29WgPnzA@X+Ve{h23Lb4vI&g0Kiv=*eTV1o+IG7 zMTASx2CGQ`E4EiG!So;*CK44UuD?&~Jr5_3(8>a0C__-<6Z>Jh5;fu^Az*@ZduSGO z@tCFb#2u>hQdZM()SVYm5vNS-!oXcasY2U@kpisS80M2e+2zq)j0bk*Bxx)^ii|Ctgq#9jHz?!?R>pvnrIval}c+k_nx%TIXKU z>*P;QI$r_h7AQlF@){@cJRXl&l}b7-_5xu(2$J2(}N6Iqa-}u64(HJ z=b6n7oQ(}5-4cp_R;QEHS#`BEb+ZJmWjatH9IBQ-AfNI2Knw?8C2%3{L=5`#`LtgH zpl{cskagpfj1d%_erk#hK>KJ!Bg>{uM-b5oC59x{a9As&5uBZQvF*KN^*)^H-f_mm zU@^mbr_A(HM`Hdt7Xh==mJjUZj{DXVQ~TaU~;YvXxnUJ<8+-sgs^-eGmD#V z;N!;$c8*pJX=Hy0>g1|%PB#*aSY;+NImcVf#F(lfBthYmE87}(d96?UjIulLd5~@l zl7+jJ9j{D#k91^Tznh*IlXo3v%p=i7=tMs>H+7XL0jU3~st>~*N5q1(DJ!N3BwE8X zpTDu^_F9!D*#Z^lR^hT1R=?ptVLwT?o(Tlkz!&F5>U-ayA%eLPo@Bgg9zcG0U=OEC z+~4H@dkZp+)R84O=;cFAdn_#JLq!(Yl)@w67%%cL`?(iltEgL;F*8pGCBmBE#>4tO zUZxf4IA*S*%xgnTbOe`^mzaP#QfzLt9jd)Z4J$cfCXu6=!%VJ{=LXFNf`(-CiaV8k z74Lw0cU{w|7njjHG>>>nw|;vGotYRm_QeQ?od=7RO3W)QNbfov5^0^k$-`1?KBjq( zX!DY_S;(JerYkv^Uwn`E+TS~KL;#YVhG4q$rh{a+_AXyvtRaP~G&0sD1B>3F@`FEw zymFG8B!^jyOB~)8`640zfh2J16(3Jw6dWm#|CB+3nFZfauEL;e%-myVR&qmGM?vNC% zY|y@Z&Rg)VS8YRCRFG18_r0mZ2;@dpn}=&W%G}Z3=5EQWeEf5RA@6NRRuy1^F)J7q{0Eg2TA;A}KST^0Q@r z7q>PH9wqFavF+Q+Hw$PL=#i0cFh7Hp%$s#~jAeGP9fZq0Qj~MXuRMntsS-Z4Q6)eY zy@2Mm#^beTvt4kn!(8{~gwCkajj4I0JP-4hKVhAE{-if(Q-lu@+!~=j&{dW$4&jy& z*$XVIS})@`SANI(&6qfV+(CjL;flVxTU4SJMmh!!@v;>zg)yeb8X|)t9aLC@NbNL1 z_4ZzUqt#spdaykFG{u#ajx!f3-(8X+-SdESO)b^bOqtPl9-J|}(!%x;QeYIK-;ahyC@NWa z_@Tm0w+rQ2z~3WV)wu-9Adtu&K|wfp51Wbn%4R2~QxnP!+n1nq;oMPIwS;c0RH#%Q zGKv0(Ili)$S_NJnIJ==e@c&rmfuL8`z;MQb>_t9UG^BIsm$NAK0&w=Xaq-56Di2Pj zm^nd@8N7b-#P&wupue^IKi-0mSf($YfPU?Me#FDzCG3!)u;4Q8b=>o3=+MY=5Z}9y zNOlCaD!%Z~MO(@~ouPsjQy`<6zz1Og5&$Cy7W}78Q+^wrDvq|$3v|m zgyiQ5No&5_{#hZj`t?=c|Koqy{r~gYGRvVtSezhGhG-ZN^1Ynl#{p7( zp~#c)S?ch)@LZCd=_Fq+g{^iVXL+2meQ$h5{}^N<;BY~8Xs6M|(8J|Yv=bc{nC3_+ zn{&9*$7<51fmozJzx`jPz_uARQ@;C@A9UdzLbL;May(c6kjHLeowpLWwuG3q7ZaI2 z8yS-yfMAAT3xKGC|HFKc$vJ?meV*{tfRu*jgj|2$-_9*)$VJoX{DLIy4?)S$^4Cv9 zY4C)0K}Sc|ZbomiNq59XnR-$=KzRPQ4}F%%R)Wpw|6=bgOox{H#Pm{gH{C%j|!a-EWlO1w5h^o)Q{o5+X0L#vm8$9{-ef-aotKf|W!{sN! zP#yee7hGW3&a|m~)~XjjY76C*k4(94XTO`dL2m@v7G|U?lR$J`QM5SPNd!v*q-BKA z=tI)@iN|-Zu?|R^L^B)KatV#g#`=TerVOjbST`*5x>=~b)$$apmTZc2_u>>M)Pi$l z@w9@~AizTBVc8KSVd~*A&(OEu#>kQyz( z>|dQe)4gxAOhNpSb64E*o>PEh(H6ur8@^fi5A4Y$OBgm2YrkJGU&qaj2rD7F?y>)`E$; zsKv7I`(J+e$NG__2FDI7k7O?JigD=53-%oK^oC^yQc%7+A;AhF}*Y{H{&Mty!up6 z>*7h_!QZF!5(u*W(VGl3yUEjIgVgbLG;^s^i;tqTvlOX-Hm)?{C?A&HR-vuEVzV#5 z*+RhO>u;L><361>(pfK2c;T#7WY1m*ev6FnSjqbB?nj(Nhok ze7wc7$L@p|IpBX#`zrqHq2V}BeVY^dczQOkXrF`}9*qR4#~WOo$A|lSByku(U5@wT#qCR}W(e?8xRR;W9E5pXhD2*Bvzb&zyBcf{q zvOhGy8>GhbLqtyWQou5Z^oL(DQ;H6K5lZeCAw+Hb$nP~;MkmA3NIU+Y0sQc#F@ION zEfC_`cG`Ytj%CED9b&e4Koap$QvwpwwR22jAS8^ZN0o} zhE(NNG!;e8xD4vGebgva)Z1x!mVZG)HCvai&Jdy_b`_^1XkAu09ObfM?$Wh!x1YJ3 zTdy^>wq&GyVm#*fc6d-D7W)LhC!87LA6j^$$<=Qs8dsJ=sAIXJK4xu~UFCr11f%wG zA5)hrUa`XzGmK|@e(3wJ=++T-`ON5Qt)*|oQ@^_#PM zmTkpF{j}=Wg3;-gm4m!_pyeRd%!%FN*{y(B|DN+LV-%w&p(*ROgS?rM08KXzX-!j0 zB~Trqa>zp|b>p|GNm|-ozP~So7Fm!w^>}vPbTafqPyLNl?Bm&Q1@=H?{F81a%11-? zPa2HfkI=kZv-F<8O-T6ih1%}+sHfQP)k{h$$q7+45^4ZF2qi;aKM1PP2F?bqo z1n4@HZ9#y$oFRu5{gSN140Vv;jSLIzn@)aQdtz=+(@$}LJ*@ZsJhGZcx@sX2t1l>v z!-{L{`;zHM?=Q2tb)vQWO`ok_VtYX)q{_BG|&1EU(IKM9X9l zwYYx%2hs54T%oJ$gW%5B-}89W=hK>9wyrAo~qK;7y^_jD>ga<0m%{G85mc? zTyJMp=DZ_r7|#^h8`;+&;wQSi8?zOdCpr94xP4FAC!L)YYvQ)f)v`kM+C|)2JwmMF z9b8dOr=$Z1seO4dw6QVu_<-QsFbSntTG`J+wRVL5FpqrHdWfu5FF%uDiI~xiPn}Gx zE83*U0HcI+p$+xqMwuysO6{-w%I8;1rxJ&huH3uHwVWBJ@?*0=BF5MaRWr+Ef6g8Ks7Jbb?t%CKL<~|r8DkN()m?4xSbPqiVNom(RFayidWYo`AqoKfq=zk<4x18 zTZym?{qO@G0F-a|Xqz4*jfiL)T&3J>ta3Vj;lm&hBo8vNxTc*qGMq7?jo^|jLz(La|S+M!ud0hS+3pBnQxYx$d>6BIkLaIUCifYf8r(c z@wihWMKNHj{y0V-8R2@azO|K)fr(sn)!vnkT0#OGY5UL|w}0nd-ma=CAcmbDGCW)G zvp*5(P!G9lYJ+3rkI1HKE#7)q4Y*UhmfCi1CELjVc;$pKsB|zF&wSqo1omKIZ*aSKE@* zh$}9j$-xJ;A|tA!n9p=hq!9-_nMCfQTe~&Lk53T9tn=ok4L1^Q^}9`2?@ps?sKOIL z|6*~&y!OwB9*vRD!c+U=l*O|jhYu@J_`glh6=$8S$UU>RSNyyKjt=Ck-gffQ>7q}s z=wQ=_EFj8IJtN3Sr%G@U`&s;^dMDE|q9FGB86tGarp+LWgYw1_9R0(P!7w@*KIYOj zN9%KoWSYQxw*ykp<1sTZNS|IpbmLO>zWpR_A6KKByNiBQ zHK@pcjUAx`FcXKO2tZ;|gX=fF5<0jvt+;rIXtP-FqAc@Hve^~$d!~1!Vh3FrwVWe0tkCx-?sBe^dCQl&d>YkJtw~J{a0eczQjvz12+x}oz#T~3GoOG*g z?LqRKge1ZAk&X;(ZyvN)Oe1|9?lxXHkpj$|nxA;82YnR6dEeIozjTt>k#n0H@dh)y zHbzq`J2B#cxFgjIY_>0NT5t4EPTH6@dl+AZ(%3)NO&t;Oz-;a4d!j%!~gul6or(pvuo~$0SXVwWYqnL zE8`q9eiw|}#yU5J>$RD+5DdPqXU9Z{l6R9eCSiuUaC-65oBdm*+h?_TK@-YNPCuF&jcR;qw<|y-PXroz5Ee>NZ*}=6~A`FZULp)VF#i?aV9dq^- z&Azh~oBqP}eL8ic5r|U$=++0jMzn%(tMDD04B*sb!CjtXKr5r&4PH7wMt~|GPghca zeU8~8cHzgf!}=xjjQmXLOI=7s{W)Rap2c?&h9hWfEvsjvD(5TD(mgD?YEO-ac~X;< zsimTe!!}r z1~ugCRxtbKY)_4r_uN?Os-$QLWjIq__+%$Ol=|j{7=k-En9yU1QyPN{fk@)szBK}` zrq{o8w3y#f9el2$8~Ysk5_)4XPMo@2eXw=))uRmRTp-7bz+spZT=Ra+=;Sb8IN+*o z-kMw%)ehpKP}LbD4rmt z;b#qfNM}?`vI1{>)bItt?=nu0&i!W2g)e+H?wnAve#R{*_QyzQL95nnrJNxrW{uS&isD8$(@leuHBcVp1T zC&;t+cIrJizj^OM*Rbw-=8eIAvo|~<*C}cdle4jHre&t%S-_E&NV_noh@PsuF&k!; z^6L&@6fFbO_YH&^>UZ)eKtw#zpI#a=^m=S>THKanVpib#g>u)*DGJ7KXL-xWfT_VZ z{nhtfIJ*WxSqB7HCd`RGIMGd0y#WD=%fnDGWsbB(TaQC2EJDFF20uMa-{2xM<(*D_ zvju$#M>ucVD)MHkpbSkVik9a@?3VyXwPUl{_M4t1nSdC)KI~$drUYo%*pW?>FEsBW zhYetiA*eh$1-UQ~cItbDLY!4^Ya123Yv~cEN+XAoJFepc;=>cyI=_&^1O!uRRF*cJ zMfHP_)|2^VPhr|=dK(1*>~`>p5UU17>I>YQG0)jibr$;gAXGRkre!9}`QVy6goM+aJ zSnHDqz*R%d8f9DmlndNn9#2dn8}PWxZFArYVQ}H#VaUw>b8^KYcE#?W=swKAG^VnUy+i(XVda`*4tierX# zKdinGw3Wi7<7Z^pofA4{#?)V7ooJFj?O4w0jjVL83;t}aalHQ7ZW=>IY4c6Z&i&`W z+=U7gb{N3TV{iGjKVaZniC*?9Kr)q0@$xQ~rb8@XyFoGtt_>K`@W#Adh~&NA$rs!W z3h|(ZjNzW-kF>6jQ6tSsinTY!+>In}*YVFv{gA{o8CXf9I~Wm{xR2ikeN=U;qlOFb zs25W!2ZdbpfrH1w(Gm;a%L8oBfE4nYSA3A3aza z!=F#RKkK&{{un!s5;N{8Gt&A&k6q&12IW-0&sw7%)or|d-0UQv8D%&YrrPwx|GZSa zo(x*_RoPP?QZv|SRic@Lw2I;T!nmUhV-&AI9E@b)h&u#*GVnfbkB_!coc8re}|1wZ90-g0r4(%o}XG0(AwM5mF)jL1t!!(s-yFc5mV5?(X+=AF`eev z?ZRZTRbcbP}1Z&q;z*0~pQechT@?#b@!=-1z(Mjv1{0=*h4B?VssHs^r- zzwgw+aVc9L2&8W10QuC6y$R`)$~t?rOq(azp(G|ZR*S;AYX-MS~^Fz?Zs*6Y&6mZzt6+F$IwW$^A$E;V5_B@ z`EmJ&K<(MVp5)!DMqrK%as^QZiAkr4GGy4NA|vFSkT=`~ERuTJX!*ARuz@RNp9pmH z%At{zma5Vd9YZ9mLhX-4 zil|rSuG+QP2L- zi05ni$}t`i&y9|D^}0iE@HFBS4(WU4kYA^9hqw8?L;4bb?Fl%*qn-^hru8s&!hy(HGlTxr3ZwIfZSW%90{y)M zLD5i`;a$P!onQ~S8kW5+OJ`HsCP%zOL^2F^tO1`jHYZR%Q%3wXHSl;^Y zFfcGMOjCp_&#)sbb?~eO*3|p2r?;mMl_KYT`6FkHXn%fGG1mgg(d5NRYt1-|6cJT zNfuyxQvOwEJAZYRF9)XZvAWUr70EX3wT<;so!-aJg=yc=1A8=Xt;-0-!lkvvm}q+S zn|xUdc9^j4Y@;b80IPgTYE9?S_J&$j^(Yp%_A8x3OZMljB9@70uR>YH<86r*vR%Ds zYkmHps2p%>?ii1efqu1EJ0JCYl+*n)xEO*jw8-)n24tmZ!)Z z!y!u;&7m(3gxB1VxvB)){v;ti*n{H7Ff79@>lyP;*Y*UROz)GXEg5r4CbJ2^Iq@Y; zJHby!mF_Y>_Q0@EH^Ot4l2k))y1-v<-Iy(QTOV&1yDZfhg|dRKfa@tG`V9nkyu%%b z)X3&ORB(dAy8=G;x9KOQ-=%+>qJO63IKzeBFeGJZ^_0mE*SIIZU2 z3cN0a+wAXhU@wIArc@Jne-P`6Ao>h3#GuxAm{4D2ZQ?$! zj=L0Q`ALyrLEVUYy~-lp^bS~I@@-jn#P>yYW3zlOO1fl!Zm#%&Zi4F#nh%}8OV_8e zf?|RSYrfJEv-vIHf@Mp5Mf)$+vTWC_t^Mb5=1n+XWn&nYXeDu^5zicTDwNc-$rpU( zmAmIEMt)J!KCdt=+wQ9K@jOc^7Fs#e?B-@0=7I4+<~5wNp-gEcHNImtVX=&|tXN}3NoDFM%`hht-j5YwdM=yw6jYc8)OVe($b37ZqsW9-iAr`QAh+h3KbJQw)z8WI!2l6BO;{PNWV#AJgJWUtjP5j8WS8#~o#S3;6indc`(F_Y7Sxc%%@nR8 z3yb8$IEELKJ*L)jAS68EG32GGYJ@heY&+`jG6QFObY!ulsoFBePH4;7 zs1d%aP?zLZpgkrQpU*@6Kk{Z#(9(0>aMv4!9Vy#-oWFs`=8NIRRzf`i1Z7Q{{Ln#D ze;O&Snm60GT|=|lY|L?e$kq*!TyqbHmFs(%wR9C|)t58N<9n;`Kz)kVo$H;Vd#`$>_FnJuG95DQ@z2H@HJKN%5`Pf)VcA%HuA4!nEa)K-?J7P$j6h zWd>}^y5}3;zH8W;^fE;BGZFABLqd>{$g$Y&xRN)~=~#V2CZwdDo=5C@XG(uVeQ!3G z@`6xXqR@UZY~*oOAf3&H&D&s-yE*4@xARL$hn_1WVuQU~-CCYemsa!vRB?9nVOuN* z!zy}f0}92s0vU(f!2sq4{~1fYS6QS2EqDA-P)nr<4ukZs1BuwbnUokA24F~$P#*@eBQ4NF1#nl3NDfh4`>*u z-BsVH#gVbjxFi&Cr@;R1&{XZQy&h&=wl#8XX49=UF-}|<^vjM$&6@8pyJl9w`Gh{& z=bYoCiMH?UaWz4+(HGZ*NaOh%6Mjm-uArWfYd@S$qsbT>C#tCxPRPe*zBmO(2s&hn z!*11m300HG;*!CE20WhQw&G=tsDXTCCKkmB5%0lEF4xg3r;YJ;E3rGH85xUPZ)S1& zh27Y7e3zjv6JuXXu_si#_n$B_B7zQU7*1)$b8ZylQv#E}(P%BnL>KLuEC@ikh=Fr@ zsFy!`rS+7VDe^lc0^kOk4;TqQ6AK!PU9>dKWgG`rsQAjMkusTNj<<7~k2LwQMMshS zJdz7_KNaRn#R}mbFgLVR((kolk5a*h8fm=GQO?REJ-a?t`+K|V@)r){3sy}|56e+kjOKf<%Z0kByMYqAaqbPD;ngy2YWGKI zqrEq}kDPY-hKHY$9?cUdZV5F3u7)d|?~_z>vyFM1Z^;cjBF3+a-1N^|i(u~AUlBQ# zu43|GakEw%Z5A!HQJ z>St`q$rmo^rVl5_)DmGLhVCrX?*&ro=6kh2X=&kAUtH&k4m;0|!5_M!GLFjid>b=0 zTFOHD*#W|M@$a(0#*Y-bM~us*5$B&r*+jfo?mwqM?klsc1e-@a|X+=HDDt z@N=g(o7cG#tO!77N70p25$6z*a>O-hROLS9w`DdJX;q~_XL^yG(vR-wcZRi2d1~8i zG$gm zr?bs@N&}LV^N5g`~e^iEzG|yX^I$5PLy9Pu&HD1SFzD2yR}2Zg<6w`E3)a{eU-DulugoKPof= zL0t67;Mj}pSIQ&j?cA0*0j&i2ZtKmGMw4@$mS(&k1ow{4pt94iml2h;>X9ZB-edM8 z`x%v_FSvg9lD@NBEyag;~!hI2l62t57luV$*3HCS}g5 z4G&Rhr$SR+eaRice^4AQTF5UKanw!cuKONLldrs~u5<_~HhFO1if-(p^J36`GpqiG zlnUPa+D95S3tHx97x2BETDs5fgS&LoJ3N83E zB!f)p^fa;sw_ht{`txhu-Vi2}K%+T+%RwYHk|^yDfnhWJ24J~Y#+wK)>iAFYWmBBPK6iGJ1dbSnRa8VFlu(J(a1i3pivG@D-F;9KKk&TSv|MQ6pitx zM|>CnN~M&YAzoZwt7nIo+>}%vr*@|F+@SE5OZ(&?cjX%M=C*Jjf}@8@2{ znKdG|*IFQ#tR1)2V2Wh3g-GISg1QvE*BVUR^A1w<1ZzDLp}v6hWbx|$mHszqKa;4KP)6x&~L`9kT9f2@aXv5 z9=jo{d)8)+YaLN14M6^;hCiorBN}9DczF2mV>v)kzlld0fvwVIfJKLqMocOUNnKVO znqq$$K8^)N<(G_zo?b>iFZmCbCZ4qSqsKjGvwr^UEOHvJkE(k!toqRzd*vRM?#6>s zy`poOkgl}5_EmO@%#>Sua~lOe3}xLvCu-m63WNiovRo9ldGMz>8Vmm`D5&Vsg4Y&Q zsy>joD9I?R8v#3Sop?M4@4P30!AIcnrbZ(1*1GC69qj`0g#`ugs#^Z(>GL;rL=Hw4 zR>o~B)r-;nOOw)qpBm=no{#}CKUBs)^3zo>GM0LiPYW}`Fg>@&ENgWCghN>kVu%Uy z90vrYS3I>T_27m`Tk?ZZ7gdG>yz&MYzc8@BM$2K0>M3g69(gen1&_(z*&zbkOf+(! zVN5nAuwnM~luP-xi(l!6R6FMN>WtKUb*?{HI%n?HpgZ;RM#ltndqyr)-x{t|=Y`_D z;2AwMd&1h=YFKt7g6q^2W)*C(s@cSOi5UdZD$8{bHrP`Smj7p ze(4v?kqEctGN!@~8*wN>o%s2z6Mj&KaN1Ys5f?+jbS1Q;dRrs8dGNs(OvW_^^}p7u zoA$1_Uywf#co6ys$}lUDR$X4Jp6Omjir}4rHmSA?6OBr}81Soh`N#D0L`yo(>3_5` z6J$4p8m7GsA^N9Z&KciHz*$=#PFpj7Fso6M;{wf;DTJ88T&?wIQmw48jXfU4v?7KgDA$xgm&aR329z!w%JkbO#YaP&kt zsq6dbMvkCukLXi1S*78EC11+`H{c!5#@=Mt=tlq}Veg#4C&^od21~Zy%oWg;L;nPl zd8PFd<*=OFgpH2nMb40-?ovTVCzEQ6lN+h^9?dP(@{f<2tUA1j*#@_Q%myg|UBQ%x z0d*3Txe<1h`K~Jg%Z%G0j5v`E;&8BV*i#L;x9;a$dvny-9>+HyC-WKG^`qY^qFiDa z_GlPVZ)$2LQ#}LhA|oiglpY?0?%RO z{m^O%jIftXQqy7`Jr6Zwd?8VYJzlX)%aGjBVd<+;9E-vP+a27OU01gxN?AC=bKjLP;ewFK{Z>KsH_ z00D6F`5ck~&@+A=$j>ahD5t8H<5?zTM&Qso+`?(($quKoI< zzt$tz5Z|G0n%=X$&0ool<;#Whi4h#^)J1#cK!ceLG#vbVu^pdbDDT=C-1Ch-0J@Cn zg@FSFa`f7rL~^;?UnW7GQ!3#`^gbce~m?5K1+Tv+xjSSuL=bnZHEo_I;%L_|r z{bScUIG&`TchJy_4Drt`OEGq?*NMQ##tAadmOF!;C1&gABQ$XS^?C|s#H$6g|FW>! z5JtQ9Ub?KgnX7WSy$upq+$%)x@N=0yzXeW2BL*iU@9S)Cqc)G-$~%rGFUXTq(E#Y% z+T_0lQS5VJr&|i3H{ANC z`pH(pqZ{|^ih6Vytc6}WW&Bju&7*R z(F9qa&+bm^`ZB1)+pgHNc4UML2>c$gfM}_8d-*dlGbZBTR`NL?5}Ks5{X|2(=hJ>& z`nTq&S2k}`9iMF$ON7ii z9W?9-XL~yQK6YOa_4tbdLjrFrCF~VS{K%sv<6xtrcES&%qLxMt0+Tnd zPn&>9z#bNk_}_m6&R`J#fvg8wJEgDhFk5q#fB)|T`^7&98a9GZu)n|M|8VV8wqx3oy!>{g5WpD+6RjXG`Q zKZ_$ZJYx{Q`MYcScd`8@V~gZJG{rm+RQCU%DgVU`Fjd3$kw)mv|1Ir*@-W7k!K909 z!)OF;vA@TJ|ChTo@Poy^bff(F&HpImf7e`=A(#~sz|EL6y5(ESNji2f~c?nC@;?< zJf~-&Nzt9~bS$hBL8)K+PfEg6l0=rfG!J1ktiAFMNV3|rRDAZU>l@IE- z%}{Vsu4V?7DW9tLFjC8gqVrwearW0H)DWo*-oaF%5L&19(K z%?(wfxwDGCn58EN<(MP&h7A$5VFRYhX2|E38TcgHsaPMCx!Ak- z3rYu=_edx;n9kvvf0;=3GVcpHa)X`B$EI<{zmI)$SaKe>m+(TwXTjiKST)>h360+x zvnmA_hzy!@r4~kyh&U?nAzZ0ad5lCP3LsX85*w%l$IBD(ICaU__;P6!gU!qvzOW+g8#2<>7x$d!&d?mohf|1tWcP3*TI@MaGh-4~C>A z9XA_yx?2y?Y2|MvEecM(Js!9`_XqLW4SV~MIleyIJw2{A{gmd7^m#-kJ7glGwe~(O zl5#)Zo;_Pw99usLwwy40#0vAIvfGC#qQ$2+`<6LXZp@ND#eD|}ne%;& zhP-r1w@O1m^eF>f|&iLn{MVD4w!io2=0QV(l?Y0gbo zB;Qz5!h$uG_Toc{oW<{UF0-X+DGGTA;Azrkx6ua|5IIHmG`@4fyRF(3&f6otHha;0 zvNr8Dwz<`-s#;t%GkY)?6EQ)H3kbpdd1yI~(S6J^U-gP@zi-sh?K(F$N4;SiS;`JiS&m5m#TEUfSFfog!|=g7o)Kp8y~9GnrQ7QX{sJ{D=3f71CDa7-U!N zEPL~O=yagc-tsRhTM^KzuHwTcPqoML^giWr)kLFf^?>nTV+S*Lx!Ogm}^|^XKt051<;8+#e`(1>!Q4 z6N1n=suKxOD|z`m9q|MIinEysh$DV?T#D+%>A?n!{XRKn4@b91Je=TsP`(e`q}xf? z`jXP!;G}pJpYVoP9xB~e#!Wzx)!>$WFS1P-k{y9)ch`Gwvarn;oM>gfR z&c~4S*9yMrb@tl2HQRjpR!tPw;773HE&_^apBX~&eEKw;41}=Z*HDR<`W~rEw7H4& z+MA(U9Sy=;&|1xUi(>e+H?gNI?6nNcnG^+l%**_m?ko6%h`U&Zh(G!Y+$5@C#Ejnr zbNs=!)<)BJ8kFJ4V;l7;}-V#geOGG1-gh zrd&zw2w`;UMm>qSS{Kmw+p)LjxMp$jP9I!Gzb^{@S|Q(-p@GJD=7+vwYGh|3{;h!cPi#HN);A$E_qoxQS5H7podcUnFO!rL2rTvDhLqQbUP*R*uP8=D!sk#Q znw+>>?2X~t|4fj*eUjxO$KfgHh9B|rKFq_h=f@Y#)bLaWWV;RwxG5qbgaqoY#AdwL zChz=&ih^Rdnp(~G8drBxkqAzW6O(~MXUEaodmU2O72cs3=NM)DF5{~gGb};>=Kg1W zfY?Ec#)Myvl}%kZE;&|+`LAfz@yb&CgSDpixUqA2&8=iE)q-9_0CwPg=EvSW)Y+go zCGUE7sC@cbx78)GBp)q)#N*K1^MGtY`mYi9>)MsQ!%l@mU){a|&tje{ui}`=Bm@{u zjLx0BDh*;XOh85Dvi#`f&GGgM6VCLfuOG0@*^Fd zo!4S2y@j0F?M6)-TeDT(OA+g*DUI4%L?P#Sr~ML^>fH(h>q~9GvKe>HJx&|`E;=Qb z_1a=A&J={QKs~w1{Z9=VHWEY1b{bD8H{Q@f3(XbZC*P%K4HK1v^0prH_L$pO_9`7O zT@}^)$;7x&tmOg~VPE!kTItU{*5iXF^!p(hV>n2OFZ;^YT*ztqF}CGH#%2uWpv~xy z9n}A3}7;)?Un=?JaFJ8-o!xuPHa#+;_X8-NmDdx@J_Y4#3sgu>FFrza5t? zUpL^0K3A|>vPh^fxm4g3X{_%l{z_-Gd9B&4*Y5reo)2D$I!C00aLHSiUY72nF}Bj! zXs<;g%)l5R+L;0hEj;A6I*yj0@%&?!f>{Xqz4|v1o($deL-Jx6dI^V6Zr?52&;aEo z@kcuJ;U}l|xdlUaSb3y+Y!+`$>^fi{%?SG;#_9q28+hIsT}!ia0nkm^)<8fB)sMB< z=?3E~(nxmO;7P~)oI}!0WL?Yih|vB60R-l*gWTc;?Ss)Qz4keF?CQ-k}#hp|p`#KRz_l(vc{=H(>^r!uQ16 zi|p1bYkad6_(m9de(Kgc>~QnY+Vk$L!HvhDanmxf0Y%S;3S$a-&#~n-KHnp1yrP#m zrvi)^H*F1R=zV;}kJN?9sS0J!X=yjS_guzsn@ULI_uZu*KhQ^bR z?zWLSysRa?Cz807e783tbM5CMPx&pH3ZP%0Oo#HR8w$b`6CCzJp1d}x;x^4sa~W5_ z9F>&#x#~4_SYLOSraCHf)0vioa%U^1nlz8&Gj7u_j(CcUwS-qZE8drKthmZM5~eZVu!`!({+%gjhxRvm*dmiO}yZX>ix z*f9YhsQJDUULy{6>&M9e^06lnOO_~txQT<;dV5xn+-Zis7jQs%wGhaemu?0Nj~9{m zE|_?In=zSWg)A05z(vyoXG%&+Qhz{R)ayuM!yL}>I7)LOgvCcPb`XQe<9T9#S>kN% z9AoW*#{@GV-CJ!!H5k1&4zKtj=ek=o-`3vKdk||Yg`c%e6HvQk$}SY`t2l1H zETL3HRSQB9&9xw50{$(ijJ8!*uv|~Rmu5Tf+s5YXpPm07pPTLxZ_t z^;_$O+M+vPF6vuD*G#OSpuxp2fnKZqL*HOFSTAeJ8i_x=pciPEqr?6iEe|rTrBVX>awu&@C65SB>aSWiL7#LzT_1 z(uy9tMrTlJ6qB1&1dP3O_Y`Vel6?)00`z4GcTczbO!UU9VvT7PhcuWS_ht!0se4(X zT+B$UGk)eiB1xxYz-Jt=OnKH_Yq?{0&?TXW&vH>JVTwiI#k+Rho`qZN=a-otf z@z`;s+Ms*ifCG15HhDyL67$BD{ne?s@YpSKUR zcfAd9GIw~9a9ccPjQZ*l#S5{JBgR0GpkP#Zdw?HF17^F#fqb$wh=-Mfp+*l8#ev?b zP`R2fvv+q>{BvHFu-MN&YbZp4&No$&C^~ZR;So_YS=lzIM)Z1j52^B|?82F-%QpFQ zEE>YtbRQm|Yw84&pDv@R>6(v@gu5FkdKUhbxLm$i(0_mB_%jpWZLn zqOKiBv}n(~SEzaKp|N9;Q>Dz<{w9O)K{@`J+Db^RUG(XR*d8Opoa}N)4H2Ph>;tN=2?FXaWVQ_I6&ud zdxHgNQlT8&DgE44r6rJ zW@xd@Bcv~Nh0tBI_|f1Fo4fH+uSGG`GITZFCNL`yXqZe+WVzS8?9+h5%mpfd{7Z2m z$u$twcwu{s1*a;tuoRRU!U}`7NQYdRVMN82fvpbFxhJZ>y_^{BB%|Kzu z#?xWCMy13Unl^%$hIx!<BQcA(sC9qnQg z1nxdE-tcFj>q8oXcsk#x=wMm*3kf9abUjOU^2HxLd1|p?JpR54RROc02uSy); zi_+%ohdmxOW$_dkb9FxOJJ;v{lK@Dy!*g{AmqSN9E^cS4 zx3;^2Cf~?gKfjgn$0p$1*?@<4{E6AUKpPrqP%g8%>Ls6EH)rK;r#ct2S<{Uc;=_ZX zRxqd8oD#E-hdi5zfbu^vt!h1mIM`bnV%M&kNra?v1YeeRz#vwCOt)MG52J1o3&Jv^S_KOlx@EOheH%jLR4l&r zfc~fLbwlDsaoJm>j+Q#r9LqS~2{}YoTKJiHcFs?A$jru=pAuW&=np05oE<41?*&Us z@6D^?w9xJsnPvms)EvsnMu?Bb7DDcso>BB3I@txQIP*)b5%eC2Jld`)+aHgLE}%(S zsZ}3}7gbrs|3CKLD!Q&**#eC*W@g5inVFd(W@cuLIor&R8DeH;W{x3dW;>3Vncwbv zPpA8I`i}Ab-p~8<@Y-6^lB!BoQ>B{!g(NrO1$^SLvY75~hwl9Bx2q?vkpd719)+M} zEJyTCQ}tjM+Xb5e-Z9(zIw(x;d4NUWV;Y!3=wU2%Y$@ta*`K=sl$392Y+Bz7aW~f) z1@W1Xr>J0%5Fuog2S%O;UMO`KVg0COg*@UZ!4t#wK$#8^ZGS$N3-69Q(@e1$1Q@YHumK1efLrPkFAK~4U!T{NzH1+o&yCrO(%hW2mTr4EW- z$)Zy};2QjRT;T4!%43J-`+N(7!%^8xtPU&+bFk|&bZRTKw=*^0k5xf(TSV)0r-nv*V;PB z3h*7l0{(jV+G^S|K@iVc#@UOq-sQLLMQ^AA&s?p-jzh9T@tDyW+#%40w)l1U+on#> zrLE;@Q-LBqmr5Z~w{-eG0Cn+tDu%p*43SLBiY*6ku_LddopBC{zKNQPQ`S%pn6B#e zB&I-~xJPMJ@Q*N!Ec!-k--UTE{hlsX_31aqf`l)w4r$){SB{q8yw9gO#}twRxE|pU zGCg5-6~j*g4+Q!EfO+Z$fbf5Tiw-QP#iV(VZ6KdxV)#xGRO2__y7BKZePhS!hNhKp zZdthGln|FBrs-l}@Xro5D?Os7VPf(_g#h4P5TUA8b$8al+oo{2Paez`iV*$G0{LBL zv$1Zx5J6Ez#>-cP25FfE9V_9YCpX-Q)z=V2!wO?l45Lxw?#?xIUW~zRl=hi=AKdeU zidY=EH-fvXy`~1EkU>`?>(?@MXbM-C!Yv89l!G!Rlzc&qS~T3u zdeIYJlB?I?Tt7Oacd4mIR>QhdA>rvi(XpxZ*EGxUX(gp%_^b7N$&SQHGAG&%(+jn+ z<=5EB7D>{fN6QdCy(70k#|fYBylpX)-X)(HqM=D7;X7Wc!;a}}L{qX|+h++XeetAN zUZu0 z#Aeymn0sbXVIim&rh?d+;}sI$0q(I6-Ml4ig^!p#!VzLPQRF=**fhuN=I|_tM(RCm zaG*?I$bK@830->lUSbdQwuG2<4|KrSN0OF!e%qJ%h7XoBt~5hwa@SFs9IMp*Yd<`3 z3yrR^n3{&0ES;*QB|}|nqhn(kWz*BcHSxdv;4U}{M|%3wH_4@khoRzO!0RR?~a^w~YiR$UT5!|pvTA3&tu z2*T?51agJJnIr??*t#-N?P0v5)b&~*{O<2M_Pg2z#C;w*$n)-XiK8(ypETDic_}J| zq=MYwx;}0Wp&B(qdp>{&ylRAoZ_sLUz)KFsOIM^85%yG6(RIJtIO>#~eWO}C4`#m< zA-|LlH6}>`!2VY$?ms9yyNy9xeS<$k-m%PPKmXvjQF+3yyfF~~41I6@hH1k55}9=+ zw(;FBVC2~dEm5z!vE?UdbQZl*qa3GiJJN@XSY18pfUA+iGKPviyMvT(fyx(2pB{5f zLp>SQqxoXL{Mf?`cN*;vL^omzWA;-!9Ol?^iL2G;{=lHjH*gp{-ihO}^D5Q!O(jzL z{ot)u@HJRVuH0O%g4lD)h{JO?f-GTtIDJQ>(Y{C<(}$C0UCpF%7Eg1$Tg+stuapQ9 zhh3z;TBXzLq>>gK;G=Hlnos`J(y`$!Ph@RWX7uWQJ(?xDNIS zN`F)o3kD4*dohBk1g}y8X{@k21?*0m;y+kaP=1>eR~a537n2J5v?aRnCE=|aB{9|) zs|A951XeOYGJ{?J3Ln{(5W{4DMcd!iIb^4DDr?x56${c>tNoI`T1=IXpaJ*KYqJ#f@BvWAmuC2WQM zK>MxKsb;(G$>e4LRf3L@na`taq5YHj2rRkct{+T zFshouE1G-Lv!*N`GHJ~0c~w5D$B*dcE?&ujlQef&6b0>N4u*sGaM5T}t435b&)A6z z;lq#V^m@S=J>&WU-u%cfT~9jRmi1|i=ICJWK^Im7V&c`eI^A+#RgRDz&nS-Lx8NP4 zrQF|@6*dmc-0u8%Xb;Iy4;~I6_(`a?eMcDMu2iIx?oBH_8%s6kbkY^e;-jGpSjJx- z!v7~Tv?3u$*Q1G50dxf*2&Qe@1LW2n5?3&(h)NnLzKv$EErw_R z7`l-94LM#mwA(Cfh_Z6`S2A!^V9a$I*ZdzYiAXe?EQDlk? zn7P1c?_)AKCmdi!8_9jzo-cgbjBsNuURr(N{d&>zjI{0`@&(EQiL2I~N=4xr{jL>x zl4x$q#|HAXbTaDg9%1!yLfhd8)~G(P#eM5^Da(tO`Y~E!eb7DZig8RE=7YoY86v-e z(QU`k`AfY$EeO0aseLm7 zte@eV{#p8-q3JX4vrvMMEY%yL5Qn8_0Z#Mp{NgwyoALsHtw-)#me8|m+twkk`m7w{ zy~Xm&$EJS8sR%MCSAm7i50+Y?*6jv1WgGUIF-J?vsX@K4OvWhHn-KcCdgML{3}zLf zeT@ntHqk+El@KI5c%yL1g&~ZOxyhCIm$Mi-Taxo9M<^!H@}YQ~a4TwoJH!`i!L}=O zD@rwVj6Oo<$K!Y>tl2`X;`jvKaE8)vA+r-ka7PjAC-r)?F?xgYVX(1vH0V-Hw?5oS zCn>(Di~_cpm!s}SKq9>l_u2L%(k7L10|vU<6)(asjHIr0!{+c7u}%=+cWCstu3{EG zym1IaaG~>{RPRbU9@?fEbMzzQo#3!CzJ!By9p7pQ0#<*n7=N8N^&@yO0yJF3UV`-O zuTH$`=-MMjH@5eaEkUjxJwMwVMGE=v+qmlZ&Mo8S=`Eab7>=jOjl5#zkLgF~>wvRL z?m`R}Zgx*&hdMSBFiA?M0vg(!bRb1J8$-PQAa?w7`X`^kdD)c#=JIQSDoaI*Cr1%h zD(IlSE2PD@AUO3_YxpHwdvvglcaQ3fPdS9=!-NCL@haMkhc8M{tt?>n0@HdlXSp&bKGI6P%~QZBSGE==vUeAG6c^P-Q0ga!uaIyEwfL~lZ`E>@1M9%w z2RE;+2dl1JU!@Jyw--RcArmMY4_L4lY!6-97c^HAZ@12Gk&P7;Bw?2R@XDpuP3_U@lE`R=!7|2sS! zY9ce-AHP$r|J~F3ueNEK?*KSy>uiP*f0v=ZiPP_YB7wqLZQB0+|No;8!FV(SMUnV+ zS{t#DMPS+?;*c?*MuCU&f<{tJaUpT`vbGh#t!jOO{es48JcUw-+_ zF(F@H;_vC!lZECaMtwdEdUA6RaeHX;KQlyqw=`3%y@A<6q)>OA9%_aEBL17wXa)iz z@tvg)EgHqFjBg92AJ%4RS{)0#9QF4*ICC>hu6aVLIk<8UGHXLt(wpqt+$w*XAx-A6 z-7;4uoazRbIK4o&&#Jpuc^ofGlA4FYz(LMFL~&cr!gE=Fb=DJ~l2L3JsvIY`peg&S z`zWH#nrujwQ+e8r%$>>p(nG5dpIaW77+=86`-o#V^}NGv&67)q_`mGgpQbH z=Xq-0Hlflv|A(1vLsa(7IBVm{24 z$mVGX;2tYtu|SNqS_!TPB4n|Z7%`D-x(&Szhnplvm)_ z_WYMwvqA<62OX!}PemjnR7jG>=##jRg5=9kb=%n{+j^gAJJ+HJryI*%Bl<7vVoa{f zK3s?8DqvzpgoIO7h5Dj-1%}Ub5h;+}bQGq`@7U7sRV9QbPn=q{i~%Mk6SWhbYnhf+ z7SaY3k)I0VXV~XjX9gwm(F8F_zRBsjD}5eqr=MP^)js=K=}I@C`y&>+tS~x$25b9g z8y<9z@xo7%3P(;_658WaqYyGVr;wKyBAkQMhFN{TgVlQP>5ce!0j&ug+@~(*J-9nZa|7(3?cxI8 zu-;5W&XS(p*9VG*BiV39GaKMip2!f4fH1Tf@D+JqJlp?afh2=Z8$@RVrUKxnZq4KR zalwnNyA6^YqOIm$|E;&s)-(2lH++N-C_{T}B#(Bhl>-EVS@}?>&xX-VH0ib&IU)E{ zD97n|kH_OdQQYF4yv`YkwU`vfbaIjLKpOgRJT)wWxP~Yq+_N#`tNjj)jUb5{9bOO; zrnzd>ZCYK8jS}P-{~;%s+0;)1IA8D3(NFb<1AW_Hj%_vCO`zFrIZ2+t--i&lPKbCE zpPk%0zQA~EFCoWg;{H@rlaqs}Z7Lwu%{Yp-HKGkL7=AGJ;tkBXxY$RTn%?4aaT{bd zF@aSoDVcu8;&30>0dR+v2uoMnJ~+{8)<8;-aZUw?5IpgGfIIqdEfZH`gIzn$e~#?w zox2maNDl59(qy<1$D1&6Wdlxt8|*(HkIHVr*zHAoEGQH)0Z&-3puzJ)*)QYZgF#R^z#)(8yq+8CfKZ^%8R+148YX4J~?_u}m8eHnp5h!`+d5D4gBgi2lz z??_GxeRAkSen6D?0V|#rLLhQ{(#`Iuuj+;uwinEWxLC5%p!8j~GC^? zlB90id0rS zXjgkbS;oC`yC~po-ILFYNkKqBT*fup4m3g2|FU|2YD7Spba&OajEWYf4-H+BFK`vw zO?J$Uayi6r=!o$ls|~nAqQ?2}0}ro8G8x?aLi$LL(>y@*#eD*j`&r~(jO7k?joGr2 zgpOH?54Z!jhetSd9fXMa_p1@==K7VjOm*ofwkilxkT>+?a-LWX?}{w3UaS)BvaUXs z=;{;TMM-j%=HLu1RP}U2k0g||au>wcWW->a`LIDz+{Yt7yeh@o66NAzn58M!FoynA3= zDS;6^oL+NwbUxeL^=?EgO$?d6jPp3=y5r(5xp<$XH5q$LV$b%RD^5wSdGyJAf{ScL zJwN}8fGAPV2vr`FQ6ul$=f#4~96LG@xz~#vXLeHT*-*i_Z_ph~3dYK3!Uw*MP(yOk zVuxjY22uD-#yJ~N9%|I89SFV5=JJf0wjA%AyaUz<19hqtS~v|60fa|i4T>^!w! z(D?|`XVyiV=I3vVLyiR&UivVhuln`+SbroHae~_x9~?Q`Ue3QEa>Hf9hnR^=j6Ttt z686NAPThMJo;oiW8Cwq|1=a zjmrQZkv<=AZN145QZayrk0mM}A4|e}@63-E92C9v2lz?Yw+wGez%nil!CqnNR>b)1Y^L=s^dl zJ`Ey}0L;R!8G?AD2Hi{P>Fe8+#PS43T0*Y~S0SiG(|fgR1E~pLcu4#9XEn5W?>Sf* z%)spA>AJ+*n6+UPQlUlqxy6?rW;$TyfHxqUvaIxN;^=hINh(^P=9!DQHjwnS-KWd7 z0b`PAH=S&*r9q(4c0X7XwIlI)PSTSRzU{_#4v3t3H!a~z65&6dSm4Mfu8>(Jz#jzc zZYDfvw0Y9~l-)0KefL3D_bi{EeX48q3t z2Rk|<#F;t&;FJrEHk$Wc&zG>5&Im-Vg9f<9Z61);$Rk>7%QVaoqC~FW@y4#mW9l2M#WbldAC6SY0Pw?4WAB;TzoJYMalvN>*XzEI zjgO}Sh54D6Rj)v{%IL)^9KsNL|+2*?pzZwp(ow z&5_|72>dh`y!hd=?zZo+(9eK|6ef*j3K#)q+%*yN-b?CCElrFMA*Dc2sTE5$JuAGy zI@w4*{MtQ_1!+fZ7K38{v;lcbLW4CQI86qYXV3Mz!)PaouisT34_0_EG}$@2mhF6h zh1&t6G@CsK6!d3d)o%adL{SO`3}QeDCI`GsiB%Tj3`RxN_{1M^zpS_oPBIZM-rbtz zhnduMN_TfR8i`JP2U%Qr;IYAcdJI(}(36f`7eg3G3(_V=cfVCfo<+)+buf(VF0be; z7L{T0v4lOML^Og4Lu@?l-pm{t7)G8O=5_>$i))<{j&L`=Udd&w!7>6=|6F?|vFJhJ zWHNiEqLo{UOiskS1(s_JbECTXXttABGa{l6VCY=VgYp&lVw<+xBeR;K`M|Hfg&#?JU!YV-Fd0I#%oD;9}>=AB!|b^XUrO?q1EnF4aM z00U^WB@G#Fox7z}OGhpeea65zJ_+e^g-(bF4V9~Jx6IlD?EO3t50y-d$!IasLP({< z-g%>!Lq{R13`n3ustL>McU=xvNSNY)3nnWc&xlxLjJT@j*gzx2i|q5?QwxjZWH;z; z-Zf&45)GMRoxNIYvq&<<1Ml-7yK7j*J!D$QI@M1P6=ebyEBC;$qwQ@!ue{_#<)eZx zQx-J|UaZgu6|p%w;}MXE)FW0-I1%v}W8^x>uLr{-v=x8}N4(bbAu(d27N&f;Lfw6g zIAW0iFCiPEMwmG_yZsc|MgvmT@5*=SZBKh<&jT9Lse4+PUWaMy&+HOuXhMC7`5~O9 zkTy49!doqRaUdr92+NW-x+JCb6BY(S?eiSI9wYXMZDI{%+3Qc@{W~yEXIH0|Qa7q6%7x)!r>EMxoYZ{+@mJ%>MN~+P9IF zg%R6d>fq1`rs7L-?e4Y9fdMsMC6MiYqNiH)$AnH9!4>2;1@^r z8c2EJijN%z_kDF6?ZHUg?%^?jwkjT*NCuAgn5-r> zMESyB)04iEAUNht?tU$`L!;3Kp8fP0*%Sn1`{EOk@7w!4_LGoZrPd^|NJ=6|Sw;4& zudfKbuLL2X)dt{{edPCdDWPvXNFh5v1#Mg3>DnDx@hw}A(^f3;F*WfO+u|>s=hPBJ zHE^~1L$Zk(P_(DEpj{_iuE&cR&;90SZZ3d_>so2%5<{(D4Mj)b_04M^@Pa}wZKc#xm zBI0c2UF>Z6BM&CDGp%A|eWr5=(t{5<4xMbhL5bysXo>gH82J0Io~z{zp`hz6>pNX+ z*Cg`eRKonRELS0@1PfGBhONWyC#SjeYxMm3?peJ*fZ|-s7>Me7f2b_e4>7!CrHyDZ zjj$A>6{7kHBqstE9^p^#YgHJXw1SF3ByX8I_7LppKvTm!ZmhUQ^K6X@FN|zA0{<~VnS#Rk+3Zo&MfcX z%4A&#l+|L+X|*?oc^L1A({aHX5D#i#m@9U1(o@dNe7k?gLAVr=!Ll81qE=iNEE~?4#|m{(f)dn?LMu~m5Ea<)${b(LhiEKj$;>s}%}XPLci9+yZds^VTg0*HoT0 z?pPnSzGgzE*F4%w3j#thnEK`Vr-64dXHI%90S~?Hq$zxyQl1;DHM_dXjRww(yu3W0 zN=>c6(#edP-xNfv0r#nOdvJqR&U}kx(})rV?wgl3kio9Pm~HB_bI4Sm2cR(Q=s?8x zfF6q(Q&-@OLC7*pCAkP)NCHY7lXb=}vG;05@+R5O9@lZ#)?^*bom*t3EF8gYHv-+> z2uwYjuR1n>Z_Xl`hktA7?x#r5Fi6XQhBVQ1qkqPM=o)3D2L9ry{qkh1xz@H zlWJv<3%~qu!5wJE;+zW`P3C_y-r0g+;M59uLqpQ9$tq9Afv@OKL`=IP+WKU&%d`r_ zhWc`e+@Sr1DvP?RQC@(71oYN|!hK5qYTN=L?p11?Pv`2iMc(d5dCdaD%ydERd6ivO z(&uZzRTsK?5S4BGaDJaQ`B&F#L3ZaFULy)P_O?(D25f5j#}^~#CR}Q4B)dd}kuZo0 z!9p>G;O8-CDuxDdkKQj-07_o+7IiZ}PfrUo2?~)%ijz_gNJ2~_y}U_27U3PAA5%Pu zDgsVi;95V^qnzJ1{8wDF)Fb*Eq8B80DsIbtF+CKp++uQx2 zkkhOvJ8_z?N%Lkt1nNigE9Y>c#=7KyU49pV{a#a{X^@-Dba$u%`x{rT<>6EvHZ9T^ zQnp!`5x}28yAA>fhq2PpGV4*YCC5L-bM-rTxSEhRMs0rF7PTo9@s-?d(2dv&XsQf+avl5$+=eIsxm~)_qY2N>hAE= zwMmApkBk~w3euL}2WJD3u_in4=h0`07!|>V0)xUdpkx?DZH&TG4h5;0+a$dzt<=M8 zy%GTPrN_%hu}f|tY*{D1C12z(4q374E{<~nmp%sRrI2h8ZA_k$9QP0KJ&(e>#R`nu zJGAc8aV22Y%<8h_+tsr~)=R3qRo@+$MjT;~Dr}d-MTi@YQ=%N5S&o-!8#usrP|UP1 zvBgQa$2!l2Y4Mm8_zC#vAap^kOL~`xpSDGDj_B1F2HztC)2t2-Lhz&$3Fed0evrt3 z6LHcT?w7B!;39Cn7R=77W)pG1wvZpHd|E;INzNMq=g6>3o7)-L;s?kLjPofdp`eu! z-4G?Ci0Lg>YIq}~pde-M4aY>ujb33ygk3=AeB%(F)mBpW2fRllDHF7{2j7;Uexn{I z@E3b8gLf6~4bg^a7@9@++&V6tGyZL#eKy{qRmRWO+rg_Hsy8;dB&Cyip+??O_EDQK zdrZg}X-0L{QWUYk#rMzSTVOncrr5Q@)*elb8V(Dqq@fYn>wSco2rKqLNt1LNVQ~UG zzx8yDu9iCTV<%|9bXa{43w7$+{0cUa8P+kU)J_EAaCDFRUu?;^tl+|gqT-ovu=BC` z`MznDu%!}lcmm3cL^oS8n^P|>(9z{Qs79TE|+R-@?oy=-x`%Phpuu? zxvTKL+%55@mk&l%*>yWk*_)ueNEV4I_U0xlGdX;At$uu-c`&Mf*Py4QsA%r}P@Bx$ z?u|ak-wYWiq7p~s+sd0#0wJ>4C~gzvHb}dRy;&2KipAo^5}lm(mJwPQWr|GV@|-C{L&eNh*--@XRy7xYJ#B8C^rKKCIorw`!jU(Z4jqQ zQT{2-@!c+MymhBktAHe7b&C_Npkoy(L{!c9Z}d_rE5R7LPUZJm)>8DdI;DTB$H3C zl2c(MIpn7u9!3csX;DKnSXDp7 zru2Vxk}BG_Svr4ycMqQx775tS>GY`1s*$xidndUA5ejMSU)V|% z#ynkGn4K?2uI{XEC{FdNSgb5JWc^j&m?`!pUzRH)7)!{#VfR(|7h);!fP5vDI;}b13-1X5G&JZ-YQ0m z7^Vy4Bbpw(T;qej{k2)r@kJA;Y8a%gSQ=%8sfv)Mde7{WgOsFD7GdI*pDlAtxkEeZT66AHnMi^N zl%$H~dknB)HfmW;0U{c7=#e&LPlJ>yxEAE3^m^@9S;+`g?1EbP%t>aipM?%7LE){c zuty1jxlV`s$Vt>z4}8!qg$qcz`HVomEFUFkP7(+*&T&)hQvu)aX5LAw10M#qc8#ZW z!RhzuY*0eGK>@@#CGQ(UH6duX2!0+9G!vUe+A^DKPZ92{u8W4e|F$$5j~q`1AwxNX z#A(^|1DDT{s!)oWO$ZWw^^pB=CZeWYYR4 z3(H{)%)2k%xw(1VZyF#~epF-y6@#nqS&Va-J0pB)qja|itBT?|NqinGBIr(U zkc}YL8u}ZM>I6cFtVs9bmneZ@uYWUYL^tqo&yB4(3ESD(@IwjV;o*}$FS5!nOaAL{ zi_$?1s(EI@pt-XXm~ZzS{Q~FOsqicU_Jbp`U@s2y?LsWp4;EbPk-PBZm+qc$w+lAyolv&yI}O)<8)4)7*}dBVCQlksz(llJ+PmJ}h&oikf86 ze$a3L^f31#p}sFp5CkkqY#L4w8!jZt$y&&T-Dg(mucVKXTmhBaE1b> z3`k&N5oE6ZrBANyG(cliL1SYIiQ$Wv8#F@FMz7jYwf7*Apv$vVJOubEZr)r~p;UN7 zC)zBzDc!Ug8CO12s9@NvF4yo9@6b;T6Mt@gu=BD{c1-i!>=l{-xeb~8z5kq z%2C|G-(0b0kbYad_v6~R+O-s<#44-0bMZyygR)9S@|1lqTsdFt84?iwJxOSO_dl6b zSvNX2t_bo{0|EMO)*$dsKK#=gkvETRS}`pg-Sk94BNf`>g(MW_m_PH$h{e_Q#x|if zs4J)JLi{0_2Esk|?NSj$+CAS)$}6k2)NoyFU3MV{VTKAM!yS3=e5I%)b=zROIH4_K zrJ*9_dIAbqY`)KIV*v6KxHM?KFW^jOm#LH&o{sca)^lwSaK5mG_XJ|g#F17;6@lXX zaC4IuZE60%^5v1MYmn)M(bgA-`D)Y_m^IdJ=%aEj^HVDyZoX;8ip?;%Ia!qd57>LQ z+ZvU!fkS^)1T~|H!|a^(qA6{{AM#t;uOwtS4SJzdbXy}xRBn`tw3+teKmkjM0f9G(#s3q`}rAfQty z?PEbRLnIUe9P(b7!e*+gZGJE()0aN0p*TFNGIIMH4bdp1Jl_6-?c_w)QElxOS3WZg zwImSlei!m>{u0=DZviQEm{q~#?)f+z+PwJLg5cK7sEF%raM*zkd(HeSG(Q15b`kC& zi=_I6L#6x32%gJhfMyzfxLP~l3B2xF9MU7{bmi0`OvEU1ym zlqYW4>&<=^{o1T<(1EE`KD#L~zYcf)Zfvr#q0o%SkH;l>Kmwa=1K2shBm@}5mziW{ z;Da&tX>Xl#1ZR*LBdAnviF|6XEQPeDF^x6Fk@BTP@|6&KTh}9oBRVI~0M(&TTu+IQ zqdKucd@D0Kl!h0jrcj4?v@sals7S$HZDM*QMc=Rf3j5W+zAMUVKBVVTNcrcmDtQA6 zSqk}pk1tOv&HTg@mn55=IhY3|;U5Q>tpFU#P5hDcLUKM>5I&0wlv8ml#^%(dzZ{-lOaEug z`S%6?19Ism2g*br`L3Di|MiRCUeQd`oek+;kP`iC?Eie}Kbi!{Py;iuWJbpSkJH27 zzZlIy9fjLvNn`s7I{$I~7@VFI{O-I>I?JunCpD+Dys4UyR4Vca6pg!`k z6(?K#(a|*nSibK69MFHaEl%!kxEYyCNi8i?BKK`?Zy!(~RW+d&-+HjZiV`#}zyt*V zHdW571c8K}Bb~=lX z3aEIawc(mlazmE<2oRS`%KHAMmYhK#Y2AD1Sava{X04<&(`ImD+@$TUQ`mrGxeucr z(c>?}#T)vtNYTvXlu^lcx07v2ZgfcaEte*u?4Qdw*$jGfY+g(?ba8g8``I^mkk2dW z-;x&pmP%(p9o4FMr0fc#@0S@+C!Yy;bL)J!qE08I^vCUxLtSeoJ3=&t*#T!)&rYZk zX(j`a7tFjWouq3xeH^MSWrjhwZJkDKtHfmo8C0x-tx5AbM)QdYsEe8y9L9(#ly_O_ zc4B*#e2(@D7WM6&7k#+i-k|+#wX??1jpvr#!+6P$Fm?R@5Owt`!MX$U=1&aB6B)m0 zvx5{kFUG8*vZ_WEWV-x|1-u30J!$O`Uo&4nZqcoMU(d|0Q>uX7@@F@x7bMjxoapV_b{W?Mhex!qTB83xM)R@D=EB^p55`s1sRmR+GBn`i>m1(CEocDJv{ zSBUk-s`{M39wS2~ij&D@hMovMFK_@!uH&w&JD~)AsuK^ZtP0MSk-#BWNPTgTLq$0Y zbDg!PV6aejXu%(v%E|mFLrvZ~Ikt9X%o4zLW>RK1iDb(d$vW2d| zTOQ?8zK&bL@B&*W?6dPNsq2BI&3MA6cX0dM_3(rIw-kZRl%Q;Llw;p|GXW5s42SXU zfaSIDN?m)2h@%8aBXY6Yq%9O7A+!x)Ok$Bwox|c?%(XtY8j)F4tmW@?~Ily^bI8=A~Gw~OF zaz`$|gs}$g$!&Suzd}vDSB&hi?(8dd9T*PJvHY(Mpt9Mnd<`Lr4k#7SLBLTg+8xoP{efFMG$?T2Gd zEUppobaQ~?pCKz$e5HNhnx~>`?^yMAILpU#cT0_MK5z2FSRzZzz4m(&p?D@Q4RZA} zMb>-rLXM{Lnx%Zx3!b^ZuUV-$aIrQO_}afwgdqinsFCEC+2|d9S#b6+odB1bN^w4A zEw-4zhj!ws-(C>NR;Y6S!(RJh;wH&vegneDwJV{wb#zYafk~F|>H~r`7`INukbZhZ zK$HF%wFKZcLJ@qFL@nR-MFTE?$>=)Rw;RfmEToq?eTNOgi}51hiRrGIyp7j&Kxy=G z%bwlnR7N3h<44T-!BI?hfgFVtyu8gT(#fN2JsCzJf#&5L39^)4^OLMI)zG(p!VSL3|y3i?ula_;aX)?+A%wd0D~a8%e_s z&uHnR_BF(FS1}eZf#ty{m!ARrsi*1~E;U9&#eEHqz$xX_oOWa#LjN^ppuEIF)ZG?pYV{T;_W5&WU_`&+r3MOYWnrmez%|idl@M=xy z<^uem-`j;`Xz&vbJ*>2}%kQu6JP!HUJh|3&{FK)e`4tSc^4Wj^>s_G5%8w%N1cn_e zdw*T_w()5~w|!uQ3wT4MY09jXaZTkI4`F0gg|@;0hIe-$ukDgep-gKYotQq|^!dpNJr6RtRGvFdaFe>-FjA7N$_ zjvle$qLwBcOs_BqY2uzp`_*mx$wP=-#SqIM4(-}Aevtc_WAf2@@X@Ux^kC{L_g!3G zE4EWke;X~->8A!xfTk2fpurc_V2xzoY+R|U=gc`RL4XVGVa_{XuK2r-{QAeVPxalS z_F~xz+BdriDjw?47XJGiQo6_?BJT+Y*DLrKk09)2_@sR?FW@exGRE~a!Eo~Vy6^{X zRd%K2|HAD5Bf{b>2cxfLZgxCZ!TfdEaU*v^4MK`Pr zBuZQ%+7E#DU{fJ`*d`mhu&s}S_tj{}C!`u!<7lD#$#2@=@mLanKe5rcGe_rk!?i@q zKv3n<_uYcbzj0VHdvmLs}YAKfe1__=+p6@>B1)_F?0hRy zr2Q0Nr}QZM4mpLfRaD-d(S!7+EMT9cFgiAAXh^4fU961hF^akc-v{M%Ff?H6lIbBi z1xEy5#c0GOj$!(;H$65tDFm$_=mDf2`6hH&p*5qodQrf z#^RnpKAMGG!51~}zOT(y9XN((4`tU)i>wus#YM~8b>CbM7Gr-pxVYC8p0J$|&}H|0 z;Q{aHABOc1kCZjMvg(%dzE*(9Rj-y41Y}Hf!M`$h!FZ9}ZW}qG=eWBd+Y@=)`&2W4 z(%8Lq6rgsmu@tMfe$?Nb85c5gldeet5;lOl$)>d_(M#h98GdWdl&)El^pQ$6&)x94 zm&BaRCPO0Z!Md9gFPA-{Qy^!ueDwt;wS^c1;dD8=i?cIeK8N%m!pmFu*=rrX`|AUZ zwUW+11EGL2k?cY|FEzis8x|}(n^oUn8iKh+7>-8Xu(bp0YG-xq%L%WZ4MK~#90>VP ze@M@%s9J+FB~8tWlW^(iZu9uoC6^#9fL$M*7EJbKzhm)$ygrj8IP3mXs8;sq4uH{m z12ctk+)&AsPMco1UW8RnPw#6JNRFRnOk)n+7DLknF5T-QA+|pL(6qjr-_B@jSoV!==h8 z@xV!rPW4&%N4XW(bak-O9A(~%e*Et%X6^V2a!WBcRi#QqjRTh2Ieh0AJRHUguXvu| za8$#@h2!K9ITzku!y^&ZjE=h!HbE|K^d5s57|LYlLtfc^aIHYwCJ=-)OfV-6l6&oDFqz^W z{UL9vp(LC2BLiE$Ju-)V0*4wp57Vx(ipEkhQh^?{TQ=v*FL#x+2P^zLs7-dI%V@!Qqp(S(2m#3*vQ71yRYQ8 zohA6E_>13{a>Q2`kZ0arFu5dqY$^j4BpHp2PK9Jc7;TrK8=M*hh{zOmRbra&;aHcG zrVehkJ`;oDyy-bj$(|Pe_dX#QFiNE8Ou(aFRe#HZ1v+(=^ZIPJ=r7~=X`BOS>sitJ z6Wo_0sPKi)c~p079Elql#s+OH`@!9l6!p`Qap*G#?9hTE%wtvhBR#QXL^yU5{Sr1) z;(3RrMXN=Yxn-qfx{c#1p8%hiCGxWFw|;XjlJ&BI(SjMW}HEIXsk=GJ21qS&bGy z>OOb5Vu4;9{;%&#O(G%?)sJ@%x&(b0FUGx$K;;)^XKLe2E7o@AZ7BoRoC*ZQakRqaGo`ZFxxD;T#Dv8N^g;l8QE02Pwrp8OT<(Ndl~ zu0@lo-em2oxM`YMGX^V|8ppUgC7gT{%-Dt-Bw1T-QaTo>y$Q^n=cmetUGSL+~nVt#CKFe&$~IU;VJIxH|`Su8dGHp>Lalj6|V zl9pP;^A_NLBwDyFniXK-CLe<0JTDvUOIiy8s02`X+)pKP(l^3v!`+N`l8C50w%B?7 z_G9*|ES~A;;dvr-e2CqYEa2+Z{XUPSUY$+w9m@$F^qR-_FS{q-1~gz?p&P9 zy!rhHYSgGvRZop)Bq*Ah3ah-kMWkJS?%1e8(P%W%nJ!bie0DQk>Ko8rgKoZr8i)o; zjN^Ts5%6%$?UCp;i+(yDO?FtaqttZ}cg+v#>ROGj!@24!)gX#&$o`Sf}~dY+_~S1zF+~)O4k(DY@pmM-J!e%pP+$rW0Rf`IP26IQ>XbInfRvnn8%c}X3KZUeA1vWjaUjM2crHnWvHB{Pbo*ZpS96; z{X?S8=nuT5YJJ`VSx@v$)Vb=-FP13$zo(|MCxAy5d$GgKPuNBCH2x(CVM?Q0SZF6_nl0Z8k{4h;|YY%3); z0^!nB?BXk7O(dY_98pMhNHU7#wmpjLb#H8%2lB4$OZ|G~pFSrxME&&i1#JRN;)Ry~ z*=~VgOkB5^!}-poxTAf(h>|xlE1t`nn!1ZKj#V;mKsiDwWhCz->+@E8(X5aiPG;&w z;kew>;pfpRoArm>Oqg6eit78`NyL{!d-XILZ{)kxxl%l#s}H<-D@yYMuxERF4$VqD zKq)0^e7azFC`sZgh1^a+U8TS;8p`5=F#%ZbH(tzm?&};U&&!PiTICR}Fzn;P9oP=9 z7lpN}CFEvUe_cc^0%W4f;M)(H_X-1^*8>3;I8TgLrpfHwT+ozORox;r@wR+WA%V3N ziaq^$mcZcOybt-UYnY>FEa;P&$tu^If-~c$SH+|?1e^j8wNL>b@a7CF($uQRd!0TIEwc zaXY}EJgjq@iyz^HwGM zOP*ZJ^lKn6dj~|rIUnjnW8*;`eMfe-z{WfPs(&L|Z-5#lcm$?OS@>SdMn+hjp?EI0 zinu^B=_daz?ek0os;O)ty?B)I8n&~*M6<~U!y3+BWLBeBs5?G=Bh8{uWi3H3-T*FX z{W6$i6&S0+rn9@At_M!FD^O{cRPSa-Ra;;-z8e3&@zYGw6=k(JCw-c|B68yS3!=W! znR4xL77&8;K+iOu#XYMSHasz}DB5mv6?575=9=$nc)`sORXuFp7l!H~oZM zOk8@y2x$zOYCoQqFfLnnp)j8Ed1AHUxI%^Y4;)hwW^-b$zaUJUZfpG}rEv+seJ_>; z3#Sivd&@{G>VI7MswhuUo3F0%G@o*GsK&+Ivf0^jZ@kU)8((~a&*7+Hg6F6Pnnu{; zHn3fq-e!yGXq?^hyZNROb1nb5Vb%C#D>8Giq)NqsAZTLNJZyD zwuNi1r7X)AgNnxJ@#S&9747lm)8Rg8+a;10<)$gh6d~I2?6z+FVHwg>nPc%G*VYAr z?%~fUn?k^wB_mIp%iPI8a2U<#Jko#7z;D?>{}aLY0wDOk_4p^oP3b~O9p^q1Ak{{K z(X(C=64s7@h*2SA_U^lgV*@A@W@LwYR^W>B_Aez#dglZVPS%ehtw+Ke-Lh}C?1~s7 zI)Y}X9h%cP!5to`vzxZ_6e^K0X6}8Xgk~Asjmo-*Q6o;8pA^}7elcYWnn?yj5C|vF zWD(x6V)j1gI&*`wa5PJ={RwgY!(L=7{6YO8oL5%?AK1dKD2xYf5i231M;(PciW@b6 z-ZadgJyA|eU)kkIoYc5^=Q!a$%$zLM?|?nClxTF7g&+hhO6Z4usvxBgAitnw)fniz zMREmRA5xOCZmiXa9 zo#MAp?#~K#ou0p z`J>U_fVC}Zq(3BYm*blHcas0lK?(RCkOQ#L1E>C_-wpZy)IgUCG2n~RJ)s+x-<_ww zY5yNm8JGD(#m>U2+yAFVf18SLsR-Z;wc)@6)BjufrltX~(9~5lefR&{QK^LjzNmx)rKJkrRRT-QS`)QQ)B3rQHk_(oYo94*6P%pzxw5DY+Kd>X zy{#z5@Tn8}kHE|b9Y>0Q*q%u=f750_MV2Pr8?0Jizga|Fk)Q;CpAK45UXl3i>jKS* zy2`fMH|l93Z;J+_|^&9 z<>ePR70QXp!QnX0p-F(jvxi%`l@_sRx)2R0jdpk7hd16Iah>Y}&r)rox#trJ)THf9 z&3p35+E6p&2pW7__01DV!0VE>N8W;1AwLGz;pO(Y9y;|*5~#r|pQYom(a0TL zreb?0$=IIi2Of7AFIL*+Q-0&s_--*n0UvA@`Qlf$KPwadu%%5cU?;1SI_Ph(o8MIc z^M3H$dHw~nC@1!~2MfuN2b3N*2X(-}nO(up+)t$#?=aRp z{0QQ6%F|cLHvrzhbN9l9te^NbPi4gd13Sf-3=?kFeR7ihY4qg8uWo6X>}eJ=F1cV3 zYP;G>n_X>i>g6E>{1Ei7yjac+6?F9S3empByWk;hlGVBG#r6k;)f(oj?|iN5&Hrv0 zeM`T0=?lXBT+8^{!8_PpE7^vm@S+%*r|li&Z;YojpFFv#z-(k#$8D@@d9N&oXk6sL8jm|-+Vgd|cz zf(qoB2SVnrO|j+Kbbb#+QcsL{VD2z_Cj!>Po3?mMcK!YE-k8e+u~kVJOZMj9&fG0K z7?!fE?VHkytZh5(%Ae|;a$RRT+YRS}wWvq**cyx3Ui#SzG=*3G*fop`^x#=KJn$(S zelIx~GI!K0LamZc`YtBc`BI|cr={PwNse9E1Yi3Kcg7BT5s&K3m=|O^kf7{%{D)|a zD+Hf^P4661GRagDt(o#|ZTW2kbN-Eok zW6f>P1MuFi7NoeChm@*+X9;Tk_uX!T24(UwxjAM3nX2SaX}UxcR}};s_PyyXikDGG zKwD(vq>%M^@1Cy6_*flF#&Z8>(U-CG8EB=Sbz+Me=@B9duJAA$)Pv##3A=9(lcW3u z1&JhgeH@SO#z@-1gVJ;vn z33xv(-27F_i(HZ7wic8lKWgB1e!?9EhFNPC7+a4#e@8$To~=+;9+<>P|8dMxR&FxA zeGPkR${O2ge=q#y1jE2e7I3qRgSq9!!FE2KF3?2=fE*Bk7pEZ*9(Jiq%U_S8UI;n@wS<+d7?URwv`?v6VYds;o4xg4w;GPBwz97aRIEN#dus zzFU-2j<*2ujBkHIsy`R|-1YlNsh7u%jN!XOud+P9ku2|dX?+h zmh822!zInHuz90W7$bi(YJC(g3t?IZd1vW)m;6k{%hO3jE@3gSJJmdiq!5J({o+<2 zo7{5iyu4SbuXV?ReKwmODQ*=KBW@b=gZ*)f@7Gb@!*5I1;H3Q2NfuLv3wj3 z@Oi&Jr@lYdS=+@Rm(Hm-;ji~V>_!h^v3nz^@>lRMh@h*D5i9hjKMFiQ7)Txeo&;BB`v`j83_xzW(m=WqvhxKh%+#@g}3BryY6 zv`ny&Sx3Qz>$=DM0~ zmRe(q1(nQ7pKhE?Qdb(0FcP7xIwz21F}0bzz{|Jhk>3kQOH;0_j%|%j7VM19K^K>s zHQr+~|HQaPQLK`!iam!?lYX_dmX10ORBu#9%^)o3tB<`7 zwx99eR`#FE+_f&8{d5P5P@kQ-eZD=wYsv;(=hCrF@ZxY|fzsYJ3$>excOf5DV7;jM zE#wUf>e1oGDSZU3rCj!CZxbcY>kekC_50VjIe71vXG_@Z<2Gz%Vc^;nR2a!3RB*(a z5lNZcycR>h&k~`j<7&|jjwB0Zg-$3%>kKi|P{KcD#DC4kP2T|F>;{!mROINQ7`qDC`7Pli>c*);m<=aCuO0MyJkmzQ| zr}IZ%Iw&dLXCdqPO95&bg1HJ@@KvnTx>H`zxsn}SVXB|p=_`-?Bj0e1j`pAXbe3s! zV;MzfevzWaCzx*%EJAO2B1-zXyD zJxS=~s!f}TEU6@&Ca><3ltishELcJ-Y+^kIsk&NIv5SQtCc)t&6W{i0)B*O*J7)n* zz*8fIq5z8=EH^97(P_EaKbgQ2Xjrm_Mb-4k$e=AHob{HSf)BXz2h5yGnb1juCT#(# zIAchqY$rDFQ0Jxro4i4wr3DVdvdoi=wo`nczV=F>Vg?maOXrrVJ z2c0>>m5I0&bi=xPe>oEK;HZXV`s=b@8IOm3wusEiA&N)8PL?^?>2q7!K8Zf-@a{+X zd8T~mQ2MOi8HYP{RQ)TDrw;iSOSaiZBZB_4xC+A448i(3?a(vpu|a+&uep}_m*vH) z6-r(;x>5zC*%TSfn>Ap)+zn14SSpeYIB_^E)?_>Qtz!(lJ)CzK6mz$QnFle)U>e51 ztjZsMSP=jv{XE4QJHX*sS9A+n75CF<$p8(jMSN-9q@q)gfPwWKxlBJZe|`4)z8rkj zK|+)c_O*>MpM=+wJrTG7~m~Hz3}b%pS(*-d)m6joE`R0|KhASS{|dyOjBAq($FF-;7?y*pc;t zA@YhW{C%HHBL1jJdBEalOQFcRD~IYmn_KaEL8RU$)*~T$>35Zqc_+*p_Un>MP3sq8 z1D+bv8U#`bC`Btww)8IpUvaD<62ta6XtxwO(o&)9FFICSbUybbT~LC<@$#NI5dFN? z%Ow)R)a&ucPrlzq2~s=StGsRGZ^VU3PsP9$I7rycrpF2?+8*hkXY~(~&BP`pTMgK$ zl`OT5T9G(o44`Gn?MAdG^-sI?(=HCv7q4A=$szU@#bO>`TAGE=7ZP2oZ# zHyVFsL_V>4pqAcW*t6p+q-jCVnd^mKm>Z=xML58%7emlM1}ChpPP#DIxH!;a=LP*9 ztC+t4NSg#yK8WMq*-MnIu_4*%pyN_z2F{at97z-3Km<$`+)Z1NB&!H6@M{( z;$+MVl{Cgd)J7=XVF@bW0U;D?@9%*sd5)llW4ojF|l}efI~mN1wnbgH1=q5 zuUaSV;C$|7`qz>D=lSi-4-}rMdm1#Jnz{XMw7|UfhT9YzNxZd;a=PJ;P^wzqQ_?v$ zD{_QKY8Kiuv>;j|2x3X;qoQ4qy>)GfifW)g+0q%UCBk03I1glrmXay^9DeISvmQ#B zC2M+;<XAE@Mq(~eegen%IQN@rtqs@?a#pUOBOa9c-QtMDH-jpI{6U5^^ z8QsnfI;n4*3n+2kG!Hb}1(3AF*4S#)Gz#C6LlLx-eF}QK1R{u5^k8!(Dft|~h6d%t zMW!Mckk7bBq+2G54)a3DsdB_t4-bhhC4CE#G-J2l48Fvp(t<-C|Ljv<4jLWatG5UZ z{r-Nxdhq;sippun_-^orlHMp=5?Ymvn}>#j}8m z5nA$}Np0=cmH%A~fS@@!dlf1*_ANcHrc?ebAx5e@L1K!%6wN zt;$Xv`sbl^K$dcE;0h*isN1_&TlE0bF^lwZL|xP1R=G`RhCNilWk6QaiDWO>_vaSi zG%KJOUIxy#;57(#=UbfJ@bGPMhOh_{Cd&HM#}$ZzGaF6{ggqw4l(9LyL>05R!^l9G zVqR!)f21_Ih*?*k%fQWCc@Z&ky{MTj>&ji3T zIsA-!3`imab_4e!h1$Io?GvNY8mAH`0@C^tVa^zChc~x!ocbRZhdyFcUA;y~VLiX3 znBlxJzDsz!V0Z~tz&ImC@av9yzYm;`M<$*UNErc^jn>4C_g`E9|GtDiP-~W;xTjcR zEVY^2RyeE=g(;AAdHpwsZqz+?)!y3>E31$7^;;j5pPk- zq{^*~0R^=_&3JcWyMT?3v5V`F7=qRLTh&!GhOAaws1AjORZ(nTHJb)_{o2zW?(i3( z^uZWGA^rk7R&Ct&Ui~V%bMLD@^SQ!YC)>B@ z^^AEbJIA!BneCs!*q=7(fyZ~Xz_{s9aNhb8dj)aln0n(@S+4o&{n4$!%~)6^D*6e! zS4KVMOr%Tp4M-xVouw#Fy74!+iuMvHW0d&C>+p|IxZ4A%uao%ljid~2ugI;Fc)im% zU-X-T1FlgL^wB2!22OhD8Mo`)-U~Z>k~sEfa1>uch36zGZ`7CY z_H8c2_EO^%z2a}i3HqZ3G(xhb-7*QqCA@fIQ6E!k3E1Ke9rcdrbvMKZhVm8udby}q z+RhPzyH?FFRiTUehX8Y(7Qs?P*I9XrVs%CrO3oyQrNLEG3mJ#25UnxQ40KbtXNJm6}M=llSHVM0}-kY|qq6pk9qkBNR zRIkxFWVrrs#~Z*l?t@xIWo~~Cp0Ze{`Jxf|@%7>R+g|g4&b~Nvs?{%RuF#ZQWC_JL z_5Mx8(aZG6Y60F-HYG+9aQf^sd?)(Ar#XsbPTvS13*jZ$vgcXN*B-X-oQ*Cvgx=Bl zB_&GL@$Qn>U4$z9tT-JH%T07PQ$GsePREWI1~*Xi6?5s;DSKW(3=!bf74c#iz2*83 z<+!K7pJ2Nq{q&dQG-M(9q2Jqsvl~16X9L=oO26XGBS?vYW1-W2ZG@O0&e_EG+F8CCQuT>YlLBP`pQ8A*ZFMxQw2qa-xMxNJ@6~7>Y9KUk&|zmE0fP7ABqbbFI7y2jmCT#>#|vkb6G| zQ>8qM(8;DLQ3l}*INgh(H3zHArn+)8kimltUG{uAyT`p|xu0o3wTo_yo)QTIa8q!R ztIIMj--QI}BmcxD#@ybk8CrfF{%u(YK+l=v;Mml1??7C9V*4iCek5jFN-mzV@Yl;z z9fsYRqAR~4Su$fbPF-reAXG{QDU?<^5 ztw`-rWwDCbrxKr=n+t7&qIQWc$y{@_Q6@wh>nR8xnYU-UYRQ~_-{l%~pqmSPIFu6D zh%y>fqZb}NW;VBz(Bmpqf3VgT8<}6eNq87^IX8u%FMi((61FW^j)oIw3foJt>4vHO zvCZxAmi8ewv4)_#c8&B-WPpV~NRahw8M9b3_;*W6emP4%1?(1-#m4Y<1bg&21S+yV z5?E@E_xwYnaSI!D!+{5rhPkakTytjQNVK4`2?ui=yT%Qp=WAj zG2|ZV@JkDKq?t{3p^}F*0<}D>N?M#l3aiYw{>+X3^Emba z0j4vU(_s{}v90dIt3z08`;mK%?c(aXg7tY0H9Y|k6}B5`_^F~VBK->7;PMBb4qDdy zQMp(I4mW4bxaokrFvjIGI83Q0PqF^;c#y`?bnt2DTmU+5$lvd#IV3P#zY?ect~L9D zlwZdX-!NWxp)!`}f@eF7(GyQvm3M8M%*dNXLW}u@QmbhTh5({gu(lbRY}u8v+)40Z zN}%=K?5#2|&{3{5y6qAdUH(wEq8&jh&qH?(3(G|uTu0(njI@K2ynV6wJCjgRlm$CP zh8p%c^6MuVhwy?7;h*i!h$)o_LKsfZ3 zvlS8^r(Vucj+nb94WtM->J>qN9Wtg($>rw-UkDK8|&EtGr zSYZ_dQ#|5lV(Mf5G}TJYX@}&fdwE*S0C_4%;c!=jt4pBytVqwmNk1zX&Sd(XxFiE9 z=w2GR-UgKdRD!=+@UF>Bz{>@N)nBYsbLxs9MMVoJsVNuIi@(!<7H}>8LND}mm&P_z zTJ>>5AN@F=^mw~FS^d#^Ls{7pnwpwMIm8t*={&kh8zR{O^n-$l4fxcyBw5@5Rqu*F zD!ZrzIzgp?Bdvy@lb{`~RwW!1ySu<%l(JQQU*v#J8m9_~rh~_?HtF-W#WPY_ay`)O zM*cS>Fc7-*$plMS~UM{dSCS^Hr zb_Mch*LqxIl?(7El_rOT=kiFw+u7>kPz{V`YjdNs?y!s$%DnqMC({+@J6dGbnTYS8 zy&y+=vk#KI5MdaHG_=U07Au9-Ly9`E_&)mzBLL!zqk0A;r97?E;R zOvD`oesfO4MS0S#=&UoWm2;0UvaYFzk*~1beZz0^*SyfEPb)cF5Z5x(e%%z=kgE7G(1wj$FaYaOcepWTpZ*QW~+AF7n(*=nv z3xY}xKLyVT!gUEXGGV8YS_7Oa67v>4j_61=;Jtp^`K^otLMOq!a>-PmZQqkGrB!!N zGA@kM6-9BN2)M&o5Y`aQu248vQp2OZ=}!~q63Mtr1A-DU%0J3s8uj?f54cYX)g`c{ zY}`pL@#*O9(`uaa4)ssi@XV+n26gxi&M?jN5iVTHx??Jigrr_Whih=B55`ntzLv1R z4s9ZQXj9jkK#VVw_rLo$%!k}2#L-ObJb2EK_JSGfCk$}T5~N54QF?ZSdcv@6eVbnq zv?Od|jyz?=4E65>fxkexscsmv_6R<%eIH#3>1M_@SCccxcTO|gwD|FpIn749GG*n> z%;lbWt|(1>B|bq`<{Rep0n(Rygdf)lbqRysUBAFv-HSvCjek$mPxXMFo9jN^VOTR2 zhZo#c?iLwHonXP@xosCK+|4pOoUbh_JaC+45=IA7Sw0Imo@=Wm@dFYt!?A*XP&kEk zgUom3A!;_&Co+aOP=k)Wxr0-QV0!K#%K}{jl3}h{hC0h%`+<9Y+P`dKg~Wu*ncR4m zT9nW%+?Jne_YYurFvk5N3jhkNC0r3*%CfPz05;VK%I(|S?Y6zHX(^CpQ5g_XOHE(w+8vyJQDE5$?buOGXuh*mtPC%)YfGA%e8gOphp4oGHx# zt3Ti%w?6TF3lO4ZPJiEKSO@u_YiTTs~| z^7D2DSBl#c{0<4vnX{n$$^!{{wOMB#7s-H=?RrU8CT6P#aznqfNRj&B0j6l&Z9Pk3 z_^N?FxWf987<-Tmr}$ycw(Navn;}-*%6SS%W7Vlo>Ur4ysU^U1>cvOU5#e1?c|rbF z(0a(@wsi(W@&|e_=!xJc%2V&E07w$UH3VTq3MLfd*V`gN)PI;|c5g{)6dX0+BI$`_ zOPsBl@W+axC>&R^X1aVX5rxARsT~N$9~AeTXM=mZRjCd)qVvUn_EIOHyQ6(eoW*TZ zmJgSo(EE=VnT6l?`3MWv5~swNp)2hcR_TPn#N>Uts*hW^3uU@DW74zyuxEE_Ug@j= z={rQ!?D*AbG-E~M9Si~c{I%zlc#41F#m0}b)>*Wa-KrCsLvXlU3}JMSz{#DV7r!&y zj!7F@7i&Dl8v0dCk-FYJ1bPy~b?rJf%Tsu7a`+F);jTzw02pC1xI{J90S6K(zB1h% zve9rh>skjEk2t+IWz}WS#x83}{-d+se0l;fO@BUNbVBz@>T55;MHCO`LK>!_*`CZ_ z?Xm#5TCM=+6M}WlpPZ{=-gdX>g8_cbSFPT%V_SH0YEtCGTWu_LgxjOjN4V!=G>KNn zYmKOww61S4^M0XygaxyhB1E+PM|r@X87WyPmb60fLb^Hjyq{qW<&-^+)o-Blwx(zB zSrr{NjTUL&O7q>^&8G6#@+}!973fmFHFfolj=BMtB+QD_W@4A|k`}Ep%+=Pq#YBdw zbt``t9)M^OedF4n>rz$-gD#=)!l{?tBA&R&oTrX13nY4~Jc+l^SMy0BiZA z_bZGKfzty_kY)twufSFps8Q46%yaV0W+gXSn4A7-_+z`uT+ugaADtf9>yV=>*mc162;NRch zP1Gi>(kc#!X%F}O(6OJqy_7+Oo) z%lGr*Ixt^<_In(X#M3{Q?B5)36J#JZGZSP!hy)d05FAzxpI(W7`|qDiZG{4)J2!R0 zR_{2;u1`JD-B^4aE9j3o_Onmf;b>j#8hqU5FNMn6SN<#(e1?r*JAKiMro=?6gN~9<}5jDPiu& zxfWO)rZY#v-FD=d@@vJkMpHC96fB|5c?)EUJL~Ia(!a5i1y{+uwn}=tD@vv+v5fxf zRG+c{RcB2jm6(!=M@J>3puhnG6D?v$L1J)W3gTJ%DL5YQ{lMD6qeVM^ymZRQGrid; zcz1E%V`LAfW7C~cOF2C7i;wEdg0GwTAm|vR(_#UOaQuKNc;w9lTpbp8H}T)>{;dfl z6MSA_tYa)>uO7D`ZfDPPkx_ExLQ3q&_NT^eGPr6)@TN7X5xDA46m_z66;Vjx*11## zMAH@67>z;ingkfGGSFm8v*r9HzYIi(DG&8Qan6|P0#ai=mvHXOoGL_xHU#I+hnuve znlwnrni$v>PBd2u>5pBkdJAW&=ZCruF1+(D3RZ8|mNwKH4*eo5Q_q@3F7CZFS5Sgc zW*3%N%|bRKrbKFgbymf;kVW>u+$|63(`HJ@M+OJ!;8PaST}X3dl}{D z{0Ir0ywK{()8(uG@v1&8nBT?&;Ob4#BVs(dOyYn&(RSxmM&F(qB*?vuYB`m1o6hJM7hFU$Ro4Lpf5@SMwZJAa-Cs9~9YUDBiO)zNp=F8knH0T5@L2r=S6p+A zXDSSq-fOM~tv^}2mS&(1J(XE+L`o%sQx6GI5+?EIEakThK^e>`M(TXdG$Ez)g4L_F zAJLsyk~57vk0-l|SdMhdn(A;5hgv;^-52m+dB)l8 zK!|suXZrdr`;rg!b*OZ_Fa{xp-7}cB4;~q@Bm^XmV|uQ*Zu+Ge=JF!m+^QD6C@2I@ z924fv+PW7$%L|Tj6g$_+A|7wkUa$oF7gbs^%L*Rrr)t*^;T057Z`ONn zUfqZ=qC2hA%pz>bHD3-e|0|C7ZS@zZ#bVXWDThrZN#ArwWOwk>bhvWoR}YK;bq{l)c_)pgC$ z)9t<&CD87Bj{Vr3k1LpKt<=0Uhe*dCjfO4Gh^!cw5|jdD?$LtABLlMtcczI_+cIh@6tAKNa! zD&_C+uwcgUB85B00Q7B#KS7IA1%)ZONc%{-@=%Xs_Ue7lb567;2d>I0rxwq{#{%Y( z=)mDDhfH!W9ub=o4!7sH!);wzzi(yST%D;QVe-vXD4=@WWa>=`$Y+I%4~1_xW!4}! z*DlKUNT$2-BLmUHmnFCux(0scM5Hb*J&9VZp9Uwn^-qQqj0bm=KcWd)O-Rl#eq=)C=zb4BX* zRuMf-KVH1)dH#WAtz$bc%Tj~JVbhUwbgo`eZBZ?7gXJ{>wQ)X2#^2y1BvZI<;PzLI zjcSvZv#qes`ypfVBRw+F5bbH+jSv~v5-JXLCP`?Pu)d~j1zElCZrK_;Zgp5WMq;^d z;-^UQY(Z_pphTp)ydJ8wip;u%7$<@h|y9xH$vYx*u_(0Y=1z<8S&t(_ikSMQC895c*{@A@ z@&)|k(v%Y82JUuC3@v2MG~%yfaSIDl<8A5ag>MFC_-xT!_b(F{sJWi+gl~V2uQ=;3 zZ`?SYy+&siSz68-&8ow?kwSK?L+o6km?zX=Q7+l8RNzZLQBvUGAgQ;v!c7}Y5%Mst zT^t7s+Fo6T#Ma2a*-}>wE36xHPSAa)$_l3KCb88=Iy#Ho*iFj4Y183+D`y>w-b|RA zuZ=t@s}&+XwVopEJ2GfwT3AHxY4GGT{x7$Fss^-f(_Y4U04!5$L?pXJtTwTta}5Bp zuq#np9o@p*I!}s7Hf$IRzmai%{ubbwmDNL*dfudPQ8JA^Zya{nkIj?|$)W{Rm z64BZ2iC+9e^A!|c9&Uv>zdGS9Fv~v-wRPe*jfi(P;iZ~vo$o4%*hOl;B42??!T-V zcrz%s<02Ml;c{*yqGR(39Q>uLe!+Fq(IZLdPWIggq;&$R8Y*(+^m*;K;&LP z1A^J$_<`0nSKd;1ezWEID#S%9LdCMzqUDF*48c(yU7N>oaH+pniQ&`GUy;v$I4jO1 zzT_uZ?~?(-!*s>QO*koUJkNVF;jAe^-x&g&OxL7X@488;*uOmF9@}CU4H40U@f+X? zKMzdWcv=S?uy$`@{J8(PpyENE?v+)Gc^gOH%OE)mugO#v4wugC#0~CPk0d=MoKdQw zlKb|?GQ9x5PJ;M8N@%L9?FqB4Szw}HG9FKQ`lBr9^b6K*QMw9Q8j1w?aiS1JkKrq6 z`ijH`h?um{M+`{)DbnMR04U!K7`G9996H9T9A3E2OH!PvZ!IiEr=&pl!1qU6V=BUO zR`+hpYZ3Km{P6R$3#LH$GuNz)*bo?PYjH>DbzBy5!DTuod`t^!+D_$ zl0nQVQcbiHC@F`G{3Y&NS$*X}1X zUrQM-cDJ0fUhtDa9raz?=*j<1Qk(+=kDJV81&-jit5Kst!J zC^M)R{#;aSLHdTqud`g*KUDQJPv{kwN@crUXF#jmH`1vz{*~wKQ&k>GCX~}6HZ8X^ z;!PgCKts4H`oUy~1yg_vGluz*wX6rI7M}6bRcz8Lnb(s=puvlP5;PU*XX$SLQ$+)J zNHRKqrp--8bp3hlElDowAK8naijk;9Ke)L_@(|Nht%N*gKqD@!jtkZ6uV;{G@$JB` z<;Yo8aP5yD_~5kgLinV@7PqEGytju9CBe~HDV1z$mjeQaO}}C)1(bCnOp}*&$>6Hr zfMt60K9A5xykjg-i50}MU>k#<_x>mgg{-a@vFiCcPqfC77ILWlF=qGC9JDju+3AP> zP)r6u?*B6|c|i3QJHn!#@X9;FT2mLBw2=dPHFaEIrbN!*R|g}ZOKlVxTE4+ZPbEi} zq}v`$%B#Nfwur|m^q$%{O%Bjdt+LS*bW{_WRF;PtO9Co zz6A(H_6?PnF)@|fkR-+6h=w9keW6Qva*9HG9akp0%l8nS0(qfo86f4+2dbR;V!6GG zFrg@Svl}$ZS9~ds;PhAl<%(U6C@T9=sp_kY;OjN2=9w?`aN?%6N$-otAx8n|O!GR4 zS|ANtny8J4*O{}A@a&iA&Oo0$7J+F2L9 z?z$wQ;-HcIQZ2p9oZu0u6f-F$blt(>w>{J2fL{S9fP-AnLK^$>K@5*^P@%xI4?~f| zn-Rg}rm7U!EuzN*9%za{v%dP|P_CVDN;zp*iniUPd`Bj0vWg!~I}(@q)Q)BlP=md` zk1g}umgtTM0WLkugH+TLz^SCAOezq5GLDCBf1%Bt6auIZAp_6lNBbHfRIJ}8szc7k zKjSK-soJw;A6wP z{t-e70utkmyYJBYr0&=ZoB;izAwDsEPmZ8rmKJWa?J3HA(bXG$REg_1&Dgi97^U~w z0F@*HrR1oROHR}9)I+igbR|_nh(?iCyEE}sT|e{a{@e{!*<~rBbU`xxU!^%)4akTe z6YF3(knXd4S*!y-HlX$7Flv^nbJFWKM|_WSN{pBr)i-iR41=88GzSSxen7&Axa^NehcP~8u<67P>mLJ55kh!Su(ct?6C zE)<)O5pX(jBEO1&w(3XUj<}SW-^$XeQl5({n*CYYH3EyhD-z@#z{JvJ;Z?~p59K# zqDY>V5-x?lSgDNFRrsKUDDm*HhlB+cgt>3GTcj)$$>d&9_KhP;a@86Dqd#e$jm=Of zrRE);9nzB<~NnBVO~fmvjL>l^geZHk_H_(CGu=Vko<5(Bl?DXWBQdQoV#*3b#r`eT|rW_(81;iXH-ykcC(#waC{v^ ztb=4(uTR}ZZd0*^izKMwPc?HI-xqVcw=D|UAk~_j_PVahF&~50sa3UnoKgUyljF-} zR+8)Pe|p-jK7%~eqXDG|fu7x|YcxH5xY2l=NRqWMVaiJ@(Fz-H$6f>rW1Ko~ZrC{& zc&X6FGO!*Q7J5y%Q4WZSx1b)kx_mict>Ylt-7E<|w&0^$-4~NTQJyP7V+5=R7w1f1 zO734l+er^GPvBslcg+97e3 zEeS~t`mvQE%HZ->BleONh6>Bh?|HcQD*aM{?8X5{Yjx`1oPEGPUp#(; z=jbVIeK*V-F|?G8EhKJ4959^dMLQ*sNEejHvNk=Qk;Exm<@R!KC#*@gZl&|B06J?e zDyH7(L}I25#@YU~?3*L;73U8^0d-x<^tQD_gnzEHP6cy}xR~dQjkHoF-=6#}NyuGw zr*puZ*eB2*dVA=3j(f5L zS^436*J0RUYoNv_79#96t9dA1dNA{XStw+!4LWq0f&6Ae%jB zHhR)N&Egaqw%IpsqS?P2GM6@Q+WG}2N6-E~(1nWNTlKD6F5vnHC!^4;xreX7%`5bC zY7UsA73qbk^nTvECc&V87cwnIxSW8Fb!eAce-g43FdVTSIWl#cdQ%?y;iNUBo@bf} z^rb0+zfo;0qFa9-on=E{^zMP0s28#jqKOt!$4aai5&0V4e>|#)#4|ux%eJ_@ek3?U zPM>b6Rfnu)O@vcP`tk4J0)Y13xxiCm-zHeYvU0IOn^LlNj`;76*v72)cFj1yuY&eP zHeo}12yE(jZK~h9#iBG=qTH{|bsfFRdRr5Aj+PhviWC)kS6wOW7R#&qZb3uf)$h%DHMq@^dRr=cTh_2k%!mdPpAOSYEO!(wiY~Uht@s zQ0H5FG`@ka*evJwY*8Vh2S+P_8L=B)!HQ;=rW*aSJwU4rTDAAO;p-YIw1*bX`R1OIL8249eHBCu>9=D zxv`YT{XsY+u&}QsS>3073+xv|eEdeWp>UwR9K<}p3c8__t#|Z2=j;xjEu$GmxI9pR z7GYCP5I#rSAfFWn8$Jp<;g0a5k-$IX_=ewJC*78o`Jgox)86Rzl26Fj}o5txo2G74QGJt*X8XbR+Rau)SKO17(rz&g5QJ>>ltNZTyU6i z_>Ubxc#IHvvz+aR_RtiixO$IMc zJdWhRwv61RI;~i(%^*TsGDImsSvHArB02qjkn54udE$6Zmc{aSNK_bIomf64goAV*K9*!CnQv*uLKCzc)ypGVpQ2Cz(N7qxDuF=L_##WcPE8^JwcdcFK#5RvwOk!lx!H&Ey*aae3v7n^>8R=ZB!#j~9i3=BW9kZ4xX$Hb>C;H-VgI z`)o{69UmHWBI=Nz`puNwgvQIeaUvdfrL@WgM4m-?gfS3X*P}VjV#6XZ3P5BF*J-si zgZHc%W1ogQ>AIPrLv6^>8+1=enhd7VnMT+}o+-2`t+^ROBQQ+A&s0Ntj)Vx%E|p=f zhQfdM>snQdCm02@j6fUk7>q(uy_1vvRS0*BdJolwyA%@NRj~E}JF793BkU$;4*heI zTEKS#D35B}*)wbS$;M~|T9|q`9{^#MJljN*2wz!q{uMK1_p!G}$IYNP$E`lLz-U}g zmnkiROwjN;q3c-C;lLTd|0D1yOMtbK>SnQX>|-R{qSou7UNNL;$n^_PKnJ$~T|Yy! z`Iq)S>n{P9g&h8od>qzCZSye)LgtMUSi+8B0E&ku^qD@hBPPP%(vy8ajc(pu)b1O!2GZCEe>IaMzaU4n5~$Ft1rg6bGRW z-+lxY&GW6!)~C~lUn@yD!Lka*U*qV#2BnSyxZymG=R8RMh{=R6Hz2S3Btz-mR~DWE z$18Hs7zx@B5oto0MB_it554g(Ru}ZgU4DAGnDe`7|GKyFZ;FmeG7D{4HtVEzlmDe< zI8Ca8(oe+25VD;!fh$=hfkU3*Lp%2f--zL?s4+y;tp6cCj%jYn%+rFi^<{3x^G_3N zVFh2;%MZf9T5(c*8E)Zzee(Fm%xVKvsb#CM;*)o1j82m^^*emYpUJJkr@nh8rIL$? zOdjh4O%8K}R<qxwB8_K@$T=nSY*3QFA#Z{hXzRx+$9lt7z-oTcN5uF9tDVbLj&X3+Ml@n1 zwe;t;4Gcvnsc410!gzuBoV{a5WxQogt`Qhce*GR4Cs%O>Mi6plKF{?M`n{t;S+mC}{=V zEVAmPX@w+A+6_TW)rn5Xg)W3FJMbR%md^&`bcb&H4f=X zSXyjiOveB+kc2?_+QHtY$X>cDr3ANf6FrYG+fdp`RD8=PO<*!5&5q!I#4T^J1l({t zLieV80(D#!j9=*YKjoC48`bHDC&&HeH}WPd=-z^OSk1#DIosUG+^53KkurH{HZ+2c zbOdU2fZSw9_!d-#V;LhlVng8g*OW%+H;#y&SGK0ry=4c`!r|k@BPMQ?m^*4${CZyk;)$3$cy0)M%g5>oJCN!#UYQE|z6Z*Gyd&Fa^F&{(a_uZHuL zV5Ac(WlNDO_+DE=TKwY8?C83MlW*vy#2)88$0iG48dYXNue16*Wtm(0bV4Aj^bHtX zR^hv)-e77**AV|q+GKn!!xi>Q<` zC*FFd{@<(aJ7F|1O)4doLHS$?JgrEI0P2liFQnZD9mzVZHcmtTk5;8|G3nv(aYrwO z05tAPf38A$Sv8X(yvyrT0ptX?n8FVV&rVg7X}AMFCE9yF+Z@yRfZ2Dh3Qv|P{e8Vs z)6khKo`)keCQe()rhHX{;Zw0Bm*`;nuur!C014=s3hn94H)(mQ`bpi6q}^8Brllqg znJSQKkLlkkw4}q zj={>A010!EyFOfnDK#KZ180ps&>16cR47(D2*-b7uyvFiBiA4AnB>>jjSHkrn*5dB zNqQ8Q9F*mR24Egso(g*K=6)}X>4H$Z+Lx4jis1u(7T!&`W|qW3zvYk^-S6xj_e+{B z-&`td5}n%Cn(`35lM^8hbJnMtL zg~q6Z)&FAMc#C#2fp=MTo=m*(4$p)yI8|Q8xO{KsV^e2kWs@yt>vk4t5f$FO)xJUT zr0V;bOwi-DqwZku`~gyIJpH?(&6Yapm?tH$bD zzFr5)WtBZ2j2eY=q)?dk=9t|(c%$w8W7BUIp+nWtQ6n>GCqLlLx1BA8%B!5i3kL15 zq8qtnQeiAfQBpGRVMi~vYA6Ej{!)0pgHjujV%TrEPu4k2)vFG--|Q?l{wi9iY8hs) z@d52&mk%ubz6`7EiW|;*zY<^OcCiLDcsT9Ge^SzacdtjD9}s!%EGQ2P;%0qKgO1+1 zDqfdqc#veaLlB|c$GxNx+Fe>|t`q!}X*q+s9T$KH)xrj{ben=Tq^A^c&+GI>0 zT!HzZQg1`$__Mg-Jl{{3*QeYOCX}bMF0AzipL_&4`^^Ad$}4z7s$5IX-FYBstDr7F z+?Q=Qnti1n)`s+?f58xWk^ey}+RnSN1JH^Zg{WsicL)vOTF4?S0&<1e+5hfGw}$7Upn{B1jtC;;Z=j`_laq#{|_?=QS5Kbp!88qOkj~& z+9W9w25_=(Bx*9)e-1Gcl2K$gSdM3uUSg>q;)?=%wT~+S6N(pW`uNR5nx+vN6Ha4w zg8n_7IpV+|BrKl&n(6b|n?+YB_Z7C1Gc}@;6PH+&xyQ|O{`WA?@!Z>W?~dZnAcp}t zid6_N12TbwxVdn8!sQJDUwwq)cYQ?<#{eHr;_6s~MucCaAZuS?#9t$`aq;o-*XQj% z!(>|=2{h`^QU7Vi-4O0!FzK{-fO0{tK{<*1FM?3HuuyY|Eb0^-RFfU=$!hdJ_X4=v zs0oU=F5=#jWVOs2taeN>y^(B&Ax^-2&Lx69SZJ8O+R~n_a1CRXEvNxKa_;o?9mYnR zlI^rOARZZ~3@mqXaeS(c3zOWckcGk+DcFg8K>K*-Gqil>o}Bu^Qsu@RMZO8~bm|G zYs)A5amL)DCtkw30{YtA7;7@rN}FV6W8Q4PZN60`kXgK|QC-9($Y2Tw`OpR5mqr9D z4B(UtfZayvy%;CNVR_$s*+HN)bBVi!l7-13xd$zCbTTo$Ys;tGJUiqsTF%F!1PopX zs))i64)JZG_aJ&JknS*{Gne!(OxWjk3KAqX?=i+aO{#zs7~pf#k|mnhInFY)a=0~J z*RI*~CH@i(H`UVj%4jO$Nv9d>y!-wSd9R1ZKnT%3z3)Kb5wZY)jPAHXAPX1u)
!5|ws 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
kk8_S6l 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 S*#+FwPe~3eTP0%}^#1_R`~fil literal 0 HcmV?d00001 diff --git a/site/content/ytt/docs/v0.49.x/ytt-text-templating.md b/site/content/ytt/docs/v0.49.x/ytt-text-templating.md new file mode 100644 index 000000000..f2e588508 --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/ytt-text-templating.md @@ -0,0 +1,54 @@ +--- +aliases: [/ytt/docs/latest/ytt-text-templating] +title: Text Templating +--- + +## Overview + +ytt supports text templating within YAML strings and `.txt` files. + +Text templating is controlled via `(@` and `@)` directives. These directives can be combined with following markers: + +- `=` to output result; result must be of type string +- `-` to trim space either to the left (if next to opening directive) or right (if next to closing directive) + +Examples: + +- `before (@ 123 @) middle (@= "tpl" @) after` produces `before middle tpl after` +- `before (@- 123 -@) middle (@-= "tpl" -@) after` produces `beforemiddletplafter` + +## Inside YAML strings + +`+` operand or [`string·format(...)`](lang-ref-string.md) method provide a good way to build strings: + +- `name: #@ name_prefix + "-secret"` +- `name: #@ name_prefix + "-" + str(1234)` +- `name: #@ "{}-secret-{}".format(name_prefix, name_suffix)` + +However, occasionally it might be useful to use text templating directly in YAML strings. To do so YAML node must be annotated with `@yaml/text-templated-strings` (v0.17.0+). Annotation will apply to node and its child nodes. + +Examples: + +- basic use +```yaml +#@ val1 = "val1" +#@ val2 = "val2" + +#@yaml/text-templated-strings +templated: "before (@= val1 @) middle (@= val2 @) after" + +non_templated: "(@ something" +``` + +- nested nodes +```yaml +#@ val1 = "val1" +#@ val2 = "val2" + +#@yaml/text-templated-strings +--- +key: + nested_key_(@= val1 @): "middle (@= val2 @) after" +``` + +See [Text template example](/ytt/#example:example-text-template) in online playground. diff --git a/site/content/ytt/docs/v0.49.x/ytt-vs-x.md b/site/content/ytt/docs/v0.49.x/ytt-vs-x.md new file mode 100644 index 000000000..fbafe2fcd --- /dev/null +++ b/site/content/ytt/docs/v0.49.x/ytt-vs-x.md @@ -0,0 +1,61 @@ +--- +aliases: [/ytt/docs/latest/ytt-vs-x] +title: ytt vs x +--- + + +## ytt vs Go text/template (and other text templating tools) + +- [Go's text/template](https://golang.org/pkg/text/template/) +- [Jinja](http://jinja.pocoo.org/) + +Most generic templating tools do not understand content that they are templating and consider it just plain text. ytt operates on YAML structures, hence typical escaping and formatting problems common to text templating tools are eliminated. Additionally, ytt provides a very easy way to make structures reusable in a much more readable way that's possible with some text templating tools. + +## ytt vs jsonnet + +- [Jsonnet](https://jsonnet.org/) + +ytt conceptually is very close to [jsonnet](https://jsonnet.org/). Both operate on data structures instead of text, hence are able to provide a better way to construct, compose and reuse structures. jsonnet introduces a custom language to help perform structure operations. ytt on the other hand, builds upon a Python-like language, which we think will be more familiar to the larger community. + +We also believe that transitioning from plain YAML to templated YAML with `ytt` is very easy and natural. + +## ytt vs Dhall + +- [Dhall](https://dhall-lang.org/) + +Dhall language is a configuration language that can output YAML, and JSON. One of its strong points is ability to provide scripting environment that is "hermetically sealed" and safe, even against malicious templates. `ytt` also embraces same goal (and builds upon the great work of Starlark community) by exposing small API in the template context. For example, there is no way to make network calls, read from file system, _or currently, even get time_. + +## ytt vs Kustomize (and CF BOSH ops files) + +- [Kustomize](https://kubernetes.io/blog/2018/05/29/introducing-kustomize-template-free-configuration-customization-for-kubernetes/) +- [CF BOSH's ops files](https://bosh.io/docs/cli-ops-files) + +Configuration customization tools are unique in a sense that they don't allow templating but rather build upon "base" configuration. `ytt` offers its own take on configuration customization via the ['overlay' feature](lang-ref-ytt-overlay.md). Unlike other tools, overlay operations (remove, replace, merge) in `ytt` mimic structure of the base configuration. For example in Kustomize to remove a particular map key, one has to use JSON patch syntax which is quite different from the normal document structure. On the other hand, `ytt` uses its ability to annotate YAML structures, hence it can mark map key that should be deleted. All in all, we think that `ytt`'s approach is superior. + +Here are a few more detailed differences: + +- `ytt` overlays + + - are not Kubernetes-specific so various types of configurations are covered that kustomize cannot deal with. + - cover all CRUD operations in one consistent style whereas kustomize needs varied syntaxes for varied types of modification (SMP vs jsonPatch, etc.). + - do not care about native kinds, CRDs vs something else since they are generic. + +- `ytt` is not just an overlay tool; it supports overlaying _and_ templating. We see configuration writing split into two categories: configuration authors, and configuration consumers. Configuration _authors_ are best supported by templating; however, configuration _consumers_ typically need more than just templating inputs. Overlaying provides the rest. Having one tool with consistent functionality across templating and overlays is powerful. +- `ytt` is more explicit about missing map keys, etc (avoids a lot of unnecessary typos early on). +- `ytt` allows you to define variables. +- `ytt` has facilties to inject data into overlays from a variety of inputs including command line arguments, environment variables, and files. + +## ytt vs Orchestration Tools (Pulumi / HELM) + +- [Pulumi](https://www.pulumi.com/) +- [HELM](https://helm.sh/) + +Orchestration tools like Pulumi, and HELM, have combined configuration management and workflow management into the same tool. There are advantages and disadvantages to that. `ytt` is designed specifically to only focus on configuration management. Though, YAML output can be used with HELM, Pulumi, or other tools. + +## ytt vs plain Ruby/Python/etc + +Key advantages for `ytt`: + +- provides an easy way to operate on structures (maps, lists, etc.). One can definitely use a regular language to do data manipulation. However, this is not what the language is optimized for, especially if data is heavily nested, typically leading to very verbose and less readable code. +- provides an _easy and safe_ way to execute templates without worrying that template code may be malicious. One can Dockerize execution of regular language templates but of course that brings in pretty heavy dependency. +- provides an _easy_ way to customize any part of configuration via [overlays](lang-ref-ytt-overlay.md). This is not possible to do with a regular language without parameterizing everything (a general anti-pattern) or bringing in an additional tool (e.g. BOSH ops files). diff --git a/site/data/imgpkg/docs/imgpkg-v0-42-x-toc.yml b/site/data/imgpkg/docs/imgpkg-v0-42-x-toc.yml new file mode 100644 index 000000000..a04ec6eeb --- /dev/null +++ b/site/data/imgpkg/docs/imgpkg-v0-42-x-toc.yml @@ -0,0 +1,43 @@ +toc: + - title: Introduction + subfolderitems: + - page: About imgpkg + url: / + - page: Install + url: /install + - title: Workflows + subfolderitems: + - page: Basic workflow + url: /basic-workflow + - page: Air-gapped workflow + url: /air-gapped-workflow + - page: Automation workflow + url: /automation-workflow + - title: Reference + subfolderitems: + - page: Authentication + url: /auth + - page: Resources + url: /resources + - page: Commands + url: /commands + - page: Working directly with images + url: /working-directly-with-images + - page: Proxy + url: /proxy + - title: FAQ + subfolderitems: + - page: General + url: /faq-generic + - page: Code of Conduct + shared_url: /code-of-conduct + - page: Contributing + shared_url: /contributing + - page: Carvel Development Guidelines + shared_url: /development_guidelines + - page: Security + shared_url: /security-policy + - page: CA Certs on Windows + url: /ca-certs-windows + - page: Debugging + url: /debugging diff --git a/site/data/imgpkg/docs/toc-mapping.yml b/site/data/imgpkg/docs/toc-mapping.yml index e201693e6..428e1a8cb 100644 --- a/site/data/imgpkg/docs/toc-mapping.yml +++ b/site/data/imgpkg/docs/toc-mapping.yml @@ -17,3 +17,4 @@ v0.38.x: imgpkg-v0-38-x-toc v0.39.x: imgpkg-v0-39-x-toc v0.40.x: imgpkg-v0-40-x-toc v0.41.x: imgpkg-v0-41-x-toc +v0.42.x: imgpkg-v0-42-x-toc diff --git a/site/data/kapp/docs/kapp-v0-61-x-toc.yml b/site/data/kapp/docs/kapp-v0-61-x-toc.yml new file mode 100644 index 000000000..ec2f4d474 --- /dev/null +++ b/site/data/kapp/docs/kapp-v0-61-x-toc.yml @@ -0,0 +1,57 @@ +toc: + - title: Introduction + subfolderitems: + - page: About kapp + url: / + - page: Install + url: /install + - page: Applications + url: /apps + - title: Deploy command + subfolderitems: + - page: Diff stage + url: /diff + - page: Apply stage + url: /apply + - page: Apply Ordering + url: /apply-ordering + - page: Apply Waiting + url: /apply-waiting + - page: Preflight Checks + url: /preflight + - title: Reference + subfolderitems: + - page: Configuration + url: /config + - page: Permissions + url: /rbac + - page: Namespace for State Storage + url: /state-namespace + - page: Cheatsheet + url: /cheatsheet + - page: Dangerous Flags + url: /dangerous-flags + - page: Configmap Migration (experimental) + url: /configmap-migration + - page: Command Reference + url: /command-reference + - title: Integrations + subfolderitems: + - page: Integrating With Other Tools + url: /integrating-with-other-tools + - page: GitOps + url: /gitops + - title: FAQ + subfolderitems: + - page: FAQ + url: /faq + - page: Resource Merge Method + url: /merge-method + - page: Code of Conduct + shared_url: /code-of-conduct + - page: Contributing + shared_url: /contributing + - page: Carvel Development Guidelines + shared_url: /development_guidelines + - page: Security + shared_url: /security-policy diff --git a/site/data/kapp/docs/toc-mapping.yml b/site/data/kapp/docs/toc-mapping.yml index 706b0cd93..8ef6cdb6c 100644 --- a/site/data/kapp/docs/toc-mapping.yml +++ b/site/data/kapp/docs/toc-mapping.yml @@ -16,3 +16,4 @@ v0.57.0: kapp-v0-57-0-toc v0.58.x: kapp-v0-58-x-toc v0.59.x: kapp-v0-59-x-toc v0.60.x: kapp-v0-60-x-toc +v0.61.x: kapp-v0-61-x-toc diff --git a/site/data/kbld/docs/kbld-v0-43-x-toc.yml b/site/data/kbld/docs/kbld-v0-43-x-toc.yml new file mode 100644 index 000000000..39a840900 --- /dev/null +++ b/site/data/kbld/docs/kbld-v0-43-x-toc.yml @@ -0,0 +1,33 @@ +toc: + - title: Introduction + subfolderitems: + - page: About kbld + url: / + - page: Install + url: /install + - title: Usage + subfolderitems: + - page: Resolving images + url: /resolving + - page: Building images + url: /building + - page: Packaging & Relocation + url: /packaging + - page: CNAB Image Mappings + url: /cnab-image-relocation + - title: Reference + subfolderitems: + - page: Configuration + url: /config + - page: Authentication + url: /auth + - title: FAQ + subfolderitems: + - page: Code of Conduct + shared_url: /code-of-conduct + - page: Contributing + shared_url: /contributing + - page: Carvel Development Guidelines + shared_url: /development_guidelines + - page: Security + shared_url: /security-policy diff --git a/site/data/kbld/docs/toc-mapping.yml b/site/data/kbld/docs/toc-mapping.yml index 7f15f045e..8b07eb0e8 100644 --- a/site/data/kbld/docs/toc-mapping.yml +++ b/site/data/kbld/docs/toc-mapping.yml @@ -10,3 +10,4 @@ v0.39.x: kbld-v0-39-x-toc v0.40.x: kbld-v0-40-x-toc v0.41.x: kbld-v0-41-x-toc v0.42.x: kbld-v0-42-x-toc +v0.43.x: kbld-v0-43-x-toc diff --git a/site/data/ytt/docs/toc-mapping.yml b/site/data/ytt/docs/toc-mapping.yml index 29ae63725..a29fc1081 100644 --- a/site/data/ytt/docs/toc-mapping.yml +++ b/site/data/ytt/docs/toc-mapping.yml @@ -10,3 +10,4 @@ v0.45.x: ytt-v0-45-x-toc v0.46.x: ytt-v0-46-x-toc v0.47.x: ytt-v0-47-x-toc v0.48.0: ytt-v0-48-0-toc +v0.49.x: ytt-v0-49-x-toc diff --git a/site/data/ytt/docs/ytt-v0-49-x-toc.yml b/site/data/ytt/docs/ytt-v0-49-x-toc.yml new file mode 100644 index 000000000..24feeb540 --- /dev/null +++ b/site/data/ytt/docs/ytt-v0-49-x-toc.yml @@ -0,0 +1,115 @@ +toc: + - title: Introduction + subfolderitems: + - page: About ytt + url: / + - page: Install + url: /install + - page: How it Works + url: /how-it-works + - title: Concepts + subfolderitems: + - page: YAML and Annotations + url: /yaml-primer + - title: Quick References + subfolderitems: + - page: Schema Validations Cheat Sheet + url: /schema-validations-cheat-sheet + - title: How To + subfolderitems: + - page: Get Started & Modularize code + url: /how-to-modularize + - page: Use Data Values + url: /how-to-use-data-values + - page: Write Schema + url: /how-to-write-schema + - page: Write Schema Validations + url: /how-to-write-validations + - page: Export Schema in OpenAPI format + url: /how-to-export-schema + - page: Use Overlays + url: /ytt-overlays + - page: Data Values vs. Overlays + url: /data-values-vs-overlays + - page: Inject secrets + url: /injecting-secrets + - title: Reference + subfolderitems: + - page: Language + url: /lang + - page: Annotations + url: /lang-ref-annotation + - page: Data Values + url: /ytt-data-values + - page: Data Values Schema + url: /lang-ref-ytt-schema + - page: Text Templating + url: /ytt-text-templating + - title: Built-in @ytt library + subfolderitems: + - page: All modules + url: /lang-ref-ytt + - page: Assert module + url: /lang-ref-ytt-assert + - page: Library module + url: /lang-ref-ytt-library + - page: Overlay module + url: /lang-ref-ytt-overlay + - page: Struct module + url: /lang-ref-ytt-struct + - page: Template module + url: /lang-ref-ytt-template + - page: Version module + url: /lang-ref-ytt-version + - title: Language + subfolderitems: + - page: General + url: /lang + - page: String + url: /lang-ref-string + - page: List + url: /lang-ref-list + - page: Dictionary + url: /lang-ref-dict + - page: Struct + url: /lang-ref-structs + - page: YAML Fragment + url: /lang-ref-yaml-fragment + - page: If conditional + url: /lang-ref-if + - page: For loop + url: /lang-ref-for + - page: Function + url: /lang-ref-def + - page: Load + url: /lang-ref-load + - title: CLI configuration + subfolderitems: + - page: Inputs + url: /inputs + - page: Outputs + url: /outputs + - page: File Marks + url: /file-marks + - page: Strict yaml + url: /strict + - title: FAQ + subfolderitems: + - page: FAQ + url: /faq + - page: Why ytt vs. x? + url: /ytt-vs-x + - page: Schema Migration Guide + url: /data-values-schema-migration-guide + - page: Code of Conduct + shared_url: /code-of-conduct + - page: Contributing + shared_url: /contributing + - page: Carvel Development Guidelines + shared_url: /development_guidelines + - page: Security + shared_url: /security-policy + - page: Known Limitations + url: /known-limitations + - page: Playground Examples + url: /index-playground-examples