├── .eslintignore ├── .eslintrc.js ├── .github ├── ISSUE_TEMPLATE │ ├── help.md │ ├── report-a-bug.md │ └── request-a-feature.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── do-spaces-workflow.yml │ └── gh-pages-workflow.yml ├── .gitignore ├── .nvmrc ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── posthtml.config.js ├── src ├── kubernetes-tool │ ├── assets │ │ ├── bottom.svg │ │ └── top.svg │ ├── descriptions │ │ ├── Certificate.yml │ │ ├── Deployment.yml │ │ ├── README.md │ │ ├── Service.yml │ │ ├── StatefulSet.yml │ │ ├── base.yml │ │ ├── index.js │ │ ├── learn_more.ts │ │ └── text_descriptions.ts │ ├── i18n │ │ ├── en │ │ │ ├── index.ts │ │ │ └── templates │ │ │ │ ├── app.ts │ │ │ │ ├── categorisation_view.ts │ │ │ │ ├── index.ts │ │ │ │ ├── shared.ts │ │ │ │ ├── splash_screen.ts │ │ │ │ └── split_view.ts │ │ └── index.ts │ ├── index.html │ ├── mount.js │ ├── scss │ │ └── style.scss │ ├── templates │ │ ├── app.vue │ │ ├── categorisation_view.vue │ │ ├── properties.vue │ │ ├── splash_screen.vue │ │ └── split_view.vue │ └── utils │ │ ├── categorisation.ts │ │ ├── compatibility_test.ts │ │ ├── gitHttpMirrorFs.ts │ │ ├── githubFs.ts │ │ ├── helm.ts │ │ ├── helm_parts │ │ ├── core_parser.ts │ │ ├── document_parser.ts │ │ ├── functions │ │ │ ├── b64enc.ts │ │ │ ├── default.ts │ │ │ ├── define.ts │ │ │ ├── env.ts │ │ │ ├── if.ts │ │ │ ├── include.ts │ │ │ ├── indent.ts │ │ │ ├── index.ts │ │ │ ├── printf.ts │ │ │ ├── quote.ts │ │ │ ├── randAlphaNum.ts │ │ │ ├── range.ts │ │ │ ├── replace.ts │ │ │ ├── template.ts │ │ │ ├── toYaml.ts │ │ │ ├── trimSuffix.ts │ │ │ ├── trunc.ts │ │ │ └── uuidv4.ts │ │ ├── helm_cache.ts │ │ ├── tokeniser.ts │ │ └── utils.ts │ │ ├── kubernetes.ts │ │ └── labeler.ts └── static │ └── robots.txt └── tsconfig.json /.eslintignore: -------------------------------------------------------------------------------- 1 | **/*.js 2 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | "plugin:@typescript-eslint/recommended", 4 | "plugin:vue/recommended", 5 | ], 6 | parserOptions: { 7 | ecmaVersion: 2018, 8 | parser: "@typescript-eslint/parser", 9 | sourceType: "module", 10 | }, 11 | rules: { 12 | "linebreak-style": ["error", "unix"], 13 | semi: ["error", "never"], 14 | "@typescript-eslint/no-non-null-assertion": 0, 15 | "@typescript-eslint/no-explicit-any": 0, 16 | "@typescript-eslint/explicit-function-return-type": 0, 17 | "require-atomic-updates": 0, 18 | "no-undef": 0, 19 | "vue/require-v-for-key": 0, 20 | "vue/require-default-prop": 0, 21 | "vue/no-v-html": 0, 22 | "vue/max-attributes-per-line": 0, 23 | "vue/html-self-closing": 0, 24 | "vue/html-indent": ["error", 4], 25 | "vue/script-indent": ["error", 4, { 26 | baseIndent: 1, 27 | }], 28 | "@typescript-eslint/indent": 0, 29 | "vue/no-unused-vars": 0, 30 | "vue/multi-word-component-names": 0, 31 | "@typescript-eslint/no-object-literal-type-assertion": 0, 32 | "@typescript-eslint/no-empty-interface": 0, 33 | "@typescript-eslint/no-empty-function": 0, 34 | "@typescript-eslint/ban-ts-comment": 0, 35 | }, 36 | } 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/help.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Help ❓ 3 | about: Encountered a problem with the tool? 4 | --- 5 | 6 | 15 | 16 | ## Information 17 | 19 | 20 | ## Help request 21 | 22 | ### Problem 23 | 24 | 25 | ### What I have tried 26 | 27 | 28 | ### Screenshots 29 | 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/report-a-bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Report a bug 🐛 3 | about: Report a bug with the tool. Only use this if you're 100% sure there's something wrong, otherwise, try "Help". 4 | --- 5 | 6 | 19 | 20 | ## Information 21 | 23 | 24 | ## Details 25 | 26 | ### Description 27 | 28 | 29 | ### Steps to reproduce 30 | 31 | 32 | ### Expected behavior 33 | 34 | 35 | ### Screenshots 36 | 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/request-a-feature.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Request a feature 🆕 3 | about: Suggest a new feature that you would like in the tool! 4 | --- 5 | 6 | 31 | 32 | ## Feature request 33 | 34 | ### Feature description 35 | 36 | 37 | ### How the feature is useful 38 | 39 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Type of Change 2 | 3 | 4 | - **Build Scripts:** 5 | - **Tool Source:** 6 | - **Something else:** 7 | 8 | ## What issue does this relate to? 9 | 10 | 11 | ### What should this PR do? 12 | 13 | 14 | ### What are the acceptance criteria? 15 | 16 | 17 | -------------------------------------------------------------------------------- /.github/workflows/do-spaces-workflow.yml: -------------------------------------------------------------------------------- 1 | name: Test and Deploy to DigitalOcean Spaces 2 | 3 | on: push 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v1 11 | 12 | - name: Read .nvmrc 13 | run: echo "##[set-output name=NVMRC;]$(cat .nvmrc)" 14 | id: nvm 15 | 16 | - name: Use Node.js (.nvmrc) 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: "${{ steps.nvm.outputs.NVMRC }}" 20 | 21 | - name: npm ci, test, and build 22 | run: | 23 | npm ci 24 | npm test 25 | npm run build 26 | 27 | - name: Deploy commit to DigitalOcean Spaces 28 | run: aws s3 sync ./dist s3://${{ secrets.SPACES_BUCKET }}/commits/kubernetes-tool/${{ github.sha }} --endpoint=https://${{ secrets.SPACES_REGION }}.digitaloceanspaces.com --acl public-read --content-encoding utf8 29 | env: 30 | AWS_ACCESS_KEY_ID: ${{ secrets.SPACES_ACCESS_KEY_ID }} 31 | AWS_SECRET_ACCESS_KEY: ${{ secrets.SPACES_SECRET_ACCESS_KEY }} 32 | AWS_DEFAULT_REGION: ${{ secrets.SPACES_REGION }} 33 | 34 | - name: Leave a comment 35 | run: npm run deploy:spaces:comment 36 | env: 37 | REPO_NAME: ${{ github.repository }} 38 | COMMIT_SHA: ${{ github.sha }} 39 | GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} 40 | SPACES_REGION: ${{ secrets.SPACES_REGION }} 41 | SPACES_BUCKET: ${{ secrets.SPACES_BUCKET }} 42 | -------------------------------------------------------------------------------- /.github/workflows/gh-pages-workflow.yml: -------------------------------------------------------------------------------- 1 | name: Test and Deploy to GitHub Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v1 14 | 15 | - name: Read .nvmrc 16 | run: echo "##[set-output name=NVMRC;]$(cat .nvmrc)" 17 | id: nvm 18 | 19 | - name: Use Node.js (.nvmrc) 20 | uses: actions/setup-node@v1 21 | with: 22 | node-version: "${{ steps.nvm.outputs.NVMRC }}" 23 | 24 | - name: npm ci, test, and build 25 | run: | 26 | npm ci 27 | npm test 28 | npm run build 29 | 30 | - name: Deploy master to GitHub Pages 31 | uses: JamesIves/github-pages-deploy-action@2.0.0 32 | env: 33 | ACCESS_TOKEN: ${{ secrets.DEV_GITHUB_TOKEN }} 34 | BASE_BRANCH: master 35 | BRANCH: gh-pages 36 | FOLDER: dist 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .cache/ 3 | dist/ 4 | dev/ 5 | .idea/ 6 | .vscode/ 7 | build/base.html 8 | build/svg/ 9 | .DS_Store 10 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v14.17.5 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Pull Requests 4 | 5 | ### Creating a Pull Request 6 | 7 | This application has been designed so that people can easily expand it. 8 | To request us to review code that you create, you will need to create a pull request. 9 | Creating a pull request is described in 10 | [this tutorial](https://www.digitalocean.com/community/tutorials/how-to-create-a-pull-request-on-github). 11 | 12 | ### Linting 13 | 14 | Before creating a pull request to this application, you will want to lint it first. 15 | This is because linting is a check that is ran when a pull request is made and cannot be merged in if it fails. 16 | 17 | To lint, simply run `npm test`. This will lint all the TS, Vue & SCSS files within the app. 18 | 19 | If there are any errors that can be automatically be fixed with the TS & Vue files, you can execute 20 | `npm run test:ts-vue:fix` to automatically do that. 21 | 22 | This project enforces LF line styles, 4 spaces and no semi-colons. 23 | The linting will fail if this is not followed. 24 | 25 | ### File Location/Types 26 | 27 | Please see [README: Source Structure](README.md#source-structure) for information on how files should be organised. 28 | 29 | ## Issue Creation 30 | 31 | In the event that you have a issue using the tool or have a suggest for a change but don't want to contribute code, 32 | we are more than happy to help. 33 | Make sure that when you create your issue, it follows the format for the type of issue you select 34 | (it has individual templates for each issue type). 35 | 36 | Issue template types include the following: 37 | - Bug Reporting 38 | - Feature Requests 39 | - Help Requests 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2019 DigitalOcean 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kubernetes Tool 2 | 3 | A tool to explain Kubernetes files and Helm charts for the DigitalOcean Community. 4 | 5 | --- 6 | 7 | ## Development/Building 8 | 9 | To setup the build/develop environment, you will need to run `npm i` with Node 12+ installed. This will install the 10 | dependencies to allow you to build the project. 11 | 12 | To develop for this tool run `npm run dev`. 13 | This will start a development server that will automatically reload the codebase when changes occur. 14 | 15 | If you wish to host this tool on a service, simply run `npm run build`. This will run all the necessary build scripts 16 | automatically to build the tool.\ 17 | You can then take the `dist` folder and put it on your web server/bucket. 18 | 19 | GitHub Actions is setup to do this automatically for this repository to deploy to gh-pages. 20 | It is also configured to deploy each PR commit to DigitalOcean Spaces for PR previews. 21 | 22 | ## Source Structure 23 | 24 | ### [`src/kubernetes-tool`](./src/kubernetes-tool) 25 | 26 | #### [`src/kubernetes-tool/assets`](./src/kubernetes-tool/assets) 27 | 28 | This directory contains assets used by the tool, such as the SVG background designs for the landing screen of the tool. 29 | These SVG assets are build to JS files in `build/svg` by the build scripts. 30 | 31 | #### [`src/kubernetes-tool/i18n`](./src/kubernetes-tool/i18n) 32 | 33 | In this directory lives all the internationalisation data and strings for the tool. 34 | Currently, this only contains the English versions of the strings but could be expanded in the future. 35 | 36 | #### [`src/kubernetes-tool/scss`](./src/kubernetes-tool/scss) 37 | 38 | The scss directory contains the main SCSS styling file for the tool, which imports our do-bulma library and then adds 39 | tool-specific customisations. 40 | 41 | #### [`src/kubernetes-tool/templates`](./src/kubernetes-tool/templates) 42 | 43 | This directory contains all the Vue templates that are used to render the tool on the client-side. 44 | `app.vue` is the main Vue file that all other templates are referenced into. 45 | 46 | #### [`src/kubernetes-tool/utils`](./src/kubernetes-tool/utils) 47 | 48 | In this directory is all the utility scripts (such as `githubFs` which provides fs like navigation of GitHub repos) and 49 | logic written in TypeScript that makes the tool work as expected. 50 | 51 | ### [`src/resource_definitions`](./src/resource_definitions) 52 | 53 | The resource definitions directory contains all the (custom) resource definitions that the Kubernetes parser within the 54 | tool uses to understand Kubernetes files. 55 | Included is a JavaScript import script that allows for new resource definitions to be loaded in. 56 | 57 | ## Contributing 58 | 59 | If you are contributing, please read the [contributing file](CONTRIBUTING.md) before submitting your pull requests. 60 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kubernetes-tool", 3 | "version": "1.0.0", 4 | "description": "A tool to explain Kubernetes files and Helm charts for the DigitalOcean Community.", 5 | "license": "Apache-2.0", 6 | "scripts": { 7 | "build": "npm run build:clean && npm run build:svg && npm run build:template && npm run build:static && npm run build:tool", 8 | "build:clean": "do-vue clean", 9 | "build:svg": "do-vue svgs src/kubernetes-tool/assets", 10 | "build:template": "do-vue template", 11 | "build:static": "copyfiles --up 2 ./src/static/{*,**/*} dist", 12 | "build:tool": "do-vue tool src/kubernetes-tool dist", 13 | "deploy:spaces:comment": "do-vue comment kubernetes-tool", 14 | "dev:prep": "npm run build:svg && npm run build:template && npm run dev:static", 15 | "dev:static": "copyfiles --up 2 ./src/static/{*,**/*} dev", 16 | "dev": "npm run dev:prep && do-vue dev ./src/kubernetes-tool ./dev/kubernetes-tool 8000", 17 | "test": "npm run test:ts-vue && npm run test:scss", 18 | "test:ts-vue": "tsc -noEmit && eslint './src/**/*.{vue,ts}'", 19 | "test:scss": "stylelint ./src/**/*.scss --config node_modules/do-bulma/.stylelintrc.json", 20 | "test:ts-vue:fix": "tsc && eslint './src/**/*.{vue,ts}' --fix" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/do-community/kubernetes-tool.git" 25 | }, 26 | "keywords": [ 27 | "kubernetes" 28 | ], 29 | "author": "DigitalOcean", 30 | "bugs": { 31 | "url": "https://github.com/do-community/kubernetes-tool/issues" 32 | }, 33 | "homepage": "https://github.com/do-community/kubernetes-tool#readme", 34 | "dependencies": { 35 | "async-lock": "^1.3.1", 36 | "do-bulma": "git+https://github.com/do-community/do-bulma.git", 37 | "do-vue": "git+https://github.com/do-community/do-vue.git", 38 | "escape-string-regexp": "^4.0.0", 39 | "js-yaml": "^4.1.0", 40 | "printj": "^1.3.1", 41 | "prismjs": "^1.27.0", 42 | "vue": "^2.6.14", 43 | "vue-autosuggest": "^2.2.0", 44 | "vue-prism-component": "^1.2.0", 45 | "vue-prism-editor": "0.3.0", 46 | "vue-tippy": "^4.13.0" 47 | }, 48 | "devDependencies": { 49 | "@types/async-lock": "^1.1.3", 50 | "@types/js-yaml": "^4.0.5", 51 | "@types/node": "^14.18.12", 52 | "@types/semver": "^7.3.9", 53 | "@types/uuid": "^8.3.4", 54 | "@typescript-eslint/eslint-plugin": "^5.12.0", 55 | "@typescript-eslint/parser": "^5.12.0", 56 | "@vue/component-compiler-utils": "^3.3.0", 57 | "copyfiles": "^2.4.1", 58 | "eslint": "^8.9.0", 59 | "eslint-plugin-vue": "^8.4.1", 60 | "jsdom": "^16.7.0", 61 | "postcss": "^8.4.6", 62 | "posthtml-extend": "^0.6.2", 63 | "sass": "^1.49.8", 64 | "stylelint": "^14.5.1", 65 | "stylelint-config-standard-scss": "^3.0.0", 66 | "stylelint-order": "^5.0.0", 67 | "typescript": "^4.5.5", 68 | "vue-template-compiler": "^2.6.14" 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /posthtml.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "plugins": { 3 | "posthtml-extend": { 4 | "root": "./src" 5 | }, 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/kubernetes-tool/assets/bottom.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 21 | 22 | 23 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/kubernetes-tool/assets/top.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 9 | 10 | 11 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/kubernetes-tool/descriptions/Certificate.yml: -------------------------------------------------------------------------------- 1 | secretName: 2 | base: "This defines the secret name." 3 | dnsNames: 4 | base: "This defines the DNS names which will be used." 5 | issuerRef: 6 | base: "The issuer which will be used to create the certificate." 7 | acme: 8 | base: "Handles the ACME configuration." 9 | -------------------------------------------------------------------------------- /src/kubernetes-tool/descriptions/Deployment.yml: -------------------------------------------------------------------------------- 1 | replicas: 2 | base: "This defines how many replicas a deployment uses." 3 | template: 4 | base: "This defines the template which the deployment uses." 5 | volumes: 6 | base: "This defines the volumes in the deployment." 7 | -------------------------------------------------------------------------------- /src/kubernetes-tool/descriptions/README.md: -------------------------------------------------------------------------------- 1 | # Kubernetes Descriptions 2 | 3 | ## YAML Descriptions 4 | 5 | Each YAML file in here accounts for descriptions which will show up on Kubernetes deployments. We welcome pull requests for custom resource definitions! 6 | 7 | The base file (`base.yml`) follows a slightly different format since it is the parent of every other deployment. This is because it contains all of the base level descriptions before the spec. 8 | 9 | Every other YAML file repersents a file. The file should be clearly marked by the kind which it repersents. The file starts from the `spec` key and is a dictionary. It follows the following structure: 10 | 11 | ```yaml 12 | Key: 13 | base: "This is the description for this key." 14 | children: 15 | 16 | ``` 17 | 18 | Commited YAML should use 2 spaces. After the file is created, you will need to import it into the `index.js` file. Import the file with the same filename you used for the above minus the extension. 19 | 20 | ```js 21 | import Filename from "./Filename.yml" 22 | ``` 23 | 24 | From here, you can add it to the exported dictionary: 25 | 26 | ```js 27 | export default { ..., Filename } 28 | ``` 29 | 30 | ## Kind Descriptions 31 | 32 | Descriptions of the kinds of deployments belong in `text_descriptions.ts`. These are used during categorisation. Additionally, the "Learn more..." links can be found in `learn_more.ts`. 33 | -------------------------------------------------------------------------------- /src/kubernetes-tool/descriptions/Service.yml: -------------------------------------------------------------------------------- 1 | clusterIP: 2 | base: "This defines the IP address which the service will be exposed at. Putting none here makes it just run internally in the cluster." 3 | ports: 4 | base: "This defines the ports which this service will forward to the pods responsible." 5 | selector: 6 | base: "This defines the labels that should be used when selecting pods. If a selector matches, it will be included." 7 | type: 8 | base: "This defines the type of IP address which will be used. This is commonly \"ClusterIP\" which a local IP address in your Kubernetes cluster." 9 | externalIPs: 10 | base: "This defines any external IP addresses which should be used." 11 | loadBalancerSourceRanges: 12 | base: "This defines what IP ranges will be allowed to connect." 13 | -------------------------------------------------------------------------------- /src/kubernetes-tool/descriptions/StatefulSet.yml: -------------------------------------------------------------------------------- 1 | serviceName: 2 | base: "This defines the name of the service that will be created based on the ports from the StatefulSet." 3 | replicas: 4 | base: "This defines the amount of pods which will be created for this set." 5 | template: 6 | base: "This defines the template which is used for creating the pods." 7 | children: 8 | metadata: 9 | base: "This defines the metadata for this deployment." 10 | children: 11 | name: 12 | base: "This defines the name of a deployment." 13 | labels: 14 | base: "This defines any set labels for this deployment. Labels can be used during deployment to select a specific deployment." 15 | annotations: 16 | base: "Annotations are used to describe what a deployment does. Unlike a label, these are normally used within in-cluster tools and not used to select a deployment." 17 | children: 18 | "service.alpha.kubernetes.io/tolerate-unready-endpoints": 19 | base: "This is for legacy Kubernetes installations to tell it that un-ready pods can still be published." 20 | kind: 21 | base: "This defines the kind of this deployment." 22 | spec: 23 | base: "This defines the specification for the template of each pod." 24 | children: 25 | serviceAccountName: 26 | base: "This defines the service account name which is used for the deployment." 27 | containers: 28 | base: "This defines all of the specifications for the pods." 29 | volumes: 30 | base: "This is used to expose secrets as volumes." 31 | volumeClaimTemplates: 32 | base: "This defines the volume claim which will be used for each pod." 33 | -------------------------------------------------------------------------------- /src/kubernetes-tool/descriptions/base.yml: -------------------------------------------------------------------------------- 1 | children: 2 | kind: 3 | base: "This defines the kind of a deployment. This is used as a unique identifier for the type of deployment based on the API version." 4 | apiVersion: 5 | base: "This defines the API version." 6 | spec: 7 | base: "This defines the specification for this deployment." 8 | metadata: 9 | base: "This defines the metadata for this deployment." 10 | children: 11 | name: 12 | base: "This defines the name of a deployment." 13 | labels: 14 | base: "This defines any set labels for this deployment. Labels can be used during deployment to select a specific deployment." 15 | annotations: 16 | base: "Annotations are used to describe what a deployment does. Unlike a label, these are normally used within in-cluster tools and not used to select a deployment." 17 | children: 18 | "service.alpha.kubernetes.io/tolerate-unready-endpoints": 19 | base: "This is for legacy Kubernetes installations to tell it that un-ready pods can still be published." 20 | kind: 21 | base: "This defines the kind of this deployment." 22 | type: 23 | base: "The type of the deployment. This is used like a genre of the kind of deployment." 24 | 25 | # Special edgecases for secrets. 26 | data: 27 | base: "This defines the data as base 64." 28 | stringData: 29 | base: "This defines the data as a string." 30 | -------------------------------------------------------------------------------- /src/kubernetes-tool/descriptions/index.js: -------------------------------------------------------------------------------- 1 | import base from "./base.yml" 2 | import Service from "./Service.yml" 3 | import StatefulSet from "./StatefulSet.yml" 4 | import Deployment from "./Deployment.yml" 5 | 6 | export default { base, Deployment, Service, StatefulSet } 7 | 8 | // Ignore this if you're just editing YAML descriptions. This is to do with text descriptions and learn more links. 9 | import textDescriptions from "./text_descriptions" 10 | import learnMore from "./learn_more" 11 | export { textDescriptions, learnMore } 12 | -------------------------------------------------------------------------------- /src/kubernetes-tool/descriptions/learn_more.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | Secret: "https://kubernetes.io/docs/concepts/configuration/secret/", 3 | Service: "https://kubernetes.io/docs/concepts/services-networking/service/", 4 | Certificate: "https://kubernetes.io/docs/concepts/cluster-administration/certificates/", 5 | Deployment: "https://kubernetes.io/docs/concepts/workloads/controllers/deployment/", 6 | CronJob: "https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/", 7 | StatefulSet: "https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/", 8 | DaemonSet: "https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/", 9 | ReplicaSet: "https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/", 10 | Pod: "https://kubernetes.io/docs/concepts/workloads/pods/pod-overview/", 11 | } as Record 12 | -------------------------------------------------------------------------------- /src/kubernetes-tool/descriptions/text_descriptions.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | Secret: "Secrets are used for storing data which you want to keep private or distribute across many different installs.", 3 | Service: "A service is used to expose a set of pods in such a way that they can be easily balanced.", 4 | StatefulSet: "A stateful set is used to allow for the graceful modification of pods (up to the replica count) by updating them based on the position of the pod in the set.", 5 | Certificate: "This is a certificate which is used to link a HTTPS certificate to a domain.", 6 | Deployment: "This is a standard deployment which has no special rules when it comes to gracefully updating.", 7 | } as Record 8 | -------------------------------------------------------------------------------- /src/kubernetes-tool/i18n/en/index.ts: -------------------------------------------------------------------------------- 1 | import templates from "./templates" 2 | 3 | export default { templates } as any 4 | -------------------------------------------------------------------------------- /src/kubernetes-tool/i18n/en/templates/app.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | title: "Kubernetes Tool", 3 | collapseAll: "Collapse All", 4 | uncollapseAll: "Un-Collapse All", 5 | oss: `This tool is {link|open-source on GitHub|https://github.com/do-community/kubernetes-tool} under the {link|Apache-2.0|https://github.com/do-community/kubernetes-tool/blob/master/LICENSE} license! We welcome feedback and contributions.`, 6 | } as {[key: string]: string} 7 | -------------------------------------------------------------------------------- /src/kubernetes-tool/i18n/en/templates/categorisation_view.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | learnMore: "Learn more...", 3 | } as {[key: string]: string} 4 | -------------------------------------------------------------------------------- /src/kubernetes-tool/i18n/en/templates/index.ts: -------------------------------------------------------------------------------- 1 | import app from "./app" 2 | import splashScreen from "./splash_screen" 3 | import shared from "./shared" 4 | import splitView from "./split_view" 5 | import categorisationView from "./categorisation_view" 6 | 7 | export default { app, splashScreen, shared, splitView, categorisationView } as {[key: string]: {[key: string]: string}} 8 | -------------------------------------------------------------------------------- /src/kubernetes-tool/i18n/en/templates/shared.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | mainMenu: "Return to Main Menu", 3 | } as {[key: string]: string} 4 | -------------------------------------------------------------------------------- /src/kubernetes-tool/i18n/en/templates/splash_screen.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | description: "A tool to allow you to search for Helm charts (or insert in a Kubernetes file) and get descriptions of how they work.", 3 | selectionPrompt: "Do you want to load a Kubernetes file or a Helm chart?", 4 | helmTitle: "Helm Chart", 5 | helmDescription: "Enter the name of the helm chart:", 6 | helmErr: "Helm Parser Error", 7 | k8sErr: "Kubernetes Parser Error", 8 | helmDoesntExist: "The Helm chart does not seem to exist.", 9 | k8sTitle: "Kubernetes File", 10 | k8sDescription: "Copy the Kubernetes file into the textbox below:", 11 | submit: "Submit", 12 | } as {[key: string]: string} 13 | -------------------------------------------------------------------------------- /src/kubernetes-tool/i18n/en/templates/split_view.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | back: "Show the other files", 3 | } as {[key: string]: string} 4 | -------------------------------------------------------------------------------- /src/kubernetes-tool/i18n/index.ts: -------------------------------------------------------------------------------- 1 | import en from "./en" 2 | 3 | const lang = "en" 4 | const packs = { en } as any 5 | 6 | export default packs[lang] 7 | -------------------------------------------------------------------------------- /src/kubernetes-tool/index.html: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | Kubernetes Tool | DigitalOcean 19 | 20 | 21 | 22 |
23 | 24 |
25 |
26 | -------------------------------------------------------------------------------- /src/kubernetes-tool/mount.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import "./scss/style" 18 | 19 | import "./utils/compatibility_test" 20 | import Vue from "vue" 21 | import App from "./templates/app.vue" 22 | import i18n from "./i18n" 23 | 24 | document.head.title = i18n.templates.app.title 25 | 26 | new Vue({ 27 | render: h => h(App), 28 | }).$mount("#app") 29 | -------------------------------------------------------------------------------- /src/kubernetes-tool/scss/style.scss: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | @import url("https://assets.digitalocean.com/prism/prism.css"); 18 | 19 | // Fix Bulma interfering with Prism 20 | code[class*="language-"] .token { 21 | &.number, 22 | &.tag { 23 | background: transparent; 24 | border-radius: initial; 25 | color: inherit; 26 | display: initial; 27 | font-size: inherit; 28 | margin: initial; 29 | padding: initial; 30 | text-align: initial; 31 | vertical-align: initial; 32 | } 33 | } 34 | 35 | $header: #0071fe; 36 | 37 | @import "~do-bulma/src/style"; 38 | 39 | .do-bulma { 40 | &.landing { 41 | + .helpfulness-cont { 42 | display: none; 43 | } 44 | } 45 | 46 | .landing { 47 | .container { 48 | .container { 49 | flex-grow: 0; 50 | padding: 0; 51 | } 52 | } 53 | } 54 | 55 | .input-container { 56 | .prism-editor-wrapper { 57 | flex-grow: 1; 58 | width: auto; 59 | 60 | pre { 61 | background: $background; 62 | border: $border-size solid $dark-grey; 63 | border-radius: $border-radius; 64 | color: $text; 65 | font-size: .85em; 66 | min-height: $height; 67 | } 68 | } 69 | } 70 | 71 | .main.container { 72 | .record-group { 73 | padding-left: $margin * 2; 74 | } 75 | } 76 | 77 | .no-search { 78 | top: $margin * 3; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/kubernetes-tool/templates/app.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 64 | 65 | 167 | -------------------------------------------------------------------------------- /src/kubernetes-tool/templates/categorisation_view.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 57 | 58 | 112 | -------------------------------------------------------------------------------- /src/kubernetes-tool/templates/properties.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 27 | 28 | 46 | -------------------------------------------------------------------------------- /src/kubernetes-tool/templates/splash_screen.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 103 | 104 | 295 | -------------------------------------------------------------------------------- /src/kubernetes-tool/templates/split_view.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 29 | 30 | 63 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/categorisation.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { textDescriptions, learnMore } from "../descriptions" 18 | 19 | // Defines the item. 20 | class Item { 21 | public kind: string 22 | public fp: string 23 | public file: Record 24 | 25 | public constructor(kind: string, fp: string, file: Record) { 26 | this.kind = kind 27 | this.fp = fp 28 | this.file = file 29 | } 30 | } 31 | 32 | // Defines a category. 33 | export class Category { 34 | public name: string 35 | public learnMore: string | undefined 36 | public description: string | undefined 37 | 38 | public constructor(name: string) { 39 | this.name = name 40 | this.description = textDescriptions[name] 41 | this.learnMore = learnMore[name] 42 | } 43 | } 44 | 45 | // Defines the categorisation. 46 | class Categorisation { 47 | // Defines the categories. 48 | private items: Item[] 49 | 50 | // Constructs the class. 51 | public constructor() { 52 | this.items = [] 53 | } 54 | 55 | // Clears all the categories. 56 | public clear() { 57 | this.items = [] 58 | } 59 | 60 | // Categorises the item. 61 | public insert(kind: string, fp: string, file: Record) { 62 | this.items.push(new Item(kind, fp, file)) 63 | } 64 | 65 | // Gets all the things. 66 | public getAll() { 67 | const kind2cat: Record = {} 68 | const cat2arr: Map = new Map() 69 | 70 | for (const i of this.items) { 71 | if (!kind2cat[i.kind]) { 72 | kind2cat[i.kind] = new Category(i.kind) 73 | cat2arr.set(kind2cat[i.kind], []) 74 | } 75 | cat2arr.get(kind2cat[i.kind])!.push(i) 76 | } 77 | 78 | return cat2arr 79 | } 80 | 81 | // Gets the cost. 82 | public getCost() { 83 | let cost = 0 84 | const all = this.getAll() 85 | for (const category of all.keys()) { 86 | if (category.name === "Service") { 87 | // Check for load balancers. 88 | for (const i of all.get(category)!) { 89 | if (i.file.spec.type === "LoadBalancer") cost += 15 90 | } 91 | } else if (category.name === "PersistentVolumeClaim") { 92 | // This will handle block storage. 93 | for (const i of all.get(category)!) { 94 | if (i.file.spec.storageClassName === "do-block-storage") { 95 | let calculatedStorage = 0 96 | if (((i.file.spec.resources || {}).requests || {}).storage) { 97 | const data = i.file.spec.resources.requests.storage 98 | if (typeof data === "string") { 99 | // Handles the storage. 100 | const endings = { 101 | "Gi": 1, 102 | "Mi": 0.1, 103 | "Ti": 10, 104 | } as Record 105 | for (const e in endings) { 106 | let d 107 | if (data.endsWith(e)) d = Number(data.substr(0, data.length - e.length)) 108 | if (d) calculatedStorage = d * endings[e] 109 | } 110 | } 111 | } 112 | cost += calculatedStorage * 0.1 113 | } 114 | } 115 | } 116 | } 117 | return cost 118 | } 119 | } 120 | 121 | // Exports a object. 122 | export default new Categorisation() 123 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/compatibility_test.ts: -------------------------------------------------------------------------------- 1 | import { fs } from "../utils/helm_parts/utils" 2 | // @ts-ignore 3 | import { HelmCoreParser } from "./helm" 4 | 5 | // Runs the compatibility test. 6 | async function test() { 7 | let errors = "" 8 | let passed = "" 9 | let total = 0 10 | let works = 0 11 | const promises = [] 12 | for (const item of await fs.ls("stable")) { 13 | if (!item.file) { 14 | total++ 15 | promises.push(new HelmCoreParser({}, item.path).promise.then(() => { 16 | passed += item.path + "\n" 17 | works++ 18 | }).catch(err => { 19 | errors += `${item.path}: ${err}\n` 20 | })) 21 | } 22 | } 23 | await Promise.all(promises) 24 | console.log(`${works}/${total}\n---\nFailed\n---\n${errors === "" ? "No errors found!\n" : errors}\nPassed\n---\n${passed}`) 25 | } 26 | 27 | // @ts-ignore 28 | window.compatibiltiyTest = test 29 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/gitHttpMirrorFs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Defines a very basic filesystem using git-http-mirror. 18 | export default class GitHTTPMirrorFS { 19 | public alias: string 20 | public hostname: string 21 | private itemCache: Record 22 | 23 | // Constructs the class. 24 | public constructor(alias: string, hostname: string) { 25 | this.alias = alias 26 | this.hostname = hostname 27 | this.itemCache = {} 28 | } 29 | 30 | // Lists the folder specified. 31 | public async ls(folder: string): Promise<{ 32 | file: boolean; 33 | path: string; 34 | name: string; 35 | }[]> { 36 | const items: { 37 | file: boolean; 38 | path: string; 39 | name: string; 40 | }[] = [] 41 | const res = await fetch(`${this.hostname}/${this.alias}/${folder}`, {headers: { 42 | Accept: "application/json", 43 | }}) 44 | if (res.headers.get("is-dir-listing") === "false") { 45 | return [] 46 | } 47 | const json = await res.json() 48 | for (const item of json) { 49 | const path = `${this.hostname}/${this.alias}/${folder}/${item}` 50 | let itemResult = this.itemCache[path] 51 | if (!this.itemCache[path]) { 52 | itemResult = await fetch(path) 53 | this.itemCache[path] = itemResult 54 | } 55 | items.push({ 56 | file: itemResult.headers.get("is-dir-listing") !== "true", 57 | name: item, 58 | path: `${folder}/${item}`, 59 | }) 60 | } 61 | return items 62 | } 63 | 64 | // Gets the item specified. Returns null if it is not found. 65 | public async get(fp: string): Promise { 66 | const res = await fetch(`${this.hostname}/${this.alias}/${fp}`) 67 | if (!res.ok) return undefined 68 | return await res.text() 69 | } 70 | 71 | // Queries the start of the name. 72 | public async queryStart(fp: string, start: string): Promise { 73 | const ls = await this.ls(fp) 74 | for (const f of ls) { 75 | if (f.name.startsWith(start)) return f.path 76 | } 77 | } 78 | 79 | // Queries all with a certian start of their name. 80 | public async queryStartAll(fp: string, start: string, limit: number): Promise { 81 | const items = [] 82 | const ls = await this.ls(fp) 83 | for (const f of ls) { 84 | if (f.name.startsWith(start)) { 85 | items.push(f.path) 86 | if (items.length === limit && limit !== 0) return items 87 | } 88 | } 89 | return items 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/githubFs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Defines a very basic filesystem using GitHub. 18 | export default class GitHubFS { 19 | public repo: string 20 | 21 | // Constructs the class. 22 | public constructor(repo: string) { 23 | this.repo = repo 24 | } 25 | 26 | // Lists the folder specified. 27 | public async ls(folder: string): Promise<{ 28 | file: boolean; 29 | path: string; 30 | name: string; 31 | }[]> { 32 | const items: { 33 | file: boolean; 34 | path: string; 35 | name: string; 36 | }[] = [] 37 | const res = await fetch(`https://api.github.com/repos/${this.repo}/contents/${folder}`) 38 | const json = await res.json() 39 | for (const item of json) { 40 | items.push({ 41 | file: item.type !== "dir", 42 | name: item.name, 43 | path: item.path, 44 | }) 45 | } 46 | return items 47 | } 48 | 49 | // Gets the item specified. Returns null if it is not found. 50 | public async get(fp: string): Promise { 51 | const res = await fetch(`https://raw.githubusercontent.com/${this.repo}/master/${fp}`) 52 | if (!res.ok) return undefined 53 | return await res.text() 54 | } 55 | 56 | // Queries the start of the name. 57 | public async queryStart(fp: string, start: string): Promise { 58 | const ls = await this.ls(fp) 59 | for (const f of ls) { 60 | if (f.name.startsWith(start)) return f.path 61 | } 62 | } 63 | 64 | // Queries all with a certian start of their name. 65 | public async queryStartAll(fp: string, start: string, limit: number): Promise { 66 | const items = [] 67 | const ls = await this.ls(fp) 68 | for (const f of ls) { 69 | if (f.name.startsWith(start)) { 70 | items.push(f.path) 71 | if (items.length === limit && limit !== 0) return items 72 | } 73 | } 74 | return items 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/helm.ts: -------------------------------------------------------------------------------- 1 | import helmCache from "./helm_parts/helm_cache" 2 | import { OperatorManager, Quote, fs, helmStatement } from "./helm_parts/utils" 3 | import HelmDocumentParser from "./helm_parts/document_parser" 4 | import HelmCoreParser from "./helm_parts/core_parser" 5 | 6 | export = { helmCache, OperatorManager, Quote, fs, helmStatement, HelmDocumentParser, HelmCoreParser, default: HelmCoreParser } 7 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/helm_parts/core_parser.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Imports needed stuff. 18 | import HelmDocumentParser from "./document_parser" 19 | import { fs } from "./utils" 20 | import { load } from "js-yaml" 21 | import helmCache from "./helm_cache" 22 | import asyncLock from "async-lock" 23 | const lock = new asyncLock() 24 | 25 | // Defines the Helm core parser. 26 | export default class HelmCoreParser { 27 | public context: HelmDocumentParser 28 | public chart: string 29 | public promise: Promise | null> 30 | 31 | // Constructs the class. 32 | public constructor(context: Record, chart: string) { 33 | this.context = new HelmDocumentParser(context) 34 | this.chart = chart 35 | this.promise = this._exec() 36 | } 37 | 38 | // Capitalized all the keys. 39 | private _capAll(records: Record): Record { 40 | const x: Record = {} 41 | for (const r in records) { 42 | const split = r.split("") 43 | x[`${split.shift()!.toUpperCase()}${split.join("").toLowerCase()}`] = records[r] 44 | } 45 | return x 46 | } 47 | 48 | // Handles the Helm folder. 49 | private async _handleFolder(path: string): Promise | null> { 50 | // Defines the unparsed Chard.json (if it exists). 51 | const unparsedChartInformation = await fs.get(`${path}/Chart.yaml`) 52 | if (!unparsedChartInformation) throw new Error("No Chart.yaml found!") 53 | 54 | // Defines the parsed chart file and load in the chart. 55 | const chartYaml = load(unparsedChartInformation) as Record 56 | this.context.context.Chart = this._capAll(chartYaml) 57 | 58 | // Defines the unparsed values.yaml (if it exists). 59 | const unparsedValuesYaml = await fs.get(`${path}/values.yaml`) 60 | if (!unparsedValuesYaml) throw new Error("No values.yaml found!") 61 | 62 | // Loads the values.yaml. 63 | const valuesYaml = load(unparsedValuesYaml) as Record 64 | this.context.context.Values = valuesYaml 65 | 66 | // Fixes for potential bufs. 67 | valuesYaml.cluster = valuesYaml.cluster ? {...valuesYaml.cluster, readinessProbe: ""} : {readinessProbe: ""} 68 | valuesYaml.global = true 69 | 70 | // Sets the release context. 71 | this.context.context.Release = { 72 | Name: "", 73 | Namespace: "", 74 | Service: "Tiller", // This is always Tiller, idk why it's a thing, seems useless to me. 75 | IsUpgrade: false, // This emulates a clean install. 76 | IsInstall: true, 77 | Revision: 1, // No upgrades, clean install! 78 | Time: Math.floor(Date.now() / 1000), 79 | } 80 | 81 | // Initialises the context. 82 | const init = await fs.get(`${path}/templates/_helpers.tpl`) 83 | if (init) this.context.eval(init) 84 | 85 | // Handles each part. 86 | const promises: Promise[] = [] 87 | const kubernetesParts: Record = {} 88 | for (const file of await fs.ls(`${path}/templates`)) { 89 | if (!file.file) continue 90 | promises.push(fs.get(file.path).then(async d => { 91 | await lock.acquire("ctx-lock", () => void(0)) 92 | const ctx = this.context.eval(d!).trim() 93 | if (file.name !== "_helpers.tpl" && ctx !== "") kubernetesParts[file.path] = ctx 94 | })) 95 | } 96 | await Promise.all(promises) 97 | 98 | return kubernetesParts 99 | } 100 | 101 | // Starts execution. 102 | private async _exec(): Promise | null> { 103 | const slashSplit = this.chart.toLowerCase().split("/") 104 | if (slashSplit.length === 1) return null 105 | const repo = helmCache[slashSplit[0]] 106 | if (!repo) return null 107 | for (const item of await repo) { 108 | if (item.name === slashSplit[1] && !item.file) { 109 | // This is the folder we want! Get results from it. 110 | return this._handleFolder(item.path) 111 | } 112 | } 113 | return null 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/helm_parts/document_parser.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { Token, Tokeniser } from "./tokeniser" 18 | import { Quote } from "./utils" 19 | import functions from "./functions/index" 20 | 21 | // The main document parser. 22 | export default class DocumentParser { 23 | // Defines the context. 24 | public context: Record 25 | public templateContext: Record 26 | public variables: Record 27 | 28 | // Constructs the class. 29 | public constructor(context: Record) { 30 | this.context = context 31 | this.templateContext = {} 32 | this.variables = {} 33 | } 34 | 35 | // Handles each specific token. 36 | private _handleToken(token: Token, additionalQuotes: Quote[]): string { 37 | // Some initialisation to get the function and arguments. 38 | const data = token.data.trim() 39 | const args: (string | Quote)[] = [] 40 | 41 | // Handles brackets in the tool. 42 | let dSplit: (string | Quote)[] = [data] 43 | for (;;) { 44 | const results: boolean[] = [] 45 | const newdSplit: (string | Quote)[] = [] 46 | for (const d of dSplit) { 47 | if (typeof d !== "string") { 48 | results.push(true) 49 | newdSplit.push(d) 50 | continue 51 | } 52 | const m = d.match(/\((.+?)\)/) 53 | if (!m) { 54 | results.push(true) 55 | newdSplit.push(d) 56 | continue 57 | } 58 | const remainingData = d.split(m[0]) 59 | const middle = new Quote(this._handleToken(new Token(m[1]), [])) 60 | newdSplit.push(remainingData[0], middle, remainingData[1]) 61 | results.push(false) 62 | } 63 | dSplit = newdSplit 64 | if (results.every(x => x)) break 65 | } 66 | 67 | // Splits the data properly. 68 | for (const d of dSplit) { 69 | if (typeof d === "string") args.push(...d.split(" ")) 70 | else args.push(d) 71 | } 72 | 73 | // Handles quotes. 74 | let quoteParts: { 75 | index: number; 76 | part: string; 77 | }[] = [] 78 | const toQuote: { 79 | index: number; 80 | count: number; 81 | toAdd: Quote; 82 | }[] = [] 83 | for (const a in args) { 84 | if (typeof args[a] === "string") { 85 | const strArg = args[a] as string 86 | if (strArg.startsWith("\"")) { 87 | quoteParts.push({ 88 | index: (a as unknown) as number, 89 | part: strArg.substr(1) as string, 90 | }) 91 | } else if (strArg.endsWith("\"")) { 92 | quoteParts.push({ 93 | index: (a as unknown) as number, 94 | part: strArg.substr(0, strArg.length - 1) as string, 95 | }) 96 | const firstIndex = quoteParts[0].index 97 | toQuote.push({ 98 | index: firstIndex, 99 | count: quoteParts.length, 100 | toAdd: new Quote(quoteParts.join(" ")), 101 | }) 102 | quoteParts = [] 103 | } 104 | } 105 | } 106 | for (const q of toQuote) args.splice(q.index, q.count, q.toAdd) 107 | for (const q of additionalQuotes) args.push(q) 108 | 109 | // Gets the function. 110 | let func = args.shift()! as string 111 | if (((func as unknown) as Quote).text) { 112 | func = ((func as unknown) as Quote).text 113 | } 114 | 115 | // Runs the function. 116 | if (functions[func] === undefined) { 117 | if (func.startsWith(".")) return String(this.helmdef2object(func)) 118 | 119 | // We should return here because even though this may not be fully accurate, it allows for an as accurate as possible result. 120 | return "" 121 | } 122 | const exec = functions[func](this, args, token) 123 | return exec 124 | } 125 | 126 | // Handles the tokens and events relating to them. 127 | public handleTokens(parts: (Token | string)[]): string { 128 | // The document that will be added to. 129 | let document = "" 130 | 131 | // Iterates all the parts. 132 | for (const p of parts) { 133 | if (typeof p === "string") { 134 | // Just reapply this. We do not need to worry about it other than trimming blank lines. 135 | document += p.replace(/\n+$/g, "") 136 | } else { 137 | // It's a token; we need to worry about this. 138 | 139 | // Handles variables. 140 | let variable: string | undefined 141 | const s = p.data.split(" ") 142 | let a = 0 143 | for (;;) { 144 | if (!s[a]) break 145 | if (s[a] === "-") delete s[a] 146 | a++ 147 | } 148 | if (s[1] === ":=") { 149 | variable = s.shift()! 150 | s.shift() 151 | p.data = s.join(" ") 152 | } 153 | 154 | // Handles the tokens. 155 | if (p.data.includes("|")) { 156 | // This includes a pipe. 157 | const pipeSplit = p.data.split("|") 158 | let lastPart: Quote | undefined 159 | for (const part of pipeSplit) { 160 | const newToken = new Token(part, p.inner, p.else) 161 | const a = [] 162 | if (lastPart) a.push(lastPart) 163 | lastPart = new Quote(this._handleToken(newToken, a)) 164 | } 165 | if (variable) this.variables[variable] = lastPart!.text 166 | else document += lastPart!.text 167 | } else { 168 | // There is no pipe. 169 | const tokenRes = this._handleToken(p, []) 170 | if (variable) this.variables[variable] = tokenRes 171 | else document += tokenRes 172 | } 173 | } 174 | } 175 | 176 | // Returns the document. 177 | return document 178 | } 179 | 180 | // Evals a document. The result can then be parsed into the Kubernetes parser. 181 | public eval(document: string): string { 182 | // Reset the variables. 183 | this.variables = {} 184 | 185 | // Gets the tokens and handles them. 186 | return this.handleTokens(new Tokeniser(document).parsed) 187 | } 188 | 189 | // Maps the Helm definition to a JS object. 190 | public helmdef2object(definition: string): any { 191 | // Make it an array and remove any pipes/the first thing which will be whitespace. 192 | definition = definition.split(/\|/g)[0].trim() 193 | const defSplit = definition.split(".") 194 | defSplit.shift() 195 | 196 | // Iterate through the beginning parts. 197 | let currentCtx = this.context 198 | for (const part of defSplit) { 199 | currentCtx = currentCtx[part] 200 | if (currentCtx === undefined) return undefined 201 | } 202 | 203 | // Returns the current context. 204 | return currentCtx 205 | } 206 | 207 | // Processes an argument. 208 | public processArg(data: string | Quote): any { 209 | // If it's a quote, just return it. 210 | if (typeof data !== "string") return data.text 211 | 212 | // Handles variables. 213 | if (data.startsWith("$")) return this.variables[data] 214 | 215 | // Handles Helm Definitions. 216 | if (data.startsWith(".")) return this.helmdef2object(data) 217 | 218 | // Returns the string. 219 | return data 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/helm_parts/functions/b64enc.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import DocumentParser from "../document_parser" 18 | import { Quote } from "../utils" 19 | 20 | export default (parser: DocumentParser, args: (string | Quote)[]): string => btoa(parser.processArg(args[0])) 21 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/helm_parts/functions/default.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import DocumentParser from "../document_parser" 18 | import { Quote } from "../utils" 19 | 20 | export default (parser: DocumentParser, args: (string | Quote)[]): string => { 21 | for (const a of args.reverse()) { 22 | if (a === "-") continue 23 | const argument = parser.processArg(a) 24 | if (argument) return String(argument) 25 | } 26 | 27 | // It can't hit here, but the IDE doesn't know that. 28 | return "" 29 | } 30 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/helm_parts/functions/define.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import DocumentParser from "../document_parser" 18 | import { Quote } from "../utils" 19 | import { Token } from "../tokeniser" 20 | 21 | export default (parser: DocumentParser, args: (string | Quote)[], token: Token): string => { 22 | const full = parser.processArg(args[0]) 23 | const numbers: string[] = [] 24 | for (const ti in token.inner!) { 25 | const t = token.inner![ti] 26 | if (typeof t === "string" && t.trim().startsWith("{{/*")) numbers.push(ti) 27 | } 28 | const newTokens: (string | Token)[] = [] 29 | for (const ti in token.inner!) { 30 | if (!numbers.includes(ti)) newTokens.push(token.inner![Number(ti)]) 31 | } 32 | parser.templateContext[full] = parser.handleTokens(newTokens).trim() 33 | return "" 34 | } 35 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/helm_parts/functions/env.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import DocumentParser from "../document_parser" 18 | import { Quote } from "../utils" 19 | 20 | export default (parser: DocumentParser, args: (string | Quote)[]): string => { 21 | const full = parser.processArg(args[0]) 22 | return `<${full} env>` 23 | } 24 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/helm_parts/functions/if.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import DocumentParser from "../document_parser" 18 | import { Quote, OperatorManager } from "../utils" 19 | import { Token } from "../tokeniser" 20 | 21 | // Processes a condition. Is it true? 22 | const processCondition = (parser: DocumentParser, args: (string | Quote)[]): boolean => { 23 | // Split this condition. 24 | const conditionSplit: (string | Quote)[] = [] 25 | for (const a of args) { 26 | if (a !== "") conditionSplit.push(a) 27 | } 28 | 29 | // Check the operator if it exists. 30 | let operator = conditionSplit.shift()! 31 | 32 | // Not is a special edgecase! It goes up here due to that. 33 | let not = false 34 | 35 | // Does one string contain another? 36 | let contain = true 37 | 38 | // Is this a empty check? 39 | let empty = false 40 | 41 | // Is this a include? 42 | let include = false 43 | 44 | // Check/set what the operator is. Can be eq, ne, lt, gt, and, or, not or a boolean value (ref: https://helm.sh/docs/chart_template_guide/#operators-are-functions) 45 | switch (operator) { 46 | case "eq": { 47 | operator = "===" 48 | break 49 | } 50 | case "ne": { 51 | operator = "!==" 52 | break 53 | } 54 | case "lt": { 55 | operator = "<" 56 | break 57 | } 58 | case "gt": { 59 | operator = ">" 60 | break 61 | } 62 | case "and": { 63 | operator = "&&" 64 | break 65 | } 66 | case "or": { 67 | operator = "||" 68 | break 69 | } 70 | case "not": { 71 | not = true 72 | break 73 | } 74 | case "contains": { 75 | contain = true 76 | break 77 | } 78 | case "semverCompare": { 79 | return false 80 | } 81 | case "empty": { 82 | empty = true 83 | break 84 | } 85 | case "include": { 86 | include = true 87 | break 88 | } 89 | default: { 90 | if (typeof operator === "string" && operator.startsWith(".")) { 91 | // This *should* be the condition. 92 | return Boolean(parser.helmdef2object(operator)) 93 | } else { 94 | // Returns false if it doesn't understand. 95 | return false 96 | } 97 | } 98 | } 99 | 100 | // Each part in a easy to iterate array. Makes the next part a lot easier. 101 | const dataParts: any[] = [] 102 | 103 | // Processes each argument. 104 | for (const p of args) dataParts.push(parser.processArg(p)) 105 | 106 | // Handles include. 107 | if (include) return dataParts[0] 108 | 109 | // If this is a not statement, we only need to worry about the first arg. 110 | if (not) return !Boolean(dataParts[0]) 111 | 112 | // Check if one contains the other. 113 | if (contain) return String(dataParts[0]).includes(dataParts[1]) 114 | 115 | // Check if any of the things are blank. 116 | if (empty) { 117 | empty = false 118 | for (const p of dataParts) { 119 | if (String(p) === "") empty = true 120 | } 121 | return empty 122 | } 123 | 124 | // Get the final result. 125 | let final = true 126 | let last: any = undefined 127 | for (const i of dataParts) { 128 | const currentLast = last 129 | last = i 130 | if (currentLast === undefined) continue 131 | const op = new OperatorManager(last, i, operator) 132 | final = op.call() 133 | } 134 | return final 135 | } 136 | 137 | // Handles the if statement. 138 | export default (parser: DocumentParser, args: (string | Quote)[], token: Token): string => { 139 | // Gets all the elses and the inner of the if statement. 140 | const ifInner = token.inner 141 | const elses = token.else || [] 142 | 143 | // Checks the if statement. 144 | if (processCondition(parser, args)) return parser.handleTokens(ifInner!) 145 | 146 | // Handle elses. 147 | for (const else_ of elses) { 148 | // Recreate the condition into elseQuoteSplit. 149 | const elseSplit = else_.data.trim().split(" ") 150 | elseSplit.shift() 151 | const elseQuoteSplit: (string | Quote)[] = [] 152 | let quoteBuffer: string[] = [] 153 | for (const split of elseSplit) { 154 | if (split.startsWith("\"")) { 155 | if (quoteBuffer.length !== 0) quoteBuffer.push(split.substr(1)) 156 | } else if (split.endsWith("\"")) { 157 | if (quoteBuffer.length === 0) { 158 | elseQuoteSplit.push(split) 159 | } else { 160 | quoteBuffer.push(split.substr(0, split.length - 1)) 161 | elseQuoteSplit.push(new Quote(quoteBuffer.join(" "))) 162 | quoteBuffer = [] 163 | } 164 | } else { 165 | if (quoteBuffer.length === 0) elseQuoteSplit.push(split) 166 | else quoteBuffer.push(split) 167 | } 168 | } 169 | 170 | // Process the argument to see if it is true; if it is, parse this. 171 | elseQuoteSplit.shift() 172 | if (processCondition(parser, elseQuoteSplit)) return parser.handleTokens(else_.inner!) 173 | } 174 | 175 | // Returns a blank string. 176 | return "" 177 | } 178 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/helm_parts/functions/include.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import DocumentParser from "../document_parser" 18 | import { Quote } from "../utils" 19 | 20 | export default (parser: DocumentParser, args: (string | Quote)[]): string => String(typeof args[0] === "string" ? parser.templateContext[args[0]] : parser.templateContext[args[0].text]) 21 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/helm_parts/functions/indent.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import DocumentParser from "../document_parser" 18 | import { Quote } from "../utils" 19 | 20 | export default (parser: DocumentParser, args: (string | Quote)[]): string => { 21 | const toRepeat = " ".repeat(Number(parser.processArg(args[0]))) 22 | const dataSplit = String(parser.processArg(args[1])).split("\n") 23 | for (const part in dataSplit) { 24 | dataSplit[part] = `${toRepeat}${dataSplit[part]}` 25 | } 26 | return `\n${dataSplit.join("\n")}` 27 | } 28 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/helm_parts/functions/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { Token } from "../tokeniser" 18 | import DocumentParser from "../document_parser" 19 | import { Quote } from "../utils" 20 | 21 | import env from "./env" 22 | import uuidv4 from "./uuidv4" 23 | import trimSuffix from "./trimSuffix" 24 | import if_ from "./if" 25 | import range from "./range" 26 | import default_ from "./default" 27 | import quote from "./quote" 28 | import define from "./define" 29 | import template from "./template" 30 | import trunc from "./trunc" 31 | import indent from "./indent" 32 | import toYaml from "./toYaml" 33 | import printf from "./printf" 34 | import include from "./include" 35 | import replace from "./replace" 36 | import b64enc from "./b64enc" 37 | import { randAlphaNum, randNum, randAlpha } from "./randAlphaNum" 38 | 39 | export default { 40 | env, uuidv4, trimSuffix, if: if_, range, default: default_, quote, define, 41 | template, trunc, indent, toYaml, printf, include, replace, b64enc, randAlphaNum, 42 | randNum, randAlpha, 43 | } as Record string> 44 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/helm_parts/functions/printf.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import DocumentParser from "../document_parser" 18 | import { Quote } from "../utils" 19 | import * as printj from "printj" 20 | 21 | export default (parser: DocumentParser, args: (string | Quote)[]): string => { 22 | const formatter = parser.processArg(args.shift()!) 23 | const transformedArgs: any[] = [] 24 | for (const a of args) transformedArgs.push(parser.processArg(a)) 25 | try { 26 | // You can't inline try and catch on JS, I tried (no pun intended!) 27 | return printj.sprintf(formatter, ...transformedArgs) 28 | } catch (_) { 29 | // Something is wrong with the formatter, this is not our issue. 30 | return "" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/helm_parts/functions/quote.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import DocumentParser from "../document_parser" 18 | import { Quote } from "../utils" 19 | 20 | export default (parser: DocumentParser, args: (string | Quote)[]): string => { 21 | const full = parser.processArg(args[0]) 22 | return JSON.stringify(String(full)) 23 | } 24 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/helm_parts/functions/randAlphaNum.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | export const randAlphaNum = (): string => "" 18 | export const randAlpha = (): string => "" 19 | export const randNum = (): string => "" 20 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/helm_parts/functions/range.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import DocumentParser from "../document_parser" 18 | import { Quote } from "../utils" 19 | import { Token } from "../tokeniser" 20 | 21 | export default (parser: DocumentParser, args: (string | Quote)[], token: Token): string => { 22 | // Defines all the variables. 23 | const variables = [] 24 | 25 | // Handles parsing the variables. 26 | if (args.length !== 1) { 27 | // Gets all the variables. 28 | for (;;) { 29 | if (typeof args[0] === "string") { 30 | let c = args[0] as string 31 | if (c.endsWith(",")) c = c.substr(0, c.length - 1) 32 | if (c.startsWith("$")) { 33 | args.shift() 34 | variables.push(c) 35 | } else { 36 | break 37 | } 38 | } else { 39 | break 40 | } 41 | } 42 | 43 | // Shift out the ":=" 44 | args.shift() 45 | } 46 | 47 | // Iterates the object. 48 | const parts = [] 49 | const obj = parser.processArg(args[0]) || {} as any 50 | for (const k in obj) { 51 | const i = obj[k] 52 | if (variables.length === 1) { 53 | parser.variables[variables[0]] = i 54 | } else if (variables.length === 2) { 55 | parser.variables[variables[0]] = k 56 | parser.variables[variables[1]] = i 57 | } 58 | parts.push(parser.handleTokens(token.inner!)) 59 | } 60 | 61 | // Returns all the parts joined. 62 | return parts.join("") 63 | } 64 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/helm_parts/functions/replace.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import DocumentParser from "../document_parser" 18 | import { Quote } from "../utils" 19 | import escapeStringRegexp from "escape-string-regexp" 20 | 21 | export default (parser: DocumentParser, args: (string | Quote)[]): string => { 22 | const a = parser.processArg(args[0]) 23 | const b = parser.processArg(args[1]) 24 | const c = parser.processArg(args[2]) 25 | return c.replace(new RegExp(escapeStringRegexp(a), "g"), b) 26 | } 27 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/helm_parts/functions/template.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import DocumentParser from "../document_parser" 18 | import { Quote } from "../utils" 19 | 20 | export default (parser: DocumentParser, args: (string | Quote)[]): string => String(parser.templateContext[parser.processArg(args[0])]) 21 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/helm_parts/functions/toYaml.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import DocumentParser from "../document_parser" 18 | import { Quote } from "../utils" 19 | import { dump } from "js-yaml" 20 | 21 | export default (parser: DocumentParser, args: (string | Quote)[]): string => { 22 | const a = parser.processArg(args[0]) 23 | if (a === "") return "" 24 | if (typeof a === "string") return dump(a) 25 | if (typeof a === "boolean") return String(a) 26 | if (!a) return "null" 27 | let d = "" 28 | if (Array.isArray(a)) { 29 | for (const x of a) { 30 | d += `- ${dump(x)}\n` 31 | } 32 | return d 33 | } 34 | for (const x in a) { 35 | let yamlDump = dump(a[x]).trim() 36 | if (a[x] instanceof Object) yamlDump = `\n${yamlDump.match(/ +/) ? yamlDump.match(/ +/)![0] : ""}${yamlDump}` 37 | d += `${dump(x).trim()}: ${yamlDump}\n` 38 | } 39 | return d 40 | } 41 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/helm_parts/functions/trimSuffix.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import DocumentParser from "../document_parser" 18 | import { Quote } from "../utils" 19 | import escapeStringRegexp from "escape-string-regexp" 20 | 21 | export default (parser: DocumentParser, args: (string | Quote)[]): string => { 22 | const a = parser.processArg(args[0]) 23 | const b = parser.processArg(args[args.length - 1]) 24 | const regex = new RegExp(`(^${escapeStringRegexp(a)}+)|(${escapeStringRegexp(a)}+$)`, "g") 25 | return b.replace(regex, "").replace(/(^\")|(\"$)/g, "") 26 | } 27 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/helm_parts/functions/trunc.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import DocumentParser from "../document_parser" 18 | import { Quote } from "../utils" 19 | 20 | export default (parser: DocumentParser, args: (string | Quote)[]): string => { 21 | const trunced = String( 22 | parser.processArg(args[1]) 23 | ).substr(0, Number(parser.processArg(args[0]))) 24 | return trunced 25 | } 26 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/helm_parts/functions/uuidv4.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | export default (): string => "" 18 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/helm_parts/helm_cache.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Imports the GitHub filesystem. 18 | import { fs } from "./utils" 19 | 20 | // Cache stable and incubator for any future reference. 21 | const helmCache = { 22 | stable: fs.ls("stable"), 23 | incubator: fs.ls("incubator"), 24 | } as Record | undefined> 29 | export default helmCache 30 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/helm_parts/tokeniser.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { helmStatement } from "./utils" 18 | 19 | // These statements require a end statement. 20 | const requireEnd = [ 21 | "if", 22 | "range", 23 | "define" 24 | ] 25 | 26 | // Defines the token. 27 | export class Token { 28 | public data: string 29 | public inner?: (Token | string)[] 30 | public else?: Token[] 31 | 32 | public constructor(data: string, inner?: (Token | string)[], _else?: Token[]) { 33 | this.data = data 34 | this.inner = inner 35 | this.else = _else 36 | } 37 | } 38 | 39 | // Handles tokenisation. 40 | export class Tokeniser { 41 | public document: string 42 | public parsed: (Token | string)[] 43 | 44 | public constructor(document: string) { 45 | this.document = document 46 | this.parsed = [] 47 | this._handle(document, this._matchAllArray(document), this.parsed) 48 | } 49 | 50 | // Matches all statements into a array. 51 | private _matchAllArray(document: string) { 52 | const all = document.matchAll(helmStatement) 53 | const arr = [] 54 | for (;;) { 55 | const r = all.next() 56 | if (r.done) break 57 | arr.push(r.value) 58 | } 59 | return arr 60 | } 61 | 62 | // Finds the end statement. 63 | private _manageEnd(matches: RegExpMatchArray[]): RegExpMatchArray[] | undefined { 64 | // Tells the parser the level. 65 | let level = 1 66 | 67 | // Contains either 1 end, or else's and a end. 68 | const returned: RegExpMatchArray[] = [] 69 | 70 | // Iterates all of the matches. 71 | for (;;) { 72 | const m = matches.shift() 73 | if (!m) break 74 | const t = m[1].split(/ +/g)[0].toLowerCase() 75 | if (requireEnd.includes(t)) { 76 | level++ 77 | } else if (t === "else") { 78 | if (level === 1) returned.push(m) 79 | } else if (t === "end") { 80 | if (level === 1) { 81 | returned.push(m) 82 | return returned 83 | } 84 | level-- 85 | } 86 | } 87 | } 88 | 89 | // The main parser. 90 | private _handle(document: string, all: RegExpMatchArray[], parsed: (string | Token)[]) { 91 | // The index that has been parsed. 92 | let doneIndex = 0 93 | 94 | for (;;) { 95 | // Get the match (it needs to be done like this since the array changes size). 96 | const match = all.shift() 97 | if (!match) break 98 | 99 | // Is the match a comment? Discard it if so. 100 | if (match[1].startsWith("/*")) continue 101 | 102 | // Get any parts of the document before this but after the last match and put them in the array. 103 | const b = document.substr(doneIndex, match.index! - doneIndex) 104 | if (b !== "") parsed.push(b) 105 | 106 | // Make the done index the index of this match plus the match length minus 1 since indexes start from 0. 107 | doneIndex = match.index! + match[0].length 108 | 109 | // If this is a if/range statement, we need to do some special stuff to get the end. 110 | if (requireEnd.includes(match[1].split(" ")[0].toLowerCase())) { 111 | // Gets the end and any elses (right now, this variable name is misleading - in a few lines it won't be). 112 | const elses = this._manageEnd(all) 113 | 114 | // If the end is not found, throw a error. 115 | if (!elses) throw new Error(`${match[0]} - End not found!`) 116 | 117 | // Gets the end. 118 | const end = elses.pop()! 119 | 120 | if (elses.length === 0) { 121 | // Pushes the token with the if statement (NO elses though). 122 | 123 | // Start: Match index + the match length. 124 | const start = match.index! + match[0].length 125 | 126 | const innerDoc = document.substr( 127 | start, 128 | 129 | // Length: End index minus the start. 130 | end.index! - start 131 | ) 132 | const innerParsed: (string | Token)[] = [] 133 | this._handle(innerDoc, this._matchAllArray(innerDoc), innerParsed) 134 | parsed.push(new Token(match[1], innerParsed)) 135 | } else { 136 | // Handle any else statements there. 137 | 138 | // Deal with the initialisation of the token firstly. 139 | const ifTokenStart = match.index! + match[0].length 140 | const ifTokenInner = document.substr( 141 | ifTokenStart, 142 | 143 | // Length: First else index minus the start. 144 | elses[0].index! - ifTokenStart 145 | ) 146 | const ifInnerParsed: (string | Token)[] = [] 147 | this._handle(ifTokenInner, this._matchAllArray(ifTokenInner), ifInnerParsed) 148 | const token = new Token(match[1], ifInnerParsed, []) 149 | 150 | // Alright! Lets handle the else statements. 151 | for (;;) { 152 | // Get the else statement. 153 | const else_ = elses.shift() 154 | if (!else_) break 155 | 156 | // Gets the start index of the else statement. 157 | const elseStart = else_.index! + else_[0].length 158 | 159 | // Get the length of the else statement. 160 | const elseLen = (elses[0] === undefined ? end.index! : elses[0].index!) - elseStart 161 | 162 | // Get the part of the document relating to this statement. 163 | const elseDoc = document.substr(elseStart, elseLen) 164 | 165 | // Create the parsed inner. 166 | const innerParsed: (string | Token)[] = [] 167 | this._handle(elseDoc, this._matchAllArray(elseDoc), innerParsed) 168 | token.else!.push(new Token(else_[1], innerParsed)) 169 | } 170 | 171 | // Push the token to the array. 172 | parsed.push(token) 173 | } 174 | 175 | // Set the done index to after the end (we don't want any inner contents being recaptured). 176 | doneIndex = end.index! + end[0].length 177 | } else { 178 | // This is just a token. Handle this. 179 | parsed.push(new Token(match[1])) 180 | } 181 | } 182 | 183 | // Handle the remainder of the document. 184 | const remainder = document.substr(doneIndex) 185 | if (remainder !== "") parsed.push(remainder) 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/helm_parts/utils.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Defines the mirror hostname. 18 | const useGitHttpMirrorFs = process.env.USE_GIT_HTTP_MIRROR_FS 19 | const mirrorHostname = useGitHttpMirrorFs === "true" ? "http://localhost:8001" : null 20 | 21 | // Imports needed stuff. 22 | import GitHubFS from "../githubFs" 23 | import GitHTTPMirrorFS from "../gitHttpMirrorFs" 24 | 25 | // The operator manager. Allows for operations to safely be evaled between 2 objects. 26 | export class OperatorManager { 27 | public a: any 28 | public b: any 29 | public operator: string 30 | 31 | public constructor(a: any, b: any, operator: string) { 32 | this.a = a 33 | this.b = b 34 | this.operator = operator 35 | } 36 | 37 | public call(): boolean { 38 | return eval(`this.a ${this.operator} this.b`) as boolean 39 | } 40 | } 41 | 42 | // A small class to define a quote. 43 | export class Quote { 44 | public text: string 45 | public constructor(text: string) { 46 | this.text = text 47 | } 48 | } 49 | 50 | // A statement in Helm. 51 | export const helmStatement = /{{[ -]*((?:[^}]|\n)+)[ -]*}}/g 52 | 53 | // Defines the filesystem for the Helm Charts official repository. 54 | export const fs = mirrorHostname ? new GitHTTPMirrorFS("helm", mirrorHostname) : new GitHubFS("helm/charts") 55 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/kubernetes.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Imports the needed stuff. 18 | import k8sData from "../descriptions" 19 | import Labeler from "./labeler" 20 | import { load } from "js-yaml" 21 | 22 | // Defines the data structure. 23 | class KVRecursiveRecord { 24 | public key: string 25 | public value: string | undefined 26 | public recursive?: KVRecursiveRecord[] 27 | 28 | public constructor(key: string, value: string | undefined) { 29 | this.key = key 30 | this.value = value 31 | } 32 | } 33 | 34 | // Parses the Kubernetes data. 35 | const p = (data: string | Record | undefined, keys?: string[], kind?: string): KVRecursiveRecord[] | undefined => { 36 | // If not keys, make the keys array. 37 | if (!keys) keys = [] 38 | 39 | // Return here. 40 | if (!data) return 41 | 42 | // Defines the parsed data. 43 | let parsedData: Record 44 | if (typeof data === "string") { 45 | try { 46 | parsedData = load(data) as Record 47 | if (!parsedData || parsedData.constructor !== Object) throw new Error() 48 | } catch (_) { 49 | // Returns nothing. 50 | return 51 | } 52 | } else { 53 | parsedData = data 54 | } 55 | 56 | // Defines the result. 57 | const result: KVRecursiveRecord[] = [] 58 | 59 | // Creates a labeler with the base. 60 | const l = new Labeler(k8sData.base) 61 | 62 | // Imports the children. 63 | l.importChildren({ 64 | children: { 65 | spec: { 66 | children: (k8sData as any)[kind || parsedData.kind] || {}, 67 | }, 68 | }, 69 | }) 70 | 71 | // Handles the label. 72 | for (const k in parsedData) { 73 | // The array of the JSON path (we clone it because it is shared). 74 | const keyPlus = keys.slice() 75 | 76 | // Push the current key to the JSON path. 77 | keyPlus.push(k) 78 | 79 | if (!parsedData[k] || parsedData[k].constructor !== Object) { 80 | // This is not a object, handle this here. 81 | result.push(new KVRecursiveRecord(k, l.getLabel(keyPlus))) 82 | } else { 83 | // This is a object, lets be recursive. 84 | const kv = new KVRecursiveRecord(k, l.getLabel(keyPlus)) 85 | kv.recursive = p(parsedData[k], keyPlus, kind || parsedData.kind) 86 | result.push(kv) 87 | } 88 | } 89 | 90 | // Returns all the KV bits. 91 | if (result.length === 0) return 92 | return result 93 | } 94 | 95 | export default p 96 | -------------------------------------------------------------------------------- /src/kubernetes-tool/utils/labeler.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 DigitalOcean 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Defines a circular type. 18 | interface LabelValue { 19 | // Base is the base name, 20 | base?: string; 21 | 22 | // Children is the children arguments. 23 | children?: LabelValueObject; 24 | } 25 | interface LabelValueObject extends Record {} 26 | 27 | // Handles labeling. 28 | export default class Labeler { 29 | // Defines the labels. 30 | public labels: LabelValue 31 | 32 | // Initialises the labeler. 33 | public constructor(base?: LabelValue) { 34 | this.labels = base || {} 35 | if (!this.labels.children) this.labels.children = {} 36 | } 37 | 38 | // Imports any children. 39 | public importChildren(object: LabelValue, path?: string[]): void { 40 | // If path does not exist, make the path array. 41 | if (!path) path = [] 42 | 43 | // Gets the relevant parent object. 44 | let parentObject = this.labels 45 | 46 | // Iterate through the path. 47 | for (const p of path) { 48 | // Ensure "children" exists on the parent object. 49 | if (!parentObject.children) parentObject.children = {} 50 | 51 | // Gets the item from the child. 52 | let child = parentObject.children[p] 53 | 54 | // If the item doesn't exist, create it. 55 | if (!child) { 56 | child = {} 57 | parentObject.children[p] = child 58 | } 59 | 60 | // Set the parent object to this. 61 | parentObject = child 62 | } 63 | 64 | // Ensure "children" exists on the final object. 65 | if (!parentObject.children) parentObject.children = {} 66 | 67 | // Handles all children. 68 | const o = object.children || {} 69 | for (const key in o) { 70 | // Gets the child. 71 | const child = o[key] 72 | 73 | // Get the relevant object from the parent. If it doesn't exist, make it. 74 | let relevant = parentObject.children[key] 75 | if (!relevant) { 76 | // The child does not exist. It needs to be made. 77 | relevant = {} 78 | parentObject.children[key] = relevant 79 | } 80 | 81 | // If the child has a base, we need to put that in the relevant key. 82 | if (child.base) relevant.base = child.base 83 | 84 | // Check if this child has any children. 85 | if (child.children) { 86 | // Makes sure the children key exists. 87 | if (!relevant.children) relevant.children = {} 88 | 89 | // Gets the true path. 90 | const truePath = path.slice() 91 | truePath.push(key) 92 | 93 | // Handles the children. 94 | this.importChildren(child, truePath) 95 | } 96 | } 97 | } 98 | 99 | // Gets the relevant label. 100 | public getLabel(path: string[]): string | undefined { 101 | // Gets the last label. 102 | let lastLabel: string | undefined 103 | 104 | // Gets the last object. 105 | let lastObject: LabelValueObject = this.labels.children || {} 106 | 107 | // Handles each part of the path. 108 | for (const p of path) { 109 | // Gets the key from the last object. 110 | const newItem: LabelValue | undefined = lastObject[p] 111 | 112 | // If last object is no more, return here. 113 | if (!newItem) return 114 | 115 | // Sets last object. 116 | lastObject = newItem.children || {} 117 | 118 | // Set the last label to this base. 119 | lastLabel = newItem.base 120 | } 121 | 122 | // Returns the last label. 123 | return lastLabel 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/static/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: / 3 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2020", 5 | "allowJs": true, 6 | "noEmit": false, 7 | "noImplicitAny": true, 8 | "strict": true, 9 | "esModuleInterop": true, 10 | "resolveJsonModule": true 11 | }, 12 | "include": [ 13 | "src/**/*" 14 | ] 15 | } 16 | --------------------------------------------------------------------------------