├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml └── workflows │ └── lint-and-test.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── docs └── README.md ├── jsonnetfile.json ├── jsonnetfile.lock.json ├── main.libsonnet ├── src └── _custom │ ├── helpers.libsonnet │ ├── main.libsonnet │ ├── meta.libsonnet │ ├── meta_provisioner.libsonnet │ └── root.libsonnet └── test ├── fixtures ├── helpers │ ├── is_string_array │ │ ├── expected.json │ │ └── test.jsonnet │ ├── mergeall_mixins │ │ ├── expected.json │ │ └── test.jsonnet │ ├── mergeall_nomixin │ │ ├── expected.json │ │ └── test.jsonnet │ ├── objitems │ │ ├── expected.json │ │ └── test.jsonnet │ ├── objitems_ignore_hidden │ │ ├── expected.json │ │ └── test.jsonnet │ └── objitemsall_with_hidden │ │ ├── expected.json │ │ └── test.jsonnet └── tfunit │ ├── data │ └── main.tf.jsonnet │ ├── local │ └── main.tf.jsonnet │ ├── meta │ └── main.tf.jsonnet │ ├── output │ └── main.tf.jsonnet │ ├── ref │ └── main.tf.jsonnet │ ├── resource │ └── main.tf.jsonnet │ └── variable │ └── main.tf.jsonnet ├── go.mod ├── go.sum ├── helpers_test.go └── unit_test.go /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **Logs** 14 | Any error logs indicating the bug. Please share debug logs as well (`--loglevel debug`). 15 | 16 | **Minimal reproducible example** 17 | Example code snippet, or github repo where running using `tf.libsonnet` will demonstrate the issue. 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Additional context** 23 | Add any other context about the problem here. 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "gomod" 4 | directory: "/test" 5 | schedule: 6 | interval: "monthly" 7 | - package-ecosystem: "github-actions" 8 | directory: "/" 9 | schedule: 10 | interval: "weekly" 11 | -------------------------------------------------------------------------------- /.github/workflows/lint-and-test.yml: -------------------------------------------------------------------------------- 1 | name: lint-and-test 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | docs: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 17 | with: 18 | token: ${{ secrets.TFLIBSONNET_CI_GITHUB_TOKEN }} 19 | 20 | - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1 21 | with: 22 | go-version: 1.19 23 | 24 | - name: install and run docsonnet 25 | run: | 26 | go install github.com/jsonnet-libs/docsonnet@latest 27 | docsonnet main.libsonnet 28 | 29 | - name: import gpg signing key 30 | uses: crazy-max/ghaction-import-gpg@72b6676b71ab476b77e676928516f6982eef7a41 # v5.3.0 31 | with: 32 | gpg_private_key: ${{ secrets.TFLIBSONNET_CI_GPG_KEY }} 33 | passphrase: ${{ secrets.TFLIBSONNET_CI_GPG_PASSPHRASE }} 34 | git_config_global: true 35 | git_user_signingkey: true 36 | git_commit_gpgsign: true 37 | git_committer_name: "tflibsonnet-ci" 38 | git_committer_email: "120686569+tflibsonnet-ci@users.noreply.github.com" 39 | 40 | - name: commit docs if files changed 41 | id: auto-commit-action 42 | uses: stefanzweifel/git-auto-commit-action@3ea6ae190baf489ba007f7c92608f33ce20ef04a # v4.16.0 43 | with: 44 | commit_message: "Update docs" 45 | file_pattern: "docs" 46 | commit_options: "-S -s" 47 | commit_user_name: "tflibsonnet-ci" 48 | commit_user_email: "120686569+tflibsonnet-ci@users.noreply.github.com" 49 | 50 | - name: halt pipeline if new commit was created 51 | if: steps.auto-commit-action.outputs.changes_detected == 'true' 52 | run: "false" 53 | 54 | lint: 55 | needs: docs 56 | runs-on: ubuntu-latest 57 | steps: 58 | - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 59 | 60 | - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1 61 | with: 62 | go-version: 1.19 63 | 64 | - name: install jsonnet-bundler, jsonnetfmt, and jsonnet-lint 65 | run: | 66 | go install github.com/jsonnet-bundler/jsonnet-bundler/cmd/jb@latest 67 | go install github.com/google/go-jsonnet/cmd/jsonnetfmt@v0.20.0 68 | go install github.com/google/go-jsonnet/cmd/jsonnet-lint@v0.20.0 69 | 70 | - name: check for fmt 71 | run: | 72 | find . -name "*.jsonnet" -or -name "*.libsonnet" | xargs -L1 jsonnetfmt --test 73 | 74 | - name: install dependencies 75 | run: | 76 | jb install 77 | 78 | - name: check for lint 79 | run: | 80 | export JSONNET_PATH="$(pwd)/vendor:$(pwd)" 81 | find . -not -path "./vendor/*" \( -name "*.jsonnet" -or -name "*.libsonnet" \) \ 82 | | xargs -L1 -t jsonnet-lint 83 | 84 | golangci: 85 | needs: docs 86 | runs-on: ubuntu-latest 87 | steps: 88 | - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1 89 | with: 90 | go-version: 1.19 91 | 92 | - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 93 | 94 | - name: golangci-lint 95 | uses: golangci/golangci-lint-action@639cd343e1d3b897ff35927a75193d57cfcba299 # v3.6.0 96 | with: 97 | version: v1.50.1 98 | args: "--timeout=10m" 99 | working-directory: "test" 100 | 101 | test: 102 | needs: docs 103 | runs-on: ubuntu-latest 104 | steps: 105 | - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 106 | 107 | - uses: hashicorp/setup-terraform@633666f66e0061ca3b725c73b2ec20cd13a8fdd1 # v2.0.3 108 | with: 109 | terraform_version: 1.3.6 110 | terraform_wrapper: false 111 | 112 | - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1 113 | with: 114 | go-version: 1.19 115 | cache: true 116 | cache-dependency-path: test/go.sum 117 | 118 | - name: run tests 119 | run: | 120 | cd test 121 | go test -v . 122 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # jsonnet-bundler vendor files 2 | vendor 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute to tf.libsonnet 2 | 3 | **Filing issues** 4 | 5 | - **Report bugs and feature requests as GitHub issues**.If you found an issue, you can open a new issue from [this 6 | link](https://github.com/tf-libsonnet/core/issues/new/choose). 7 | - **Do not open a GitHub issue if you are reporting a security vulnerability with the tf.libsonnet project**. Instead, 8 | refer to our [security policy](/SECURITY.md). 9 | 10 | 11 | **Contributing a change** 12 | 13 | - **Contribute with pull requests**. If you have a suggested change, open a [Pull 14 | Request](https://docs.github.com/en/pull-requests) with your proposed patch. 15 | - **All commits must be signed off to adhere to the Developer Certificate of Origin (DCO)**. Note that the 16 | `tf.libsonnet` org requires all contributors to sign off on [the DCO](https://developercertificate.org/). You can do 17 | this with the `-s` flag when making commits with `git commit`. 18 | - If you forgot to sign off on any commits in a Pull Request, you can amend the offending commit with `git commit -s 19 | --amend`. 20 | - **All commits must be signed**. The `tf.libsonnet` org requires all contributors to [sign their 21 | commits](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits). If you do 22 | not wish to maintain a set of private keys on your account, you can also use the 23 | [sigstore/gitsign](https://github.com/sigstore/gitsign) project for keyless signing with your GitHub account. 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

tf-libsonnet/core

2 | 3 |

4 | Generate Terraform with Jsonnet 5 |

6 | 7 |

8 | 9 | LICENSE 10 | 11 | 12 | main branch CI 13 | 14 | 15 | latest release 16 | 17 |

18 | 19 | This repository contains [Jsonnet](https://jsonnet.org/) functions for generating Terraform code. 20 | 21 | Learn more about `tf.libsonnet` at [docs.tflibsonnet.com](https://docs.tflibsonnet.com/docs). 22 | 23 | ## Usage 24 | 25 | Install the package using [jsonnet-bundler](https://github.com/jsonnet-bundler/jsonnet-bundler): 26 | 27 | ``` 28 | jb install github.com/tf-libsonnet/core@main 29 | 30 | # Or if you want to install a specific release 31 | # jb install github.com/tf-libsonnet/core@v0.0.1 32 | ``` 33 | 34 | You can then import the package in your Jsonnet code: 35 | 36 | ```jsonnet 37 | // main.tf.json.jsonnet 38 | local tf = import 'github.com/tf-libsonnet/core/main.libsonnet'; 39 | 40 | tf.withVariable('some_input', type='string') 41 | + tf.withLocal('some_local', '${var.some_input}') 42 | + tf.withOutput('some_output', '${local.some_local}') 43 | ``` 44 | 45 | This will generate the following Terraform JSON : 46 | 47 | ```json 48 | { 49 | "locals": { 50 | "some_local": "${var.some_input}" 51 | }, 52 | "output": { 53 | "some_output": { 54 | "value": "${local.some_local}" 55 | } 56 | }, 57 | "variable": { 58 | "some_input": { 59 | "type": "string" 60 | } 61 | } 62 | } 63 | ``` 64 | 65 | Refer to the [reference docs](/docs/README.md) for a reference of all exporeted functions. 66 | 67 | 68 | ## Support 69 | 70 | If you have any questions about how to use the `tf.libsonnet` libraries, ask in the [Organization GitHub 71 | Discussion](https://github.com/orgs/tf-libsonnet/discussions). 72 | 73 | 74 | ## Contributing 75 | 76 | Refer to the [CONTRIBUTING.md](/CONTRIBUTING.md) document for information on how to contribute to `tf.libsonnet`. 77 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | If you find or notice any security vulnerability in `tf.libsonnet`, you can report the finding to the team at: 6 | 7 | security@tflibsonnet.com 8 | 9 | We will review the potential threat and work towards issuing a fix based on the level of risk associated with the 10 | vulnerability. During this time, we may follow up with additional information or guidance. Once a proper assessment has 11 | been made, we will provide a detailed response indicating the next steps in handling your report, and endeavor to keep 12 | you infomred of the progress towards a fix and full announcement. 13 | 14 | Thank you for your help in improving the security of our open source software. We appreciate your efforts and 15 | responsible disclosure and will make every effort to acknowledge your contributions. 16 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | permalink: / 3 | --- 4 | 5 | # tf 6 | 7 | ```jsonnet 8 | local tf = import "github.com/tf-libsonnet/core/main.libsonnet" 9 | ``` 10 | 11 | `tf` implements core utility functions for generating Terraform code. 12 | 13 | 14 | ## Index 15 | 16 | * [`fn isStringArray(v)`](#fn-isstringarray) 17 | * [`fn mergeAll(objs)`](#fn-mergeall) 18 | * [`fn objItems(obj)`](#fn-objitems) 19 | * [`fn objItemsAll(obj)`](#fn-objitemsall) 20 | * [`fn withData(type, label, attrs, _meta={})`](#fn-withdata) 21 | * [`fn withLocal(name, value)`](#fn-withlocal) 22 | * [`fn withLocalList(locals)`](#fn-withlocallist) 23 | * [`fn withLocalMap(map)`](#fn-withlocalmap) 24 | * [`fn withModule(name, source, inpuuts, version='null', _meta={})`](#fn-withmodule) 25 | * [`fn withOutput(name, value, description='null', sensitive='null')`](#fn-withoutput) 26 | * [`fn withOutputList(outputs)`](#fn-withoutputlist) 27 | * [`fn withOutputMap(map)`](#fn-withoutputmap) 28 | * [`fn withProvider(name, attrs, alias='null', src='null', version='null')`](#fn-withprovider) 29 | * [`fn withResource(type, label, attrs, _meta={})`](#fn-withresource) 30 | * [`fn withSensitiveOutputMap(map)`](#fn-withsensitiveoutputmap) 31 | * [`fn withVariable(name, isRequired=true, type='null', description='null', default='null')`](#fn-withvariable) 32 | * [`obj meta`](#obj-meta) 33 | * [`fn new(count='null', depends_on='null', for_each='null', provider='null', lifecycle='null', connection='null', provisioner='null')`](#fn-metanew) 34 | * [`fn newForModule(count='null', depends_on='null', for_each='null', providers='null')`](#fn-metanewformodule) 35 | * [`obj meta.lifecycle`](#obj-metalifecycle) 36 | * [`fn new(create_before_destroy='null', prevent_destroy='null', ignore_changes='null', replace_triggered_by='null', precondition='null', postcondition='null')`](#fn-metalifecyclenew) 37 | * [`obj meta.lifecycle.condition`](#obj-metalifecyclecondition) 38 | * [`fn new(condition, error_message)`](#fn-metalifecycleconditionnew) 39 | * [`obj meta.provisioner`](#obj-metaprovisioner) 40 | * [`fn newFile(destination, source='null', content='null', connection='null', when='null', on_failure='null')`](#fn-metaprovisionernewfile) 41 | * [`fn newLocalExec(command, working_dir='null', interpreter='null', environment='null', when='null', on_failure='null')`](#fn-metaprovisionernewlocalexec) 42 | * [`fn newRemoteExec(inline='null', script='null', scripts='null', connection='null', when='null', on_failure='null')`](#fn-metaprovisionernewremoteexec) 43 | * [`obj meta.provisioner.connection`](#obj-metaprovisionerconnection) 44 | * [`fn new(host, type='null', user='null', password='null', port='null', timeout='null', script_path='null', private_key='null', certificate='null', agent='null', agent_identity='null', host_key='null', target_platform='null', bastion_host='null', bastion_host_key='null', bastion_port='null', bastion_user='null', bastion_password='null', bastion_private_key='null', bastion_certificate='null', https='null', insecure='null', use_ntlm='null', cacert='null')`](#fn-metaprovisionerconnectionnew) 45 | 46 | ## Fields 47 | 48 | ### fn isStringArray 49 | 50 | ```ts 51 | isStringArray(v) 52 | ``` 53 | 54 | `tf.isStringArray` returns `true` if the given value is an array with all elements as string. 55 | 56 | **Args**: 57 | - `v` (`any`): The value being evaluated. 58 | 59 | **Returns**: 60 | - A boolean indicating whether the given arg is a string array. 61 | 62 | 63 | ### fn mergeAll 64 | 65 | ```ts 66 | mergeAll(objs) 67 | ``` 68 | 69 | `tf.mergeAll` takes a list of objects and merges them together into a single object, processing the objects from left 70 | to right. This is useful when you want to create many Terraform resources using list comprehension. 71 | 72 | **Args**: 73 | - `objs` (`list[obj]`): List of objects to be merged into one. 74 | 75 | **Returns**: 76 | - A single object that is the result of merging all the input objects together. 77 | 78 | 79 | ### fn objItems 80 | 81 | ```ts 82 | objItems(obj) 83 | ``` 84 | 85 | `tf.objItems` takes an object and returns a list of objects with two attributes: 86 | - `k`: The object key being iterated. 87 | - `v`: The object value being iterated. 88 | 89 | This is useful when iterating the keys and values of an object. For example, if you had the object: 90 | 91 | { 92 | one: 1, 93 | two: 2, 94 | three: 3, 95 | } 96 | 97 | You can create iterate the fields like so: 98 | 99 | [i.k + ' = ' + i.v for i in objItems(obj)] 100 | 101 | **Args**: 102 | - `obj` (`obj`): The object whose fields and values to iterate. 103 | 104 | **Returns**: 105 | - A list of objects with attributes k and v to denote the object keys and values. 106 | 107 | 108 | ### fn objItemsAll 109 | 110 | ```ts 111 | objItemsAll(obj) 112 | ``` 113 | 114 | `tf.objItemsAll` is like `objItems`, but also includes hidden fields. 115 | 116 | **Args**: 117 | - `obj` (`obj`): The object whose fields and values to iterate. 118 | 119 | **Returns**: 120 | - A list of objects with attributes k and v to denote the object keys and values. 121 | 122 | 123 | ### fn withData 124 | 125 | ```ts 126 | withData(type, label, attrs, _meta={}) 127 | ``` 128 | 129 | `tf.withData` injects a new Terraform `data` source block into the root configuration. 130 | 131 | Additionally, this inserts a private function into the \_ref attribute that generates references to attributes of the 132 | data source. For example, if you added a new data source using: 133 | 134 | withData('azurerm_virtual_network', 'foo', {}) 135 | 136 | You can get the reference to the `id` field of the `azurerm_virtual_network` using the reference: 137 | 138 | $._ref.azurerm_virtual_network.foo.get('id') 139 | 140 | NOTE: When chaining and merging multiple calls to [withResource](#fn-withresource), [withData](#fn-withdata), and 141 | [withModule](#fn-withmodule), you may not be able to use `super`, `self`, or `$` to get the reference to `_ref`. 142 | Instead, make an explicit binding to the outer object using `local`. 143 | 144 | **Args**: 145 | - `type` (`string`): The data source type to create (e.g., `aws_instance`, `local_file`, etc). 146 | - `label` (`string`): The label to apply to the instance of the data source. 147 | - `attrs` (`obj`): The attributes for the instance of the data source being created. 148 | - `_meta` (`obj`): An optional meta-argument object that (see `meta.libsonnet`). Note that while technically you 149 | can set the meta-arguments on the `attrs` object, it is recommended to use the `_meta` arg to 150 | highlight the meta-arguments. 151 | 152 | **Returns**: 153 | - A mixin object that injects the new data source into the root Terraform configuration. 154 | 155 | 156 | ### fn withLocal 157 | 158 | ```ts 159 | withLocal(name, value) 160 | ``` 161 | 162 | `tf.withLocal` injects a new Terraform `local` definition into the root configuration. 163 | 164 | **Args**: 165 | - `name` (`string`): The name of the `local` to define. 166 | - `value` (`any`): The value to bind to the `local`. 167 | 168 | **Returns**: 169 | - A mixin object that injects the new local into the root Terraform configuration. 170 | 171 | 172 | ### fn withLocalList 173 | 174 | ```ts 175 | withLocalList(locals) 176 | ``` 177 | 178 | `tf.withLocalList` injects the list of local configurations as Terraform `local` definitions in the root 179 | configuration. 180 | 181 | **Args**: 182 | - `locals` (`list[obj]`): List of local configurations, where each element describes a `local`. Each element 183 | should have the keys `n` (for `name`) and `v` (for `value`). 184 | 185 | **Returns**: 186 | - A mixin object that injects all the locals into the Terraform config. 187 | 188 | 189 | ### fn withLocalMap 190 | 191 | ```ts 192 | withLocalMap(map) 193 | ``` 194 | 195 | `tf.withLocalMap` injects all the key value pairs of the input map as Terraform `local` definitions in the root 196 | configuration. 197 | 198 | **Args**: 199 | - `map` (`map[str, str]`): Map of local keys to local values. 200 | 201 | **Returns**: 202 | - A mixin object that injects all the key value pairs as locals. 203 | 204 | 205 | ### fn withModule 206 | 207 | ```ts 208 | withModule(name, source, inpuuts, version='null', _meta={}) 209 | ``` 210 | 211 | `tf.withModule` injects a new `module` block into the root configuration. 212 | 213 | Additionally, this inserts a private function into the \_ref attribute that generates references to attributes of the 214 | module call. For example, if you added a new module call using: 215 | 216 | withModule('foo', 'some-source', {}) 217 | 218 | You can get the reference to the `id` output using the reference: 219 | 220 | $._ref.module.foo.get('id') 221 | 222 | NOTE: When chaining and merging multiple calls to [withResource](#fn-withresource), [withData](#fn-withdata), and 223 | [withModule](#fn-withmodule), you may not be able to use `super`, `self`, or `$` to get the reference to `_ref`. 224 | Instead, make an explicit binding to the outer object using `local`. 225 | 226 | **Args**: 227 | - `name` (`string`): The name of the module block. 228 | - `source` (`string`): The source for the module block. 229 | - `inputs` (`obj`): The input values to pass into the module block. 230 | - `version` (`string`): The version of the module source to pull in, if the module source references a registry. 231 | When `null`, the `version` field is omitted from the resulting module block. 232 | - `_meta` (`obj`): An optional meta-argument object that (see `meta.libsonnet`). Note that while technically you 233 | can set the meta-arguments on the `inputs` object, it is recommended to use the `_meta` arg to 234 | highlight the meta-arguments. 235 | 236 | **Returns**: 237 | - A mixin object that injects the new module block into the root Terraform configuration. 238 | 239 | 240 | ### fn withOutput 241 | 242 | ```ts 243 | withOutput(name, value, description='null', sensitive='null') 244 | ``` 245 | 246 | `tf.withOutput` injects a new Terraform `output` block into the root configuration. 247 | 248 | **Args**: 249 | - `name` (`string`): The name of the output. 250 | - `value` (`string`): The expression to bind to the output name. 251 | - `description` (`string`): The description of the output. When `null`, the `description` field is omitted from 252 | the object. 253 | - `sensitive` (`bool`): Whether the output contains sensitive information. When `null`, the `sensitive` field is 254 | omitted from the object. 255 | 256 | **Returns**: 257 | - A mixin object that injects the new output into the root Terraform configuration. 258 | 259 | 260 | ### fn withOutputList 261 | 262 | ```ts 263 | withOutputList(outputs) 264 | ``` 265 | 266 | `withOutputList` injects the list of output configurations as Terraform `output` blocks into the root 267 | configuration. 268 | 269 | **Args**: 270 | - `outputs` (`list[obj]`): List of output configurations, where each element describes an `output` block. Each 271 | element should have the keys `n` (for `name`), `v` (for `value`), `d` (for 272 | `description`), and `s` (for `sensitive`). 273 | 274 | **Returns**: 275 | - A mixin object that injects all the outputs as output blocks. 276 | 277 | 278 | ### fn withOutputMap 279 | 280 | ```ts 281 | withOutputMap(map) 282 | ``` 283 | 284 | `tf.withOutputMap` injects all the key value pairs of the input map as Terraform `output` blocks into the root 285 | configuration. 286 | 287 | **Args**: 288 | - `map` (`map[str, str]`): Map of output keys to output values. 289 | 290 | **Returns**: 291 | - A mixin object that injects all the key value pairs as output blocks. 292 | 293 | 294 | ### fn withProvider 295 | 296 | ```ts 297 | withProvider(name, attrs, alias='null', src='null', version='null') 298 | ``` 299 | 300 | `tf.withProvider` injects a new Terraform `provider` block into the root configuration 301 | 302 | **Args**: 303 | - `name` (`string`): The name of the provider to inject. 304 | - `attrs` (`obj`): The attributes to apply to the provider block being injected. 305 | - `alias` (`string`): The `alias` to bind to the provider block. When `null`, the `alias` attribute is omitted 306 | from the provider attributes. 307 | - `src` (`string`): Where to source the provider. If specified, an entry to `required_providers` will be added 308 | specifying the source. If both `src` and `version` is `null`, the required_providers entry is 309 | omitted. 310 | - `version` (`string`): What `version` of the provider to use. If specified, an entry to `required_providers` will 311 | be added specifying the version. If both `src` and `version` is `null`, the 312 | `required_providers` entry is omitted. 313 | 314 | **Returns**: 315 | - A mixin object that injects the new provider block into the root Terraform configuration. 316 | 317 | 318 | ### fn withResource 319 | 320 | ```ts 321 | withResource(type, label, attrs, _meta={}) 322 | ``` 323 | 324 | `tf.withResource` injects a new Terraform `resource` block into the root configuration. 325 | 326 | Additionally, this inserts a private function into the \_ref attribute that generates references to attributes of the 327 | resource. For example, if you added a new resource using: 328 | 329 | withResource('null_resource', 'foo', {}) 330 | 331 | You can get the reference to the `id` field of the `null_resource` using the reference: 332 | 333 | $._ref.null_resource.foo.get('id') 334 | 335 | NOTE: When chaining and merging multiple calls to [withResource](#fn-withresource), [withData](#fn-withdata), and 336 | [withModule](#fn-withmodule), you may not be able to use `super`, `self`, or `$` to get the reference to `_ref`. 337 | Instead, make an explicit binding to the outer object using `local`. 338 | 339 | **Args**: 340 | - `type` (`string`): The resource type to create (e.g., `aws_instance`, `null_resource`, etc). 341 | - `label` (`string`): The label to apply to the instance of the resource. 342 | - `attrs` (`obj`): The attributes for the instance of the resource being created. 343 | - `_meta` (`obj`): An optional meta-argument object that (see `meta.libsonnet`). Note that while technically you 344 | can set the meta-arguments on the `attrs` object, it is recommended to use the `_meta` arg to 345 | highlight the meta-arguments. 346 | 347 | **Returns**: 348 | - A mixin object that injects the new resource into the root Terraform configuration. 349 | 350 | 351 | ### fn withSensitiveOutputMap 352 | 353 | ```ts 354 | withSensitiveOutputMap(map) 355 | ``` 356 | 357 | `tf.withSensitiveOutputMap` injects all the key value pairs of the input map as Terraform `output` blocks with 358 | `sensitive` set to `true` into the root configuration. 359 | 360 | **Args**: 361 | - `map` (`map[str, str]`): Map of output keys to output values. 362 | 363 | **Returns**: 364 | - A mixin object that injects all the key value pairs as output blocks. 365 | 366 | 367 | ### fn withVariable 368 | 369 | ```ts 370 | withVariable(name, isRequired=true, type='null', description='null', default='null') 371 | ``` 372 | 373 | `tf.withVariable` injects a new Terraform `variable` block into the root configuration. 374 | 375 | **Args**: 376 | - `name` (`string`): The name of the variable. 377 | - `isRequired` (`bool`): Whether the variable is required. When `true`, the `default` value is omitted from the 378 | object. 379 | - `type` (`string`): The type of the variable. When `null`, the `type` field is omitted from the object. 380 | - `description` (`string`): The description of the variable. When `null`, the `description` field is omitted from the object. 381 | - `default` (`any`): The default value of the variable. Omitted when `isRequired` is `true`. 382 | 383 | **Returns**: 384 | - A mixin object that injects the new variable into the root Terraform configuration. 385 | 386 | 387 | ## obj meta 388 | 389 | 390 | 391 | ### fn meta.new 392 | 393 | ```ts 394 | new(count='null', depends_on='null', for_each='null', provider='null', lifecycle='null', connection='null', provisioner='null') 395 | ``` 396 | 397 | `tf.meta.new` will generate an object that can be mixed into any resource or data source to set the Terraform meta 398 | arguments. This can be passed in as the `_meta` parameter for any call to [tf.withResource](#fn-withresource] or 399 | [tf.withData](#fn-withdata). 400 | 401 | Note that this is for resources and data sources. If you wish to generate meta-arguments for a module block, use 402 | the [tf.meta.newForModule](#fn-metanewformodule) function. 403 | 404 | Refer to the meta-arguments tab on the [Terraform language 405 | reference](https://developer.hashicorp.com/terraform/language) for more information. 406 | 407 | **Args**: 408 | - `count` (`number`): Set the `count` meta-argument on the block. When `null`, the `count` attribute is omitted. 409 | - `depends_on` (`list[str]` or `string`): Set the `depends_on` meta-argument on the block. When `null`, the 410 | `depends_on` attribute is omitted. This can be a string if using 411 | interpolation in the resulting Terraform. 412 | - `for_each` (`map[str, any]`, `list[any]` or `string`): Set the `for_each` meta-argument on the block. When 413 | `null`, the `for_each` attribute is omitted. This can be 414 | a string if using interpolation in the resulting 415 | Terraform. 416 | - `provider` (`string`): Set the `provider` meta-argument on the block. When `null`, the `provider` attribute is 417 | omitted. 418 | - `lifecycle` (`obj`): Set the `lifecycle` meta-argument block on the resulting block. When `null`, the 419 | `lifecycle` block is omitted. It is recommended to generate this using 420 | [tf.meta.lifecycle.new](#fn-metalifecyclenew). 421 | - `connection` (`obj`): Set the `connection` meta-argument blocks on the resulting block. When 422 | `null`, there will be no `provisioner` blocks added. It is recommended to generate 423 | this using in [tf.meta.provisioner.connection.new](#obj-metaprovisionerconnectionnew). 424 | - `provisioner` (`list[obj]`): Set the list of `provisioner` meta-argument blocks on the resulting block. When 425 | `null`, there will be no `provisioner` blocks added. It is recommended to generate 426 | this using functions in [tf.meta.provisioner](#obj-metaprovisioner). 427 | 428 | **Returns**: 429 | - A mixin that can be merged with a resource or data source object to set meta-arguments. 430 | 431 | 432 | ### fn meta.newForModule 433 | 434 | ```ts 435 | newForModule(count='null', depends_on='null', for_each='null', providers='null') 436 | ``` 437 | 438 | `tf.meta.newForModule` will generate an object that can be mixed into any module block to set the Terraform meta 439 | arguments. This can be passed in as the `_meta` parameter for any call to [tf.withModule](#fn-withmodule]. 440 | 441 | Note that this is for module calls. If you wish to generate meta-arguments for a resource or data source block, 442 | use the [tf.meta.new](#fn-metanew) function. 443 | 444 | Refer to the meta-arguments tab on the [Terraform language 445 | reference](https://developer.hashicorp.com/terraform/language) for more information. 446 | 447 | **Args**: 448 | - `count` (`number`): Set the `count` meta-argument on the block. When `null`, the `count` attribute is omitted. 449 | - `depends_on` (`list[str]` or `string`): Set the `depends_on` meta-argument on the block. When `null`, the 450 | `depends_on` attribute is omitted. This can be a string if using 451 | interpolation in the resulting Terraform. 452 | - `for_each` (`map[str, any]`, `list[any]` or `string`): Set the `for_each` meta-argument on the block. When 453 | `null`, the `for_each` attribute is omitted. This can be 454 | a string if using interpolation in the resulting 455 | Terraform. 456 | - `providers` (`map[str, str]`): Set the `providers` meta-argument on the block. When `null`, the `providers` 457 | attribute is omitted. 458 | 459 | **Returns**: 460 | - A mixin that can be merged with a resource or data source object to set meta-arguments. 461 | 462 | 463 | ## obj meta.lifecycle 464 | 465 | 466 | 467 | ### fn meta.lifecycle.new 468 | 469 | ```ts 470 | new(create_before_destroy='null', prevent_destroy='null', ignore_changes='null', replace_triggered_by='null', precondition='null', postcondition='null') 471 | ``` 472 | 473 | `tf.meta.lifecycle.new` will generate a new `lifecycle` block. 474 | 475 | Note that unlike the other functions, this includes type checking due to the Terraform requirement that the 476 | lifecycle block only supports literal values only. As such, it is easier to do a type check on the args since 477 | there is no possibility to use complex Terraform expressions (which will reduce to a string type in jsonnet). 478 | 479 | **Args**: 480 | - `create_before_destroy` (`bool`): Set `create_before_destroy` on the block. When `null`, the 481 | `create_before_destroy` attribute is omitted. 482 | - `prevent_destroy` (`bool`): Set `prevent_destroy` on the block. When `null`, the `prevent_destroy` attribute is 483 | omitted. 484 | - `ignore_changes` (`list[str]`): Set `ignore_changes` on the block. When `null`, the `ignore_changes` attribute 485 | is omitted. 486 | - `replace_triggered_by` (`list[str]`): Set `replace_triggered_by` on the block. When `null`, the 487 | `replace_triggered_by` attribute is omitted. 488 | - `precondition` (`list[object]`): Set `precondition` subblocks on the block. When `null`, no `precondition` 489 | subblocks will be rendered. It is recommended to construct this using 490 | [tf.meta.lifecycle.condition.new](#fn-metalifecycleconditionnew). 491 | - `postcondition` (`list[object]`): Set `postcondition` subblocks on the block. When `null`, no `postcondition` 492 | subblocks will be rendered. It is recommended to construct this using 493 | [tf.meta.lifecycle.condition.new](#fn-metalifecycleconditionnew). 494 | 495 | **Returns**: 496 | - A mixin that can be merged with a meta-argument block to set the `lifecycle` block. 497 | 498 | 499 | ## obj meta.lifecycle.condition 500 | 501 | 502 | 503 | ### fn meta.lifecycle.condition.new 504 | 505 | ```ts 506 | new(condition, error_message) 507 | ``` 508 | 509 | `tf.meta.lifecycle.condition.new` will generate a new `condition` block that can be used as part of `precondition` 510 | or `postcondition` in the `lifecycle` block. 511 | 512 | **Args**: 513 | - `condition` (`string`): Set the `condition` attribute on the block. This should be a Terraform expression 514 | escaped with `${}`. 515 | - `error_message` (`string`): Set the `error_message` attribute on the block. 516 | 517 | **Returns**: 518 | - An object that can be used as a `precondition` or `postcondition` subblock for a `lifecycle` block. 519 | 520 | 521 | ## obj meta.provisioner 522 | 523 | 524 | 525 | ### fn meta.provisioner.newFile 526 | 527 | ```ts 528 | newFile(destination, source='null', content='null', connection='null', when='null', on_failure='null') 529 | ``` 530 | 531 | `tf.meta.provisioner.newFile` will generate a new `file` provisioner block that can be used as part of 532 | the `provisioner` meta-argument block. 533 | 534 | **Args**: 535 | - `source` (`str`): The source file or directory to copy. Exactly one of `source` or `content` must be provided. 536 | - `content` (`str`): The direct content to copy to the destination. Exactly one of `source` or `content` must be provied. 537 | - `destination` (`str`): The destination path to write on the remote system. 538 | - `connection` (`obj`): Set the `connection` meta-argument blocks on the resulting block. When 539 | `null`, there will be no `provisioner` blocks added. It is recommended to generate 540 | this using in [tf.meta.provisioner.connection.new](#fn-metaprovisionerconnectionnew). 541 | - `when` (`str`): Specifies when Terraform should run the command. Can only be set to `"destroy"`. When `null`, 542 | the attribute is omitted from the resulting block. 543 | - `on_failure` (`str`): Modify how Terraform handles errors in the underlying command. Must be one of `"continue"` 544 | or `"fail"`. When `null`, the attribute is omitted from the resulting block. 545 | 546 | **Returns**: 547 | - An object that can be used as a `provisioner` meta-argument block in the `provisioner` block list of `tf.meta.new`. 548 | 549 | 550 | ### fn meta.provisioner.newLocalExec 551 | 552 | ```ts 553 | newLocalExec(command, working_dir='null', interpreter='null', environment='null', when='null', on_failure='null') 554 | ``` 555 | 556 | `tf.meta.provisioner.newLocalExec` will generate a new `local-exec` provisioner block that can be used as part of 557 | the `provisioner` meta-argument block. 558 | 559 | **Args**: 560 | - `command` (`str`): The command to execute on the operator machine as part of the resource lifecycle. 561 | - `working_dir` (`str`): The working directory where `command` will be executed. When `null`, the attribute is 562 | omitted from the resulting block. 563 | - `interpreter` (`list[str]`): The list of interpreter arguments used to execute the command. When `null`, the 564 | attribute is omitted from the resulting block. 565 | - `environment` (`map[str, str]`): Map of key-value pairs representing the environment variables that should be 566 | set. When `null`, the attribute is omitted from the resulting block. 567 | - `when` (`str`): Specifies when Terraform should run the command. Can only be set to `"destroy"`. When `null`, 568 | the attribute is omitted from the resulting block. 569 | - `on_failure` (`str`): Modify how Terraform handles errors in the underlying command. Must be one of `"continue"` 570 | or `"fail"`. When `null`, the attribute is omitted from the resulting block. 571 | 572 | **Returns**: 573 | - An object that can be used as a `provisioner` meta-argument block in the `provisioner` block list of `tf.meta.new`. 574 | 575 | 576 | ### fn meta.provisioner.newRemoteExec 577 | 578 | ```ts 579 | newRemoteExec(inline='null', script='null', scripts='null', connection='null', when='null', on_failure='null') 580 | ``` 581 | 582 | `tf.meta.provisioner.newRemoteExec` will generate a new `remote-exec` provisioner block that can be used as part of 583 | the `provisioner` meta-argument block. 584 | 585 | **Args**: 586 | - `inline` (`list[str]`): The list of commands to execute on the remote machine as part of the resource lifecycle. 587 | Exactly one of `inline`, `script`, or `scripts` must be provied. 588 | - `script` (`str`): The path to a local script that will be copied to the remote machine and then executed. 589 | Exactly one of `inline`, `script`, or `scripts` must be provied. 590 | - `scripts` (`list[str]`): The list of paths to local scripts that will be copied to the remote machine and then 591 | executed. Exactly one of `inline`, `script`, or `scripts` must be provied. 592 | - `connection` (`obj`): Set the `connection` meta-argument blocks on the resulting block. When 593 | `null`, there will be no `provisioner` blocks added. It is recommended to generate 594 | this using in [tf.meta.provisioner.connection.new](#fn-metaprovisionerconnectionnew). 595 | - `when` (`str`): Specifies when Terraform should run the command. Can only be set to `"destroy"`. When `null`, 596 | the attribute is omitted from the resulting block. 597 | - `on_failure` (`str`): Modify how Terraform handles errors in the underlying command. Must be one of `"continue"` 598 | or `"fail"`. When `null`, the attribute is omitted from the resulting block. 599 | 600 | **Returns**: 601 | - An object that can be used as a `provisioner` meta-argument block in the `provisioner` block list of `tf.meta.new`. 602 | 603 | 604 | ## obj meta.provisioner.connection 605 | 606 | 607 | 608 | ### fn meta.provisioner.connection.new 609 | 610 | ```ts 611 | new(host, type='null', user='null', password='null', port='null', timeout='null', script_path='null', private_key='null', certificate='null', agent='null', agent_identity='null', host_key='null', target_platform='null', bastion_host='null', bastion_host_key='null', bastion_port='null', bastion_user='null', bastion_password='null', bastion_private_key='null', bastion_certificate='null', https='null', insecure='null', use_ntlm='null', cacert='null') 612 | ``` 613 | 614 | `tf.meta.provisioner.connection.new` will generate a new `connection` block that can be used as part of 615 | the `provisioner` meta-argument block. 616 | 617 | **Args**: 618 | - `host` (`str`): The address of the remote resource to connect to. 619 | - `type` (`str`): The connection type to use when connecting to the instance. Must be one of `"ssh"` or `"winrm"`. 620 | When `null`, the attribute is omitted from the resulting block and defaults to what Terraform 621 | has set internally (`"ssh"`). 622 | - `user` (`str`): The user to use for the connection. When `null`, the attribute is omitted from the resulting 623 | block and defaults to what Terraform has set internally. 624 | - `password` (`str`): The password to use for the connection. When `null`, the attribute is omitted from the 625 | resulting block. 626 | - `port` (`number`): The port to use for the connection. When `null`, the attribute is omitted from the resulting 627 | block and defaults to what Terraform has set internally. 628 | - `timeout` (`str`): The timeout to wait for the connection. When `null`, the attribute is omitted from the 629 | resulting block and defaults to what Terraform has set internally (`"5m"`). 630 | - `script_path` (`str`): The path used to copy scripts meant for remote execution. When `null`, the attribute is 631 | omitted from the resulting block. 632 | - `private_key` (`str`): The contents of an SSH key to use for the connection. When `null`, the attribute is 633 | omitted from the resulting block. Can only be set when `type` is `"ssh"` or `null`. 634 | - `certificate` (`str`): The contents of a signed CA certificate to be used in conjunction with the `private_key` 635 | arg. When `null`, the attribute is omitted from the resulting block. Can only be set when 636 | `type` is `"ssh"` or `null`. 637 | - `agent` (`bool`): Whether to use the `ssh-agent` for authenticating. When `null`, the attribute is omitted from 638 | the resulting block. Can only be set when `type` is `"ssh"` or `null`. 639 | - `agent_identity` (`str`): The preferred identity from the ssh agent to use for authentication. When `null`, the 640 | attribute is omitted from the resulting block. Can only be set when `type` is `"ssh"` 641 | or `null`. 642 | - `host_key` (`str`): The public key from the remote host or the signing CA. This is used to verify the 643 | connection. When `null`, the attribute is omitted from the resulting block. Can only be set 644 | when `type` is `"ssh"` or `null`. 645 | - `target_platform` (`str`): The target platform to connect to. Must be one of `"unix"` or `"windows"`. When 646 | `null`, the attribute is omitted from the resulting block and defaults to what 647 | Terraform has set internally (`"unix"`). Can only be set when `type` is `"ssh"` or 648 | `null`. 649 | - `bastion_host` (`str`): The address of a bastion host to hop the connection through. When `null`, the attribute 650 | is omitted from the resulting block. Can only be set when `type` is `"ssh"` or `null`. 651 | - `bastion_host_key` (`str`): The public key from the bastion host or the signing CA. This is used to verify the 652 | connection. When `null`, the attribute is omitted from the resulting block. Can 653 | only be set when `type` is `"ssh"` or `null`. 654 | - `bastion_port` (`number`): The port to use for the bastion connection. When `null`, the attribute is omitted 655 | from the resulting block. Can only be set when `type` is `"ssh"` or `null`. 656 | - `bastion_user` (`str`): The user to use for the bastion connection. When `null`, the attribute is omitted 657 | from the resulting block. Can only be set when `type` is `"ssh"` or `null`. 658 | - `bastion_password` (`str`): The password to use for the bastion connection. When `null`, the attribute is 659 | omitted from the resulting block. Can only be set when `type` is `"ssh"` or `null`. 660 | - `bastion_private_key` (`str`): The contents of an SSH key file to use for the bastion connection. When `null`, 661 | the attribute is omitted from the resulting block. Can only be set when `type` is 662 | `"ssh"` or `null`. 663 | - `bastion_certificate` (`str`): The contents of a signed CA certificate to be used in conjunction with the 664 | `bastion_private_key` arg. When `null`, the attribute is omitted from the 665 | resulting block. Can only be set when `type` is `"ssh"` or `null`. 666 | - `https` (`bool`): Whether to connect using HTTPS as opposed to HTTP. When `null`, the attribute is omitted from 667 | the resulting block. Can only be set when `type` is `"winrm"`. 668 | - `insecure` (`bool`): Whether to skip validation of the HTTPS certificate chain. When `null`, the attribute is 669 | omitted from the resulting block. Can only be set when `type` is `"winrm"`. 670 | - `use_ntlm` (`bool`): Whether to use NTLM authentication. When `null`, the attribute is omitted from the 671 | resulting block. Can only be set when `type` is `"winrm"`. 672 | - `cacert` (`str`): The CA certificate to validate against. When `null`, the attribute is omitted from the 673 | resulting block. Can only be set when `type` is `"winrm"`. 674 | 675 | **Returns**: 676 | - An object that can be used as a `connection` sub block on any `remote-exec` or `file` `provisioner` meta-argument block. 677 | -------------------------------------------------------------------------------- /jsonnetfile.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "dependencies": [ 4 | { 5 | "source": { 6 | "git": { 7 | "remote": "https://github.com/jsonnet-libs/docsonnet.git", 8 | "subdir": "" 9 | } 10 | }, 11 | "version": "e7f3020f5733ac3dd0a1998f49591a5afd24948d" 12 | } 13 | ], 14 | "legacyImports": true 15 | } 16 | -------------------------------------------------------------------------------- /jsonnetfile.lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "dependencies": [ 4 | { 5 | "source": { 6 | "git": { 7 | "remote": "https://github.com/jsonnet-libs/docsonnet.git", 8 | "subdir": "" 9 | } 10 | }, 11 | "version": "e7f3020f5733ac3dd0a1998f49591a5afd24948d", 12 | "sum": "z5cKvk9VSOc42OcAbUCUe0b1JawlupyzPrcRInR1rx8=" 13 | } 14 | ], 15 | "legacyImports": false 16 | } 17 | -------------------------------------------------------------------------------- /main.libsonnet: -------------------------------------------------------------------------------- 1 | local d = import 'github.com/jsonnet-libs/docsonnet/doc-util/main.libsonnet'; 2 | 3 | { 4 | '#': d.pkg( 5 | name='tf', 6 | url='github.com/tf-libsonnet/core/main.libsonnet', 7 | help=||| 8 | `tf` implements core utility functions for generating Terraform code. 9 | |||, 10 | ), 11 | } 12 | + (import 'src/_custom/main.libsonnet') 13 | -------------------------------------------------------------------------------- /src/_custom/helpers.libsonnet: -------------------------------------------------------------------------------- 1 | local d = import 'github.com/jsonnet-libs/docsonnet/doc-util/main.libsonnet'; 2 | 3 | local mergeAllDoc = 4 | d.fn( 5 | ||| 6 | `tf.mergeAll` takes a list of objects and merges them together into a single object, processing the objects from left 7 | to right. This is useful when you want to create many Terraform resources using list comprehension. 8 | 9 | **Args**: 10 | - `objs` (`list[obj]`): List of objects to be merged into one. 11 | 12 | **Returns**: 13 | - A single object that is the result of merging all the input objects together. 14 | |||, 15 | [ 16 | d.arg('objs', d.T.array), 17 | ], 18 | ); 19 | local mergeAll(objs) = std.foldl( 20 | function(x, y) (x + y), 21 | objs, 22 | {}, 23 | ); 24 | 25 | 26 | local objItemsDoc = 27 | d.fn( 28 | ||| 29 | `tf.objItems` takes an object and returns a list of objects with two attributes: 30 | - `k`: The object key being iterated. 31 | - `v`: The object value being iterated. 32 | 33 | This is useful when iterating the keys and values of an object. For example, if you had the object: 34 | 35 | { 36 | one: 1, 37 | two: 2, 38 | three: 3, 39 | } 40 | 41 | You can create iterate the fields like so: 42 | 43 | [i.k + ' = ' + i.v for i in objItems(obj)] 44 | 45 | **Args**: 46 | - `obj` (`obj`): The object whose fields and values to iterate. 47 | 48 | **Returns**: 49 | - A list of objects with attributes k and v to denote the object keys and values. 50 | |||, 51 | [ 52 | d.arg('obj', d.T.object), 53 | ], 54 | ); 55 | local objItems(obj) = 56 | [ 57 | { k: k, v: std.get(obj, k) } 58 | for k in std.objectFields(obj) 59 | ]; 60 | 61 | 62 | local objItemsAllDoc = 63 | d.fn( 64 | ||| 65 | `tf.objItemsAll` is like `objItems`, but also includes hidden fields. 66 | 67 | **Args**: 68 | - `obj` (`obj`): The object whose fields and values to iterate. 69 | 70 | **Returns**: 71 | - A list of objects with attributes k and v to denote the object keys and values. 72 | |||, 73 | [ 74 | d.arg('obj', d.T.object), 75 | ], 76 | ); 77 | local objItemsAll(obj) = 78 | [ 79 | { k: k, v: std.get(obj, k) } 80 | for k in std.objectFieldsAll(obj) 81 | ]; 82 | 83 | 84 | local isStringArrayDoc = 85 | d.fn( 86 | ||| 87 | `tf.isStringArray` returns `true` if the given value is an array with all elements as string. 88 | 89 | **Args**: 90 | - `v` (`any`): The value being evaluated. 91 | 92 | **Returns**: 93 | - A boolean indicating whether the given arg is a string array. 94 | |||, 95 | [ 96 | d.arg('v', d.T.any), 97 | ], 98 | ); 99 | local isStringArray(v) = 100 | std.isArray(v) 101 | && ( 102 | // We temporarily avoid using std.all since the linter does not support it. 103 | std.foldl( 104 | function(x, y) (x && y), 105 | [ 106 | std.isString(i) 107 | for i in v 108 | ], 109 | true, 110 | ) 111 | ); 112 | 113 | 114 | { 115 | '#mergeAll':: mergeAllDoc, 116 | mergeAll:: mergeAll, 117 | '#objItems':: objItemsDoc, 118 | objItems:: objItems, 119 | '#objItemsAll':: objItemsAllDoc, 120 | objItemsAll:: objItemsAll, 121 | '#isStringArray':: isStringArrayDoc, 122 | isStringArray:: isStringArray, 123 | } 124 | -------------------------------------------------------------------------------- /src/_custom/main.libsonnet: -------------------------------------------------------------------------------- 1 | (import './root.libsonnet') 2 | + (import './meta.libsonnet') 3 | + (import './helpers.libsonnet') 4 | -------------------------------------------------------------------------------- /src/_custom/meta.libsonnet: -------------------------------------------------------------------------------- 1 | // Meta-argument constructor functions. Refer to the meta-arguments tab on 2 | // https://developer.hashicorp.com/terraform/language for more information. 3 | // 4 | // This can be used as arguments to the `_meta` parameter for any resource 5 | // or data source constructor generated by libgenerator. 6 | 7 | local d = import 'github.com/jsonnet-libs/docsonnet/doc-util/main.libsonnet'; 8 | 9 | local h = import './helpers.libsonnet'; 10 | 11 | local newMetaDoc = 12 | d.fn( 13 | ||| 14 | `tf.meta.new` will generate an object that can be mixed into any resource or data source to set the Terraform meta 15 | arguments. This can be passed in as the `_meta` parameter for any call to [tf.withResource](#fn-withresource] or 16 | [tf.withData](#fn-withdata). 17 | 18 | Note that this is for resources and data sources. If you wish to generate meta-arguments for a module block, use 19 | the [tf.meta.newForModule](#fn-metanewformodule) function. 20 | 21 | Refer to the meta-arguments tab on the [Terraform language 22 | reference](https://developer.hashicorp.com/terraform/language) for more information. 23 | 24 | **Args**: 25 | - `count` (`number`): Set the `count` meta-argument on the block. When `null`, the `count` attribute is omitted. 26 | - `depends_on` (`list[str]` or `string`): Set the `depends_on` meta-argument on the block. When `null`, the 27 | `depends_on` attribute is omitted. This can be a string if using 28 | interpolation in the resulting Terraform. 29 | - `for_each` (`map[str, any]`, `list[any]` or `string`): Set the `for_each` meta-argument on the block. When 30 | `null`, the `for_each` attribute is omitted. This can be 31 | a string if using interpolation in the resulting 32 | Terraform. 33 | - `provider` (`string`): Set the `provider` meta-argument on the block. When `null`, the `provider` attribute is 34 | omitted. 35 | - `lifecycle` (`obj`): Set the `lifecycle` meta-argument block on the resulting block. When `null`, the 36 | `lifecycle` block is omitted. It is recommended to generate this using 37 | [tf.meta.lifecycle.new](#fn-metalifecyclenew). 38 | - `connection` (`obj`): Set the `connection` meta-argument blocks on the resulting block. When 39 | `null`, there will be no `provisioner` blocks added. It is recommended to generate 40 | this using in [tf.meta.provisioner.connection.new](#obj-metaprovisionerconnectionnew). 41 | - `provisioner` (`list[obj]`): Set the list of `provisioner` meta-argument blocks on the resulting block. When 42 | `null`, there will be no `provisioner` blocks added. It is recommended to generate 43 | this using functions in [tf.meta.provisioner](#obj-metaprovisioner). 44 | 45 | **Returns**: 46 | - A mixin that can be merged with a resource or data source object to set meta-arguments. 47 | |||, 48 | [ 49 | d.arg('count', d.T.number, d.T.nil), 50 | d.arg('depends_on', d.T.array, d.T.nil), 51 | d.arg('for_each', d.T.array, d.T.nil), 52 | d.arg('provider', d.T.string, d.T.nil), 53 | d.arg('lifecycle', d.T.object, d.T.nil), 54 | d.arg('connection', d.T.object, d.T.nil), 55 | d.arg('provisioner', d.T.array, d.T.nil), 56 | ], 57 | ); 58 | local newMeta(count=null, depends_on=null, for_each=null, provider=null, lifecycle=null, connection=null, provisioner=null) = 59 | local maybeCount = 60 | if count != null then 61 | { count: count } 62 | else 63 | {}; 64 | 65 | local maybeDependsOn = 66 | if depends_on != null then 67 | { depends_on: depends_on } 68 | else 69 | {}; 70 | 71 | local maybeForEach = 72 | if for_each != null then 73 | { for_each: for_each } 74 | else 75 | {}; 76 | 77 | local maybeProvider = 78 | if provider != null then 79 | { provider: provider } 80 | else 81 | {}; 82 | 83 | local maybeLifecycle = 84 | if lifecycle != null then 85 | { lifecycle: lifecycle } 86 | else 87 | {}; 88 | 89 | local maybeConnection = 90 | if connection != null then 91 | { connection: connection } 92 | else 93 | {}; 94 | 95 | local maybeProvisioner = 96 | if provisioner != null then 97 | { provisioner: provisioner } 98 | else 99 | {}; 100 | 101 | maybeCount 102 | + maybeDependsOn 103 | + maybeForEach 104 | + maybeProvider 105 | + maybeLifecycle 106 | + maybeConnection 107 | + maybeProvisioner; 108 | 109 | 110 | local newModuleMetaDoc = 111 | d.fn( 112 | ||| 113 | `tf.meta.newForModule` will generate an object that can be mixed into any module block to set the Terraform meta 114 | arguments. This can be passed in as the `_meta` parameter for any call to [tf.withModule](#fn-withmodule]. 115 | 116 | Note that this is for module calls. If you wish to generate meta-arguments for a resource or data source block, 117 | use the [tf.meta.new](#fn-metanew) function. 118 | 119 | Refer to the meta-arguments tab on the [Terraform language 120 | reference](https://developer.hashicorp.com/terraform/language) for more information. 121 | 122 | **Args**: 123 | - `count` (`number`): Set the `count` meta-argument on the block. When `null`, the `count` attribute is omitted. 124 | - `depends_on` (`list[str]` or `string`): Set the `depends_on` meta-argument on the block. When `null`, the 125 | `depends_on` attribute is omitted. This can be a string if using 126 | interpolation in the resulting Terraform. 127 | - `for_each` (`map[str, any]`, `list[any]` or `string`): Set the `for_each` meta-argument on the block. When 128 | `null`, the `for_each` attribute is omitted. This can be 129 | a string if using interpolation in the resulting 130 | Terraform. 131 | - `providers` (`map[str, str]`): Set the `providers` meta-argument on the block. When `null`, the `providers` 132 | attribute is omitted. 133 | 134 | **Returns**: 135 | - A mixin that can be merged with a resource or data source object to set meta-arguments. 136 | |||, 137 | [ 138 | d.arg('count', d.T.number, d.T.nil), 139 | d.arg('depends_on', d.T.array, d.T.nil), 140 | d.arg('for_each', d.T.array, d.T.nil), 141 | d.arg('providers', d.T.object, d.T.nil), 142 | ], 143 | ); 144 | local newModuleMeta(count=null, depends_on=null, for_each=null, providers=null) = 145 | local maybeCount = 146 | if count != null then 147 | { count: count } 148 | else 149 | {}; 150 | 151 | local maybeDependsOn = 152 | if depends_on != null then 153 | { depends_on: depends_on } 154 | else 155 | {}; 156 | 157 | local maybeForEach = 158 | if for_each != null then 159 | { for_each: for_each } 160 | else 161 | {}; 162 | 163 | local maybeProviders = 164 | if providers != null then 165 | if std.isObject(providers) then 166 | { providers: providers } 167 | else 168 | error 'providers meta argument must be a map' 169 | else 170 | {}; 171 | 172 | maybeCount 173 | + maybeDependsOn 174 | + maybeForEach 175 | + maybeProviders; 176 | 177 | 178 | local newLifecycleDoc = 179 | d.fn( 180 | ||| 181 | `tf.meta.lifecycle.new` will generate a new `lifecycle` block. 182 | 183 | Note that unlike the other functions, this includes type checking due to the Terraform requirement that the 184 | lifecycle block only supports literal values only. As such, it is easier to do a type check on the args since 185 | there is no possibility to use complex Terraform expressions (which will reduce to a string type in jsonnet). 186 | 187 | **Args**: 188 | - `create_before_destroy` (`bool`): Set `create_before_destroy` on the block. When `null`, the 189 | `create_before_destroy` attribute is omitted. 190 | - `prevent_destroy` (`bool`): Set `prevent_destroy` on the block. When `null`, the `prevent_destroy` attribute is 191 | omitted. 192 | - `ignore_changes` (`list[str]`): Set `ignore_changes` on the block. When `null`, the `ignore_changes` attribute 193 | is omitted. 194 | - `replace_triggered_by` (`list[str]`): Set `replace_triggered_by` on the block. When `null`, the 195 | `replace_triggered_by` attribute is omitted. 196 | - `precondition` (`list[object]`): Set `precondition` subblocks on the block. When `null`, no `precondition` 197 | subblocks will be rendered. It is recommended to construct this using 198 | [tf.meta.lifecycle.condition.new](#fn-metalifecycleconditionnew). 199 | - `postcondition` (`list[object]`): Set `postcondition` subblocks on the block. When `null`, no `postcondition` 200 | subblocks will be rendered. It is recommended to construct this using 201 | [tf.meta.lifecycle.condition.new](#fn-metalifecycleconditionnew). 202 | 203 | **Returns**: 204 | - A mixin that can be merged with a meta-argument block to set the `lifecycle` block. 205 | |||, 206 | [ 207 | d.arg('create_before_destroy', d.T.bool, d.T.nil), 208 | d.arg('prevent_destroy', d.T.bool, d.T.nil), 209 | d.arg('ignore_changes', d.T.array, d.T.nil), 210 | d.arg('replace_triggered_by', d.T.array, d.T.nil), 211 | d.arg('precondition', d.T.array, d.T.nil), 212 | d.arg('postcondition', d.T.array, d.T.nil), 213 | ], 214 | ); 215 | local newLifecycle( 216 | create_before_destroy=null, 217 | prevent_destroy=null, 218 | ignore_changes=null, 219 | replace_triggered_by=null, 220 | precondition=null, 221 | postcondition=null, 222 | ) = 223 | local maybeCreateBeforeDestroy = 224 | if create_before_destroy != null then 225 | if std.isBoolean(create_before_destroy) then 226 | { create_before_destroy: create_before_destroy } 227 | else 228 | error 'lifecycle meta argument attr create_before_destroy must be a boolean' 229 | else 230 | {}; 231 | 232 | local maybePreventDestroy = 233 | if prevent_destroy != null then 234 | if std.isBoolean(prevent_destroy) then 235 | { prevent_destroy: prevent_destroy } 236 | else 237 | error 'lifecycle meta argument attr prevent_destroy must be a boolean' 238 | else 239 | {}; 240 | 241 | local maybeIgnoreChanges = 242 | if ignore_changes != null then 243 | if h.isStringArray(ignore_changes) then 244 | { ignore_changes: ignore_changes } 245 | else 246 | error 'lifecycle meta argument attr ignore_changes must be a string array' 247 | else 248 | {}; 249 | 250 | local maybeReplaceTriggeredBy = 251 | if replace_triggered_by != null then 252 | if h.isStringArray(replace_triggered_by) then 253 | { replace_triggered_by: replace_triggered_by } 254 | else 255 | error 'lifecycle meta argument attr replace_triggered_by must be a string array' 256 | else 257 | {}; 258 | 259 | local maybePrecondition = 260 | if precondition != null then 261 | if std.isArray(precondition) then 262 | { precondition: precondition } 263 | else 264 | error 'lifecycle meta argument attr precondition must be an array of condition blocks' 265 | else 266 | {}; 267 | 268 | local maybePostcondition = 269 | if postcondition != null then 270 | if std.isArray(postcondition) then 271 | { postcondition: postcondition } 272 | else 273 | error 'lifecycle meta argument attr postcondition must be an array of condition blocks' 274 | else 275 | {}; 276 | 277 | maybeCreateBeforeDestroy 278 | + maybePreventDestroy 279 | + maybeIgnoreChanges 280 | + maybeReplaceTriggeredBy 281 | + maybePrecondition 282 | + maybePostcondition; 283 | 284 | 285 | local newConditionDoc = 286 | d.fn( 287 | ||| 288 | `tf.meta.lifecycle.condition.new` will generate a new `condition` block that can be used as part of `precondition` 289 | or `postcondition` in the `lifecycle` block. 290 | 291 | **Args**: 292 | - `condition` (`string`): Set the `condition` attribute on the block. This should be a Terraform expression 293 | escaped with `${}`. 294 | - `error_message` (`string`): Set the `error_message` attribute on the block. 295 | 296 | **Returns**: 297 | - An object that can be used as a `precondition` or `postcondition` subblock for a `lifecycle` block. 298 | |||, 299 | [ 300 | d.arg('condition', d.T.string), 301 | d.arg('error_message', d.T.string), 302 | ], 303 | ); 304 | local newCondition(condition, error_message) = 305 | { 306 | condition: condition, 307 | error_message: error_message, 308 | }; 309 | 310 | 311 | // root object 312 | { 313 | meta:: { 314 | '#new':: newMetaDoc, 315 | new:: newMeta, 316 | '#newForModule':: newModuleMetaDoc, 317 | newForModule:: newModuleMeta, 318 | lifecycle:: { 319 | '#new':: newLifecycleDoc, 320 | new:: newLifecycle, 321 | condition:: { 322 | '#new':: newConditionDoc, 323 | new:: newCondition, 324 | }, 325 | }, 326 | provisioner:: (import './meta_provisioner.libsonnet'), 327 | }, 328 | } 329 | -------------------------------------------------------------------------------- /src/_custom/meta_provisioner.libsonnet: -------------------------------------------------------------------------------- 1 | // Provisioner meta-argument constructor functions. Refer to 2 | // https://developer.hashicorp.com/terraform/language/resources/provisioners/syntax 3 | // for more information. 4 | 5 | local d = import 'github.com/jsonnet-libs/docsonnet/doc-util/main.libsonnet'; 6 | 7 | 8 | // typeCheckWhen is a private unexported function for type checking the when attribute of provisioner blocks. It makes 9 | // sure the the value is either `null` or `"destroy"`. 10 | local typeCheckWhen(whenVal) = 11 | if whenVal == null || whenVal == 'destroy' then 12 | { when: whenVal } 13 | else 14 | error 'when provisioner attribute must be either null or "destroy"'; 15 | 16 | 17 | // typeCheckOnFailure is a private unexported function for type checking the on_failure attribute of provisioner blocks. 18 | // It makes sure the the value is either `null`, `"continue"`, or `"fail"`. 19 | local typeCheckOnFailure(onFailureVal) = 20 | if onFailureVal == null || onFailureVal == 'continue' || onFailureVal == 'fail' then 21 | { on_failure: onFailureVal } 22 | else 23 | error 'on_failure provisioner attribute must be either null, "continue", or "fail"'; 24 | 25 | 26 | // typeCheckConnType is a private unexported function for type checking the type attribute of connection blocks. It 27 | // makes sure the the value is either `null`, `"ssh"`, or `"winrm"`. 28 | local typeCheckConnType(val) = 29 | if val == null || val == 'ssh' || val == 'winrm' then 30 | { type: val } 31 | else 32 | error 'type connection attribute must be either null, "ssh", or "winrm"'; 33 | 34 | 35 | // assertSSH is a private unexported function for making sure that the connection type is set to ssh if the given value 36 | // is not null. 37 | local assertSSH(connType, val, name) = 38 | local isSSH = connType == null || connType == 'ssh'; 39 | if val == null || (isSSH && val != null) then 40 | { [name]: val } 41 | else 42 | error name + ' can only be set when type is "ssh" on connection block'; 43 | 44 | 45 | // assertWinRM is a private unexported function for making sure that the connection type is set to winrm if the given 46 | // value is not null. 47 | local assertWinRM(connType, val, name) = 48 | if val == null || (connType == 'winrm' && val != null) then 49 | { [name]: val } 50 | else 51 | error name + ' can only be set when type is "winrm" on connection block'; 52 | 53 | 54 | local newLocalExecProvisionerDoc = 55 | d.fn( 56 | ||| 57 | `tf.meta.provisioner.newLocalExec` will generate a new `local-exec` provisioner block that can be used as part of 58 | the `provisioner` meta-argument block. 59 | 60 | **Args**: 61 | - `command` (`str`): The command to execute on the operator machine as part of the resource lifecycle. 62 | - `working_dir` (`str`): The working directory where `command` will be executed. When `null`, the attribute is 63 | omitted from the resulting block. 64 | - `interpreter` (`list[str]`): The list of interpreter arguments used to execute the command. When `null`, the 65 | attribute is omitted from the resulting block. 66 | - `environment` (`map[str, str]`): Map of key-value pairs representing the environment variables that should be 67 | set. When `null`, the attribute is omitted from the resulting block. 68 | - `when` (`str`): Specifies when Terraform should run the command. Can only be set to `"destroy"`. When `null`, 69 | the attribute is omitted from the resulting block. 70 | - `on_failure` (`str`): Modify how Terraform handles errors in the underlying command. Must be one of `"continue"` 71 | or `"fail"`. When `null`, the attribute is omitted from the resulting block. 72 | 73 | **Returns**: 74 | - An object that can be used as a `provisioner` meta-argument block in the `provisioner` block list of `tf.meta.new`. 75 | |||, 76 | [ 77 | d.arg('command', d.T.string), 78 | d.arg('working_dir', d.T.string, d.T.nil), 79 | d.arg('interpreter', d.T.array, d.T.nil), 80 | d.arg('environment', d.T.object, d.T.nil), 81 | d.arg('when', d.T.string, d.T.nil), 82 | d.arg('on_failure', d.T.string, d.T.nil), 83 | ], 84 | ); 85 | local newLocalExecProvisioner( 86 | command, working_dir=null, interpreter=null, environment=null, when=null, on_failure=null, 87 | ) = 88 | local maybeWorkingDir = 89 | if working_dir != null then 90 | { working_dir: working_dir } 91 | else 92 | {}; 93 | 94 | local maybeInterpreter = 95 | if interpreter != null then 96 | { interpreter: interpreter } 97 | else 98 | {}; 99 | 100 | local maybeEnvironment = 101 | if environment != null then 102 | { environment: environment } 103 | else 104 | {}; 105 | 106 | local maybeWhen = 107 | if when != null then 108 | typeCheckWhen(when) 109 | else 110 | {}; 111 | 112 | local maybeOnFailure = 113 | if on_failure != null then 114 | typeCheckOnFailure(on_failure) 115 | else 116 | {}; 117 | 118 | { command: command } 119 | + maybeWorkingDir 120 | + maybeInterpreter 121 | + maybeEnvironment 122 | + maybeWhen 123 | + maybeOnFailure; 124 | 125 | 126 | local newRemoteExecProvisionerDoc = 127 | d.fn( 128 | ||| 129 | `tf.meta.provisioner.newRemoteExec` will generate a new `remote-exec` provisioner block that can be used as part of 130 | the `provisioner` meta-argument block. 131 | 132 | **Args**: 133 | - `inline` (`list[str]`): The list of commands to execute on the remote machine as part of the resource lifecycle. 134 | Exactly one of `inline`, `script`, or `scripts` must be provied. 135 | - `script` (`str`): The path to a local script that will be copied to the remote machine and then executed. 136 | Exactly one of `inline`, `script`, or `scripts` must be provied. 137 | - `scripts` (`list[str]`): The list of paths to local scripts that will be copied to the remote machine and then 138 | executed. Exactly one of `inline`, `script`, or `scripts` must be provied. 139 | - `connection` (`obj`): Set the `connection` meta-argument blocks on the resulting block. When 140 | `null`, there will be no `provisioner` blocks added. It is recommended to generate 141 | this using in [tf.meta.provisioner.connection.new](#fn-metaprovisionerconnectionnew). 142 | - `when` (`str`): Specifies when Terraform should run the command. Can only be set to `"destroy"`. When `null`, 143 | the attribute is omitted from the resulting block. 144 | - `on_failure` (`str`): Modify how Terraform handles errors in the underlying command. Must be one of `"continue"` 145 | or `"fail"`. When `null`, the attribute is omitted from the resulting block. 146 | 147 | **Returns**: 148 | - An object that can be used as a `provisioner` meta-argument block in the `provisioner` block list of `tf.meta.new`. 149 | |||, 150 | [ 151 | d.arg('inline', d.T.array, d.T.nil), 152 | d.arg('script', d.T.string, d.T.nil), 153 | d.arg('scripts', d.T.array, d.T.nil), 154 | d.arg('connection', d.T.object, d.T.nil), 155 | d.arg('when', d.T.string, d.T.nil), 156 | d.arg('on_failure', d.T.string, d.T.nil), 157 | ], 158 | ); 159 | local newRemoteExecProvisioner( 160 | inline=null, script=null, scripts=null, connection=null, when=null, on_failure=null, 161 | ) = 162 | local mutexErr = 'exactly one of inline, script, or scripts must be set on remote-exec provisioner block'; 163 | local mutexCheck = 164 | if inline != null && (script == null && scripts == null) 165 | || script != null && (inline == null && scripts == null) 166 | || scripts != null && (inline == null && script == null) 167 | then 168 | true 169 | else 170 | error mutexErr; 171 | assert mutexCheck; 172 | 173 | local maybeInline = 174 | if inline != null then 175 | { inline: inline } 176 | else 177 | {}; 178 | 179 | local maybeScript = 180 | if script != null then 181 | { script: script } 182 | else 183 | {}; 184 | 185 | local maybeScripts = 186 | if scripts != null then 187 | { scripts: scripts } 188 | else 189 | {}; 190 | 191 | local maybeConn = 192 | if connection != null then 193 | { connection: connection } 194 | else 195 | {}; 196 | 197 | local maybeWhen = 198 | if when != null then 199 | typeCheckWhen(when) 200 | else 201 | {}; 202 | 203 | local maybeOnFailure = 204 | if on_failure != null then 205 | typeCheckOnFailure(on_failure) 206 | else 207 | {}; 208 | 209 | maybeInline 210 | + maybeScript 211 | + maybeScripts 212 | + maybeConn 213 | + maybeWhen 214 | + maybeOnFailure; 215 | 216 | 217 | local newFileProvisionerDoc = 218 | d.fn( 219 | ||| 220 | `tf.meta.provisioner.newFile` will generate a new `file` provisioner block that can be used as part of 221 | the `provisioner` meta-argument block. 222 | 223 | **Args**: 224 | - `source` (`str`): The source file or directory to copy. Exactly one of `source` or `content` must be provided. 225 | - `content` (`str`): The direct content to copy to the destination. Exactly one of `source` or `content` must be provied. 226 | - `destination` (`str`): The destination path to write on the remote system. 227 | - `connection` (`obj`): Set the `connection` meta-argument blocks on the resulting block. When 228 | `null`, there will be no `provisioner` blocks added. It is recommended to generate 229 | this using in [tf.meta.provisioner.connection.new](#fn-metaprovisionerconnectionnew). 230 | - `when` (`str`): Specifies when Terraform should run the command. Can only be set to `"destroy"`. When `null`, 231 | the attribute is omitted from the resulting block. 232 | - `on_failure` (`str`): Modify how Terraform handles errors in the underlying command. Must be one of `"continue"` 233 | or `"fail"`. When `null`, the attribute is omitted from the resulting block. 234 | 235 | **Returns**: 236 | - An object that can be used as a `provisioner` meta-argument block in the `provisioner` block list of `tf.meta.new`. 237 | |||, 238 | [ 239 | d.arg('destination', d.T.string), 240 | d.arg('source', d.T.string, d.T.nil), 241 | d.arg('content', d.T.string, d.T.nil), 242 | d.arg('connection', d.T.object, d.T.nil), 243 | d.arg('when', d.T.string, d.T.nil), 244 | d.arg('on_failure', d.T.string, d.T.nil), 245 | ], 246 | ); 247 | local newFileProvisioner( 248 | destination, source=null, content=null, connection=null, when=null, on_failure=null, 249 | ) = 250 | local mutexErr = 'exactly one of source or content must be set on file provisioner block'; 251 | local mutexCheck = 252 | if source != null && content == null 253 | || content != null && source == null 254 | then 255 | true 256 | else 257 | error mutexErr; 258 | assert mutexCheck; 259 | 260 | local maybeSource = 261 | if source != null then 262 | { source: source } 263 | else 264 | {}; 265 | 266 | local maybeContent = 267 | if content != null then 268 | { content: content } 269 | else 270 | {}; 271 | 272 | local maybeConn = 273 | if connection != null then 274 | { connection: connection } 275 | else 276 | {}; 277 | 278 | local maybeWhen = 279 | if when != null then 280 | typeCheckWhen(when) 281 | else 282 | {}; 283 | 284 | local maybeOnFailure = 285 | if on_failure != null then 286 | typeCheckOnFailure(on_failure) 287 | else 288 | {}; 289 | 290 | { destination: destination } 291 | + maybeSource 292 | + maybeContent 293 | + maybeConn 294 | + maybeWhen 295 | + maybeOnFailure; 296 | 297 | 298 | local newProvisionerConnectionDoc = 299 | d.fn( 300 | ||| 301 | `tf.meta.provisioner.connection.new` will generate a new `connection` block that can be used as part of 302 | the `provisioner` meta-argument block. 303 | 304 | **Args**: 305 | - `host` (`str`): The address of the remote resource to connect to. 306 | - `type` (`str`): The connection type to use when connecting to the instance. Must be one of `"ssh"` or `"winrm"`. 307 | When `null`, the attribute is omitted from the resulting block and defaults to what Terraform 308 | has set internally (`"ssh"`). 309 | - `user` (`str`): The user to use for the connection. When `null`, the attribute is omitted from the resulting 310 | block and defaults to what Terraform has set internally. 311 | - `password` (`str`): The password to use for the connection. When `null`, the attribute is omitted from the 312 | resulting block. 313 | - `port` (`number`): The port to use for the connection. When `null`, the attribute is omitted from the resulting 314 | block and defaults to what Terraform has set internally. 315 | - `timeout` (`str`): The timeout to wait for the connection. When `null`, the attribute is omitted from the 316 | resulting block and defaults to what Terraform has set internally (`"5m"`). 317 | - `script_path` (`str`): The path used to copy scripts meant for remote execution. When `null`, the attribute is 318 | omitted from the resulting block. 319 | - `private_key` (`str`): The contents of an SSH key to use for the connection. When `null`, the attribute is 320 | omitted from the resulting block. Can only be set when `type` is `"ssh"` or `null`. 321 | - `certificate` (`str`): The contents of a signed CA certificate to be used in conjunction with the `private_key` 322 | arg. When `null`, the attribute is omitted from the resulting block. Can only be set when 323 | `type` is `"ssh"` or `null`. 324 | - `agent` (`bool`): Whether to use the `ssh-agent` for authenticating. When `null`, the attribute is omitted from 325 | the resulting block. Can only be set when `type` is `"ssh"` or `null`. 326 | - `agent_identity` (`str`): The preferred identity from the ssh agent to use for authentication. When `null`, the 327 | attribute is omitted from the resulting block. Can only be set when `type` is `"ssh"` 328 | or `null`. 329 | - `host_key` (`str`): The public key from the remote host or the signing CA. This is used to verify the 330 | connection. When `null`, the attribute is omitted from the resulting block. Can only be set 331 | when `type` is `"ssh"` or `null`. 332 | - `target_platform` (`str`): The target platform to connect to. Must be one of `"unix"` or `"windows"`. When 333 | `null`, the attribute is omitted from the resulting block and defaults to what 334 | Terraform has set internally (`"unix"`). Can only be set when `type` is `"ssh"` or 335 | `null`. 336 | - `bastion_host` (`str`): The address of a bastion host to hop the connection through. When `null`, the attribute 337 | is omitted from the resulting block. Can only be set when `type` is `"ssh"` or `null`. 338 | - `bastion_host_key` (`str`): The public key from the bastion host or the signing CA. This is used to verify the 339 | connection. When `null`, the attribute is omitted from the resulting block. Can 340 | only be set when `type` is `"ssh"` or `null`. 341 | - `bastion_port` (`number`): The port to use for the bastion connection. When `null`, the attribute is omitted 342 | from the resulting block. Can only be set when `type` is `"ssh"` or `null`. 343 | - `bastion_user` (`str`): The user to use for the bastion connection. When `null`, the attribute is omitted 344 | from the resulting block. Can only be set when `type` is `"ssh"` or `null`. 345 | - `bastion_password` (`str`): The password to use for the bastion connection. When `null`, the attribute is 346 | omitted from the resulting block. Can only be set when `type` is `"ssh"` or `null`. 347 | - `bastion_private_key` (`str`): The contents of an SSH key file to use for the bastion connection. When `null`, 348 | the attribute is omitted from the resulting block. Can only be set when `type` is 349 | `"ssh"` or `null`. 350 | - `bastion_certificate` (`str`): The contents of a signed CA certificate to be used in conjunction with the 351 | `bastion_private_key` arg. When `null`, the attribute is omitted from the 352 | resulting block. Can only be set when `type` is `"ssh"` or `null`. 353 | - `https` (`bool`): Whether to connect using HTTPS as opposed to HTTP. When `null`, the attribute is omitted from 354 | the resulting block. Can only be set when `type` is `"winrm"`. 355 | - `insecure` (`bool`): Whether to skip validation of the HTTPS certificate chain. When `null`, the attribute is 356 | omitted from the resulting block. Can only be set when `type` is `"winrm"`. 357 | - `use_ntlm` (`bool`): Whether to use NTLM authentication. When `null`, the attribute is omitted from the 358 | resulting block. Can only be set when `type` is `"winrm"`. 359 | - `cacert` (`str`): The CA certificate to validate against. When `null`, the attribute is omitted from the 360 | resulting block. Can only be set when `type` is `"winrm"`. 361 | 362 | **Returns**: 363 | - An object that can be used as a `connection` sub block on any `remote-exec` or `file` `provisioner` meta-argument block. 364 | |||, 365 | [ 366 | d.arg('host', d.T.string), 367 | d.arg('type', d.T.string, d.T.nil), 368 | d.arg('user', d.T.string, d.T.nil), 369 | d.arg('password', d.T.string, d.T.nil), 370 | d.arg('port', d.T.number, d.T.nil), 371 | d.arg('timeout', d.T.string, d.T.nil), 372 | d.arg('script_path', d.T.string, d.T.nil), 373 | d.arg('private_key', d.T.string, d.T.nil), 374 | d.arg('certificate', d.T.string, d.T.nil), 375 | d.arg('agent', d.T.bool, d.T.nil), 376 | d.arg('agent_identity', d.T.string, d.T.nil), 377 | d.arg('host_key', d.T.string, d.T.nil), 378 | d.arg('target_platform', d.T.string, d.T.nil), 379 | d.arg('bastion_host', d.T.string, d.T.nil), 380 | d.arg('bastion_host_key', d.T.string, d.T.nil), 381 | d.arg('bastion_port', d.T.number, d.T.nil), 382 | d.arg('bastion_user', d.T.string, d.T.nil), 383 | d.arg('bastion_password', d.T.string, d.T.nil), 384 | d.arg('bastion_private_key', d.T.string, d.T.nil), 385 | d.arg('bastion_certificate', d.T.string, d.T.nil), 386 | d.arg('https', d.T.bool, d.T.nil), 387 | d.arg('insecure', d.T.bool, d.T.nil), 388 | d.arg('use_ntlm', d.T.bool, d.T.nil), 389 | d.arg('cacert', d.T.string, d.T.nil), 390 | ], 391 | ); 392 | local newProvisionerConnection( 393 | host, 394 | type=null, 395 | user=null, 396 | password=null, 397 | port=null, 398 | timeout=null, 399 | script_path=null, 400 | private_key=null, 401 | certificate=null, 402 | agent=null, 403 | agent_identity=null, 404 | host_key=null, 405 | target_platform=null, 406 | bastion_host=null, 407 | bastion_host_key=null, 408 | bastion_port=null, 409 | bastion_user=null, 410 | bastion_password=null, 411 | bastion_private_key=null, 412 | bastion_certificate=null, 413 | https=null, 414 | insecure=null, 415 | use_ntlm=null, 416 | cacert=null, 417 | ) = 418 | 419 | local maybeType = 420 | if type != null then 421 | typeCheckConnType(type) 422 | else 423 | {}; 424 | 425 | local maybeUser = 426 | if user != null then 427 | { user: user } 428 | else 429 | {}; 430 | 431 | local maybePass = 432 | if password != null then 433 | { password: password } 434 | else 435 | {}; 436 | 437 | local maybePort = 438 | if port != null then 439 | { port: port } 440 | else 441 | {}; 442 | 443 | local maybeTimeout = 444 | if timeout != null then 445 | { timeout: timeout } 446 | else 447 | {}; 448 | 449 | local maybeScriptPath = 450 | if script_path != null then 451 | { script_path: script_path } 452 | else 453 | {}; 454 | 455 | local maybePrivateKey = 456 | if private_key != null then 457 | assertSSH(type, private_key, 'private_key') 458 | else 459 | {}; 460 | 461 | local maybeCertificate = 462 | if certificate != null then 463 | assertSSH(type, certificate, 'certificate') 464 | else 465 | {}; 466 | 467 | local maybeAgent = 468 | if agent != null then 469 | assertSSH(type, agent, 'agent') 470 | else 471 | {}; 472 | 473 | local maybeAgentIdentity = 474 | if agent_identity != null then 475 | assertSSH(type, agent_identity, 'agent_identity') 476 | else 477 | {}; 478 | 479 | local maybeHostKey = 480 | if host_key != null then 481 | assertSSH(type, host_key, 'host_key') 482 | else 483 | {}; 484 | 485 | local maybeTargetPlatform = 486 | if target_platform != null then 487 | assertSSH(type, target_platform, 'target_platform') 488 | else 489 | {}; 490 | 491 | local maybeBastionHost = 492 | if bastion_host != null then 493 | assertSSH(type, bastion_host, 'bastion_host') 494 | else 495 | {}; 496 | 497 | local maybeBastionHostKey = 498 | if bastion_host_key != null then 499 | assertSSH(type, bastion_host_key, 'bastion_host_key') 500 | else 501 | {}; 502 | 503 | local maybeBastionPort = 504 | if bastion_port != null then 505 | assertSSH(type, bastion_port, 'bastion_port') 506 | else 507 | {}; 508 | 509 | local maybeBastionUser = 510 | if bastion_user != null then 511 | assertSSH(type, bastion_user, 'bastion_user') 512 | else 513 | {}; 514 | 515 | local maybeBastionPassword = 516 | if bastion_password != null then 517 | assertSSH(type, bastion_password, 'bastion_password') 518 | else 519 | {}; 520 | 521 | local maybeBastionPrivateKey = 522 | if bastion_private_key != null then 523 | assertSSH(type, bastion_private_key, 'bastion_private_key') 524 | else 525 | {}; 526 | 527 | local maybeBastionCertificate = 528 | if bastion_certificate != null then 529 | assertSSH(type, bastion_certificate, 'bastion_certificate') 530 | else 531 | {}; 532 | 533 | local maybeHTTPS = 534 | if https != null then 535 | assertWinRM(type, https, 'https') 536 | else 537 | {}; 538 | 539 | local maybeInsecure = 540 | if insecure != null then 541 | assertWinRM(type, insecure, 'insecure') 542 | else 543 | {}; 544 | 545 | local maybeUseNTLM = 546 | if use_ntlm != null then 547 | assertWinRM(type, use_ntlm, 'use_ntlm') 548 | else 549 | {}; 550 | 551 | local maybeCACert = 552 | if cacert != null then 553 | assertWinRM(type, cacert, 'cacert') 554 | else 555 | {}; 556 | 557 | { host: host } 558 | + maybeType 559 | + maybeUser 560 | + maybePass 561 | + maybePort 562 | + maybeTimeout 563 | + maybeScriptPath 564 | + maybePrivateKey 565 | + maybeCertificate 566 | + maybeAgent 567 | + maybeAgentIdentity 568 | + maybeHostKey 569 | + maybeTargetPlatform 570 | + maybeBastionHost 571 | + maybeBastionHostKey 572 | + maybeBastionPort 573 | + maybeBastionUser 574 | + maybeBastionPassword 575 | + maybeBastionPrivateKey 576 | + maybeBastionCertificate 577 | + maybeHTTPS 578 | + maybeInsecure 579 | + maybeUseNTLM 580 | + maybeCACert; 581 | 582 | { 583 | '#newLocalExec': newLocalExecProvisionerDoc, 584 | newLocalExec:: newLocalExecProvisioner, 585 | '#newRemoteExec': newRemoteExecProvisionerDoc, 586 | newRemoteExec:: newRemoteExecProvisioner, 587 | '#newFile': newFileProvisionerDoc, 588 | newFile:: newFileProvisioner, 589 | connection:: { 590 | '#new':: newProvisionerConnectionDoc, 591 | new:: newProvisionerConnection, 592 | }, 593 | } 594 | -------------------------------------------------------------------------------- /src/_custom/root.libsonnet: -------------------------------------------------------------------------------- 1 | local d = import 'github.com/jsonnet-libs/docsonnet/doc-util/main.libsonnet'; 2 | 3 | local h = import './helpers.libsonnet'; 4 | 5 | local withProviderDoc = 6 | d.fn( 7 | ||| 8 | `tf.withProvider` injects a new Terraform `provider` block into the root configuration 9 | 10 | **Args**: 11 | - `name` (`string`): The name of the provider to inject. 12 | - `attrs` (`obj`): The attributes to apply to the provider block being injected. 13 | - `alias` (`string`): The `alias` to bind to the provider block. When `null`, the `alias` attribute is omitted 14 | from the provider attributes. 15 | - `src` (`string`): Where to source the provider. If specified, an entry to `required_providers` will be added 16 | specifying the source. If both `src` and `version` is `null`, the required_providers entry is 17 | omitted. 18 | - `version` (`string`): What `version` of the provider to use. If specified, an entry to `required_providers` will 19 | be added specifying the version. If both `src` and `version` is `null`, the 20 | `required_providers` entry is omitted. 21 | 22 | **Returns**: 23 | - A mixin object that injects the new provider block into the root Terraform configuration. 24 | |||, 25 | [ 26 | d.arg('name', d.T.string), 27 | d.arg('attrs', d.T.object), 28 | d.arg('alias', d.T.string, d.T.nil), 29 | d.arg('src', d.T.string, d.T.nil), 30 | d.arg('version', d.T.string, d.T.nil), 31 | ], 32 | ); 33 | local withProvider(name, attrs, alias=null, src=null, version=null) = 34 | local maybeAlias = 35 | if alias != null then 36 | { alias: alias } 37 | else 38 | {}; 39 | 40 | local maybeRequiredProviders = 41 | if src != null || version != null then 42 | { 43 | terraform+: { 44 | required_providers+: { 45 | [name]: ( 46 | if src != null then 47 | { source: src } 48 | else 49 | {} 50 | ) + ( 51 | if version != null then 52 | { version: version } 53 | else 54 | {} 55 | ), 56 | }, 57 | }, 58 | } 59 | else 60 | {}; 61 | 62 | { 63 | provider+: { 64 | [name]+: [ 65 | maybeAlias + attrs, 66 | ], 67 | }, 68 | } 69 | + maybeRequiredProviders; 70 | 71 | 72 | local withResourceDoc = 73 | d.fn( 74 | ||| 75 | `tf.withResource` injects a new Terraform `resource` block into the root configuration. 76 | 77 | Additionally, this inserts a private function into the \_ref attribute that generates references to attributes of the 78 | resource. For example, if you added a new resource using: 79 | 80 | withResource('null_resource', 'foo', {}) 81 | 82 | You can get the reference to the `id` field of the `null_resource` using the reference: 83 | 84 | $._ref.null_resource.foo.get('id') 85 | 86 | NOTE: When chaining and merging multiple calls to [withResource](#fn-withresource), [withData](#fn-withdata), and 87 | [withModule](#fn-withmodule), you may not be able to use `super`, `self`, or `$` to get the reference to `_ref`. 88 | Instead, make an explicit binding to the outer object using `local`. 89 | 90 | **Args**: 91 | - `type` (`string`): The resource type to create (e.g., `aws_instance`, `null_resource`, etc). 92 | - `label` (`string`): The label to apply to the instance of the resource. 93 | - `attrs` (`obj`): The attributes for the instance of the resource being created. 94 | - `_meta` (`obj`): An optional meta-argument object that (see `meta.libsonnet`). Note that while technically you 95 | can set the meta-arguments on the `attrs` object, it is recommended to use the `_meta` arg to 96 | highlight the meta-arguments. 97 | 98 | **Returns**: 99 | - A mixin object that injects the new resource into the root Terraform configuration. 100 | |||, 101 | [ 102 | d.arg('type', d.T.string), 103 | d.arg('label', d.T.string), 104 | d.arg('attrs', d.T.object), 105 | d.arg('_meta', d.T.object, {}), 106 | ], 107 | ); 108 | // TODO: add type checking for _meta 109 | local withResource(type, label, attrs, _meta={}) = { 110 | resource+: { 111 | [type]+: { 112 | [label]: ( 113 | attrs 114 | + _meta 115 | ), 116 | }, 117 | }, 118 | 119 | _ref+:: { 120 | [type]+:: { 121 | [label]:: { 122 | getRef:: function(attr) ( 123 | type + '.' + label + '.' + attr 124 | ), 125 | get:: function(attr) ( 126 | '${' + self.getRef(attr) + '}' 127 | ), 128 | }, 129 | }, 130 | }, 131 | }; 132 | 133 | 134 | local withDataDoc = 135 | d.fn( 136 | ||| 137 | `tf.withData` injects a new Terraform `data` source block into the root configuration. 138 | 139 | Additionally, this inserts a private function into the \_ref attribute that generates references to attributes of the 140 | data source. For example, if you added a new data source using: 141 | 142 | withData('azurerm_virtual_network', 'foo', {}) 143 | 144 | You can get the reference to the `id` field of the `azurerm_virtual_network` using the reference: 145 | 146 | $._ref.azurerm_virtual_network.foo.get('id') 147 | 148 | NOTE: When chaining and merging multiple calls to [withResource](#fn-withresource), [withData](#fn-withdata), and 149 | [withModule](#fn-withmodule), you may not be able to use `super`, `self`, or `$` to get the reference to `_ref`. 150 | Instead, make an explicit binding to the outer object using `local`. 151 | 152 | **Args**: 153 | - `type` (`string`): The data source type to create (e.g., `aws_instance`, `local_file`, etc). 154 | - `label` (`string`): The label to apply to the instance of the data source. 155 | - `attrs` (`obj`): The attributes for the instance of the data source being created. 156 | - `_meta` (`obj`): An optional meta-argument object that (see `meta.libsonnet`). Note that while technically you 157 | can set the meta-arguments on the `attrs` object, it is recommended to use the `_meta` arg to 158 | highlight the meta-arguments. 159 | 160 | **Returns**: 161 | - A mixin object that injects the new data source into the root Terraform configuration. 162 | |||, 163 | [ 164 | d.arg('type', d.T.string), 165 | d.arg('label', d.T.string), 166 | d.arg('attrs', d.T.object), 167 | d.arg('_meta', d.T.object, {}), 168 | ], 169 | ); 170 | local withData(type, label, attrs, _meta={}) = { 171 | data+: { 172 | [type]+: { 173 | [label]: ( 174 | attrs 175 | + _meta 176 | ), 177 | }, 178 | }, 179 | 180 | _ref+:: { 181 | data+:: { 182 | [type]+:: { 183 | [label]:: { 184 | getRef:: function(attr) ( 185 | 'data.' + type + '.' + label + '.' + attr 186 | ), 187 | get:: function(attr) ( 188 | '${' + self.getRef(attr) + '}' 189 | ), 190 | }, 191 | }, 192 | }, 193 | }, 194 | }; 195 | 196 | 197 | local withModuleDoc = 198 | d.fn( 199 | ||| 200 | `tf.withModule` injects a new `module` block into the root configuration. 201 | 202 | Additionally, this inserts a private function into the \_ref attribute that generates references to attributes of the 203 | module call. For example, if you added a new module call using: 204 | 205 | withModule('foo', 'some-source', {}) 206 | 207 | You can get the reference to the `id` output using the reference: 208 | 209 | $._ref.module.foo.get('id') 210 | 211 | NOTE: When chaining and merging multiple calls to [withResource](#fn-withresource), [withData](#fn-withdata), and 212 | [withModule](#fn-withmodule), you may not be able to use `super`, `self`, or `$` to get the reference to `_ref`. 213 | Instead, make an explicit binding to the outer object using `local`. 214 | 215 | **Args**: 216 | - `name` (`string`): The name of the module block. 217 | - `source` (`string`): The source for the module block. 218 | - `inputs` (`obj`): The input values to pass into the module block. 219 | - `version` (`string`): The version of the module source to pull in, if the module source references a registry. 220 | When `null`, the `version` field is omitted from the resulting module block. 221 | - `_meta` (`obj`): An optional meta-argument object that (see `meta.libsonnet`). Note that while technically you 222 | can set the meta-arguments on the `inputs` object, it is recommended to use the `_meta` arg to 223 | highlight the meta-arguments. 224 | 225 | **Returns**: 226 | - A mixin object that injects the new module block into the root Terraform configuration. 227 | |||, 228 | [ 229 | d.arg('name', d.T.string), 230 | d.arg('source', d.T.string), 231 | d.arg('inpuuts', d.T.object), 232 | d.arg('version', d.T.string, d.T.nil), 233 | d.arg('_meta', d.T.object, {}), 234 | ], 235 | ); 236 | // TODO: add type checking for _meta 237 | local withModule(name, source, inputs, version=null, _meta={}) = 238 | local maybeVersion = 239 | if version != null then 240 | { version: version } 241 | else 242 | {}; 243 | 244 | { 245 | module+: { 246 | [name]: 247 | { source: source } 248 | + maybeVersion 249 | + inputs 250 | + _meta, 251 | }, 252 | 253 | _ref+:: { 254 | module+:: { 255 | [name]:: { 256 | getRef:: function(attr) ( 257 | 'module.' + name + '.' + attr 258 | ), 259 | get:: function(attr) ( 260 | '${' + self.getRef(attr) + '}' 261 | ), 262 | }, 263 | }, 264 | }, 265 | }; 266 | 267 | 268 | local withVariableDoc = 269 | d.fn( 270 | ||| 271 | `tf.withVariable` injects a new Terraform `variable` block into the root configuration. 272 | 273 | **Args**: 274 | - `name` (`string`): The name of the variable. 275 | - `isRequired` (`bool`): Whether the variable is required. When `true`, the `default` value is omitted from the 276 | object. 277 | - `type` (`string`): The type of the variable. When `null`, the `type` field is omitted from the object. 278 | - `description` (`string`): The description of the variable. When `null`, the `description` field is omitted from the object. 279 | - `default` (`any`): The default value of the variable. Omitted when `isRequired` is `true`. 280 | 281 | **Returns**: 282 | - A mixin object that injects the new variable into the root Terraform configuration. 283 | |||, 284 | [ 285 | d.arg('name', d.T.string), 286 | d.arg('isRequired', d.T.bool, true), 287 | d.arg('type', d.T.string, d.T.nil), 288 | d.arg('description', d.T.string, d.T.nil), 289 | d.arg('default', d.T.any, d.T.nil), 290 | ], 291 | ); 292 | // MAINTAINER'S NOTE: The isRequired field is necessary to support emitting null as a default value for the variable, as 293 | // opposed to omitting the default field. 294 | local withVariable(name, isRequired=true, type=null, description=null, default=null) = 295 | local maybeType = 296 | if type != null then 297 | { type: type } 298 | else 299 | {}; 300 | local maybeDescription = 301 | if description != null then 302 | { description: description } 303 | else 304 | {}; 305 | local maybeDefault = 306 | if !isRequired then 307 | { default: default } 308 | else 309 | {}; 310 | 311 | { 312 | variable+: { 313 | [name]: 314 | maybeType 315 | + maybeDescription 316 | + maybeDefault, 317 | }, 318 | }; 319 | 320 | 321 | local withOutputDoc = 322 | d.fn( 323 | ||| 324 | `tf.withOutput` injects a new Terraform `output` block into the root configuration. 325 | 326 | **Args**: 327 | - `name` (`string`): The name of the output. 328 | - `value` (`string`): The expression to bind to the output name. 329 | - `description` (`string`): The description of the output. When `null`, the `description` field is omitted from 330 | the object. 331 | - `sensitive` (`bool`): Whether the output contains sensitive information. When `null`, the `sensitive` field is 332 | omitted from the object. 333 | 334 | **Returns**: 335 | - A mixin object that injects the new output into the root Terraform configuration. 336 | |||, 337 | [ 338 | d.arg('name', d.T.string), 339 | d.arg('value', d.T.string), 340 | d.arg('description', d.T.string, d.T.nil), 341 | d.arg('sensitive', d.T.bool, d.T.nil), 342 | ], 343 | ); 344 | local withOutput(name, value, description=null, sensitive=null) = 345 | local maybeDescription = 346 | if description != null then 347 | { description: description } 348 | else 349 | {}; 350 | 351 | local maybeSensitive = 352 | if sensitive != null then 353 | { sensitive: sensitive } 354 | else 355 | {}; 356 | 357 | { 358 | output+: { 359 | [name]: 360 | { value: value } 361 | + maybeDescription 362 | + maybeSensitive, 363 | }, 364 | }; 365 | 366 | 367 | local withOutputMapDoc = 368 | d.fn( 369 | ||| 370 | `tf.withOutputMap` injects all the key value pairs of the input map as Terraform `output` blocks into the root 371 | configuration. 372 | 373 | **Args**: 374 | - `map` (`map[str, str]`): Map of output keys to output values. 375 | 376 | **Returns**: 377 | - A mixin object that injects all the key value pairs as output blocks. 378 | |||, 379 | [ 380 | d.arg('map', d.T.object), 381 | ], 382 | ); 383 | local withOutputMap(map) = 384 | h.mergeAll([ 385 | withOutput(i.k, i.v) 386 | for i in h.objItems(map) 387 | ]); 388 | 389 | 390 | local withSensitiveOutputMapDoc = 391 | d.fn( 392 | ||| 393 | `tf.withSensitiveOutputMap` injects all the key value pairs of the input map as Terraform `output` blocks with 394 | `sensitive` set to `true` into the root configuration. 395 | 396 | **Args**: 397 | - `map` (`map[str, str]`): Map of output keys to output values. 398 | 399 | **Returns**: 400 | - A mixin object that injects all the key value pairs as output blocks. 401 | |||, 402 | [ 403 | d.arg('map', d.T.object), 404 | ], 405 | ); 406 | local withSensitiveOutputMap(map) = 407 | h.mergeAll([ 408 | withOutput(i.k, i.v, sensitive=true) 409 | for i in h.objItems(map) 410 | ]); 411 | 412 | 413 | local withOutputListDoc = 414 | d.fn( 415 | ||| 416 | `withOutputList` injects the list of output configurations as Terraform `output` blocks into the root 417 | configuration. 418 | 419 | **Args**: 420 | - `outputs` (`list[obj]`): List of output configurations, where each element describes an `output` block. Each 421 | element should have the keys `n` (for `name`), `v` (for `value`), `d` (for 422 | `description`), and `s` (for `sensitive`). 423 | 424 | **Returns**: 425 | - A mixin object that injects all the outputs as output blocks. 426 | |||, 427 | [ 428 | d.arg('outputs', d.T.array), 429 | ], 430 | ); 431 | local withOutputList(outputs) = 432 | h.mergeAll([ 433 | withOutput(o.n, o.v, std.get(o, 'd', null), std.get(o, 's', null)) 434 | for o in outputs 435 | ]); 436 | 437 | 438 | local withLocalDoc = 439 | d.fn( 440 | ||| 441 | `tf.withLocal` injects a new Terraform `local` definition into the root configuration. 442 | 443 | **Args**: 444 | - `name` (`string`): The name of the `local` to define. 445 | - `value` (`any`): The value to bind to the `local`. 446 | 447 | **Returns**: 448 | - A mixin object that injects the new local into the root Terraform configuration. 449 | |||, 450 | [ 451 | d.arg('name', d.T.string), 452 | d.arg('value', d.T.any), 453 | ], 454 | ); 455 | local withLocal(name, value) = { 456 | locals+: { 457 | [name]: value, 458 | }, 459 | }; 460 | 461 | 462 | local withLocalMapDoc = 463 | d.fn( 464 | ||| 465 | `tf.withLocalMap` injects all the key value pairs of the input map as Terraform `local` definitions in the root 466 | configuration. 467 | 468 | **Args**: 469 | - `map` (`map[str, str]`): Map of local keys to local values. 470 | 471 | **Returns**: 472 | - A mixin object that injects all the key value pairs as locals. 473 | |||, 474 | [ 475 | d.arg('map', d.T.object), 476 | ], 477 | ); 478 | local withLocalMap(map) = 479 | h.mergeAll([ 480 | withLocal(i.k, i.v) 481 | for i in h.objItems(map) 482 | ]); 483 | 484 | 485 | local withLocalListDoc = 486 | d.fn( 487 | ||| 488 | `tf.withLocalList` injects the list of local configurations as Terraform `local` definitions in the root 489 | configuration. 490 | 491 | **Args**: 492 | - `locals` (`list[obj]`): List of local configurations, where each element describes a `local`. Each element 493 | should have the keys `n` (for `name`) and `v` (for `value`). 494 | 495 | **Returns**: 496 | - A mixin object that injects all the locals into the Terraform config. 497 | |||, 498 | [ 499 | d.arg('locals', d.T.array), 500 | ], 501 | ); 502 | local withLocalList(locals) = 503 | h.mergeAll([ 504 | withLocal(l.n, l.v) 505 | for l in locals 506 | ]); 507 | 508 | 509 | { 510 | '#withProvider':: withProviderDoc, 511 | withProvider:: withProvider, 512 | '#withResource':: withResourceDoc, 513 | withResource:: withResource, 514 | '#withData':: withDataDoc, 515 | withData:: withData, 516 | '#withModule':: withModuleDoc, 517 | withModule:: withModule, 518 | '#withVariable':: withVariableDoc, 519 | withVariable:: withVariable, 520 | '#withOutput':: withOutputDoc, 521 | withOutput:: withOutput, 522 | '#withOutputMap':: withOutputMapDoc, 523 | withOutputMap:: withOutputMap, 524 | '#withSensitiveOutputMap':: withSensitiveOutputMapDoc, 525 | withSensitiveOutputMap:: withSensitiveOutputMap, 526 | '#withOutputList':: withOutputListDoc, 527 | withOutputList:: withOutputList, 528 | '#withLocal':: withLocalDoc, 529 | withLocal:: withLocal, 530 | '#withLocalMap':: withLocalMapDoc, 531 | withLocalMap:: withLocalMap, 532 | '#withLocalList':: withLocalListDoc, 533 | withLocalList:: withLocalList, 534 | } 535 | -------------------------------------------------------------------------------- /test/fixtures/helpers/is_string_array/expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "arrayNestedString": false, 3 | "arrayNestedStringFlattened": true, 4 | "arrayNumber": false, 5 | "arrayObject": false, 6 | "arrayString": true, 7 | "emptyArray": true, 8 | "number": false, 9 | "object": false, 10 | "string": false 11 | } 12 | -------------------------------------------------------------------------------- /test/fixtures/helpers/is_string_array/test.jsonnet: -------------------------------------------------------------------------------- 1 | local h = import 'src/_custom/helpers.libsonnet'; 2 | 3 | { 4 | number: h.isStringArray(42), 5 | string: h.isStringArray('hello world'), 6 | object: h.isStringArray({ msg: 'hello world' }), 7 | emptyArray: h.isStringArray([]), 8 | arrayNumber: h.isStringArray([42]), 9 | arrayObject: h.isStringArray([{ msg: 'hello world' }]), 10 | arrayString: h.isStringArray(['hello', 'world']), 11 | arrayNestedString: h.isStringArray([ 12 | ['hello'], 13 | ['world'], 14 | ]), 15 | arrayNestedStringFlattened: h.isStringArray( 16 | std.flattenArrays([ 17 | ['hello'], 18 | ['world'], 19 | ]), 20 | ), 21 | } 22 | -------------------------------------------------------------------------------- /test/fixtures/helpers/mergeall_mixins/expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "msgs": [ 3 | "hello", 4 | "world" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/helpers/mergeall_mixins/test.jsonnet: -------------------------------------------------------------------------------- 1 | local h = import 'src/_custom/helpers.libsonnet'; 2 | 3 | h.mergeAll([ 4 | { msgs+: ['hello'] }, 5 | { msgs+: ['world'] }, 6 | ]) 7 | -------------------------------------------------------------------------------- /test/fixtures/helpers/mergeall_nomixin/expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "greeting": "hello", 3 | "message": "world" 4 | } 5 | -------------------------------------------------------------------------------- /test/fixtures/helpers/mergeall_nomixin/test.jsonnet: -------------------------------------------------------------------------------- 1 | local h = import 'src/_custom/helpers.libsonnet'; 2 | 3 | h.mergeAll([ 4 | { greeting: 'hello' }, 5 | { message: 'world' }, 6 | ]) 7 | -------------------------------------------------------------------------------- /test/fixtures/helpers/objitems/expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "one": 1, 3 | "three": { 4 | "three": 3 5 | }, 6 | "two": "2-" 7 | } 8 | -------------------------------------------------------------------------------- /test/fixtures/helpers/objitems/test.jsonnet: -------------------------------------------------------------------------------- 1 | local h = import 'src/_custom/helpers.libsonnet'; 2 | 3 | local obj = { 4 | one: 1, 5 | two: '2-', 6 | three: { 7 | three: 3, 8 | }, 9 | }; 10 | 11 | { 12 | [f.k]: f.v 13 | for f in h.objItems(obj) 14 | } 15 | -------------------------------------------------------------------------------- /test/fixtures/helpers/objitems_ignore_hidden/expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "one": 1, 3 | "two": "2-" 4 | } 5 | -------------------------------------------------------------------------------- /test/fixtures/helpers/objitems_ignore_hidden/test.jsonnet: -------------------------------------------------------------------------------- 1 | local h = import 'src/_custom/helpers.libsonnet'; 2 | 3 | local obj = { 4 | one: 1, 5 | two: '2-', 6 | three:: { 7 | three: 3, 8 | }, 9 | }; 10 | 11 | { 12 | [f.k]: f.v 13 | for f in h.objItems(obj) 14 | } 15 | -------------------------------------------------------------------------------- /test/fixtures/helpers/objitemsall_with_hidden/expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "one": 1, 3 | "three": { 4 | "three": 3 5 | }, 6 | "two": "2-" 7 | } 8 | -------------------------------------------------------------------------------- /test/fixtures/helpers/objitemsall_with_hidden/test.jsonnet: -------------------------------------------------------------------------------- 1 | local h = import 'src/_custom/helpers.libsonnet'; 2 | 3 | local obj = { 4 | one: 1, 5 | two: '2-', 6 | three:: { 7 | three: 3, 8 | }, 9 | }; 10 | 11 | { 12 | [f.k]: f.v 13 | for f in h.objItemsAll(obj) 14 | } 15 | -------------------------------------------------------------------------------- /test/fixtures/tfunit/data/main.tf.jsonnet: -------------------------------------------------------------------------------- 1 | local tf = import 'main.libsonnet'; 2 | 3 | tf.withData('null_data_source', 'foo', { 4 | inputs: { 5 | msg: 'hello world', 6 | }, 7 | }) 8 | + tf.withOutput('output', { msg: '${data.null_data_source.foo.outputs.msg}' }) 9 | -------------------------------------------------------------------------------- /test/fixtures/tfunit/local/main.tf.jsonnet: -------------------------------------------------------------------------------- 1 | local tf = import 'main.libsonnet'; 2 | 3 | tf.withLocal('foo', { msg: 'hello world' }) 4 | + tf.withOutput('output', '${local.foo}') 5 | -------------------------------------------------------------------------------- /test/fixtures/tfunit/meta/main.tf.jsonnet: -------------------------------------------------------------------------------- 1 | local tf = import 'main.libsonnet'; 2 | 3 | tf.withResource( 4 | 'null_resource', 5 | 'foo', 6 | {}, 7 | _meta=tf.meta.new(count=5), 8 | ) 9 | + tf.withOutput('output', { num_created: '${length(null_resource.foo)}' }) 10 | -------------------------------------------------------------------------------- /test/fixtures/tfunit/output/main.tf.jsonnet: -------------------------------------------------------------------------------- 1 | local tf = import 'main.libsonnet'; 2 | 3 | tf.withOutput('output', { msg: 'hello world' }) 4 | -------------------------------------------------------------------------------- /test/fixtures/tfunit/ref/main.tf.jsonnet: -------------------------------------------------------------------------------- 1 | local tf = import 'main.libsonnet'; 2 | 3 | local o = 4 | tf.withData('null_data_source', 'foo', { 5 | inputs: { 6 | msg: 'hello world', 7 | }, 8 | }) 9 | + tf.withResource('null_resource', 'foo', {}) 10 | + tf.withOutput('output', { 11 | msg: o._ref.data.null_data_source.foo.get('outputs.msg'), 12 | null_resource_id: o._ref.null_resource.foo.get('id'), 13 | }); 14 | 15 | o 16 | -------------------------------------------------------------------------------- /test/fixtures/tfunit/resource/main.tf.jsonnet: -------------------------------------------------------------------------------- 1 | local tf = import 'main.libsonnet'; 2 | 3 | tf.withResource('null_resource', 'foo', {}) 4 | -------------------------------------------------------------------------------- /test/fixtures/tfunit/variable/main.tf.jsonnet: -------------------------------------------------------------------------------- 1 | local tf = import 'main.libsonnet'; 2 | 3 | tf.withVariable('required_input', type='string') 4 | + tf.withVariable('optional_number', isRequired=false, type='number', default=0) 5 | -------------------------------------------------------------------------------- /test/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/fensak-io/tf-libsonnet/test 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/google/go-jsonnet v0.20.0 7 | github.com/gruntwork-io/terratest v0.43.3 8 | github.com/onsi/gomega v1.27.8 9 | ) 10 | 11 | require ( 12 | cloud.google.com/go v0.105.0 // indirect 13 | cloud.google.com/go/compute v1.12.1 // indirect 14 | cloud.google.com/go/compute/metadata v0.2.1 // indirect 15 | cloud.google.com/go/iam v0.7.0 // indirect 16 | cloud.google.com/go/storage v1.27.0 // indirect 17 | github.com/agext/levenshtein v1.2.3 // indirect 18 | github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect 19 | github.com/aws/aws-sdk-go v1.44.122 // indirect 20 | github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect 21 | github.com/davecgh/go-spew v1.1.1 // indirect 22 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 23 | github.com/golang/protobuf v1.5.3 // indirect 24 | github.com/google/go-cmp v0.5.9 // indirect 25 | github.com/google/uuid v1.3.0 // indirect 26 | github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect 27 | github.com/googleapis/gax-go/v2 v2.7.0 // indirect 28 | github.com/hashicorp/errwrap v1.0.0 // indirect 29 | github.com/hashicorp/go-cleanhttp v0.5.2 // indirect 30 | github.com/hashicorp/go-getter v1.7.1 // indirect 31 | github.com/hashicorp/go-multierror v1.1.0 // indirect 32 | github.com/hashicorp/go-safetemp v1.0.0 // indirect 33 | github.com/hashicorp/go-version v1.6.0 // indirect 34 | github.com/hashicorp/hcl/v2 v2.9.1 // indirect 35 | github.com/hashicorp/terraform-json v0.13.0 // indirect 36 | github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a // indirect 37 | github.com/jmespath/go-jmespath v0.4.0 // indirect 38 | github.com/klauspost/compress v1.15.11 // indirect 39 | github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326 // indirect 40 | github.com/mitchellh/go-homedir v1.1.0 // indirect 41 | github.com/mitchellh/go-testing-interface v1.14.1 // indirect 42 | github.com/mitchellh/go-wordwrap v1.0.1 // indirect 43 | github.com/pmezard/go-difflib v1.0.0 // indirect 44 | github.com/stretchr/testify v1.8.1 // indirect 45 | github.com/tmccombs/hcl2json v0.3.3 // indirect 46 | github.com/ulikunitz/xz v0.5.10 // indirect 47 | github.com/zclconf/go-cty v1.9.1 // indirect 48 | go.opencensus.io v0.24.0 // indirect 49 | golang.org/x/crypto v0.1.0 // indirect 50 | golang.org/x/net v0.10.0 // indirect 51 | golang.org/x/oauth2 v0.1.0 // indirect 52 | golang.org/x/sys v0.8.0 // indirect 53 | golang.org/x/text v0.9.0 // indirect 54 | golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect 55 | google.golang.org/api v0.103.0 // indirect 56 | google.golang.org/appengine v1.6.7 // indirect 57 | google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c // indirect 58 | google.golang.org/grpc v1.51.0 // indirect 59 | google.golang.org/protobuf v1.28.1 // indirect 60 | gopkg.in/yaml.v2 v2.4.0 // indirect 61 | gopkg.in/yaml.v3 v3.0.1 // indirect 62 | sigs.k8s.io/yaml v1.3.0 // indirect 63 | ) 64 | -------------------------------------------------------------------------------- /test/helpers_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "testing" 7 | 8 | "github.com/google/go-jsonnet" 9 | . "github.com/onsi/gomega" 10 | ) 11 | 12 | const ( 13 | helpersFixtureDir = "fixtures/helpers" 14 | ) 15 | 16 | func TestHelpers(t *testing.T) { 17 | t.Parallel() 18 | g := NewGomegaWithT(t) 19 | 20 | rootPath, err := filepath.Abs("..") 21 | g.Expect(err).ToNot(HaveOccurred()) 22 | 23 | dir, err := os.Open(helpersFixtureDir) 24 | g.Expect(err).ToNot(HaveOccurred()) 25 | defer dir.Close() 26 | 27 | testCases, err := dir.Readdir(0) 28 | g.Expect(err).ToNot(HaveOccurred()) 29 | 30 | for _, tc := range testCases { 31 | tc := tc 32 | t.Run(tc.Name(), func(t *testing.T) { 33 | t.Parallel() 34 | g := NewGomegaWithT(t) 35 | 36 | tcPath := filepath.Join(helpersFixtureDir, tc.Name()) 37 | jsonnetFPath := filepath.Join(tcPath, "test.jsonnet") 38 | expectedJSONPath := filepath.Join(tcPath, "expected.json") 39 | expectedJSON, err := os.ReadFile(expectedJSONPath) 40 | g.Expect(err).ToNot(HaveOccurred()) 41 | 42 | vm := jsonnet.MakeVM() 43 | importer := jsonnet.FileImporter{ 44 | JPaths: []string{rootPath}, 45 | } 46 | vm.Importer(&importer) 47 | actual, err := vm.EvaluateFile(jsonnetFPath) 48 | g.Expect(err).ToNot(HaveOccurred()) 49 | g.Expect(actual).To(Equal(string(expectedJSON))) 50 | }) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /test/unit_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "testing" 7 | 8 | "github.com/google/go-jsonnet" 9 | "github.com/gruntwork-io/terratest/modules/terraform" 10 | . "github.com/onsi/gomega" 11 | ) 12 | 13 | const ( 14 | unitTestFixtureDir = "fixtures/tfunit" 15 | ) 16 | 17 | func TestUnitVariableDefaultRequired(t *testing.T) { 18 | t.Parallel() 19 | g := NewGomegaWithT(t) 20 | 21 | variablePath := filepath.Join(unitTestFixtureDir, "variable") 22 | jsonnetFPath := filepath.Join(variablePath, "main.tf.jsonnet") 23 | err := renderAndApplyE(t, jsonnetFPath, nil, nil) 24 | g.Expect(err).To(HaveOccurred()) 25 | } 26 | 27 | func TestUnitVariableWithInput(t *testing.T) { 28 | t.Parallel() 29 | g := NewGomegaWithT(t) 30 | 31 | variablePath := filepath.Join(unitTestFixtureDir, "variable") 32 | jsonnetFPath := filepath.Join(variablePath, "main.tf.jsonnet") 33 | err := renderAndApplyE( 34 | t, 35 | jsonnetFPath, 36 | map[string]interface{}{ 37 | "required_input": "pass in", 38 | }, 39 | nil, 40 | ) 41 | g.Expect(err).NotTo(HaveOccurred()) 42 | } 43 | 44 | func TestUnitVariableWithType(t *testing.T) { 45 | t.Parallel() 46 | g := NewGomegaWithT(t) 47 | 48 | variablePath := filepath.Join(unitTestFixtureDir, "variable") 49 | jsonnetFPath := filepath.Join(variablePath, "main.tf.jsonnet") 50 | err := renderAndApplyE( 51 | t, 52 | jsonnetFPath, 53 | map[string]interface{}{ 54 | "required_input": "pass in", 55 | "optional_number": "not-a-number", 56 | }, 57 | nil, 58 | ) 59 | g.Expect(err).To(HaveOccurred()) 60 | 61 | withNumberErr := renderAndApplyE( 62 | t, 63 | jsonnetFPath, 64 | map[string]interface{}{ 65 | "required_input": "pass in", 66 | "optional_number": 1337, 67 | }, 68 | nil, 69 | ) 70 | g.Expect(withNumberErr).NotTo(HaveOccurred()) 71 | } 72 | 73 | func TestUnitResource(t *testing.T) { 74 | t.Parallel() 75 | g := NewGomegaWithT(t) 76 | 77 | resourcePath := filepath.Join(unitTestFixtureDir, "resource") 78 | jsonnetFPath := filepath.Join(resourcePath, "main.tf.jsonnet") 79 | err := renderAndApplyE(t, jsonnetFPath, nil, nil) 80 | g.Expect(err).NotTo(HaveOccurred()) 81 | } 82 | 83 | func TestUnitOutput(t *testing.T) { 84 | t.Parallel() 85 | g := NewGomegaWithT(t) 86 | 87 | var out struct { 88 | Msg string 89 | } 90 | 91 | outputPath := filepath.Join(unitTestFixtureDir, "output") 92 | jsonnetFPath := filepath.Join(outputPath, "main.tf.jsonnet") 93 | err := renderAndApplyE(t, jsonnetFPath, nil, &out) 94 | g.Expect(err).NotTo(HaveOccurred()) 95 | g.Expect(out.Msg).To(Equal("hello world")) 96 | } 97 | 98 | func TestUnitLocal(t *testing.T) { 99 | t.Parallel() 100 | g := NewGomegaWithT(t) 101 | 102 | var out struct { 103 | Msg string 104 | } 105 | 106 | localPath := filepath.Join(unitTestFixtureDir, "local") 107 | jsonnetFPath := filepath.Join(localPath, "main.tf.jsonnet") 108 | err := renderAndApplyE(t, jsonnetFPath, nil, &out) 109 | g.Expect(err).NotTo(HaveOccurred()) 110 | g.Expect(out.Msg).To(Equal("hello world")) 111 | } 112 | 113 | func TestUnitData(t *testing.T) { 114 | t.Parallel() 115 | g := NewGomegaWithT(t) 116 | 117 | var out struct { 118 | Msg string 119 | } 120 | 121 | dataPath := filepath.Join(unitTestFixtureDir, "data") 122 | jsonnetFPath := filepath.Join(dataPath, "main.tf.jsonnet") 123 | err := renderAndApplyE(t, jsonnetFPath, nil, &out) 124 | g.Expect(err).NotTo(HaveOccurred()) 125 | g.Expect(out.Msg).To(Equal("hello world")) 126 | } 127 | 128 | func TestUnitRef(t *testing.T) { 129 | t.Parallel() 130 | g := NewGomegaWithT(t) 131 | 132 | var out struct { 133 | Msg string 134 | NullResourceID string `json:"null_resource_id"` 135 | } 136 | 137 | refPath := filepath.Join(unitTestFixtureDir, "ref") 138 | jsonnetFPath := filepath.Join(refPath, "main.tf.jsonnet") 139 | err := renderAndApplyE(t, jsonnetFPath, nil, &out) 140 | g.Expect(err).NotTo(HaveOccurred()) 141 | g.Expect(out.Msg).To(Equal("hello world")) 142 | g.Expect(out.NullResourceID).NotTo(Equal("")) 143 | } 144 | 145 | func TestUnitMeta(t *testing.T) { 146 | t.Parallel() 147 | g := NewGomegaWithT(t) 148 | 149 | var out struct { 150 | NumCreated int `json:"num_created"` 151 | } 152 | 153 | metaPath := filepath.Join(unitTestFixtureDir, "meta") 154 | jsonnetFPath := filepath.Join(metaPath, "main.tf.jsonnet") 155 | err := renderAndApplyE(t, jsonnetFPath, nil, &out) 156 | g.Expect(err).NotTo(HaveOccurred()) 157 | g.Expect(out.NumCreated).To(Equal(5)) 158 | } 159 | 160 | func renderAndApplyE( 161 | t *testing.T, 162 | jsonnetFPath string, 163 | vars map[string]interface{}, 164 | outputStruct interface{}, 165 | ) error { 166 | g := NewGomegaWithT(t) 167 | 168 | rootPath, err := filepath.Abs("..") 169 | g.Expect(err).ToNot(HaveOccurred()) 170 | 171 | tmpDir, err := os.MkdirTemp("", "tf-libsonnet-test-*") 172 | g.Expect(err).ToNot(HaveOccurred()) 173 | defer os.RemoveAll(tmpDir) 174 | 175 | vm := jsonnet.MakeVM() 176 | importer := jsonnet.FileImporter{ 177 | JPaths: []string{rootPath}, 178 | } 179 | vm.Importer(&importer) 180 | rendered, err := vm.EvaluateFile(jsonnetFPath) 181 | g.Expect(err).ToNot(HaveOccurred()) 182 | 183 | writeErr := os.WriteFile(filepath.Join(tmpDir, "main.tf.json"), []byte(rendered), 0o644) 184 | g.Expect(writeErr).ToNot(HaveOccurred()) 185 | 186 | tfOpts := &terraform.Options{ 187 | TerraformDir: tmpDir, 188 | Vars: vars, 189 | NoColor: true, 190 | } 191 | _, applyErr := terraform.InitAndApplyE(t, tfOpts) 192 | if applyErr != nil { 193 | return applyErr 194 | } 195 | 196 | if outputStruct != nil { 197 | terraform.OutputStruct(t, tfOpts, "output", outputStruct) 198 | } 199 | return nil 200 | } 201 | --------------------------------------------------------------------------------