├── .eslintrc.json ├── .gitattributes ├── .github ├── pull_request_template.md └── workflows │ ├── auto-approve.yml │ ├── automerge.yml │ ├── build.yml │ ├── pull-request-lint.yml │ ├── release.yml │ └── upgrade-master.yml ├── .gitignore ├── .gitpod.yml ├── .mergify.yml ├── .npmignore ├── .projen ├── deps.json ├── files.json └── tasks.json ├── .projenrc.js ├── API.md ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── images ├── ks3cluster.pptx ├── ks3clusterconsume.png ├── ks3clustercreatecluster.png └── ks3clusterdeploy.png ├── package.json ├── src ├── index.ts └── integ.default.ts ├── test ├── __snapshots__ │ └── integ.snapshot.test.ts.snap ├── cluster.test.ts └── integ.snapshot.test.ts ├── tsconfig.dev.json ├── version.json └── yarn.lock /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jest": true, 4 | "node": true 5 | }, 6 | "root": true, 7 | "plugins": [ 8 | "@typescript-eslint", 9 | "import" 10 | ], 11 | "parser": "@typescript-eslint/parser", 12 | "parserOptions": { 13 | "ecmaVersion": 2018, 14 | "sourceType": "module", 15 | "project": "./tsconfig.dev.json" 16 | }, 17 | "extends": [ 18 | "plugin:import/typescript" 19 | ], 20 | "settings": { 21 | "import/parsers": { 22 | "@typescript-eslint/parser": [ 23 | ".ts", 24 | ".tsx" 25 | ] 26 | }, 27 | "import/resolver": { 28 | "node": {}, 29 | "typescript": { 30 | "project": "./tsconfig.dev.json", 31 | "alwaysTryTypes": true 32 | } 33 | } 34 | }, 35 | "ignorePatterns": [ 36 | "*.js", 37 | "!.projenrc.js", 38 | "*.d.ts", 39 | "node_modules/", 40 | "*.generated.ts", 41 | "coverage" 42 | ], 43 | "rules": { 44 | "indent": [ 45 | "off" 46 | ], 47 | "@typescript-eslint/indent": [ 48 | "error", 49 | 2 50 | ], 51 | "quotes": [ 52 | "error", 53 | "single", 54 | { 55 | "avoidEscape": true 56 | } 57 | ], 58 | "comma-dangle": [ 59 | "error", 60 | "always-multiline" 61 | ], 62 | "comma-spacing": [ 63 | "error", 64 | { 65 | "before": false, 66 | "after": true 67 | } 68 | ], 69 | "no-multi-spaces": [ 70 | "error", 71 | { 72 | "ignoreEOLComments": false 73 | } 74 | ], 75 | "array-bracket-spacing": [ 76 | "error", 77 | "never" 78 | ], 79 | "array-bracket-newline": [ 80 | "error", 81 | "consistent" 82 | ], 83 | "object-curly-spacing": [ 84 | "error", 85 | "always" 86 | ], 87 | "object-curly-newline": [ 88 | "error", 89 | { 90 | "multiline": true, 91 | "consistent": true 92 | } 93 | ], 94 | "object-property-newline": [ 95 | "error", 96 | { 97 | "allowAllPropertiesOnSameLine": true 98 | } 99 | ], 100 | "keyword-spacing": [ 101 | "error" 102 | ], 103 | "brace-style": [ 104 | "error", 105 | "1tbs", 106 | { 107 | "allowSingleLine": true 108 | } 109 | ], 110 | "space-before-blocks": [ 111 | "error" 112 | ], 113 | "curly": [ 114 | "error", 115 | "multi-line", 116 | "consistent" 117 | ], 118 | "@typescript-eslint/member-delimiter-style": [ 119 | "error" 120 | ], 121 | "semi": [ 122 | "error", 123 | "always" 124 | ], 125 | "max-len": [ 126 | "error", 127 | { 128 | "code": 150, 129 | "ignoreUrls": true, 130 | "ignoreStrings": true, 131 | "ignoreTemplateLiterals": true, 132 | "ignoreComments": true, 133 | "ignoreRegExpLiterals": true 134 | } 135 | ], 136 | "quote-props": [ 137 | "error", 138 | "consistent-as-needed" 139 | ], 140 | "@typescript-eslint/no-require-imports": [ 141 | "error" 142 | ], 143 | "import/no-extraneous-dependencies": [ 144 | "error", 145 | { 146 | "devDependencies": [ 147 | "**/test/**", 148 | "**/build-tools/**" 149 | ], 150 | "optionalDependencies": false, 151 | "peerDependencies": true 152 | } 153 | ], 154 | "import/no-unresolved": [ 155 | "error" 156 | ], 157 | "import/order": [ 158 | "warn", 159 | { 160 | "groups": [ 161 | "builtin", 162 | "external" 163 | ], 164 | "alphabetize": { 165 | "order": "asc", 166 | "caseInsensitive": true 167 | } 168 | } 169 | ], 170 | "no-duplicate-imports": [ 171 | "error" 172 | ], 173 | "no-shadow": [ 174 | "off" 175 | ], 176 | "@typescript-eslint/no-shadow": [ 177 | "error" 178 | ], 179 | "key-spacing": [ 180 | "error" 181 | ], 182 | "no-multiple-empty-lines": [ 183 | "error" 184 | ], 185 | "@typescript-eslint/no-floating-promises": [ 186 | "error" 187 | ], 188 | "no-return-await": [ 189 | "off" 190 | ], 191 | "@typescript-eslint/return-await": [ 192 | "error" 193 | ], 194 | "no-trailing-spaces": [ 195 | "error" 196 | ], 197 | "dot-notation": [ 198 | "error" 199 | ], 200 | "no-bitwise": [ 201 | "error" 202 | ], 203 | "@typescript-eslint/member-ordering": [ 204 | "error", 205 | { 206 | "default": [ 207 | "public-static-field", 208 | "public-static-method", 209 | "protected-static-field", 210 | "protected-static-method", 211 | "private-static-field", 212 | "private-static-method", 213 | "field", 214 | "constructor", 215 | "method" 216 | ] 217 | } 218 | ] 219 | }, 220 | "overrides": [ 221 | { 222 | "files": [ 223 | ".projenrc.js" 224 | ], 225 | "rules": { 226 | "@typescript-eslint/no-require-imports": "off", 227 | "import/no-extraneous-dependencies": "off" 228 | } 229 | } 230 | ] 231 | } 232 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | *.snap linguist-generated 4 | /.eslintrc.json linguist-generated 5 | /.gitattributes linguist-generated 6 | /.github/pull_request_template.md linguist-generated 7 | /.github/workflows/auto-approve.yml linguist-generated 8 | /.github/workflows/build.yml linguist-generated 9 | /.github/workflows/pull-request-lint.yml linguist-generated 10 | /.github/workflows/release.yml linguist-generated 11 | /.github/workflows/upgrade-master.yml linguist-generated 12 | /.gitignore linguist-generated 13 | /.gitpod.yml linguist-generated 14 | /.mergify.yml linguist-generated 15 | /.npmignore linguist-generated 16 | /.projen/** linguist-generated 17 | /.projen/deps.json linguist-generated 18 | /.projen/files.json linguist-generated 19 | /.projen/tasks.json linguist-generated 20 | /API.md linguist-generated 21 | /LICENSE linguist-generated 22 | /package.json linguist-generated 23 | /tsconfig.dev.json linguist-generated 24 | /yarn.lock linguist-generated -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Fixes # -------------------------------------------------------------------------------- /.github/workflows/auto-approve.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | name: auto-approve 4 | on: 5 | pull_request_target: 6 | types: 7 | - labeled 8 | - opened 9 | - synchronize 10 | - reopened 11 | - ready_for_review 12 | jobs: 13 | approve: 14 | runs-on: ubuntu-latest 15 | permissions: 16 | pull-requests: write 17 | if: contains(github.event.pull_request.labels.*.name, 'auto-approve') && (github.event.pull_request.user.login == 'mreferre' || github.event.pull_request.user.login == 'pahud') 18 | steps: 19 | - uses: hmarr/auto-approve-action@v2.2.1 20 | with: 21 | github-token: ${{ secrets.GITHUB_TOKEN }} 22 | -------------------------------------------------------------------------------- /.github/workflows/automerge.yml: -------------------------------------------------------------------------------- 1 | name: Automerge 2 | on: 3 | pull_request: 4 | types: 5 | - labeled 6 | - unlabeled 7 | - synchronize 8 | - opened 9 | - edited 10 | - ready_for_review 11 | - reopened 12 | - unlocked 13 | pull_request_review: 14 | types: 15 | - submitted 16 | check_suite: 17 | types: 18 | - completed 19 | status: {} 20 | jobs: 21 | automerge: 22 | runs-on: ubuntu-latest 23 | steps: 24 | - name: automerge 25 | uses: pascalgn/automerge-action@v0.13.1 26 | if: contains(github.event.pull_request.labels.*.name, 'auto-merge') 27 | env: 28 | GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" 29 | MERGE_LABELS: "auto-merge,!work in progress" 30 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | name: build 4 | on: 5 | pull_request: {} 6 | workflow_dispatch: {} 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | contents: write 12 | outputs: 13 | self_mutation_happened: ${{ steps.self_mutation.outputs.self_mutation_happened }} 14 | env: 15 | CI: "true" 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v3 19 | with: 20 | ref: ${{ github.event.pull_request.head.ref }} 21 | repository: ${{ github.event.pull_request.head.repo.full_name }} 22 | - name: Install dependencies 23 | run: yarn install --check-files 24 | - name: build 25 | run: npx projen build 26 | - name: Find mutations 27 | id: self_mutation 28 | run: |- 29 | git add . 30 | git diff --staged --patch --exit-code > .repo.patch || echo "self_mutation_happened=true" >> $GITHUB_OUTPUT 31 | - name: Upload patch 32 | if: steps.self_mutation.outputs.self_mutation_happened 33 | uses: actions/upload-artifact@v3 34 | with: 35 | name: .repo.patch 36 | path: .repo.patch 37 | - name: Fail build on mutation 38 | if: steps.self_mutation.outputs.self_mutation_happened 39 | run: |- 40 | echo "::error::Files were changed during build (see build log). If this was triggered from a fork, you will need to update your branch." 41 | cat .repo.patch 42 | exit 1 43 | - name: Backup artifact permissions 44 | run: cd dist && getfacl -R . > permissions-backup.acl 45 | continue-on-error: true 46 | - name: Upload artifact 47 | uses: actions/upload-artifact@v3 48 | with: 49 | name: build-artifact 50 | path: dist 51 | container: 52 | image: jsii/superchain:1-buster-slim 53 | self-mutation: 54 | needs: build 55 | runs-on: ubuntu-latest 56 | permissions: 57 | contents: write 58 | if: always() && needs.build.outputs.self_mutation_happened && !(github.event.pull_request.head.repo.full_name != github.repository) 59 | steps: 60 | - name: Checkout 61 | uses: actions/checkout@v3 62 | with: 63 | token: ${{ secrets.PROJEN_GITHUB_TOKEN }} 64 | ref: ${{ github.event.pull_request.head.ref }} 65 | repository: ${{ github.event.pull_request.head.repo.full_name }} 66 | - name: Download patch 67 | uses: actions/download-artifact@v3 68 | with: 69 | name: .repo.patch 70 | path: ${{ runner.temp }} 71 | - name: Apply patch 72 | run: '[ -s ${{ runner.temp }}/.repo.patch ] && git apply ${{ runner.temp }}/.repo.patch || echo "Empty patch. Skipping."' 73 | - name: Set git identity 74 | run: |- 75 | git config user.name "github-actions" 76 | git config user.email "github-actions@github.com" 77 | - name: Push changes 78 | run: |2- 79 | git add . 80 | git commit -s -m "chore: self mutation" 81 | git push origin HEAD:${{ github.event.pull_request.head.ref }} 82 | package-js: 83 | needs: build 84 | runs-on: ubuntu-latest 85 | permissions: {} 86 | if: "! needs.build.outputs.self_mutation_happened" 87 | steps: 88 | - uses: actions/setup-node@v3 89 | with: 90 | node-version: 14.x 91 | - name: Download build artifacts 92 | uses: actions/download-artifact@v3 93 | with: 94 | name: build-artifact 95 | path: dist 96 | - name: Restore build artifact permissions 97 | run: cd dist && setfacl --restore=permissions-backup.acl 98 | continue-on-error: true 99 | - name: Prepare Repository 100 | run: mv dist .repo 101 | - name: Install Dependencies 102 | run: cd .repo && yarn install --check-files --frozen-lockfile 103 | - name: Create js artifact 104 | run: cd .repo && npx projen package:js 105 | - name: Collect js Artifact 106 | run: mv .repo/dist dist 107 | package-python: 108 | needs: build 109 | runs-on: ubuntu-latest 110 | permissions: {} 111 | if: "! needs.build.outputs.self_mutation_happened" 112 | steps: 113 | - uses: actions/setup-node@v3 114 | with: 115 | node-version: 14.x 116 | - uses: actions/setup-python@v4 117 | with: 118 | python-version: 3.x 119 | - name: Download build artifacts 120 | uses: actions/download-artifact@v3 121 | with: 122 | name: build-artifact 123 | path: dist 124 | - name: Restore build artifact permissions 125 | run: cd dist && setfacl --restore=permissions-backup.acl 126 | continue-on-error: true 127 | - name: Prepare Repository 128 | run: mv dist .repo 129 | - name: Install Dependencies 130 | run: cd .repo && yarn install --check-files --frozen-lockfile 131 | - name: Create python artifact 132 | run: cd .repo && npx projen package:python 133 | - name: Collect python Artifact 134 | run: mv .repo/dist dist 135 | -------------------------------------------------------------------------------- /.github/workflows/pull-request-lint.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | name: pull-request-lint 4 | on: 5 | pull_request_target: 6 | types: 7 | - labeled 8 | - opened 9 | - synchronize 10 | - reopened 11 | - ready_for_review 12 | - edited 13 | jobs: 14 | validate: 15 | name: Validate PR title 16 | runs-on: ubuntu-latest 17 | permissions: 18 | pull-requests: write 19 | steps: 20 | - uses: amannn/action-semantic-pull-request@v4.5.0 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | with: 24 | types: |- 25 | feat 26 | fix 27 | chore 28 | requireScope: false 29 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | name: release 4 | on: 5 | push: 6 | branches: 7 | - master 8 | workflow_dispatch: {} 9 | jobs: 10 | release: 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: write 14 | outputs: 15 | latest_commit: ${{ steps.git_remote.outputs.latest_commit }} 16 | env: 17 | CI: "true" 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v3 21 | with: 22 | fetch-depth: 0 23 | - name: Set git identity 24 | run: |- 25 | git config user.name "github-actions" 26 | git config user.email "github-actions@github.com" 27 | - name: Install dependencies 28 | run: yarn install --check-files --frozen-lockfile 29 | - name: release 30 | run: npx projen release 31 | - name: Check for new commits 32 | id: git_remote 33 | run: echo "latest_commit=$(git ls-remote origin -h ${{ github.ref }} | cut -f1)" >> $GITHUB_OUTPUT 34 | - name: Backup artifact permissions 35 | if: ${{ steps.git_remote.outputs.latest_commit == github.sha }} 36 | run: cd dist && getfacl -R . > permissions-backup.acl 37 | continue-on-error: true 38 | - name: Upload artifact 39 | if: ${{ steps.git_remote.outputs.latest_commit == github.sha }} 40 | uses: actions/upload-artifact@v3 41 | with: 42 | name: build-artifact 43 | path: dist 44 | container: 45 | image: jsii/superchain:1-buster-slim 46 | release_github: 47 | name: Publish to GitHub Releases 48 | needs: release 49 | runs-on: ubuntu-latest 50 | permissions: 51 | contents: write 52 | if: needs.release.outputs.latest_commit == github.sha 53 | steps: 54 | - uses: actions/setup-node@v3 55 | with: 56 | node-version: 14.x 57 | - name: Download build artifacts 58 | uses: actions/download-artifact@v3 59 | with: 60 | name: build-artifact 61 | path: dist 62 | - name: Restore build artifact permissions 63 | run: cd dist && setfacl --restore=permissions-backup.acl 64 | continue-on-error: true 65 | - name: Prepare Repository 66 | run: mv dist .repo 67 | - name: Collect GitHub Metadata 68 | run: mv .repo/dist dist 69 | - name: Release 70 | env: 71 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 72 | GITHUB_REPOSITORY: ${{ github.repository }} 73 | GITHUB_REF: ${{ github.ref }} 74 | run: errout=$(mktemp); gh release create $(cat dist/releasetag.txt) -R $GITHUB_REPOSITORY -F dist/changelog.md -t $(cat dist/releasetag.txt) --target $GITHUB_REF 2> $errout && true; exitcode=$?; if [ $exitcode -ne 0 ] && ! grep -q "Release.tag_name already exists" $errout; then cat $errout; exit $exitcode; fi 75 | release_npm: 76 | name: Publish to npm 77 | needs: release 78 | runs-on: ubuntu-latest 79 | permissions: 80 | contents: read 81 | if: needs.release.outputs.latest_commit == github.sha 82 | steps: 83 | - uses: actions/setup-node@v3 84 | with: 85 | node-version: 14.x 86 | - name: Download build artifacts 87 | uses: actions/download-artifact@v3 88 | with: 89 | name: build-artifact 90 | path: dist 91 | - name: Restore build artifact permissions 92 | run: cd dist && setfacl --restore=permissions-backup.acl 93 | continue-on-error: true 94 | - name: Prepare Repository 95 | run: mv dist .repo 96 | - name: Install Dependencies 97 | run: cd .repo && yarn install --check-files --frozen-lockfile 98 | - name: Create js artifact 99 | run: cd .repo && npx projen package:js 100 | - name: Collect js Artifact 101 | run: mv .repo/dist dist 102 | - name: Release 103 | env: 104 | NPM_DIST_TAG: latest 105 | NPM_REGISTRY: registry.npmjs.org 106 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 107 | run: npx -p publib@latest publib-npm 108 | release_pypi: 109 | name: Publish to PyPI 110 | needs: release 111 | runs-on: ubuntu-latest 112 | permissions: 113 | contents: read 114 | if: needs.release.outputs.latest_commit == github.sha 115 | steps: 116 | - uses: actions/setup-node@v3 117 | with: 118 | node-version: 14.x 119 | - uses: actions/setup-python@v4 120 | with: 121 | python-version: 3.x 122 | - name: Download build artifacts 123 | uses: actions/download-artifact@v3 124 | with: 125 | name: build-artifact 126 | path: dist 127 | - name: Restore build artifact permissions 128 | run: cd dist && setfacl --restore=permissions-backup.acl 129 | continue-on-error: true 130 | - name: Prepare Repository 131 | run: mv dist .repo 132 | - name: Install Dependencies 133 | run: cd .repo && yarn install --check-files --frozen-lockfile 134 | - name: Create python artifact 135 | run: cd .repo && npx projen package:python 136 | - name: Collect python Artifact 137 | run: mv .repo/dist dist 138 | - name: Release 139 | env: 140 | TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }} 141 | TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} 142 | run: npx -p publib@latest publib-pypi 143 | -------------------------------------------------------------------------------- /.github/workflows/upgrade-master.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | name: upgrade-master 4 | on: 5 | workflow_dispatch: {} 6 | schedule: 7 | - cron: 0 0 * * * 8 | jobs: 9 | upgrade: 10 | name: Upgrade 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: read 14 | outputs: 15 | patch_created: ${{ steps.create_patch.outputs.patch_created }} 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v3 19 | with: 20 | ref: master 21 | - name: Install dependencies 22 | run: yarn install --check-files --frozen-lockfile 23 | - name: Upgrade dependencies 24 | run: npx projen upgrade 25 | - name: Find mutations 26 | id: create_patch 27 | run: |- 28 | git add . 29 | git diff --staged --patch --exit-code > .repo.patch || echo "patch_created=true" >> $GITHUB_OUTPUT 30 | - name: Upload patch 31 | if: steps.create_patch.outputs.patch_created 32 | uses: actions/upload-artifact@v3 33 | with: 34 | name: .repo.patch 35 | path: .repo.patch 36 | container: 37 | image: jsii/superchain:1-buster-slim 38 | pr: 39 | name: Create Pull Request 40 | needs: upgrade 41 | runs-on: ubuntu-latest 42 | permissions: 43 | contents: write 44 | pull-requests: write 45 | if: ${{ needs.upgrade.outputs.patch_created }} 46 | steps: 47 | - name: Checkout 48 | uses: actions/checkout@v3 49 | with: 50 | token: ${{ secrets.PROJEN_GITHUB_TOKEN }} 51 | ref: master 52 | - name: Download patch 53 | uses: actions/download-artifact@v3 54 | with: 55 | name: .repo.patch 56 | path: ${{ runner.temp }} 57 | - name: Apply patch 58 | run: '[ -s ${{ runner.temp }}/.repo.patch ] && git apply ${{ runner.temp }}/.repo.patch || echo "Empty patch. Skipping."' 59 | - name: Set git identity 60 | run: |- 61 | git config user.name "github-actions" 62 | git config user.email "github-actions@github.com" 63 | - name: Create Pull Request 64 | id: create-pr 65 | uses: peter-evans/create-pull-request@v3 66 | with: 67 | token: ${{ secrets.PROJEN_GITHUB_TOKEN }} 68 | commit-message: |- 69 | chore(deps): upgrade dependencies 70 | 71 | Upgrades project dependencies. See details in [workflow run]. 72 | 73 | [Workflow Run]: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} 74 | 75 | ------ 76 | 77 | *Automatically created by projen via the "upgrade-master" workflow* 78 | branch: github-actions/upgrade-master 79 | title: "chore(deps): upgrade dependencies" 80 | labels: auto-approve,auto-merge 81 | body: |- 82 | Upgrades project dependencies. See details in [workflow run]. 83 | 84 | [Workflow Run]: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} 85 | 86 | ------ 87 | 88 | *Automatically created by projen via the "upgrade-master" workflow* 89 | author: github-actions 90 | committer: github-actions 91 | signoff: true 92 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | !/.gitattributes 3 | !/.projen/tasks.json 4 | !/.projen/deps.json 5 | !/.projen/files.json 6 | !/.github/workflows/pull-request-lint.yml 7 | !/.github/workflows/auto-approve.yml 8 | !/package.json 9 | !/LICENSE 10 | !/.npmignore 11 | logs 12 | *.log 13 | npm-debug.log* 14 | yarn-debug.log* 15 | yarn-error.log* 16 | lerna-debug.log* 17 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 18 | pids 19 | *.pid 20 | *.seed 21 | *.pid.lock 22 | lib-cov 23 | coverage 24 | *.lcov 25 | .nyc_output 26 | build/Release 27 | node_modules/ 28 | jspm_packages/ 29 | *.tsbuildinfo 30 | .eslintcache 31 | *.tgz 32 | .yarn-integrity 33 | .cache 34 | !/.projenrc.js 35 | /test-reports/ 36 | junit.xml 37 | /coverage/ 38 | !/.github/workflows/build.yml 39 | /dist/changelog.md 40 | /dist/version.txt 41 | !/.github/workflows/release.yml 42 | !/.mergify.yml 43 | !/.github/workflows/upgrade-master.yml 44 | !/.github/pull_request_template.md 45 | !/test/ 46 | !/tsconfig.dev.json 47 | !/src/ 48 | /lib 49 | /dist/ 50 | !/.eslintrc.json 51 | .jsii 52 | tsconfig.json 53 | !/API.md 54 | !/.gitpod.yml 55 | cdk.out 56 | cdk.context.json 57 | images 58 | yarn-error.log 59 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | image: public.ecr.aws/pahudnet/gitpod-workspace:latest 4 | tasks: 5 | - command: npx projen upgrade 6 | init: yarn gitpod:prebuild 7 | github: 8 | prebuilds: 9 | addCheck: true 10 | addBadge: true 11 | addLabel: true 12 | branches: true 13 | pullRequests: true 14 | pullRequestsFromForks: true 15 | vscode: 16 | extensions: 17 | - dbaeumer.vscode-eslint 18 | - ms-azuretools.vscode-docker 19 | - AmazonWebServices.aws-toolkit-vscode 20 | -------------------------------------------------------------------------------- /.mergify.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | queue_rules: 4 | - name: default 5 | conditions: 6 | - "#approved-reviews-by>=1" 7 | - -label~=(do-not-merge) 8 | - status-success=build 9 | - status-success=package-js 10 | - status-success=package-python 11 | pull_request_rules: 12 | - name: Automatic merge on approval and successful build 13 | actions: 14 | delete_head_branch: {} 15 | queue: 16 | method: squash 17 | name: default 18 | commit_message_template: |- 19 | {{ title }} (#{{ number }}) 20 | 21 | {{ body }} 22 | conditions: 23 | - "#approved-reviews-by>=1" 24 | - -label~=(do-not-merge) 25 | - status-success=build 26 | - status-success=package-js 27 | - status-success=package-python 28 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | /.projen/ 3 | /test-reports/ 4 | junit.xml 5 | /coverage/ 6 | permissions-backup.acl 7 | /dist/changelog.md 8 | /dist/version.txt 9 | /.mergify.yml 10 | /test/ 11 | /tsconfig.dev.json 12 | /src/ 13 | !/lib/ 14 | !/lib/**/*.js 15 | !/lib/**/*.d.ts 16 | dist 17 | /tsconfig.json 18 | /.github/ 19 | /.vscode/ 20 | /.idea/ 21 | /.projenrc.js 22 | tsconfig.tsbuildinfo 23 | /.eslintrc.json 24 | !.jsii 25 | cdk.out 26 | cdk.context.json 27 | images 28 | yarn-error.log 29 | -------------------------------------------------------------------------------- /.projen/deps.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": [ 3 | { 4 | "name": "@types/jest", 5 | "version": "^27", 6 | "type": "build" 7 | }, 8 | { 9 | "name": "@types/node", 10 | "version": "^14", 11 | "type": "build" 12 | }, 13 | { 14 | "name": "@typescript-eslint/eslint-plugin", 15 | "version": "^5", 16 | "type": "build" 17 | }, 18 | { 19 | "name": "@typescript-eslint/parser", 20 | "version": "^5", 21 | "type": "build" 22 | }, 23 | { 24 | "name": "eslint-import-resolver-node", 25 | "type": "build" 26 | }, 27 | { 28 | "name": "eslint-import-resolver-typescript", 29 | "type": "build" 30 | }, 31 | { 32 | "name": "eslint-plugin-import", 33 | "type": "build" 34 | }, 35 | { 36 | "name": "eslint", 37 | "version": "^8", 38 | "type": "build" 39 | }, 40 | { 41 | "name": "jest-junit", 42 | "version": "^13", 43 | "type": "build" 44 | }, 45 | { 46 | "name": "jest", 47 | "version": "^27", 48 | "type": "build" 49 | }, 50 | { 51 | "name": "jsii", 52 | "type": "build" 53 | }, 54 | { 55 | "name": "jsii-diff", 56 | "type": "build" 57 | }, 58 | { 59 | "name": "jsii-docgen", 60 | "type": "build" 61 | }, 62 | { 63 | "name": "jsii-pacmak", 64 | "type": "build" 65 | }, 66 | { 67 | "name": "json-schema", 68 | "type": "build" 69 | }, 70 | { 71 | "name": "npm-check-updates", 72 | "version": "^16", 73 | "type": "build" 74 | }, 75 | { 76 | "name": "projen", 77 | "type": "build" 78 | }, 79 | { 80 | "name": "standard-version", 81 | "version": "^9", 82 | "type": "build" 83 | }, 84 | { 85 | "name": "ts-jest", 86 | "version": "^27", 87 | "type": "build" 88 | }, 89 | { 90 | "name": "typescript", 91 | "type": "build" 92 | }, 93 | { 94 | "name": "@types/prettier", 95 | "version": "2.6.0", 96 | "type": "override" 97 | }, 98 | { 99 | "name": "@types/responselike", 100 | "version": "1.0.0", 101 | "type": "override" 102 | }, 103 | { 104 | "name": "got", 105 | "version": "12.3.1", 106 | "type": "override" 107 | }, 108 | { 109 | "name": "@aws-cdk/aws-autoscaling", 110 | "version": "^1.62.0", 111 | "type": "peer" 112 | }, 113 | { 114 | "name": "@aws-cdk/aws-ec2", 115 | "version": "^1.62.0", 116 | "type": "peer" 117 | }, 118 | { 119 | "name": "@aws-cdk/aws-iam", 120 | "version": "^1.62.0", 121 | "type": "peer" 122 | }, 123 | { 124 | "name": "@aws-cdk/aws-lambda", 125 | "version": "^1.62.0", 126 | "type": "peer" 127 | }, 128 | { 129 | "name": "@aws-cdk/aws-logs", 130 | "version": "^1.62.0", 131 | "type": "peer" 132 | }, 133 | { 134 | "name": "@aws-cdk/aws-s3", 135 | "version": "^1.62.0", 136 | "type": "peer" 137 | }, 138 | { 139 | "name": "@aws-cdk/core", 140 | "version": "^1.62.0", 141 | "type": "peer" 142 | }, 143 | { 144 | "name": "@aws-cdk/custom-resources", 145 | "version": "^1.62.0", 146 | "type": "peer" 147 | }, 148 | { 149 | "name": "constructs", 150 | "version": "^3.2.27", 151 | "type": "peer" 152 | }, 153 | { 154 | "name": "@aws-cdk/aws-autoscaling", 155 | "version": "^1.62.0", 156 | "type": "runtime" 157 | }, 158 | { 159 | "name": "@aws-cdk/aws-ec2", 160 | "version": "^1.62.0", 161 | "type": "runtime" 162 | }, 163 | { 164 | "name": "@aws-cdk/aws-iam", 165 | "version": "^1.62.0", 166 | "type": "runtime" 167 | }, 168 | { 169 | "name": "@aws-cdk/aws-lambda", 170 | "version": "^1.62.0", 171 | "type": "runtime" 172 | }, 173 | { 174 | "name": "@aws-cdk/aws-logs", 175 | "version": "^1.62.0", 176 | "type": "runtime" 177 | }, 178 | { 179 | "name": "@aws-cdk/aws-s3", 180 | "version": "^1.62.0", 181 | "type": "runtime" 182 | }, 183 | { 184 | "name": "@aws-cdk/core", 185 | "version": "^1.62.0", 186 | "type": "runtime" 187 | }, 188 | { 189 | "name": "@aws-cdk/custom-resources", 190 | "version": "^1.62.0", 191 | "type": "runtime" 192 | }, 193 | { 194 | "name": "@aws-cdk/assert", 195 | "version": "^1.62.0", 196 | "type": "test" 197 | } 198 | ], 199 | "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"npx projen\"." 200 | } 201 | -------------------------------------------------------------------------------- /.projen/files.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | ".eslintrc.json", 4 | ".gitattributes", 5 | ".github/pull_request_template.md", 6 | ".github/workflows/auto-approve.yml", 7 | ".github/workflows/build.yml", 8 | ".github/workflows/pull-request-lint.yml", 9 | ".github/workflows/release.yml", 10 | ".github/workflows/upgrade-master.yml", 11 | ".gitignore", 12 | ".gitpod.yml", 13 | ".mergify.yml", 14 | ".projen/deps.json", 15 | ".projen/files.json", 16 | ".projen/tasks.json", 17 | "LICENSE", 18 | "tsconfig.dev.json" 19 | ], 20 | "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"npx projen\"." 21 | } 22 | -------------------------------------------------------------------------------- /.projen/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "build": { 4 | "name": "build", 5 | "description": "Full release build", 6 | "steps": [ 7 | { 8 | "spawn": "default" 9 | }, 10 | { 11 | "spawn": "pre-compile" 12 | }, 13 | { 14 | "spawn": "compile" 15 | }, 16 | { 17 | "spawn": "post-compile" 18 | }, 19 | { 20 | "spawn": "test" 21 | }, 22 | { 23 | "spawn": "package" 24 | } 25 | ] 26 | }, 27 | "bump": { 28 | "name": "bump", 29 | "description": "Bumps version based on latest git tag and generates a changelog entry", 30 | "env": { 31 | "OUTFILE": "package.json", 32 | "CHANGELOG": "dist/changelog.md", 33 | "BUMPFILE": "dist/version.txt", 34 | "RELEASETAG": "dist/releasetag.txt", 35 | "RELEASE_TAG_PREFIX": "" 36 | }, 37 | "steps": [ 38 | { 39 | "builtin": "release/bump-version" 40 | } 41 | ], 42 | "condition": "! git log --oneline -1 | grep -q \"chore(release):\"" 43 | }, 44 | "clobber": { 45 | "name": "clobber", 46 | "description": "hard resets to HEAD of origin and cleans the local repo", 47 | "env": { 48 | "BRANCH": "$(git branch --show-current)" 49 | }, 50 | "steps": [ 51 | { 52 | "exec": "git checkout -b scratch", 53 | "name": "save current HEAD in \"scratch\" branch" 54 | }, 55 | { 56 | "exec": "git checkout $BRANCH" 57 | }, 58 | { 59 | "exec": "git fetch origin", 60 | "name": "fetch latest changes from origin" 61 | }, 62 | { 63 | "exec": "git reset --hard origin/$BRANCH", 64 | "name": "hard reset to origin commit" 65 | }, 66 | { 67 | "exec": "git clean -fdx", 68 | "name": "clean all untracked files" 69 | }, 70 | { 71 | "say": "ready to rock! (unpushed commits are under the \"scratch\" branch)" 72 | } 73 | ], 74 | "condition": "git diff --exit-code > /dev/null" 75 | }, 76 | "compat": { 77 | "name": "compat", 78 | "description": "Perform API compatibility check against latest version", 79 | "steps": [ 80 | { 81 | "exec": "jsii-diff npm:$(node -p \"require('./package.json').name\") -k --ignore-file .compatignore || (echo \"\nUNEXPECTED BREAKING CHANGES: add keys such as 'removed:constructs.Node.of' to .compatignore to skip.\n\" && exit 1)" 82 | } 83 | ] 84 | }, 85 | "compile": { 86 | "name": "compile", 87 | "description": "Only compile", 88 | "steps": [ 89 | { 90 | "exec": "jsii --silence-warnings=reserved-word" 91 | } 92 | ] 93 | }, 94 | "default": { 95 | "name": "default", 96 | "description": "Synthesize project files", 97 | "steps": [ 98 | { 99 | "exec": "node .projenrc.js" 100 | } 101 | ] 102 | }, 103 | "docgen": { 104 | "name": "docgen", 105 | "description": "Generate API.md from .jsii manifest", 106 | "steps": [ 107 | { 108 | "exec": "jsii-docgen -o API.md" 109 | } 110 | ] 111 | }, 112 | "eject": { 113 | "name": "eject", 114 | "description": "Remove projen from the project", 115 | "env": { 116 | "PROJEN_EJECTING": "true" 117 | }, 118 | "steps": [ 119 | { 120 | "spawn": "default" 121 | } 122 | ] 123 | }, 124 | "eslint": { 125 | "name": "eslint", 126 | "description": "Runs eslint against the codebase", 127 | "steps": [ 128 | { 129 | "exec": "eslint --ext .ts,.tsx --fix --no-error-on-unmatched-pattern src test build-tools .projenrc.js" 130 | } 131 | ] 132 | }, 133 | "gitpod:prebuild": { 134 | "name": "gitpod:prebuild", 135 | "description": "Prebuild setup for Gitpod", 136 | "steps": [ 137 | { 138 | "exec": "yarn install --frozen-lockfile --check-files" 139 | }, 140 | { 141 | "exec": "npx projen compile" 142 | } 143 | ] 144 | }, 145 | "package": { 146 | "name": "package", 147 | "description": "Creates the distribution package", 148 | "steps": [ 149 | { 150 | "exec": "if [ ! -z ${CI} ]; then rsync -a . .repo --exclude .git --exclude node_modules && rm -rf dist && mv .repo dist; else npx projen package-all; fi" 151 | } 152 | ] 153 | }, 154 | "package-all": { 155 | "name": "package-all", 156 | "description": "Packages artifacts for all target languages", 157 | "steps": [ 158 | { 159 | "spawn": "package:js" 160 | }, 161 | { 162 | "spawn": "package:python" 163 | } 164 | ] 165 | }, 166 | "package:js": { 167 | "name": "package:js", 168 | "description": "Create js language bindings", 169 | "steps": [ 170 | { 171 | "exec": "jsii-pacmak -v --target js" 172 | } 173 | ] 174 | }, 175 | "package:python": { 176 | "name": "package:python", 177 | "description": "Create python language bindings", 178 | "steps": [ 179 | { 180 | "exec": "jsii-pacmak -v --target python" 181 | } 182 | ] 183 | }, 184 | "post-compile": { 185 | "name": "post-compile", 186 | "description": "Runs after successful compilation", 187 | "steps": [ 188 | { 189 | "spawn": "docgen" 190 | } 191 | ] 192 | }, 193 | "post-upgrade": { 194 | "name": "post-upgrade", 195 | "description": "Runs after upgrading dependencies" 196 | }, 197 | "pre-compile": { 198 | "name": "pre-compile", 199 | "description": "Prepare the project for compilation" 200 | }, 201 | "release": { 202 | "name": "release", 203 | "description": "Prepare a release from \"master\" branch", 204 | "env": { 205 | "RELEASE": "true" 206 | }, 207 | "steps": [ 208 | { 209 | "exec": "rm -fr dist" 210 | }, 211 | { 212 | "spawn": "bump" 213 | }, 214 | { 215 | "spawn": "build" 216 | }, 217 | { 218 | "spawn": "unbump" 219 | }, 220 | { 221 | "exec": "git diff --ignore-space-at-eol --exit-code" 222 | } 223 | ] 224 | }, 225 | "test": { 226 | "name": "test", 227 | "description": "Run tests", 228 | "steps": [ 229 | { 230 | "exec": "jest --passWithNoTests --all --updateSnapshot" 231 | }, 232 | { 233 | "spawn": "eslint" 234 | } 235 | ] 236 | }, 237 | "test:watch": { 238 | "name": "test:watch", 239 | "description": "Run jest in watch mode", 240 | "steps": [ 241 | { 242 | "exec": "jest --watch" 243 | } 244 | ] 245 | }, 246 | "unbump": { 247 | "name": "unbump", 248 | "description": "Restores version to 0.0.0", 249 | "env": { 250 | "OUTFILE": "package.json", 251 | "CHANGELOG": "dist/changelog.md", 252 | "BUMPFILE": "dist/version.txt", 253 | "RELEASETAG": "dist/releasetag.txt", 254 | "RELEASE_TAG_PREFIX": "" 255 | }, 256 | "steps": [ 257 | { 258 | "builtin": "release/reset-version" 259 | } 260 | ] 261 | }, 262 | "upgrade": { 263 | "name": "upgrade", 264 | "description": "upgrade dependencies", 265 | "env": { 266 | "CI": "0" 267 | }, 268 | "steps": [ 269 | { 270 | "exec": "yarn upgrade npm-check-updates" 271 | }, 272 | { 273 | "exec": "npm-check-updates --dep dev --upgrade --target=minor" 274 | }, 275 | { 276 | "exec": "npm-check-updates --dep optional --upgrade --target=minor" 277 | }, 278 | { 279 | "exec": "npm-check-updates --dep peer --upgrade --target=minor" 280 | }, 281 | { 282 | "exec": "npm-check-updates --dep prod --upgrade --target=minor" 283 | }, 284 | { 285 | "exec": "npm-check-updates --dep bundle --upgrade --target=minor" 286 | }, 287 | { 288 | "exec": "yarn install --check-files" 289 | }, 290 | { 291 | "exec": "yarn upgrade" 292 | }, 293 | { 294 | "exec": "npx projen" 295 | }, 296 | { 297 | "spawn": "post-upgrade" 298 | } 299 | ] 300 | }, 301 | "watch": { 302 | "name": "watch", 303 | "description": "Watch & compile in the background", 304 | "steps": [ 305 | { 306 | "exec": "jsii -w --silence-warnings=reserved-word" 307 | } 308 | ] 309 | } 310 | }, 311 | "env": { 312 | "PATH": "$(npx -c \"node -e \\\"console.log(process.env.PATH)\\\"\")" 313 | }, 314 | "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"npx projen\"." 315 | } 316 | -------------------------------------------------------------------------------- /.projenrc.js: -------------------------------------------------------------------------------- 1 | const { awscdk, DevEnvironmentDockerImage, Gitpod } = require('projen'); 2 | 3 | const AWS_CDK_LATEST_RELEASE = '1.62.0'; 4 | const PROJECT_NAME = 'cdk-k3s-cluster'; 5 | const PROJECT_DESCRIPTION = 'A JSII construct lib to deploy a K3s cluster on AWS with CDK'; 6 | const AUTOMATION_TOKEN = 'PROJEN_GITHUB_TOKEN'; 7 | 8 | const project = new awscdk.AwsCdkConstructLibrary({ 9 | authorName: 'Massimo Re Ferre', 10 | authorEmail: 'mreferre@amazon.com', 11 | name: PROJECT_NAME, 12 | repository: 'https://github.com/aws-samples/aws-cdk-for-k3scluster', 13 | description: PROJECT_DESCRIPTION, 14 | license: 'MIT', 15 | copyrightOwner: 'AWS Samples', 16 | copyrightPeriod: '2020', 17 | keywords: [ 18 | 'aws', 19 | 'kubernetes', 20 | 'k3s', 21 | 'graviton', 22 | 'spot', 23 | ], 24 | autoApproveOptions: { 25 | secret: 'PROJEN_GITHUB_TOKEN', 26 | }, 27 | depsUpgradeOptions: { 28 | ignoreProjen: true, 29 | workflowOptions: { 30 | labels: ['auto-approve', 'auto-merge'], 31 | secret: AUTOMATION_TOKEN, 32 | }, 33 | }, 34 | autoApproveOptions: { 35 | secret: 'GITHUB_TOKEN', 36 | allowedUsernames: ['mreferre', 'pahud'], 37 | }, 38 | defaultReleaseBranch: 'master', 39 | catalog: { 40 | twitter: 'mreferre', 41 | announce: false, 42 | }, 43 | cdkVersion: AWS_CDK_LATEST_RELEASE, 44 | cdkDependencies: [ 45 | '@aws-cdk/core', 46 | '@aws-cdk/aws-ec2', 47 | '@aws-cdk/aws-s3', 48 | '@aws-cdk/aws-iam', 49 | '@aws-cdk/aws-autoscaling', 50 | '@aws-cdk/custom-resources', 51 | '@aws-cdk/aws-logs', 52 | '@aws-cdk/aws-lambda', 53 | ], 54 | python: { 55 | distName: 'cdk-k3s-cluster', 56 | module: 'cdk_k3s_cluster', 57 | }, 58 | }); 59 | 60 | // project.package.addField('resolutions', { 61 | // 'pac-resolver': '^5.0.0', 62 | // 'set-value': '^4.0.1', 63 | // 'ansi-regex': '^5.0.1', 64 | // }); 65 | 66 | const gitpodPrebuild = project.addTask('gitpod:prebuild', { 67 | description: 'Prebuild setup for Gitpod', 68 | }); 69 | // install and compile only, do not test or package. 70 | gitpodPrebuild.exec('yarn install --frozen-lockfile --check-files'); 71 | gitpodPrebuild.exec('npx projen compile'); 72 | 73 | let gitpod = new Gitpod(project, { 74 | dockerImage: DevEnvironmentDockerImage.fromImage('public.ecr.aws/pahudnet/gitpod-workspace:latest'), 75 | prebuilds: { 76 | addCheck: true, 77 | addBadge: true, 78 | addLabel: true, 79 | branches: true, 80 | pullRequests: true, 81 | pullRequestsFromForks: true, 82 | }, 83 | }); 84 | 85 | gitpod.addCustomTask({ 86 | init: 'yarn gitpod:prebuild', 87 | // always upgrade after init 88 | command: 'npx projen upgrade', 89 | }); 90 | 91 | gitpod.addVscodeExtensions( 92 | 'dbaeumer.vscode-eslint', 93 | 'ms-azuretools.vscode-docker', 94 | 'AmazonWebServices.aws-toolkit-vscode', 95 | ); 96 | 97 | const common_exclude = ['cdk.out', 'cdk.context.json', 'images', 'yarn-error.log']; 98 | project.npmignore.exclude(...common_exclude); 99 | project.gitignore.exclude(...common_exclude); 100 | 101 | project.synth(); 102 | -------------------------------------------------------------------------------- /API.md: -------------------------------------------------------------------------------- 1 | # API Reference 2 | 3 | **Classes** 4 | 5 | Name|Description 6 | ----|----------- 7 | [AmiProvider](#cdk-k3s-cluster-amiprovider)|The AMI provider to get the latest Amazon Linux 2 AMI for ARM64. 8 | [Cluster](#cdk-k3s-cluster-cluster)|Represents the k3sCluster construct. 9 | [VpcProvider](#cdk-k3s-cluster-vpcprovider)|The VPC provider to create or import the VPC. 10 | 11 | 12 | **Structs** 13 | 14 | Name|Description 15 | ----|----------- 16 | [ClusterProps](#cdk-k3s-cluster-clusterprops)|*No description* 17 | 18 | 19 | 20 | ## class AmiProvider 21 | 22 | The AMI provider to get the latest Amazon Linux 2 AMI for ARM64. 23 | 24 | 25 | ### Initializer 26 | 27 | 28 | 29 | 30 | ```ts 31 | new AmiProvider() 32 | ``` 33 | 34 | 35 | 36 | 37 | ### Properties 38 | 39 | 40 | Name | Type | Description 41 | -----|------|------------- 42 | **amiId** | [IMachineImage](#aws-cdk-aws-ec2-imachineimage) | 43 | 44 | 45 | 46 | ## class Cluster 47 | 48 | Represents the k3sCluster construct. 49 | 50 | __Implements__: [IConstruct](#constructs-iconstruct), [IConstruct](#aws-cdk-core-iconstruct), [IConstruct](#constructs-iconstruct), [IDependable](#aws-cdk-core-idependable) 51 | __Extends__: [Construct](#aws-cdk-core-construct) 52 | 53 | ### Initializer 54 | 55 | 56 | 57 | 58 | ```ts 59 | new Cluster(scope: Construct, id: string, props?: ClusterProps) 60 | ``` 61 | 62 | * **scope** ([Construct](#aws-cdk-core-construct)) *No description* 63 | * **id** (string) *No description* 64 | * **props** ([ClusterProps](#cdk-k3s-cluster-clusterprops)) *No description* 65 | * **bucketRemovalPolicy** ([RemovalPolicy](#aws-cdk-core-removalpolicy)) The bucket removal policy. __*Default*__: cdk.RemovalPolicy.RETAIN 66 | * **controlPlaneInstanceType** ([InstanceType](#aws-cdk-aws-ec2-instancetype)) control plane node ec2 instance type. __*Default*__: mg6.medium 67 | * **spotWorkerNodes** (boolean) Run worker nodes as EC2 Spot. __*Default*__: true 68 | * **vpc** ([IVpc](#aws-cdk-aws-ec2-ivpc)) VPC. __*Default*__: create new VPC 69 | * **workerInstanceType** ([InstanceType](#aws-cdk-aws-ec2-instancetype)) worker node instance type. __*Default*__: mg6.medium 70 | * **workerMinCapacity** (number) minimal number of worker nodes. __*Default*__: 3 71 | 72 | 73 | 74 | ### Properties 75 | 76 | 77 | Name | Type | Description 78 | -----|------|------------- 79 | **controlPlaneInstanceType** | [InstanceType](#aws-cdk-aws-ec2-instancetype) | The instance type of the control plane. 80 | **endpointUri** | string | The endpoint URL of the control plan. 81 | **workerInstanceType** | [InstanceType](#aws-cdk-aws-ec2-instancetype) | The instance type of the worker node. 82 | 83 | 84 | 85 | ## class VpcProvider 86 | 87 | The VPC provider to create or import the VPC. 88 | 89 | 90 | ### Initializer 91 | 92 | 93 | 94 | 95 | ```ts 96 | new VpcProvider() 97 | ``` 98 | 99 | 100 | 101 | ### Methods 102 | 103 | 104 | #### *static* getOrCreate(scope) 105 | 106 | 107 | 108 | ```ts 109 | static getOrCreate(scope: Construct): IVpc 110 | ``` 111 | 112 | * **scope** ([Construct](#aws-cdk-core-construct)) *No description* 113 | 114 | __Returns__: 115 | * [IVpc](#aws-cdk-aws-ec2-ivpc) 116 | 117 | 118 | 119 | ## struct ClusterProps 120 | 121 | 122 | 123 | 124 | 125 | 126 | Name | Type | Description 127 | -----|------|------------- 128 | **bucketRemovalPolicy**? | [RemovalPolicy](#aws-cdk-core-removalpolicy) | The bucket removal policy.
__*Default*__: cdk.RemovalPolicy.RETAIN 129 | **controlPlaneInstanceType**? | [InstanceType](#aws-cdk-aws-ec2-instancetype) | control plane node ec2 instance type.
__*Default*__: mg6.medium 130 | **spotWorkerNodes**? | boolean | Run worker nodes as EC2 Spot.
__*Default*__: true 131 | **vpc**? | [IVpc](#aws-cdk-aws-ec2-ivpc) | VPC.
__*Default*__: create new VPC 132 | **workerInstanceType**? | [InstanceType](#aws-cdk-aws-ec2-instancetype) | worker node instance type.
__*Default*__: mg6.medium 133 | **workerMinCapacity**? | number | minimal number of worker nodes.
__*Default*__: 3 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [0.0.51](https://github.com/aws-samples/aws-cdk-for-k3scluster/compare/v0.0.50...v0.0.51) (2021-03-16) 6 | 7 | ### [0.0.50](https://github.com/aws-samples/aws-cdk-for-k3scluster/compare/v0.0.47...v0.0.50) (2021-03-16) 8 | 9 | 10 | ### Bug Fixes 11 | 12 | * invalid iam profile ([4324820](https://github.com/aws-samples/aws-cdk-for-k3scluster/commit/43248202fdd81b45d1263ec72e2e8907e738601b)) 13 | * projen now requires defaultReleaseBranch ([4566cdc](https://github.com/aws-samples/aws-cdk-for-k3scluster/commit/4566cdcd92dfb5ffd61f171880f65004357e00d1)) 14 | * remove-custom-resource ([3d0c765](https://github.com/aws-samples/aws-cdk-for-k3scluster/commit/3d0c76595287e85e9cfca66084af3b08daddb3be)) 15 | 16 | ### 0.0.48 (2020-11-28) 17 | 18 | ### 0.0.47 (2020-10-19) 19 | 20 | ### 0.0.46 (2020-10-15) 21 | 22 | ### 0.0.45 (2020-10-15) 23 | 24 | ### 0.0.44 (2020-10-12) 25 | 26 | ### 0.0.43 (2020-10-07) 27 | 28 | ### 0.0.42 (2020-10-07) 29 | 30 | ### 0.0.41 (2020-10-07) 31 | 32 | ### 0.0.40 (2020-10-07) 33 | 34 | ### 0.0.39 (2020-10-06) 35 | 36 | ### 0.0.38 (2020-10-06) 37 | 38 | ### 0.0.37 (2020-10-05) 39 | 40 | ### 0.0.36 (2020-10-03) 41 | 42 | ### 0.0.35 (2020-10-01) 43 | 44 | ### 0.0.34 (2020-10-01) 45 | 46 | ### 0.0.33 (2020-09-30) 47 | 48 | ### 0.0.32 (2020-09-30) 49 | 50 | ### 0.0.31 (2020-09-30) 51 | 52 | ### 0.0.30 (2020-09-30) 53 | 54 | ### 0.0.29 (2020-09-29) 55 | 56 | ### 0.0.28 (2020-09-25) 57 | 58 | ### 0.0.27 (2020-09-25) 59 | 60 | ### 0.0.26 (2020-09-22) 61 | 62 | ### 0.0.25 (2020-09-22) 63 | 64 | ### 0.0.24 (2020-09-21) 65 | 66 | ### 0.0.23 (2020-09-21) 67 | 68 | ### 0.0.22 (2020-09-17) 69 | 70 | 71 | ### Features 72 | 73 | * **core:** create ASG with launch template ([#23](https://github.com/aws-samples/aws-cdk-for-k3scluster/issues/23)) ([994dcda](https://github.com/aws-samples/aws-cdk-for-k3scluster/commit/994dcda5d8f9ea98fc6bffc14f0162eb0b582b83)) 74 | 75 | ### 0.0.21 (2020-09-17) 76 | 77 | ### 0.0.20 (2020-09-17) 78 | 79 | ### 0.0.19 (2020-09-17) 80 | 81 | ### 0.0.18 (2020-09-17) 82 | 83 | ### 0.0.17 (2020-09-16) 84 | 85 | ### 0.0.16 (2020-09-16) 86 | 87 | ### 0.0.15 (2020-09-16) 88 | 89 | ### 0.0.14 (2020-09-16) 90 | 91 | 92 | ### Features 93 | 94 | * **core:** add t4g instance types in the PriceMap ([#19](https://github.com/aws-samples/aws-cdk-for-k3scluster/issues/19)) ([547a393](https://github.com/aws-samples/aws-cdk-for-k3scluster/commit/547a3935e53aeb73a7820e371d84f39b02063e39)) 95 | 96 | ### 0.0.13 (2020-09-15) 97 | 98 | ### 0.0.12 (2020-09-15) 99 | 100 | ### 0.0.11 (2020-09-15) 101 | 102 | ### 0.0.10 (2020-09-14) 103 | 104 | ### 0.0.9 (2020-09-14) 105 | 106 | ### 0.0.8 (2020-09-14) 107 | 108 | ### 0.0.7 (2020-09-12) 109 | 110 | ### [0.0.6](https://github.com/aws-samples/aws-cdk-for-k3scluster/compare/v0.0.5...v0.0.6) (2020-09-11) 111 | 112 | ### 0.0.5 (2020-09-11) 113 | 114 | ### 0.0.4 (2020-09-11) 115 | 116 | ### 0.0.3 (2020-09-11) 117 | 118 | ### 0.0.2 (2020-09-10) 119 | 120 | ### 0.0.1 (2020-09-10) 121 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *master* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | 61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 AWS Samples 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![NPM version](https://badge.fury.io/js/cdk-k3s-cluster.svg)](https://badge.fury.io/js/cdk-k3s-cluster) 2 | [![PyPI version](https://badge.fury.io/py/cdk-k3s-cluster.svg)](https://badge.fury.io/py/cdk-k3s-cluster) 3 | ![Release](https://github.com/aws-samples/aws-cdk-for-k3scluster/workflows/Release/badge.svg) 4 | 5 | ## What is cdk-k3s-cluster? 6 | 7 | `cdk-k3s-cluster` is a new JSII construct library for AWS CDK that deploys a scalable Kubernetes [K3s](https://k3s.io/) cluster on **Graviton2 Arm-based** (mg6 by default) **Spot** instances with one ~~click~~ command on AWS. 8 | 9 | 10 | ## What problem does cdk-k3s-cluster solve? 11 | 12 | `cdk-k3s-cluster` is just another way to deploy Kubernetes (`K3s` specifically) on AWS. `K3s` is a minimalist Kubernetes distribution from Rancher often related to Edge and IoT use cases. There is a gazillion of articles on the Internet about how to setup `K3s` on a Raspberry cluster. The great [Alex Ellis](https://twitter.com/alexellisuk) has a [blog post](https://blog.alexellis.io/test-drive-k3s-on-raspberry-pi/) on it, for example. Since I don't have a Raspberry cluster and I was not planning to buy one I thought I'd use the power of the cloud to mimic it. The result of this experiment is that you could deploy an ephemeral, cheap Kubernetes Arm-based cluster on AWS in a matter of minutes. Unleash your imagination re how you can use it (e.g. an ephemeral cluster part of your deployment pipeline in the cloud?). Note the class today only supports Arm-based instances for deomstration purposes but can easily be adapted to support x86-based instances. 13 | 14 | ## How do you deploy and consume cdk-k3s-cluster? 15 | 16 | ### The cdk-k3s-cluster Cluster API 17 | 18 | The `Cluster` API available in the `cdk-k3s-cluster` library, not to be confused with the Kubernetes Cluster API project, allows you to build the k3s cluster on AWS with AWS CDK. This library is currently available in both `NPM` and `PyPi`. 19 | 20 | Creating a default cluster could be as simple as: 21 | 22 | ```typescript 23 | import * as k3s from 'cdk-k3s-cluster' 24 | 25 | new k3s.Cluster(stack, 'Cluster') 26 | ``` 27 | 28 | See below for a complete example. 29 | 30 | ### Deployment 31 | 32 | `cdk-k3s-cluster` first deploys a Graviton2 EC2 instance (`m6g.large` by default) where it starts the `K3s` control plane. The `kubeconfig` file generated and the `K3s` token are copied to an S3 bucket. It then creates an ASG that spins up the number of Graviton2 worker nodes that you specify (3 by default - the ceiling limit is based on your account limits). These instances launch `K3s` worker nodes that join the cluster by downloading the token from S3. Ultimately the CDK outputs the link to the `kubeconfig` file on your private S3 bucket for you to copy it and use it with `kubectl`. This construct can deploy the worker node instances using either the `on-demand` or the `Spot` life cycles to reduce costs further (`Spot` is used by default). This construct cretes by default a new VPC but it can be configured to deploy either on the `default` VPC in your account or on a specific existing VPC id. 33 | 34 | This is a high level view of the architecture and the deployment flow described above: 35 | ![ks3clusterdeploy](./images/ks3clusterdeploy.png) 36 | 37 | This is a screenshot of the cluster creation user experience using CDK: 38 | 39 | ![ks3clustercreatecluster](./images/ks3clustercreatecluster.png) 40 | 41 | ### Consumption 42 | 43 | Once the `cdk-k3s-cluster` has been deployed, this a high level view of the consumption flow: 44 | ![ks3clusterconsume](./images/ks3clusterconsume.png) 45 | 46 | This is an example of the cluster consumption user experience using the `aws cli` (used to copy the `kubeconfig` file from S3) and `kubectl`: 47 | 48 | ```bash 49 | $ aws s3 cp s3://k3sCluster-clusterk3sbucketxxxxxxxxxxxxx/kubeconfig.yaml . 50 | download: s3://k3sCluster-clusterk3sbucketxxxxxxxxxxxxx/kubeconfig.yaml to ./kubeconfig.yaml 51 | 52 | $ kubectl get nodes --kubeconfig=./kubeconfig.yaml 53 | NAME STATUS ROLES AGE VERSION 54 | ip-172-31-43-198.us-west-2.compute.internal Ready master 2m49s v1.16.9+k3s1 55 | ip-172-31-10-252.us-west-2.compute.internal Ready 15s v1.16.13+k3s1 56 | ip-172-31-18-126.us-west-2.compute.internal Ready 8s v1.16.13+k3s1 57 | ip-172-31-60-174.us-west-2.compute.internal Ready 1s v1.16.13+k3s1 58 | ``` 59 | 60 | ### How does this relate to EKS 61 | 62 | It doesn't. As noted above, `cdk-k3s-cluster` is just yet another experimental and peculiar way to run Kubernetes on AWS. 63 | 64 | 65 | ## What are the running costs for a cluster built with cdk-k3s-cluster? 66 | 67 | It obviously depends how many worker nodes you deploy. If we stick to all the defaults (`Spot` lifecycle and the `m6g.medium` instance type with 1 Graviton2 CPU and 4GB of memory), as of August 2020 the unit cost in `Oregon` is $0.0177 which translates to a unit cost of $0.1416 per 8 hours (a work day). A 3 worker nodes cluster would be $0.5664 (including the control plane) per 8 hours (or $1.6992 per 24 hours full day). 68 | 69 | A *100 worker nodes cluster* (or 101 vCPUs and 404GB of memory) would be *$0.0177 x 101 = $1.7877 per hour* or $0.0177 x 101 x 24 = *$42.9048 per day*. 70 | 71 | This does not include, for example, the costs of the S3 bucket (probably marginal) or network traffic. 72 | 73 | 74 | ## Getting started 75 | 76 | `cdk-k3s-cluster` is available in both `NPM` and `PyPi` modules ready to be imported into your CDK program. 77 | 78 | ### I am a Typescript type of person 79 | 80 | This is an example of how to consume the `NPM` module with a CDK application written in Typescript. If you need to setup your Typescript environment [this is a good guide](https://docs.aws.amazon.com/cdk/latest/guide/work-with-cdk-typescript.html). 81 | 82 | ```bash 83 | $ mkdir myk3scluster-typescript 84 | $ cd myk3scluster-typescript 85 | # initialize the AWS CDK project 86 | $ cdk init -l typescript 87 | # install the cdk-k3s-cluster npm module 88 | $ yarn add cdk-k3s-cluster 89 | ``` 90 | 91 | Update your `./bin/myk3scluster-typescript.ts` file with the following content. 92 | 93 | ```typescript 94 | import * as cdk from '@aws-cdk/core'; 95 | import * as ec2 from '@aws-cdk/aws-ec2'; 96 | import * as k3s from 'cdk-k3s-cluster'; 97 | 98 | const app = new cdk.App(); 99 | 100 | const env = { 101 | region: app.node.tryGetContext('region') || process.env.CDK_INTEG_REGION || process.env.CDK_DEFAULT_REGION, 102 | account: app.node.tryGetContext('account') || process.env.CDK_INTEG_ACCOUNT || process.env.CDK_DEFAULT_ACCOUNT 103 | }; 104 | 105 | const stack = new cdk.Stack(app, 'k8sCluster', { env }) 106 | 107 | new k3s.Cluster(stack, 'Cluster', { 108 | vpc: k3s.VpcProvider.getOrCreate(stack), 109 | spotWorkerNodes: true, 110 | workerMinCapacity: 3, 111 | workerInstanceType: new ec2.InstanceType('m6g.medium'), 112 | controlPlaneInstanceType: new ec2.InstanceType('m6g.medium') 113 | bucketRemovalPolicy: cdk.RemovalPolicy.DESTROY 114 | }) 115 | ``` 116 | In this `typescript` example, we are using all of the properties available today. Note that we set the bucket removal policy to `DESTROY` (this will remove completely the S3 bucket - the safe default behavior is to leave the bucket in the account). For an up to date list of all the properties please refer to the `API.md` file in this repo. 117 | 118 | 119 | ### I am a Python type of person 120 | 121 | This is an example of how to consume the `PyPi` module with a CDK application written in Typescript. If you need to setup your Python environment [this is a good guide](https://docs.aws.amazon.com/cdk/latest/guide/work-with-cdk-python.html). 122 | 123 | ```bash 124 | $ mkdir myk3scluster-python 125 | $ cd myk3scluster-python 126 | # initialize the AWS CDK project 127 | $ cdk init -l python 128 | # activate the Python virtual environment 129 | $ source .env/bin/activate 130 | # install the dependencies 131 | $ pip install cdk-k3s-cluster 132 | ``` 133 | 134 | Update your `./app.py` file with the following content. Note how, in this example, we are only using two of the parameters available. To use all the defaults you could use `cluster = k3s.Cluster(self, "MyK3sClusters")`: 135 | 136 | ```python 137 | #!/usr/bin/env python3 138 | 139 | from aws_cdk import core 140 | import cdk_k3s_cluster as k3s 141 | 142 | class K3sCluster(core.Stack): 143 | def __init__(self, scope: core.Construct, id: str, **kwargs): 144 | super().__init__(scope, id, **kwargs) 145 | k3s.Cluster(self, "MyK3sClusters", worker_min_capacity=5, spot_worker_nodes=True) 146 | 147 | app = core.App() 148 | K3sCluster(app, "K3sCluster") 149 | app.synth() 150 | ``` 151 | 152 | 153 | ### Deploying the stack (regardless of the language you have used) 154 | 155 | deploy the CDK stack: 156 | 157 | ```bash 158 | # see the difference before the deployment 159 | $ cdk diff 160 | # deploy it 161 | $ cdk deploy 162 | ``` 163 | 164 | If you want to deploy in an existing VPC use either `cdk deploy --context use_default_vpc=1` or `cdk deploy --context use_vpc_id=` 165 | 166 | ### Clean up 167 | 168 | Cleaning up the environment is as easy as running `cdk destroy` from where you left your prompt. 169 | 170 | 171 | ## Known issues and limitations 172 | 173 | * First and foremost this is a learning experiment. We have done limited tests with it. 174 | 175 | * We have not tested this beyond a mere `kubectl get nodes` test. Let alone trying anything like [arkade](https://github.com/alexellis/arkade) 176 | 177 | * `cdk-k3s-cluster` only deploys Arm-based instances. It would be trivial to add x86 based instances support but it's not there today 178 | 179 | * All the control plane and worker nodes are deployed in public subnets and the SGs are fairly permissive in terms of "source". Picking private subnets would have probably broken the use case of deploying into the `default` VPC (which is handy). This prototype over-indexes more on deployment convenience and ease of use than on best practices. Be mindful of that 180 | 181 | * The control plane instance always deploys `on-demand` while for worker nodes you can pick between `on-demand` and `Spot` 182 | 183 | * The ASG for worker nodes is configured with a single parameter that becomes the `min`, `max` and `desired` count for the ASG 184 | 185 | * For simplicity, both the control plane instance and the worker nodes share the same instance type 186 | 187 | 188 | ## Credits 189 | 190 | This library has been authored by [Massimo](https://github.com/mreferre/) and [Pahud](https://github.com/pahud/). 191 | -------------------------------------------------------------------------------- /images/ks3cluster.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-cdk-for-k3scluster/21d211d8d5fe027a517e1575c54e9bf896c9ac92/images/ks3cluster.pptx -------------------------------------------------------------------------------- /images/ks3clusterconsume.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-cdk-for-k3scluster/21d211d8d5fe027a517e1575c54e9bf896c9ac92/images/ks3clusterconsume.png -------------------------------------------------------------------------------- /images/ks3clustercreatecluster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-cdk-for-k3scluster/21d211d8d5fe027a517e1575c54e9bf896c9ac92/images/ks3clustercreatecluster.png -------------------------------------------------------------------------------- /images/ks3clusterdeploy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-cdk-for-k3scluster/21d211d8d5fe027a517e1575c54e9bf896c9ac92/images/ks3clusterdeploy.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cdk-k3s-cluster", 3 | "description": "A JSII construct lib to deploy a K3s cluster on AWS with CDK", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/aws-samples/aws-cdk-for-k3scluster" 7 | }, 8 | "scripts": { 9 | "build": "npx projen build", 10 | "bump": "npx projen bump", 11 | "clobber": "npx projen clobber", 12 | "compat": "npx projen compat", 13 | "compile": "npx projen compile", 14 | "default": "npx projen default", 15 | "docgen": "npx projen docgen", 16 | "eject": "npx projen eject", 17 | "eslint": "npx projen eslint", 18 | "gitpod:prebuild": "npx projen gitpod:prebuild", 19 | "package": "npx projen package", 20 | "package-all": "npx projen package-all", 21 | "package:js": "npx projen package:js", 22 | "package:python": "npx projen package:python", 23 | "post-compile": "npx projen post-compile", 24 | "post-upgrade": "npx projen post-upgrade", 25 | "pre-compile": "npx projen pre-compile", 26 | "release": "npx projen release", 27 | "test": "npx projen test", 28 | "test:watch": "npx projen test:watch", 29 | "unbump": "npx projen unbump", 30 | "upgrade": "npx projen upgrade", 31 | "watch": "npx projen watch", 32 | "projen": "npx projen" 33 | }, 34 | "author": { 35 | "name": "Massimo Re Ferre", 36 | "email": "mreferre@amazon.com", 37 | "organization": false 38 | }, 39 | "devDependencies": { 40 | "@aws-cdk/assert": "^1.62.0", 41 | "@types/jest": "^27", 42 | "@types/node": "^14", 43 | "@typescript-eslint/eslint-plugin": "^5", 44 | "@typescript-eslint/parser": "^5", 45 | "eslint": "^8", 46 | "eslint-import-resolver-node": "^0.3.6", 47 | "eslint-import-resolver-typescript": "^2.7.1", 48 | "eslint-plugin-import": "^2.26.0", 49 | "jest": "^27", 50 | "jest-junit": "^13", 51 | "jsii": "^1.70.0", 52 | "jsii-diff": "^1.70.0", 53 | "jsii-docgen": "^1.8.110", 54 | "jsii-pacmak": "^1.70.0", 55 | "json-schema": "^0.4.0", 56 | "npm-check-updates": "^16", 57 | "projen": "^0.65.2", 58 | "standard-version": "^9", 59 | "ts-jest": "^27", 60 | "typescript": "^3.9.10" 61 | }, 62 | "peerDependencies": { 63 | "@aws-cdk/aws-autoscaling": "^1.62.0", 64 | "@aws-cdk/aws-ec2": "^1.62.0", 65 | "@aws-cdk/aws-iam": "^1.62.0", 66 | "@aws-cdk/aws-lambda": "^1.62.0", 67 | "@aws-cdk/aws-logs": "^1.62.0", 68 | "@aws-cdk/aws-s3": "^1.62.0", 69 | "@aws-cdk/core": "^1.62.0", 70 | "@aws-cdk/custom-resources": "^1.62.0", 71 | "constructs": "^3.2.27" 72 | }, 73 | "dependencies": { 74 | "@aws-cdk/aws-autoscaling": "^1.62.0", 75 | "@aws-cdk/aws-ec2": "^1.62.0", 76 | "@aws-cdk/aws-iam": "^1.62.0", 77 | "@aws-cdk/aws-lambda": "^1.62.0", 78 | "@aws-cdk/aws-logs": "^1.62.0", 79 | "@aws-cdk/aws-s3": "^1.62.0", 80 | "@aws-cdk/core": "^1.62.0", 81 | "@aws-cdk/custom-resources": "^1.62.0" 82 | }, 83 | "keywords": [ 84 | "aws", 85 | "cdk", 86 | "graviton", 87 | "k3s", 88 | "kubernetes", 89 | "spot" 90 | ], 91 | "main": "lib/index.js", 92 | "license": "MIT", 93 | "version": "0.0.0", 94 | "jest": { 95 | "testMatch": [ 96 | "/src/**/__tests__/**/*.ts?(x)", 97 | "/(test|src)/**/*(*.)@(spec|test).ts?(x)" 98 | ], 99 | "clearMocks": true, 100 | "collectCoverage": true, 101 | "coverageReporters": [ 102 | "json", 103 | "lcov", 104 | "clover", 105 | "cobertura", 106 | "text" 107 | ], 108 | "coverageDirectory": "coverage", 109 | "coveragePathIgnorePatterns": [ 110 | "/node_modules/" 111 | ], 112 | "testPathIgnorePatterns": [ 113 | "/node_modules/" 114 | ], 115 | "watchPathIgnorePatterns": [ 116 | "/node_modules/" 117 | ], 118 | "reporters": [ 119 | "default", 120 | [ 121 | "jest-junit", 122 | { 123 | "outputDirectory": "test-reports" 124 | } 125 | ] 126 | ], 127 | "preset": "ts-jest", 128 | "globals": { 129 | "ts-jest": { 130 | "tsconfig": "tsconfig.dev.json" 131 | } 132 | } 133 | }, 134 | "types": "lib/index.d.ts", 135 | "stability": "stable", 136 | "jsii": { 137 | "outdir": "dist", 138 | "targets": { 139 | "python": { 140 | "distName": "cdk-k3s-cluster", 141 | "module": "cdk_k3s_cluster" 142 | } 143 | }, 144 | "tsc": { 145 | "outDir": "lib", 146 | "rootDir": "src" 147 | } 148 | }, 149 | "awscdkio": { 150 | "twitter": "mreferre", 151 | "announce": false 152 | }, 153 | "resolutions": { 154 | "@types/responselike": "1.0.0", 155 | "got": "12.3.1", 156 | "@types/prettier": "2.6.0" 157 | }, 158 | "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"npx projen\"." 159 | } 160 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import * as autoscaling from '@aws-cdk/aws-autoscaling'; 2 | import * as ec2 from '@aws-cdk/aws-ec2'; 3 | import * as iam from '@aws-cdk/aws-iam'; 4 | import * as s3 from '@aws-cdk/aws-s3'; 5 | import * as cdk from '@aws-cdk/core'; 6 | 7 | const DEFAULT_INSTANCE_TYPE = ec2.InstanceType.of(ec2.InstanceClass.M6G, ec2.InstanceSize.MEDIUM); 8 | 9 | export interface ClusterProps { 10 | /** 11 | * VPC 12 | * 13 | * @default - create new VPC 14 | */ 15 | readonly vpc?: ec2.IVpc; 16 | 17 | /** 18 | * Run worker nodes as EC2 Spot 19 | * 20 | * @default true 21 | */ 22 | readonly spotWorkerNodes?: boolean; 23 | 24 | /** 25 | * control plane node ec2 instance type 26 | * 27 | * @default mg6.medium 28 | */ 29 | readonly controlPlaneInstanceType?: ec2.InstanceType; 30 | 31 | /** 32 | * worker node instance type 33 | * 34 | * @default mg6.medium 35 | */ 36 | readonly workerInstanceType?: ec2.InstanceType; 37 | 38 | /** 39 | * minimal number of worker nodes 40 | * 41 | * @default 3 42 | */ 43 | readonly workerMinCapacity?: number; 44 | 45 | /** 46 | * The bucket removal policy. When specicified as `DESTROY`, the S3 bucket for the cluster state 47 | * will be completely removed on stack destroy. 48 | * 49 | * @default - cdk.RemovalPolicy.RETAIN 50 | */ 51 | readonly bucketRemovalPolicy?: cdk.RemovalPolicy; 52 | 53 | } 54 | 55 | /** 56 | * Represents the k3sCluster construct 57 | */ 58 | export class Cluster extends cdk.Construct { 59 | /** 60 | * The instance type of the control plane 61 | */ 62 | readonly controlPlaneInstanceType: ec2.InstanceType; 63 | 64 | /** 65 | * The instance type of the worker node 66 | */ 67 | readonly workerInstanceType: ec2.InstanceType; 68 | 69 | /** 70 | * The endpoint URL of the control plan 71 | */ 72 | readonly endpointUri: string; 73 | 74 | constructor(scope: cdk.Construct, id: string, props: ClusterProps = {}) { 75 | super(scope, id); 76 | 77 | // VPC configuration 78 | const vpc = props.vpc ?? new ec2.Vpc(this, 'Vpc', { maxAzs: 3, natGateways: 1 }); 79 | 80 | // S3 bucket to host K3s token + kubeconfig file 81 | // support s3 bucket autoDeleteObjects native. 82 | // see - https://docs.aws.amazon.com/cdk/api/latest/docs/aws-s3-readme.html#bucket-deletion 83 | // PR - https://github.com/aws/aws-cdk/commit/32e9c23be2852cfca79a57c90e52b9301b1c7081 84 | let k3sBucket: s3.Bucket; 85 | if (props.bucketRemovalPolicy === cdk.RemovalPolicy.DESTROY) { 86 | k3sBucket = new s3.Bucket(this, 'k3sBucket', { 87 | removalPolicy: props.bucketRemovalPolicy, 88 | autoDeleteObjects: true, 89 | }); 90 | } else { 91 | k3sBucket = new s3.Bucket(this, 'k3sBucket', { 92 | removalPolicy: cdk.RemovalPolicy.RETAIN, 93 | }); 94 | } 95 | 96 | // control plane node Security Group 97 | const k3scontrolplanesg = new ec2.SecurityGroup(this, 'k3s-controlplane-SG', { vpc }); 98 | k3scontrolplanesg.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(22), 'SSH'); 99 | k3scontrolplanesg.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(6443), 'K3s port'); 100 | 101 | // worker nodes Security Group 102 | const k3sworkersg = new ec2.SecurityGroup(this, 'k3s-worker-SG', { vpc }); 103 | // for this prototype the workers are being placed in a public subnet 104 | // ideally they should land on a private subnet 105 | /// also ingress traffic - ssh (bastion style) or 6443 - should come from the control plane node only 106 | k3sworkersg.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(22), 'SSH'); 107 | k3sworkersg.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(6443), 'K3s port'); 108 | 109 | // check if the user requires a particular instance type for workers and control plane 110 | // if not, the default instance type is used 111 | this.controlPlaneInstanceType = props.controlPlaneInstanceType ?? DEFAULT_INSTANCE_TYPE; 112 | this.workerInstanceType = props.workerInstanceType ?? DEFAULT_INSTANCE_TYPE; 113 | 114 | // create control plane node 115 | const k3scontrolplane = new ec2.Instance(this, 'k3s-controlplane', { 116 | instanceType: this.controlPlaneInstanceType, 117 | machineImage: new AmiProvider().amiId, 118 | vpc, 119 | vpcSubnets: { 120 | subnets: vpc.publicSubnets, 121 | }, 122 | instanceName: 'k3s-controlplane', 123 | securityGroup: k3scontrolplanesg, 124 | }); 125 | 126 | k3scontrolplane.addUserData(` 127 | #!/bin/bash 128 | curl -L -o k3s https://github.com/rancher/k3s/releases/download/v1.16.9%2Bk3s1/k3s-arm64 129 | chmod +x k3s 130 | ./k3s server & 131 | sleep 30 132 | ENDPOINT=$(curl http://169.254.169.254/latest/meta-data/public-hostname) 133 | cp /etc/rancher/k3s/k3s.yaml /etc/rancher/k3s/kubeconfig.yaml 134 | sed -i s/127.0.0.1/$ENDPOINT/ /etc/rancher/k3s/kubeconfig.yaml 135 | aws s3 cp /var/lib/rancher/k3s/server/node-token s3://${k3sBucket.bucketName}/node-token 136 | aws s3 cp /etc/rancher/k3s/kubeconfig.yaml s3://${k3sBucket.bucketName}/kubeconfig.yaml 137 | `); 138 | 139 | 140 | this.endpointUri = k3scontrolplane.instancePublicIp; 141 | 142 | // create launch template for worker ASG 143 | // prepare the userData 144 | const userData = ec2.UserData.forLinux(); 145 | userData.addCommands(` 146 | #!/bin/bash 147 | LOGFILE='/var/log/k3s.log' 148 | curl -L -o k3s https://github.com/rancher/k3s/releases/download/v1.16.13%2Bk3s1/k3s-arm64 149 | chmod +x k3s 150 | echo the bucket name is ${k3sBucket.bucketName} 151 | aws s3 cp s3://${k3sBucket.bucketName}/node-token /node-token 152 | (./k3s agent --server https://${k3scontrolplane.instancePrivateIp}:6443 \ 153 | --token $(cat /node-token) 2>&1 | tee -a $LOGFILE || echo "failed" > $LOGFILE &) 154 | `); 155 | 156 | // create worker ASG 157 | const workerAsg = new autoscaling.AutoScalingGroup(this, 'WorkerAsg', { 158 | instanceType: this.workerInstanceType, 159 | machineImage: new AmiProvider().amiId, 160 | vpc, 161 | vpcSubnets: { 162 | subnetType: ec2.SubnetType.PUBLIC, 163 | }, 164 | minCapacity: props.workerMinCapacity ?? 3, 165 | }); 166 | 167 | const cfnInstanceProfile = workerAsg.node.tryFindChild('InstanceProfile') as iam.CfnInstanceProfile; 168 | const lt = new ec2.CfnLaunchTemplate(this, 'WorkerLaunchTemplate', { 169 | launchTemplateData: { 170 | imageId: new AmiProvider().amiId.getImage(this).imageId, 171 | instanceType: this.workerInstanceType.toString(), 172 | instanceMarketOptions: { 173 | marketType: props.spotWorkerNodes ? 'spot' : undefined, 174 | spotOptions: props.spotWorkerNodes ? { 175 | spotInstanceType: 'one-time', 176 | } : undefined, 177 | }, 178 | userData: cdk.Fn.base64(userData.render()), 179 | iamInstanceProfile: { 180 | arn: cfnInstanceProfile.attrArn, 181 | }, 182 | }, 183 | }); 184 | const cfnAsg = workerAsg.node.tryFindChild('ASG') as autoscaling.CfnAutoScalingGroup; 185 | cfnAsg.addPropertyDeletionOverride('LaunchConfigurationName'); 186 | cfnAsg.addPropertyOverride('LaunchTemplate', { 187 | LaunchTemplateId: lt.ref, 188 | Version: lt.attrLatestVersionNumber, 189 | }); 190 | 191 | workerAsg.addSecurityGroup(k3sworkersg); 192 | 193 | // enable the SSM session manager 194 | workerAsg.role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMManagedInstanceCore')); 195 | 196 | // grant the S3 write permission to the control plane node and read permissions to the worker nodes 197 | k3sBucket.grantWrite(k3scontrolplane.role); 198 | k3sBucket.grantRead(workerAsg.role); 199 | 200 | // endpoint info 201 | new cdk.CfnOutput(this, 'Endpoint', { value: `https://${k3scontrolplane.instancePublicIp}:6443` }); 202 | 203 | // kubeconfig.yaml path 204 | new cdk.CfnOutput(this, 'Kubernetes configuration file', { value: `s3://${k3sBucket.bucketName}/kubeconfig.yaml` }); 205 | 206 | workerAsg.node.addDependency(k3scontrolplane); 207 | } 208 | } 209 | 210 | /** 211 | * The AMI provider to get the latest Amazon Linux 2 AMI for ARM64 212 | */ 213 | export class AmiProvider { 214 | public get amiId() { 215 | return ec2.MachineImage.latestAmazonLinux({ 216 | cpuType: ec2.AmazonLinuxCpuType.ARM_64, 217 | generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2, 218 | }); 219 | } 220 | } 221 | 222 | /** 223 | * The VPC provider to create or import the VPC 224 | */ 225 | export class VpcProvider { 226 | public static getOrCreate(scope: cdk.Construct) { 227 | const vpc = scope.node.tryGetContext('use_default_vpc') === '1' ? 228 | ec2.Vpc.fromLookup(scope, 'Vpc', { isDefault: true }) : 229 | scope.node.tryGetContext('use_vpc_id') ? 230 | ec2.Vpc.fromLookup(scope, 'Vpc', { vpcId: scope.node.tryGetContext('use_vpc_id') }) : 231 | new ec2.Vpc(scope, 'Vpc', { maxAzs: 3, natGateways: 1 }); 232 | return vpc; 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /src/integ.default.ts: -------------------------------------------------------------------------------- 1 | import * as ec2 from '@aws-cdk/aws-ec2'; 2 | import { App, Stack, CfnOutput, RemovalPolicy } from '@aws-cdk/core'; 3 | import * as k3s from './'; 4 | 5 | export class IntegTesting { 6 | readonly stack: Stack[]; 7 | 8 | constructor() { 9 | const app = new App(); 10 | const env = { 11 | region: process.env.CDK_DEFAULT_REGION, 12 | account: process.env.CDK_DEFAULT_ACCOUNT, 13 | }; 14 | 15 | const stack = new Stack(app, 'testing-stack', { env }); 16 | 17 | const vpc = k3s.VpcProvider.getOrCreate(stack); 18 | 19 | const cluster = new k3s.Cluster(stack, 'Cluster', { 20 | vpc, 21 | spotWorkerNodes: true, 22 | workerMinCapacity: 1, 23 | workerInstanceType: new ec2.InstanceType('m6g.medium'), 24 | controlPlaneInstanceType: new ec2.InstanceType('m6g.medium'), 25 | bucketRemovalPolicy: RemovalPolicy.DESTROY, 26 | }); 27 | 28 | new CfnOutput(stack, 'EndpointURI', { value: cluster.endpointUri }); 29 | new CfnOutput(stack, 'Region', { value: Stack.of(stack).region }); 30 | this.stack = [stack]; 31 | }; 32 | } 33 | 34 | // run the integ testing 35 | new IntegTesting(); 36 | -------------------------------------------------------------------------------- /test/__snapshots__/integ.snapshot.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`integ snapshot validation 1`] = ` 4 | Object { 5 | "Outputs": Object { 6 | "ClusterEndpoint6A9A88C6": Object { 7 | "Value": Object { 8 | "Fn::Join": Array [ 9 | "", 10 | Array [ 11 | "https://", 12 | Object { 13 | "Fn::GetAtt": Array [ 14 | "Clusterk3scontrolplaneB59785BE", 15 | "PublicIp", 16 | ], 17 | }, 18 | ":6443", 19 | ], 20 | ], 21 | }, 22 | }, 23 | "ClusterKubernetesconfigurationfileF12D6B54": Object { 24 | "Value": Object { 25 | "Fn::Join": Array [ 26 | "", 27 | Array [ 28 | "s3://", 29 | Object { 30 | "Ref": "Clusterk3sBucket0ECC11AD", 31 | }, 32 | "/kubeconfig.yaml", 33 | ], 34 | ], 35 | }, 36 | }, 37 | "EndpointURI": Object { 38 | "Value": Object { 39 | "Fn::GetAtt": Array [ 40 | "Clusterk3scontrolplaneB59785BE", 41 | "PublicIp", 42 | ], 43 | }, 44 | }, 45 | "Region": Object { 46 | "Value": Object { 47 | "Ref": "AWS::Region", 48 | }, 49 | }, 50 | }, 51 | "Parameters": Object { 52 | "AssetParameters87992593f580de1d66f5bb69d244245c18849ba03c37132d3a14899191352a68ArtifactHash4B825997": Object { 53 | "Description": "Artifact hash for asset \\"87992593f580de1d66f5bb69d244245c18849ba03c37132d3a14899191352a68\\"", 54 | "Type": "String", 55 | }, 56 | "AssetParameters87992593f580de1d66f5bb69d244245c18849ba03c37132d3a14899191352a68S3Bucket9D103635": Object { 57 | "Description": "S3 bucket for asset \\"87992593f580de1d66f5bb69d244245c18849ba03c37132d3a14899191352a68\\"", 58 | "Type": "String", 59 | }, 60 | "AssetParameters87992593f580de1d66f5bb69d244245c18849ba03c37132d3a14899191352a68S3VersionKey2A13B0CB": Object { 61 | "Description": "S3 key for asset version \\"87992593f580de1d66f5bb69d244245c18849ba03c37132d3a14899191352a68\\"", 62 | "Type": "String", 63 | }, 64 | "SsmParameterValueawsserviceamiamazonlinuxlatestamzn2amihvmarm64gp2C96584B6F00A464EAD1953AFF4B05118Parameter": Object { 65 | "Default": "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-arm64-gp2", 66 | "Type": "AWS::SSM::Parameter::Value", 67 | }, 68 | }, 69 | "Resources": Object { 70 | "ClusterWorkerAsgASGFC685DD3": Object { 71 | "DependsOn": Array [ 72 | "Clusterk3scontrolplaneInstanceProfile86CC8202", 73 | "Clusterk3scontrolplaneInstanceRoleDefaultPolicyCB631D58", 74 | "Clusterk3scontrolplaneInstanceRole5E5C95C5", 75 | "Clusterk3scontrolplaneB59785BE", 76 | ], 77 | "Properties": Object { 78 | "LaunchTemplate": Object { 79 | "LaunchTemplateId": Object { 80 | "Ref": "ClusterWorkerLaunchTemplate84D2244A", 81 | }, 82 | "Version": Object { 83 | "Fn::GetAtt": Array [ 84 | "ClusterWorkerLaunchTemplate84D2244A", 85 | "LatestVersionNumber", 86 | ], 87 | }, 88 | }, 89 | "MaxSize": "1", 90 | "MinSize": "1", 91 | "Tags": Array [ 92 | Object { 93 | "Key": "Name", 94 | "PropagateAtLaunch": true, 95 | "Value": "testing-stack/Cluster/WorkerAsg", 96 | }, 97 | ], 98 | "VPCZoneIdentifier": Array [ 99 | Object { 100 | "Ref": "VpcPublicSubnet1Subnet5C2D37C4", 101 | }, 102 | Object { 103 | "Ref": "VpcPublicSubnet2Subnet691E08A3", 104 | }, 105 | ], 106 | }, 107 | "Type": "AWS::AutoScaling::AutoScalingGroup", 108 | "UpdatePolicy": Object { 109 | "AutoScalingScheduledAction": Object { 110 | "IgnoreUnmodifiedGroupSizeProperties": true, 111 | }, 112 | }, 113 | }, 114 | "ClusterWorkerAsgInstanceProfile3B8CC231": Object { 115 | "DependsOn": Array [ 116 | "Clusterk3scontrolplaneInstanceProfile86CC8202", 117 | "Clusterk3scontrolplaneInstanceRoleDefaultPolicyCB631D58", 118 | "Clusterk3scontrolplaneInstanceRole5E5C95C5", 119 | "Clusterk3scontrolplaneB59785BE", 120 | ], 121 | "Properties": Object { 122 | "Roles": Array [ 123 | Object { 124 | "Ref": "ClusterWorkerAsgInstanceRole91EE5653", 125 | }, 126 | ], 127 | }, 128 | "Type": "AWS::IAM::InstanceProfile", 129 | }, 130 | "ClusterWorkerAsgInstanceRole91EE5653": Object { 131 | "DependsOn": Array [ 132 | "Clusterk3scontrolplaneInstanceProfile86CC8202", 133 | "Clusterk3scontrolplaneInstanceRoleDefaultPolicyCB631D58", 134 | "Clusterk3scontrolplaneInstanceRole5E5C95C5", 135 | "Clusterk3scontrolplaneB59785BE", 136 | ], 137 | "Properties": Object { 138 | "AssumeRolePolicyDocument": Object { 139 | "Statement": Array [ 140 | Object { 141 | "Action": "sts:AssumeRole", 142 | "Effect": "Allow", 143 | "Principal": Object { 144 | "Service": Object { 145 | "Fn::Join": Array [ 146 | "", 147 | Array [ 148 | "ec2.", 149 | Object { 150 | "Ref": "AWS::URLSuffix", 151 | }, 152 | ], 153 | ], 154 | }, 155 | }, 156 | }, 157 | ], 158 | "Version": "2012-10-17", 159 | }, 160 | "ManagedPolicyArns": Array [ 161 | Object { 162 | "Fn::Join": Array [ 163 | "", 164 | Array [ 165 | "arn:", 166 | Object { 167 | "Ref": "AWS::Partition", 168 | }, 169 | ":iam::aws:policy/AmazonSSMManagedInstanceCore", 170 | ], 171 | ], 172 | }, 173 | ], 174 | "Tags": Array [ 175 | Object { 176 | "Key": "Name", 177 | "Value": "testing-stack/Cluster/WorkerAsg", 178 | }, 179 | ], 180 | }, 181 | "Type": "AWS::IAM::Role", 182 | }, 183 | "ClusterWorkerAsgInstanceRoleDefaultPolicy4C92ADD4": Object { 184 | "DependsOn": Array [ 185 | "Clusterk3scontrolplaneInstanceProfile86CC8202", 186 | "Clusterk3scontrolplaneInstanceRoleDefaultPolicyCB631D58", 187 | "Clusterk3scontrolplaneInstanceRole5E5C95C5", 188 | "Clusterk3scontrolplaneB59785BE", 189 | ], 190 | "Properties": Object { 191 | "PolicyDocument": Object { 192 | "Statement": Array [ 193 | Object { 194 | "Action": Array [ 195 | "s3:GetObject*", 196 | "s3:GetBucket*", 197 | "s3:List*", 198 | ], 199 | "Effect": "Allow", 200 | "Resource": Array [ 201 | Object { 202 | "Fn::GetAtt": Array [ 203 | "Clusterk3sBucket0ECC11AD", 204 | "Arn", 205 | ], 206 | }, 207 | Object { 208 | "Fn::Join": Array [ 209 | "", 210 | Array [ 211 | Object { 212 | "Fn::GetAtt": Array [ 213 | "Clusterk3sBucket0ECC11AD", 214 | "Arn", 215 | ], 216 | }, 217 | "/*", 218 | ], 219 | ], 220 | }, 221 | ], 222 | }, 223 | ], 224 | "Version": "2012-10-17", 225 | }, 226 | "PolicyName": "ClusterWorkerAsgInstanceRoleDefaultPolicy4C92ADD4", 227 | "Roles": Array [ 228 | Object { 229 | "Ref": "ClusterWorkerAsgInstanceRole91EE5653", 230 | }, 231 | ], 232 | }, 233 | "Type": "AWS::IAM::Policy", 234 | }, 235 | "ClusterWorkerAsgInstanceSecurityGroupB2CC9585": Object { 236 | "DependsOn": Array [ 237 | "Clusterk3scontrolplaneInstanceProfile86CC8202", 238 | "Clusterk3scontrolplaneInstanceRoleDefaultPolicyCB631D58", 239 | "Clusterk3scontrolplaneInstanceRole5E5C95C5", 240 | "Clusterk3scontrolplaneB59785BE", 241 | ], 242 | "Properties": Object { 243 | "GroupDescription": "testing-stack/Cluster/WorkerAsg/InstanceSecurityGroup", 244 | "SecurityGroupEgress": Array [ 245 | Object { 246 | "CidrIp": "0.0.0.0/0", 247 | "Description": "Allow all outbound traffic by default", 248 | "IpProtocol": "-1", 249 | }, 250 | ], 251 | "Tags": Array [ 252 | Object { 253 | "Key": "Name", 254 | "Value": "testing-stack/Cluster/WorkerAsg", 255 | }, 256 | ], 257 | "VpcId": Object { 258 | "Ref": "Vpc8378EB38", 259 | }, 260 | }, 261 | "Type": "AWS::EC2::SecurityGroup", 262 | }, 263 | "ClusterWorkerAsgLaunchConfig70B7BCB1": Object { 264 | "DependsOn": Array [ 265 | "Clusterk3scontrolplaneInstanceProfile86CC8202", 266 | "Clusterk3scontrolplaneInstanceRoleDefaultPolicyCB631D58", 267 | "Clusterk3scontrolplaneInstanceRole5E5C95C5", 268 | "Clusterk3scontrolplaneB59785BE", 269 | "ClusterWorkerAsgInstanceRoleDefaultPolicy4C92ADD4", 270 | "ClusterWorkerAsgInstanceRole91EE5653", 271 | ], 272 | "Properties": Object { 273 | "IamInstanceProfile": Object { 274 | "Ref": "ClusterWorkerAsgInstanceProfile3B8CC231", 275 | }, 276 | "ImageId": Object { 277 | "Ref": "SsmParameterValueawsserviceamiamazonlinuxlatestamzn2amihvmarm64gp2C96584B6F00A464EAD1953AFF4B05118Parameter", 278 | }, 279 | "InstanceType": "m6g.medium", 280 | "SecurityGroups": Array [ 281 | Object { 282 | "Fn::GetAtt": Array [ 283 | "ClusterWorkerAsgInstanceSecurityGroupB2CC9585", 284 | "GroupId", 285 | ], 286 | }, 287 | Object { 288 | "Fn::GetAtt": Array [ 289 | "Clusterk3sworkerSGA0EEA026", 290 | "GroupId", 291 | ], 292 | }, 293 | ], 294 | "UserData": Object { 295 | "Fn::Base64": "#!/bin/bash", 296 | }, 297 | }, 298 | "Type": "AWS::AutoScaling::LaunchConfiguration", 299 | }, 300 | "ClusterWorkerLaunchTemplate84D2244A": Object { 301 | "Properties": Object { 302 | "LaunchTemplateData": Object { 303 | "IamInstanceProfile": Object { 304 | "Arn": Object { 305 | "Fn::GetAtt": Array [ 306 | "ClusterWorkerAsgInstanceProfile3B8CC231", 307 | "Arn", 308 | ], 309 | }, 310 | }, 311 | "ImageId": Object { 312 | "Ref": "SsmParameterValueawsserviceamiamazonlinuxlatestamzn2amihvmarm64gp2C96584B6F00A464EAD1953AFF4B05118Parameter", 313 | }, 314 | "InstanceMarketOptions": Object { 315 | "MarketType": "spot", 316 | "SpotOptions": Object { 317 | "SpotInstanceType": "one-time", 318 | }, 319 | }, 320 | "InstanceType": "m6g.medium", 321 | "UserData": Object { 322 | "Fn::Base64": Object { 323 | "Fn::Join": Array [ 324 | "", 325 | Array [ 326 | "#!/bin/bash 327 | 328 | #!/bin/bash 329 | LOGFILE='/var/log/k3s.log' 330 | curl -L -o k3s https://github.com/rancher/k3s/releases/download/v1.16.13%2Bk3s1/k3s-arm64 331 | chmod +x k3s 332 | echo the bucket name is ", 333 | Object { 334 | "Ref": "Clusterk3sBucket0ECC11AD", 335 | }, 336 | " 337 | aws s3 cp s3://", 338 | Object { 339 | "Ref": "Clusterk3sBucket0ECC11AD", 340 | }, 341 | "/node-token /node-token 342 | (./k3s agent --server https://", 343 | Object { 344 | "Fn::GetAtt": Array [ 345 | "Clusterk3scontrolplaneB59785BE", 346 | "PrivateIp", 347 | ], 348 | }, 349 | ":6443 --token $(cat /node-token) 2>&1 | tee -a $LOGFILE || echo \\"failed\\" > $LOGFILE &) 350 | ", 351 | ], 352 | ], 353 | }, 354 | }, 355 | }, 356 | }, 357 | "Type": "AWS::EC2::LaunchTemplate", 358 | }, 359 | "Clusterk3sBucket0ECC11AD": Object { 360 | "DeletionPolicy": "Delete", 361 | "Properties": Object { 362 | "Tags": Array [ 363 | Object { 364 | "Key": "aws-cdk:auto-delete-objects", 365 | "Value": "true", 366 | }, 367 | ], 368 | }, 369 | "Type": "AWS::S3::Bucket", 370 | "UpdateReplacePolicy": "Delete", 371 | }, 372 | "Clusterk3sBucketAutoDeleteObjectsCustomResource0E94CC15": Object { 373 | "DeletionPolicy": "Delete", 374 | "DependsOn": Array [ 375 | "Clusterk3sBucketPolicy8881C289", 376 | ], 377 | "Properties": Object { 378 | "BucketName": Object { 379 | "Ref": "Clusterk3sBucket0ECC11AD", 380 | }, 381 | "ServiceToken": Object { 382 | "Fn::GetAtt": Array [ 383 | "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", 384 | "Arn", 385 | ], 386 | }, 387 | }, 388 | "Type": "Custom::S3AutoDeleteObjects", 389 | "UpdateReplacePolicy": "Delete", 390 | }, 391 | "Clusterk3sBucketPolicy8881C289": Object { 392 | "Properties": Object { 393 | "Bucket": Object { 394 | "Ref": "Clusterk3sBucket0ECC11AD", 395 | }, 396 | "PolicyDocument": Object { 397 | "Statement": Array [ 398 | Object { 399 | "Action": Array [ 400 | "s3:GetBucket*", 401 | "s3:List*", 402 | "s3:DeleteObject*", 403 | ], 404 | "Effect": "Allow", 405 | "Principal": Object { 406 | "AWS": Object { 407 | "Fn::GetAtt": Array [ 408 | "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", 409 | "Arn", 410 | ], 411 | }, 412 | }, 413 | "Resource": Array [ 414 | Object { 415 | "Fn::GetAtt": Array [ 416 | "Clusterk3sBucket0ECC11AD", 417 | "Arn", 418 | ], 419 | }, 420 | Object { 421 | "Fn::Join": Array [ 422 | "", 423 | Array [ 424 | Object { 425 | "Fn::GetAtt": Array [ 426 | "Clusterk3sBucket0ECC11AD", 427 | "Arn", 428 | ], 429 | }, 430 | "/*", 431 | ], 432 | ], 433 | }, 434 | ], 435 | }, 436 | ], 437 | "Version": "2012-10-17", 438 | }, 439 | }, 440 | "Type": "AWS::S3::BucketPolicy", 441 | }, 442 | "Clusterk3scontrolplaneB59785BE": Object { 443 | "DependsOn": Array [ 444 | "Clusterk3scontrolplaneInstanceRoleDefaultPolicyCB631D58", 445 | "Clusterk3scontrolplaneInstanceRole5E5C95C5", 446 | ], 447 | "Properties": Object { 448 | "AvailabilityZone": Object { 449 | "Fn::Select": Array [ 450 | 0, 451 | Object { 452 | "Fn::GetAZs": "", 453 | }, 454 | ], 455 | }, 456 | "IamInstanceProfile": Object { 457 | "Ref": "Clusterk3scontrolplaneInstanceProfile86CC8202", 458 | }, 459 | "ImageId": Object { 460 | "Ref": "SsmParameterValueawsserviceamiamazonlinuxlatestamzn2amihvmarm64gp2C96584B6F00A464EAD1953AFF4B05118Parameter", 461 | }, 462 | "InstanceType": "m6g.medium", 463 | "SecurityGroupIds": Array [ 464 | Object { 465 | "Fn::GetAtt": Array [ 466 | "Clusterk3scontrolplaneSGCFC3EC51", 467 | "GroupId", 468 | ], 469 | }, 470 | ], 471 | "SubnetId": Object { 472 | "Ref": "VpcPublicSubnet1Subnet5C2D37C4", 473 | }, 474 | "Tags": Array [ 475 | Object { 476 | "Key": "Name", 477 | "Value": "k3s-controlplane", 478 | }, 479 | ], 480 | "UserData": Object { 481 | "Fn::Base64": Object { 482 | "Fn::Join": Array [ 483 | "", 484 | Array [ 485 | "#!/bin/bash 486 | 487 | #!/bin/bash 488 | curl -L -o k3s https://github.com/rancher/k3s/releases/download/v1.16.9%2Bk3s1/k3s-arm64 489 | chmod +x k3s 490 | ./k3s server & 491 | sleep 30 492 | ENDPOINT=$(curl http://169.254.169.254/latest/meta-data/public-hostname) 493 | cp /etc/rancher/k3s/k3s.yaml /etc/rancher/k3s/kubeconfig.yaml 494 | sed -i s/127.0.0.1/$ENDPOINT/ /etc/rancher/k3s/kubeconfig.yaml 495 | aws s3 cp /var/lib/rancher/k3s/server/node-token s3://", 496 | Object { 497 | "Ref": "Clusterk3sBucket0ECC11AD", 498 | }, 499 | "/node-token 500 | aws s3 cp /etc/rancher/k3s/kubeconfig.yaml s3://", 501 | Object { 502 | "Ref": "Clusterk3sBucket0ECC11AD", 503 | }, 504 | "/kubeconfig.yaml 505 | ", 506 | ], 507 | ], 508 | }, 509 | }, 510 | }, 511 | "Type": "AWS::EC2::Instance", 512 | }, 513 | "Clusterk3scontrolplaneInstanceProfile86CC8202": Object { 514 | "Properties": Object { 515 | "Roles": Array [ 516 | Object { 517 | "Ref": "Clusterk3scontrolplaneInstanceRole5E5C95C5", 518 | }, 519 | ], 520 | }, 521 | "Type": "AWS::IAM::InstanceProfile", 522 | }, 523 | "Clusterk3scontrolplaneInstanceRole5E5C95C5": Object { 524 | "Properties": Object { 525 | "AssumeRolePolicyDocument": Object { 526 | "Statement": Array [ 527 | Object { 528 | "Action": "sts:AssumeRole", 529 | "Effect": "Allow", 530 | "Principal": Object { 531 | "Service": Object { 532 | "Fn::Join": Array [ 533 | "", 534 | Array [ 535 | "ec2.", 536 | Object { 537 | "Ref": "AWS::URLSuffix", 538 | }, 539 | ], 540 | ], 541 | }, 542 | }, 543 | }, 544 | ], 545 | "Version": "2012-10-17", 546 | }, 547 | "Tags": Array [ 548 | Object { 549 | "Key": "Name", 550 | "Value": "k3s-controlplane", 551 | }, 552 | ], 553 | }, 554 | "Type": "AWS::IAM::Role", 555 | }, 556 | "Clusterk3scontrolplaneInstanceRoleDefaultPolicyCB631D58": Object { 557 | "Properties": Object { 558 | "PolicyDocument": Object { 559 | "Statement": Array [ 560 | Object { 561 | "Action": Array [ 562 | "s3:DeleteObject*", 563 | "s3:PutObject*", 564 | "s3:Abort*", 565 | ], 566 | "Effect": "Allow", 567 | "Resource": Array [ 568 | Object { 569 | "Fn::GetAtt": Array [ 570 | "Clusterk3sBucket0ECC11AD", 571 | "Arn", 572 | ], 573 | }, 574 | Object { 575 | "Fn::Join": Array [ 576 | "", 577 | Array [ 578 | Object { 579 | "Fn::GetAtt": Array [ 580 | "Clusterk3sBucket0ECC11AD", 581 | "Arn", 582 | ], 583 | }, 584 | "/*", 585 | ], 586 | ], 587 | }, 588 | ], 589 | }, 590 | ], 591 | "Version": "2012-10-17", 592 | }, 593 | "PolicyName": "Clusterk3scontrolplaneInstanceRoleDefaultPolicyCB631D58", 594 | "Roles": Array [ 595 | Object { 596 | "Ref": "Clusterk3scontrolplaneInstanceRole5E5C95C5", 597 | }, 598 | ], 599 | }, 600 | "Type": "AWS::IAM::Policy", 601 | }, 602 | "Clusterk3scontrolplaneSGCFC3EC51": Object { 603 | "Properties": Object { 604 | "GroupDescription": "testing-stack/Cluster/k3s-controlplane-SG", 605 | "SecurityGroupEgress": Array [ 606 | Object { 607 | "CidrIp": "0.0.0.0/0", 608 | "Description": "Allow all outbound traffic by default", 609 | "IpProtocol": "-1", 610 | }, 611 | ], 612 | "SecurityGroupIngress": Array [ 613 | Object { 614 | "CidrIp": "0.0.0.0/0", 615 | "Description": "SSH", 616 | "FromPort": 22, 617 | "IpProtocol": "tcp", 618 | "ToPort": 22, 619 | }, 620 | Object { 621 | "CidrIp": "0.0.0.0/0", 622 | "Description": "K3s port", 623 | "FromPort": 6443, 624 | "IpProtocol": "tcp", 625 | "ToPort": 6443, 626 | }, 627 | ], 628 | "VpcId": Object { 629 | "Ref": "Vpc8378EB38", 630 | }, 631 | }, 632 | "Type": "AWS::EC2::SecurityGroup", 633 | }, 634 | "Clusterk3sworkerSGA0EEA026": Object { 635 | "Properties": Object { 636 | "GroupDescription": "testing-stack/Cluster/k3s-worker-SG", 637 | "SecurityGroupEgress": Array [ 638 | Object { 639 | "CidrIp": "0.0.0.0/0", 640 | "Description": "Allow all outbound traffic by default", 641 | "IpProtocol": "-1", 642 | }, 643 | ], 644 | "SecurityGroupIngress": Array [ 645 | Object { 646 | "CidrIp": "0.0.0.0/0", 647 | "Description": "SSH", 648 | "FromPort": 22, 649 | "IpProtocol": "tcp", 650 | "ToPort": 22, 651 | }, 652 | Object { 653 | "CidrIp": "0.0.0.0/0", 654 | "Description": "K3s port", 655 | "FromPort": 6443, 656 | "IpProtocol": "tcp", 657 | "ToPort": 6443, 658 | }, 659 | ], 660 | "VpcId": Object { 661 | "Ref": "Vpc8378EB38", 662 | }, 663 | }, 664 | "Type": "AWS::EC2::SecurityGroup", 665 | }, 666 | "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F": Object { 667 | "DependsOn": Array [ 668 | "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", 669 | ], 670 | "Properties": Object { 671 | "Code": Object { 672 | "S3Bucket": Object { 673 | "Ref": "AssetParameters87992593f580de1d66f5bb69d244245c18849ba03c37132d3a14899191352a68S3Bucket9D103635", 674 | }, 675 | "S3Key": Object { 676 | "Fn::Join": Array [ 677 | "", 678 | Array [ 679 | Object { 680 | "Fn::Select": Array [ 681 | 0, 682 | Object { 683 | "Fn::Split": Array [ 684 | "||", 685 | Object { 686 | "Ref": "AssetParameters87992593f580de1d66f5bb69d244245c18849ba03c37132d3a14899191352a68S3VersionKey2A13B0CB", 687 | }, 688 | ], 689 | }, 690 | ], 691 | }, 692 | Object { 693 | "Fn::Select": Array [ 694 | 1, 695 | Object { 696 | "Fn::Split": Array [ 697 | "||", 698 | Object { 699 | "Ref": "AssetParameters87992593f580de1d66f5bb69d244245c18849ba03c37132d3a14899191352a68S3VersionKey2A13B0CB", 700 | }, 701 | ], 702 | }, 703 | ], 704 | }, 705 | ], 706 | ], 707 | }, 708 | }, 709 | "Description": Object { 710 | "Fn::Join": Array [ 711 | "", 712 | Array [ 713 | "Lambda function for auto-deleting objects in ", 714 | Object { 715 | "Ref": "Clusterk3sBucket0ECC11AD", 716 | }, 717 | " S3 bucket.", 718 | ], 719 | ], 720 | }, 721 | "Handler": "__entrypoint__.handler", 722 | "MemorySize": 128, 723 | "Role": Object { 724 | "Fn::GetAtt": Array [ 725 | "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", 726 | "Arn", 727 | ], 728 | }, 729 | "Runtime": "nodejs14.x", 730 | "Timeout": 900, 731 | }, 732 | "Type": "AWS::Lambda::Function", 733 | }, 734 | "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092": Object { 735 | "Properties": Object { 736 | "AssumeRolePolicyDocument": Object { 737 | "Statement": Array [ 738 | Object { 739 | "Action": "sts:AssumeRole", 740 | "Effect": "Allow", 741 | "Principal": Object { 742 | "Service": "lambda.amazonaws.com", 743 | }, 744 | }, 745 | ], 746 | "Version": "2012-10-17", 747 | }, 748 | "ManagedPolicyArns": Array [ 749 | Object { 750 | "Fn::Sub": "arn:\${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", 751 | }, 752 | ], 753 | }, 754 | "Type": "AWS::IAM::Role", 755 | }, 756 | "Vpc8378EB38": Object { 757 | "Properties": Object { 758 | "CidrBlock": "10.0.0.0/16", 759 | "EnableDnsHostnames": true, 760 | "EnableDnsSupport": true, 761 | "InstanceTenancy": "default", 762 | "Tags": Array [ 763 | Object { 764 | "Key": "Name", 765 | "Value": "testing-stack/Vpc", 766 | }, 767 | ], 768 | }, 769 | "Type": "AWS::EC2::VPC", 770 | }, 771 | "VpcIGWD7BA715C": Object { 772 | "Properties": Object { 773 | "Tags": Array [ 774 | Object { 775 | "Key": "Name", 776 | "Value": "testing-stack/Vpc", 777 | }, 778 | ], 779 | }, 780 | "Type": "AWS::EC2::InternetGateway", 781 | }, 782 | "VpcPrivateSubnet1DefaultRouteBE02A9ED": Object { 783 | "Properties": Object { 784 | "DestinationCidrBlock": "0.0.0.0/0", 785 | "NatGatewayId": Object { 786 | "Ref": "VpcPublicSubnet1NATGateway4D7517AA", 787 | }, 788 | "RouteTableId": Object { 789 | "Ref": "VpcPrivateSubnet1RouteTableB2C5B500", 790 | }, 791 | }, 792 | "Type": "AWS::EC2::Route", 793 | }, 794 | "VpcPrivateSubnet1RouteTableAssociation70C59FA6": Object { 795 | "Properties": Object { 796 | "RouteTableId": Object { 797 | "Ref": "VpcPrivateSubnet1RouteTableB2C5B500", 798 | }, 799 | "SubnetId": Object { 800 | "Ref": "VpcPrivateSubnet1Subnet536B997A", 801 | }, 802 | }, 803 | "Type": "AWS::EC2::SubnetRouteTableAssociation", 804 | }, 805 | "VpcPrivateSubnet1RouteTableB2C5B500": Object { 806 | "Properties": Object { 807 | "Tags": Array [ 808 | Object { 809 | "Key": "Name", 810 | "Value": "testing-stack/Vpc/PrivateSubnet1", 811 | }, 812 | ], 813 | "VpcId": Object { 814 | "Ref": "Vpc8378EB38", 815 | }, 816 | }, 817 | "Type": "AWS::EC2::RouteTable", 818 | }, 819 | "VpcPrivateSubnet1Subnet536B997A": Object { 820 | "Properties": Object { 821 | "AvailabilityZone": Object { 822 | "Fn::Select": Array [ 823 | 0, 824 | Object { 825 | "Fn::GetAZs": "", 826 | }, 827 | ], 828 | }, 829 | "CidrBlock": "10.0.128.0/18", 830 | "MapPublicIpOnLaunch": false, 831 | "Tags": Array [ 832 | Object { 833 | "Key": "aws-cdk:subnet-name", 834 | "Value": "Private", 835 | }, 836 | Object { 837 | "Key": "aws-cdk:subnet-type", 838 | "Value": "Private", 839 | }, 840 | Object { 841 | "Key": "Name", 842 | "Value": "testing-stack/Vpc/PrivateSubnet1", 843 | }, 844 | ], 845 | "VpcId": Object { 846 | "Ref": "Vpc8378EB38", 847 | }, 848 | }, 849 | "Type": "AWS::EC2::Subnet", 850 | }, 851 | "VpcPrivateSubnet2DefaultRoute060D2087": Object { 852 | "Properties": Object { 853 | "DestinationCidrBlock": "0.0.0.0/0", 854 | "NatGatewayId": Object { 855 | "Ref": "VpcPublicSubnet1NATGateway4D7517AA", 856 | }, 857 | "RouteTableId": Object { 858 | "Ref": "VpcPrivateSubnet2RouteTableA678073B", 859 | }, 860 | }, 861 | "Type": "AWS::EC2::Route", 862 | }, 863 | "VpcPrivateSubnet2RouteTableA678073B": Object { 864 | "Properties": Object { 865 | "Tags": Array [ 866 | Object { 867 | "Key": "Name", 868 | "Value": "testing-stack/Vpc/PrivateSubnet2", 869 | }, 870 | ], 871 | "VpcId": Object { 872 | "Ref": "Vpc8378EB38", 873 | }, 874 | }, 875 | "Type": "AWS::EC2::RouteTable", 876 | }, 877 | "VpcPrivateSubnet2RouteTableAssociationA89CAD56": Object { 878 | "Properties": Object { 879 | "RouteTableId": Object { 880 | "Ref": "VpcPrivateSubnet2RouteTableA678073B", 881 | }, 882 | "SubnetId": Object { 883 | "Ref": "VpcPrivateSubnet2Subnet3788AAA1", 884 | }, 885 | }, 886 | "Type": "AWS::EC2::SubnetRouteTableAssociation", 887 | }, 888 | "VpcPrivateSubnet2Subnet3788AAA1": Object { 889 | "Properties": Object { 890 | "AvailabilityZone": Object { 891 | "Fn::Select": Array [ 892 | 1, 893 | Object { 894 | "Fn::GetAZs": "", 895 | }, 896 | ], 897 | }, 898 | "CidrBlock": "10.0.192.0/18", 899 | "MapPublicIpOnLaunch": false, 900 | "Tags": Array [ 901 | Object { 902 | "Key": "aws-cdk:subnet-name", 903 | "Value": "Private", 904 | }, 905 | Object { 906 | "Key": "aws-cdk:subnet-type", 907 | "Value": "Private", 908 | }, 909 | Object { 910 | "Key": "Name", 911 | "Value": "testing-stack/Vpc/PrivateSubnet2", 912 | }, 913 | ], 914 | "VpcId": Object { 915 | "Ref": "Vpc8378EB38", 916 | }, 917 | }, 918 | "Type": "AWS::EC2::Subnet", 919 | }, 920 | "VpcPublicSubnet1DefaultRoute3DA9E72A": Object { 921 | "DependsOn": Array [ 922 | "VpcVPCGWBF912B6E", 923 | ], 924 | "Properties": Object { 925 | "DestinationCidrBlock": "0.0.0.0/0", 926 | "GatewayId": Object { 927 | "Ref": "VpcIGWD7BA715C", 928 | }, 929 | "RouteTableId": Object { 930 | "Ref": "VpcPublicSubnet1RouteTable6C95E38E", 931 | }, 932 | }, 933 | "Type": "AWS::EC2::Route", 934 | }, 935 | "VpcPublicSubnet1EIPD7E02669": Object { 936 | "Properties": Object { 937 | "Domain": "vpc", 938 | "Tags": Array [ 939 | Object { 940 | "Key": "Name", 941 | "Value": "testing-stack/Vpc/PublicSubnet1", 942 | }, 943 | ], 944 | }, 945 | "Type": "AWS::EC2::EIP", 946 | }, 947 | "VpcPublicSubnet1NATGateway4D7517AA": Object { 948 | "Properties": Object { 949 | "AllocationId": Object { 950 | "Fn::GetAtt": Array [ 951 | "VpcPublicSubnet1EIPD7E02669", 952 | "AllocationId", 953 | ], 954 | }, 955 | "SubnetId": Object { 956 | "Ref": "VpcPublicSubnet1Subnet5C2D37C4", 957 | }, 958 | "Tags": Array [ 959 | Object { 960 | "Key": "Name", 961 | "Value": "testing-stack/Vpc/PublicSubnet1", 962 | }, 963 | ], 964 | }, 965 | "Type": "AWS::EC2::NatGateway", 966 | }, 967 | "VpcPublicSubnet1RouteTable6C95E38E": Object { 968 | "Properties": Object { 969 | "Tags": Array [ 970 | Object { 971 | "Key": "Name", 972 | "Value": "testing-stack/Vpc/PublicSubnet1", 973 | }, 974 | ], 975 | "VpcId": Object { 976 | "Ref": "Vpc8378EB38", 977 | }, 978 | }, 979 | "Type": "AWS::EC2::RouteTable", 980 | }, 981 | "VpcPublicSubnet1RouteTableAssociation97140677": Object { 982 | "Properties": Object { 983 | "RouteTableId": Object { 984 | "Ref": "VpcPublicSubnet1RouteTable6C95E38E", 985 | }, 986 | "SubnetId": Object { 987 | "Ref": "VpcPublicSubnet1Subnet5C2D37C4", 988 | }, 989 | }, 990 | "Type": "AWS::EC2::SubnetRouteTableAssociation", 991 | }, 992 | "VpcPublicSubnet1Subnet5C2D37C4": Object { 993 | "Properties": Object { 994 | "AvailabilityZone": Object { 995 | "Fn::Select": Array [ 996 | 0, 997 | Object { 998 | "Fn::GetAZs": "", 999 | }, 1000 | ], 1001 | }, 1002 | "CidrBlock": "10.0.0.0/18", 1003 | "MapPublicIpOnLaunch": true, 1004 | "Tags": Array [ 1005 | Object { 1006 | "Key": "aws-cdk:subnet-name", 1007 | "Value": "Public", 1008 | }, 1009 | Object { 1010 | "Key": "aws-cdk:subnet-type", 1011 | "Value": "Public", 1012 | }, 1013 | Object { 1014 | "Key": "Name", 1015 | "Value": "testing-stack/Vpc/PublicSubnet1", 1016 | }, 1017 | ], 1018 | "VpcId": Object { 1019 | "Ref": "Vpc8378EB38", 1020 | }, 1021 | }, 1022 | "Type": "AWS::EC2::Subnet", 1023 | }, 1024 | "VpcPublicSubnet2DefaultRoute97F91067": Object { 1025 | "DependsOn": Array [ 1026 | "VpcVPCGWBF912B6E", 1027 | ], 1028 | "Properties": Object { 1029 | "DestinationCidrBlock": "0.0.0.0/0", 1030 | "GatewayId": Object { 1031 | "Ref": "VpcIGWD7BA715C", 1032 | }, 1033 | "RouteTableId": Object { 1034 | "Ref": "VpcPublicSubnet2RouteTable94F7E489", 1035 | }, 1036 | }, 1037 | "Type": "AWS::EC2::Route", 1038 | }, 1039 | "VpcPublicSubnet2RouteTable94F7E489": Object { 1040 | "Properties": Object { 1041 | "Tags": Array [ 1042 | Object { 1043 | "Key": "Name", 1044 | "Value": "testing-stack/Vpc/PublicSubnet2", 1045 | }, 1046 | ], 1047 | "VpcId": Object { 1048 | "Ref": "Vpc8378EB38", 1049 | }, 1050 | }, 1051 | "Type": "AWS::EC2::RouteTable", 1052 | }, 1053 | "VpcPublicSubnet2RouteTableAssociationDD5762D8": Object { 1054 | "Properties": Object { 1055 | "RouteTableId": Object { 1056 | "Ref": "VpcPublicSubnet2RouteTable94F7E489", 1057 | }, 1058 | "SubnetId": Object { 1059 | "Ref": "VpcPublicSubnet2Subnet691E08A3", 1060 | }, 1061 | }, 1062 | "Type": "AWS::EC2::SubnetRouteTableAssociation", 1063 | }, 1064 | "VpcPublicSubnet2Subnet691E08A3": Object { 1065 | "Properties": Object { 1066 | "AvailabilityZone": Object { 1067 | "Fn::Select": Array [ 1068 | 1, 1069 | Object { 1070 | "Fn::GetAZs": "", 1071 | }, 1072 | ], 1073 | }, 1074 | "CidrBlock": "10.0.64.0/18", 1075 | "MapPublicIpOnLaunch": true, 1076 | "Tags": Array [ 1077 | Object { 1078 | "Key": "aws-cdk:subnet-name", 1079 | "Value": "Public", 1080 | }, 1081 | Object { 1082 | "Key": "aws-cdk:subnet-type", 1083 | "Value": "Public", 1084 | }, 1085 | Object { 1086 | "Key": "Name", 1087 | "Value": "testing-stack/Vpc/PublicSubnet2", 1088 | }, 1089 | ], 1090 | "VpcId": Object { 1091 | "Ref": "Vpc8378EB38", 1092 | }, 1093 | }, 1094 | "Type": "AWS::EC2::Subnet", 1095 | }, 1096 | "VpcVPCGWBF912B6E": Object { 1097 | "Properties": Object { 1098 | "InternetGatewayId": Object { 1099 | "Ref": "VpcIGWD7BA715C", 1100 | }, 1101 | "VpcId": Object { 1102 | "Ref": "Vpc8378EB38", 1103 | }, 1104 | }, 1105 | "Type": "AWS::EC2::VPCGatewayAttachment", 1106 | }, 1107 | }, 1108 | } 1109 | `; 1110 | -------------------------------------------------------------------------------- /test/cluster.test.ts: -------------------------------------------------------------------------------- 1 | import * as ec2 from '@aws-cdk/aws-ec2'; 2 | import { App, Stack, RemovalPolicy } from '@aws-cdk/core'; 3 | import * as k3s from '../src'; 4 | import '@aws-cdk/assert/jest'; 5 | 6 | test('create the default cluster', () => { 7 | 8 | // GIVEN 9 | const app = new App(); 10 | const stack = new Stack(app, 'testing-stack'); 11 | 12 | // WHEN 13 | new k3s.Cluster(stack, 'Cluster'); 14 | 15 | // THEN 16 | 17 | expect(stack).toHaveResource('AWS::AutoScaling::AutoScalingGroup', { 18 | MaxSize: '3', 19 | MinSize: '3', 20 | LaunchTemplate: { 21 | LaunchTemplateId: { 22 | Ref: 'ClusterWorkerLaunchTemplate84D2244A', 23 | }, 24 | Version: { 25 | 'Fn::GetAtt': [ 26 | 'ClusterWorkerLaunchTemplate84D2244A', 27 | 'LatestVersionNumber', 28 | ], 29 | }, 30 | }, 31 | }); 32 | 33 | expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration'); 34 | }); 35 | 36 | test('add s3 removalPolicy', () => { 37 | const app = new App(); 38 | const stack = new Stack(app, 'testing-stack'); 39 | new k3s.Cluster(stack, 'Cluster-s3-removalPolicy', { 40 | bucketRemovalPolicy: RemovalPolicy.DESTROY, 41 | }); 42 | expect(stack).toHaveResource('AWS::S3::Bucket'); 43 | }); 44 | 45 | test('support m6g instance types', () => { 46 | // GIVEN 47 | const app = new App(); 48 | const stack = new Stack(app, 'testing-stack'); 49 | // WHEN 50 | new k3s.Cluster(stack, 'Cluster-test', { 51 | bucketRemovalPolicy: RemovalPolicy.DESTROY, 52 | controlPlaneInstanceType: new ec2.InstanceType('m6g.large'), 53 | workerInstanceType: new ec2.InstanceType('m6g.medium'), 54 | spotWorkerNodes: true, 55 | }); 56 | // THEN 57 | // worker nodes ASG 58 | expect(stack).toHaveResourceLike('AWS::EC2::LaunchTemplate', { 59 | LaunchTemplateData: { 60 | ImageId: { 61 | Ref: 'SsmParameterValueawsserviceamiamazonlinuxlatestamzn2amihvmarm64gp2C96584B6F00A464EAD1953AFF4B05118Parameter', 62 | }, 63 | InstanceMarketOptions: { 64 | MarketType: 'spot', 65 | SpotOptions: { 66 | SpotInstanceType: 'one-time', 67 | }, 68 | }, 69 | InstanceType: 'm6g.medium', 70 | }, 71 | }); 72 | // control plane ec2 73 | expect(stack).toHaveResource('AWS::EC2::Instance', { 74 | InstanceType: 'm6g.large', 75 | }); 76 | }); 77 | 78 | test('support t4g instance types', () => { 79 | // GIVEN 80 | const app = new App(); 81 | const stack = new Stack(app, 'testing-stack'); 82 | // WHEN 83 | new k3s.Cluster(stack, 'Cluster-test', { 84 | bucketRemovalPolicy: RemovalPolicy.DESTROY, 85 | controlPlaneInstanceType: new ec2.InstanceType('t4g.large'), 86 | workerInstanceType: new ec2.InstanceType('t4g.medium'), 87 | spotWorkerNodes: true, 88 | }); 89 | // THEN 90 | // worker nodes ASG 91 | expect(stack).toHaveResourceLike('AWS::EC2::LaunchTemplate', { 92 | LaunchTemplateData: { 93 | ImageId: { 94 | Ref: 'SsmParameterValueawsserviceamiamazonlinuxlatestamzn2amihvmarm64gp2C96584B6F00A464EAD1953AFF4B05118Parameter', 95 | }, 96 | InstanceMarketOptions: { 97 | MarketType: 'spot', 98 | SpotOptions: { 99 | SpotInstanceType: 'one-time', 100 | }, 101 | }, 102 | InstanceType: 't4g.medium', 103 | }, 104 | }); 105 | // control plane ec2 106 | expect(stack).toHaveResource('AWS::EC2::Instance', { 107 | InstanceType: 't4g.large', 108 | }); 109 | }); 110 | -------------------------------------------------------------------------------- /test/integ.snapshot.test.ts: -------------------------------------------------------------------------------- 1 | 2 | import '@aws-cdk/assert/jest'; 3 | import { SynthUtils } from '@aws-cdk/assert'; 4 | import { IntegTesting } from '../src/integ.default'; 5 | 6 | test('integ snapshot validation', () => { 7 | const integ = new IntegTesting(); 8 | integ.stack.forEach(stack => { 9 | expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot(); 10 | }); 11 | }); -------------------------------------------------------------------------------- /tsconfig.dev.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "alwaysStrict": true, 4 | "declaration": true, 5 | "esModuleInterop": true, 6 | "experimentalDecorators": true, 7 | "inlineSourceMap": true, 8 | "inlineSources": true, 9 | "lib": [ 10 | "es2019" 11 | ], 12 | "module": "CommonJS", 13 | "noEmitOnError": false, 14 | "noFallthroughCasesInSwitch": true, 15 | "noImplicitAny": true, 16 | "noImplicitReturns": true, 17 | "noImplicitThis": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": true, 20 | "resolveJsonModule": true, 21 | "strict": true, 22 | "strictNullChecks": true, 23 | "strictPropertyInitialization": true, 24 | "stripInternal": true, 25 | "target": "ES2019" 26 | }, 27 | "include": [ 28 | ".projenrc.js", 29 | "src/**/*.ts", 30 | "test/**/*.ts" 31 | ], 32 | "exclude": [ 33 | "node_modules" 34 | ], 35 | "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"npx projen\"." 36 | } 37 | -------------------------------------------------------------------------------- /version.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.0.51" 3 | } 4 | --------------------------------------------------------------------------------