Compare commits
	
		
			518 Commits
		
	
	
		
			5af4d729ef
			...
			Test_SemRe
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 1f55ff7cfa | |||
| ba4a0148ff | |||
| c177dbd03b | |||
| 2e8ce6cc00 | |||
| 7fd1cf73db | |||
| cf001cd0ce | |||
| 438b944011 | |||
| 679a9457b5 | |||
| 8b4a1e380c | |||
| 0468cd6269 | |||
| b808397d47 | |||
| 8fd0136bb7 | |||
| 479d896599 | |||
| 263f156eb1 | |||
| f1dfc83d7c | |||
| 5b950a3834 | |||
| 978f39d45b | |||
| 9b9ab6b784 | |||
| 24dca2755a | |||
| 0d1db2f29f | |||
| cce39a5bb7 | |||
| 823cc467fa | |||
| 9cb89bf055 | |||
| 358cbe39ea | |||
| 0fee2df2a6 | |||
| e4e58e4789 | |||
| 75158a8a5b | |||
| c83d541a0d | |||
| a46610f828 | |||
| fe5147bd2e | |||
| 6d168f0517 | |||
| 68445ee13f | |||
| 31b21c9b7a | |||
| e03cd20d65 | |||
| fd1c306061 | |||
| ca8044b4ab | |||
| 3c98e16e74 | |||
| 1860d8e2dd | |||
| 16fdd66328 | |||
| d73320da32 | |||
| 572b7df74c | |||
| ee08fd47b5 | |||
| 75277e285a | |||
| debe80a2a1 | |||
| 2534cea4a0 | |||
| 05c3a09ab3 | |||
| 2f91c0f7c3 | |||
| c385baf630 | |||
| 5c18869d60 | |||
| 1941e02d94 | |||
| 610495e424 | |||
| 4e6a0549b5 | |||
| db090ac564 | |||
| 2b56677e9a | |||
| 641ee2d9a7 | |||
| 979ac38794 | |||
| 86a0b684e2 | |||
| 56a33134a0 | |||
| 915660f618 | |||
| d0c4251e06 | |||
| 9ff0e09625 | |||
| 8e76617794 | |||
| 5a82c9e122 | |||
| cde92b4514 | |||
| 6942c33ae8 | |||
| 7ac4cc0914 | |||
| c054c76b60 | |||
| 25230fdda2 | |||
| 89cf69adc7 | |||
| 3f9fc4b7aa | |||
| 570047df3b | |||
| d187f60091 | |||
| 933615adeb | |||
| 1c60214f5a | |||
| 414b72bcb8 | |||
| 29396de154 | |||
| 5effe00c19 | |||
| 767be3b8f5 | |||
| eb2f491f72 | |||
| cd5fa89a0d | |||
| d7e8685225 | |||
| 5113dd5b6c | |||
| 89fd23f66a | |||
| fa0b72a903 | |||
| ec6f712427 | |||
| 1c19708855 | |||
| 942c13dde7 | |||
| 439223c56e | |||
| b644dc1a04 | |||
| 2de2259c76 | |||
| 214a3d189a | |||
| df91de5516 | |||
| 68f0524bda | |||
| ff555ce0de | |||
| ad0c511651 | |||
| bae9696023 | |||
| 23e1ec1e71 | |||
| 6bd49750a4 | |||
| daa7a240cc | |||
| c0b2857be1 | |||
| 925bc5be39 | |||
| b6a03484e1 | |||
| 462aebdf17 | |||
| 230dc5e0cd | |||
| f47777763a | |||
| cabf813daa | |||
| 5aa2141f84 | |||
| 70a4962afa | |||
| e8da87afd8 | |||
| 90efda336a | |||
| 8f17551d50 | |||
| 75e2250a50 | |||
| 6c9a8e4abd | |||
| 6940bdb1d3 | |||
| 64644d7eff | |||
| c9c8b79891 | |||
| 8aa42ffb67 | |||
| 0f335d6841 | |||
| 0170ee7944 | |||
| e0726f858c | |||
| b7a3669681 | |||
| 438b40dd53 | |||
| 9e7eaf2ff7 | |||
| 7931b1ed44 | |||
| b8cb76e7ac | |||
| 4d2513c1a5 | |||
| df069672f3 | |||
| 27106b1f34 | |||
| abcf530b49 | |||
| 1c950086fa | |||
| ede82ea7e7 | |||
| 6d5b8e2d96 | |||
| a020ac0e15 | |||
| f74d94a5e0 | |||
| d874da0cb3 | |||
| 07e95d82a2 | |||
| 5aecf61a01 | |||
| be4b6177f9 | |||
| aacfbfc2fa | |||
| 0c44f1fd54 | |||
| e5908fde1c | |||
| c793ced9f3 | |||
| 2870041530 | |||
| 9887faa7c4 | |||
| 51cabfa8d2 | |||
| 95f5750291 | |||
| e3ce60bcb4 | |||
| 79b794dba2 | |||
| 907ec8bf3b | |||
| a4db841a7a | |||
| 3f2f19b36a | |||
| 8a753daed7 | |||
| fdfe5f100b | |||
| 4bcb1198f3 | |||
| 64c2c35383 | |||
| e21b11a37a | |||
| dd802e0620 | |||
| 17cf7925d6 | |||
| 2b81d4caa0 | |||
| a4e243e882 | |||
| 2531a4fb5d | |||
| ca51303602 | |||
| e7d89006fc | |||
| 531ead868a | |||
| 3dec92a955 | |||
| d67bf86dab | |||
| 43d83e8e31 | |||
| 1428fe73f7 | |||
| cd308d116b | |||
| d915c21e44 | |||
| 89532ff7fb | |||
| 1f7fb31afe | |||
| 52fbc561dd | |||
| 849c86b22b | |||
| c1bff94cd1 | |||
| 8ba8b5aaab | |||
| f2dadb3e47 | |||
| 8e2df51993 | |||
| 2a9841fb0c | |||
| c2219a5ddc | |||
| 336150b00c | |||
| d1b1635942 | |||
| 62660c8d6c | |||
| 2a5a154df0 | |||
| 36e3a2b99f | |||
| 07b61d8bf3 | |||
| 3710f97b38 | |||
| 6c3867fb57 | |||
| edc19464e2 | |||
| 85dcbb73a4 | |||
| 9e63e243b8 | |||
| d343b84b30 | |||
| 31a91d826f | |||
| 3f24a4af1a | |||
| dc4fa31070 | |||
| d91acb9c0d | |||
| 9c6e1ff386 | |||
| f03e0c3bda | |||
| deb524d1f5 | |||
| 3674862ff4 | |||
| 08a543e27f | |||
| 0fd4cbb92f | |||
| 6c31038b1a | |||
| fdead3c41c | |||
| 3efee69602 | |||
| 0c8272c9e4 | |||
| fcb05e2e12 | |||
| 6de8ea3029 | |||
| 4b5acf5e95 | |||
| 5c68b87d67 | |||
| e538fce0f8 | |||
| b67bc43a70 | |||
| 78c1d7fb54 | |||
| ab5f082933 | |||
| e3f44fab0a | |||
| f68189e3b7 | |||
| f3224416cb | |||
| 96ee96b470 | |||
| a20cb4417c | |||
| 433a231487 | |||
| b890a03760 | |||
| 92eee0744e | |||
| 83ce1be8bf | |||
| a364a7c359 | |||
| edca98549c | |||
| 146887e9e1 | |||
| 9bfa6bf658 | |||
| b9575d2de4 | |||
| e249498109 | |||
| 01a1c0bf0a | |||
| c27712bc20 | |||
| 2d99511360 | |||
| 46a927b777 | |||
| c9a8598a35 | |||
| 0d7b1ab269 | |||
| 4786507366 | |||
| ed4c1145b6 | |||
| 3aa44e0f83 | |||
| 3a48676ca7 | |||
| 44daf9191a | |||
| 83ee632ff9 | |||
| be562f0124 | |||
| f711908ddb | |||
| 6f7b8e99f2 | |||
| 6261bfdda7 | |||
| 5751e7200c | |||
| 6ce1a66d3e | |||
| 85fb68b2e0 | |||
| b47dda7a50 | |||
| 838c7b6361 | |||
| 2ca91b5dea | |||
| 42d79a95ca | |||
| 1f4bbca7ec | |||
| fe8700f300 | |||
| ac4b011e83 | |||
| 7ca9d20b65 | |||
| 0f79832d96 | |||
| 8926b72344 | |||
| 5985615868 | |||
| 4655fe7465 | |||
| 15622d8a21 | |||
| 5c75452315 | |||
| f27dea92e3 | |||
| 67ec47b2b3 | |||
| c6170436f4 | |||
| 5b4eb2e443 | |||
| 68a1534d9c | |||
| e656780f56 | |||
| 464ed497fe | |||
| eb46c384a8 | |||
| 78526527bb | |||
| 22c06e2388 | |||
| 09c4a17050 | |||
| 35ced380a4 | |||
| 9199809da5 | |||
| 806bf24fc0 | |||
| 4733d8e5f8 | |||
| 65346db100 | |||
| 8eb85e4b11 | |||
| 716818c23c | |||
| f4d32d7828 | |||
| c44b40568d | |||
| 67676ff03b | |||
| 204faa7415 | |||
| 35a5656c59 | |||
| 3fe83457ea | |||
| 3c11ce5dde | |||
| 7a1b563851 | |||
| 0bddae0440 | |||
| 8181ae4017 | |||
| ac3f162dd4 | |||
| aa5c45e6e6 | |||
| a67ef0e1bd | |||
| 1794b24998 | |||
| 7c7333690d | |||
| 7b17b8ad63 | |||
| 1141225907 | |||
| 6c4fe7a0e6 | |||
| 8d13b527be | |||
| d8299ee90c | |||
| b34ac733f4 | |||
| 9f2e6ee160 | |||
| 042b9eb36f | |||
| 2097dec958 | |||
| 0c1fca9643 | |||
| b0dad1caf7 | |||
| 2cd2c4c6d0 | |||
| bd0b74ba19 | |||
| 521b323de2 | |||
| 35b3d5d3b9 | |||
| 675dce4160 | |||
| bd7c1f92e8 | |||
| 7d837a1711 | |||
| e1b57cfdea | |||
| 84d644db67 | |||
| 5cffb61544 | |||
| 1083937d5d | |||
| e52c63f80c | |||
| c7579ea4a6 | |||
| fba2e3e4b1 | |||
| 1c43bb19d2 | |||
| 9a3898e0b8 | |||
| a3da5b8f93 | |||
| 5f02ddab49 | |||
| 585e39cb97 | |||
| 1cd7e1510f | |||
| 0534b031fa | |||
| c8509aa3d5 | |||
| 158af986c3 | |||
| 5e537966f6 | |||
| 3849b79493 | |||
| 5f1d1bfa8a | |||
| fe306bd845 | |||
| ccbd4ed984 | |||
| fdc5c44e6a | |||
| c57291af6d | |||
| d652cf0346 | |||
| a1b8837cc5 | |||
| 5b7b93dd30 | |||
| d89ccd57da | |||
| 53da641926 | |||
| a3e9bc659a | |||
| fc51bf3f94 | |||
| 3d96f8c13b | |||
| 488fe10e1e | |||
| 01e168a7f9 | |||
| c48f27c42e | |||
| 185b332764 | |||
| e89505bef6 | |||
| b763d2b562 | |||
| d607f615e9 | |||
| ed7a474dbb | |||
| 14c6720196 | |||
| 277c91eeba | |||
| c9f3c648b7 | |||
| 8e680c45be | |||
| 7440d5824c | |||
| 8e75925b52 | |||
| d6234321d9 | |||
| ea20b1290c | |||
| d986d5ab25 | |||
| c29728594c | |||
| 10d5d6f389 | |||
| 96dccef450 | |||
| 05e0c50217 | |||
| 261e91ee2e | |||
| 1746af9b9d | |||
| 7c2ff54019 | |||
| 193ce9a534 | |||
| 9e91bef7b7 | |||
| 05b30287bd | |||
| 54caff8fb6 | |||
| 2f976898eb | |||
| 44befeda4b | |||
| 81847d3b93 | |||
| 39cc83ac57 | |||
| 0263b2dfc4 | |||
| e7e3b69d95 | |||
| b6ac086a31 | |||
| 20ce62fb6d | |||
| 0918eb36fe | |||
| 93e7d4dc9b | |||
| 9a0a33816c | |||
| eeb1364f1b | |||
| f04095db8c | |||
| 2847542976 | |||
| 929186d123 | |||
| d6c885240a | |||
| 0b97ae2fc5 | |||
| abacbf90ce | |||
| 243cf426d7 | |||
| 64b7ea45c0 | |||
| 3a2dbe572e | |||
| 2be42989e5 | |||
| cb97703406 | |||
| 16df0b65fc | |||
| 847b255e3b | |||
| d005697438 | |||
| ab010643df | |||
| e17cd1b633 | |||
| 504764af10 | |||
| 405fb5938f | |||
| 77e0f7b7cb | |||
| 7fb0e80537 | |||
| fb8b9b735f | |||
| 86e99b1515 | |||
| 952e92082f | |||
| 33e0220e34 | |||
| a51d922f00 | |||
| b441717ee1 | |||
| b7da591571 | |||
| fa0fa30062 | |||
| dd4e79901e | |||
| 1fcaa4b212 | |||
| 164fd15c60 | |||
| 241551169a | |||
| a2b20f49cc | |||
| fe8765ded7 | |||
| caf45b5270 | |||
| 9ba2df08cd | |||
| 9458f49744 | |||
| 369aaaa0b5 | |||
| 6220e2a9aa | |||
| e3e46bae7d | |||
| 6e6e7900da | |||
| 6c329a36e9 | |||
| f33f2912f1 | |||
| 9541942c23 | |||
| 2f937aded7 | |||
| 95dea97382 | |||
| a840306245 | |||
| bbd103d527 | |||
| b2ceee8720 | |||
| d5c886f02b | |||
| 1d59cd4b3c | |||
| f2d9147291 | |||
| bc9f1c260f | |||
| 368f84769b | |||
| 51366476cc | |||
| dcbaf6b807 | |||
| 5dfc3a7813 | |||
| 0989d0c586 | |||
| 8a83f47572 | |||
| a3bbf88ce3 | |||
| 5e0cebf733 | |||
| 00e3266360 | |||
| c6a8f9f7bd | |||
| 4f1231f973 | |||
| 049bedbd8f | |||
| 0e7cfa0934 | |||
| 5435f73402 | |||
| 6917e0799a | |||
| 4616b9b070 | |||
| 8c741dc120 | |||
| 8cbfcb016b | |||
| 4ba7b590ba | |||
| 52660e1414 | |||
| 0ab6aaeaa5 | |||
| 02c26b2465 | |||
| 1842a08a39 | |||
| 0c01f024e9 | |||
| 40489ff373 | |||
| c491066384 | |||
| 4c054cc434 | |||
| dcbe6c397f | |||
| cb84a02b6f | |||
| 8f432d3353 | |||
| 1cdbcaccaf | |||
| f1c6161bcb | |||
| 123518a787 | |||
| 2ec6a756b7 | |||
| a1779be079 | |||
| 8ed9b2f754 | |||
| 72202d9f21 | |||
| 9eb5fbd0a3 | |||
| c58ede04c4 | |||
| 662e8984c3 | |||
| b7abf25907 | |||
| 18fa7742fa | |||
| f6993c2052 | |||
| 59d1730ca5 | |||
| b087203cfb | |||
| d39d594bf0 | |||
| 487239365e | |||
| 6ea03d152c | |||
| 01991435ae | |||
| a64b5b2325 | |||
| 38d7442025 | |||
| cf91519076 | |||
| bae044e145 | |||
| f39b4bbb62 | |||
| 9739c51100 | |||
| 0df98d4341 | |||
| fc23dc068d | |||
| 4d78d65ad8 | |||
| c1440d9dcd | |||
| 4a5f390ae1 | |||
| e0a5b5a5da | |||
| 081aaaaa19 | |||
| 2bd0f8df0a | |||
| 2c57dbcddc | |||
| 0f01e803f2 | |||
| bd82e13fc4 | |||
| 2f902fa27c | |||
| 37ecd4a1b9 | |||
| 8a8cb09984 | |||
| bc46b09708 | |||
| d508b28213 | |||
| 03ed59680f | |||
| 40ade38c78 | |||
| c89edd3ee9 | |||
| e5880c222e | |||
| 236db40016 | |||
| b340c777ba | |||
| 10624f8c90 | |||
| eca6ae515e | |||
| b6656c3d9c | |||
| 82b8eeca4f | 
							
								
								
									
										183
									
								
								.drone.yml
									
									
									
									
									
								
							
							
						
						
									
										183
									
								
								.drone.yml
									
									
									
									
									
								
							| @@ -10,62 +10,207 @@ volumes: | ||||
|   claim: | ||||
|     name: flexvolsmb-drone-scratch | ||||
|  | ||||
| trigger: | ||||
|   event: | ||||
|     exclude: | ||||
|     - tag | ||||
|  | ||||
| steps: | ||||
| - name: Debugging information | ||||
|   image: bv11-cr01.bessems.eu/library/packer-extended | ||||
|   pull: always | ||||
|   commands: | ||||
|   - yamllint --version | ||||
|   - packer --version | ||||
|   - ansible --version | ||||
|   - ovftool --version | ||||
| - name: Ubuntu Server 20.04 | ||||
|   - packer --version | ||||
|   - yamllint --version | ||||
|  | ||||
| - name: Linting | ||||
|   depends_on: | ||||
|   - Debugging information | ||||
|   image: bv11-cr01.bessems.eu/library/packer-extended | ||||
|   pull: always | ||||
|   commands: | ||||
|   - | | ||||
|     yamllint -d "{extends: relaxed, rules: {line-length: disable}}" \ | ||||
|       ansible \ | ||||
|       packer/preseed/UbuntuServer22.04/user-data \ | ||||
|       scripts | ||||
|  | ||||
| - name: Semantic Release (Dry-run) | ||||
|   depends_on: | ||||
|   - Linting | ||||
|   image: bv11-cr01.bessems.eu/proxy/library/node:20-slim | ||||
|   pull: always | ||||
|   commands: | ||||
|   - | | ||||
|     apt-get update | ||||
|   - | | ||||
|     apt-get install -y --no-install-recommends \ | ||||
|       git-core \ | ||||
|       ca-certificates | ||||
|   - | | ||||
|     npm install \ | ||||
|       semantic-release \ | ||||
|       @semantic-release/commit-analyzer \ | ||||
|       @semantic-release/exec \ | ||||
|   - | | ||||
|     export GIT_CREDENTIALS=$${GIT_USERNAME}:$${GIT_APIKEY} | ||||
|   - | | ||||
|     npx semantic-release \ | ||||
|       --package @semantic-release/exec \ | ||||
|       --package semantic-release \ | ||||
|       --branches ${DRONE_BRANCH} \ | ||||
|       --tag-format "K8s_1.25.9-v\$${version}" \ | ||||
|       --dry-run \ | ||||
|       --plugins @semantic-release/commit-analyzer,@semantic-release/exec \ | ||||
|       --analyzeCommits @semantic-release/commit-analyzer \ | ||||
|       --verifyRelease @semantic-release/exec \ | ||||
|       --verifyReleaseCmd 'echo "$${nextRelease.version}" > .version' | ||||
|   environment: | ||||
|     GIT_APIKEY: | ||||
|       from_secret: git_apikey | ||||
|     GIT_USERNAME: djpbessems | ||||
|  | ||||
| - name: Install Ansible Galaxy collections | ||||
|   depends_on: | ||||
|   - Semantic Release (Dry-run) | ||||
|   image: bv11-cr01.bessems.eu/library/packer-extended | ||||
|   pull: always | ||||
|   commands: | ||||
|   - | | ||||
|     ansible-galaxy collection install \ | ||||
|       -r ansible/requirements.yml \ | ||||
|       -p ./ansible/collections | ||||
|  | ||||
| - name: Kubernetes Bootstrap Appliance | ||||
|   depends_on: | ||||
|   - Install Ansible Galaxy collections | ||||
|   image: bv11-cr01.bessems.eu/library/packer-extended | ||||
|   pull: always | ||||
|   commands: | ||||
|   - | | ||||
|     sed -i -e "s/<<img-password>>/$${SSH_PASSWORD}/g" \ | ||||
|       packer/preseed/UbuntuServer20.04/user-data | ||||
|       packer/preseed/UbuntuServer22.04/user-data | ||||
|   - | | ||||
|     yamllint -d "{extends: relaxed, rules: {line-length: disable}}" \ | ||||
|       ansible \ | ||||
|       packer/preseed/UbuntuServer20.04/user-data \ | ||||
|       scripts | ||||
|     export K8S_VERSION=$(yq '.components.clusterapi.workload.version.k8s' < ./ansible/vars/metacluster.yml) | ||||
|     export NEXT_RELEASE_VERSION=$(cat .version) | ||||
|   - | | ||||
|     packer init -upgrade \ | ||||
|       ./packer | ||||
|   - | | ||||
|     packer validate \ | ||||
|       -var vm_name=$DRONE_BUILD_NUMBER-${DRONE_COMMIT_SHA:0:10} \ | ||||
|       -var vm_guestos=ubuntuserver20.04 \ | ||||
|       -only=vsphere-iso.bootstrap \ | ||||
|       -var vm_name=${DRONE_BUILD_NUMBER}-${DRONE_COMMIT_SHA:0:10}-$(openssl rand -hex 3) \ | ||||
|       -var docker_username=$${DOCKER_USERNAME} \ | ||||
|       -var docker_password=$${DOCKER_PASSWORD} \ | ||||
|       -var repo_username=$${REPO_USERNAME} \ | ||||
|       -var repo_password=$${REPO_PASSWORD} \ | ||||
|       -var vsphere_password=$${VSPHERE_PASSWORD} \ | ||||
|       -var ssh_password=$${SSH_PASSWORD} \ | ||||
|       -var vsphere_password=$${VSPHERE_PASSWORD} \ | ||||
|       -var k8s_version=$K8S_VERSION \ | ||||
|       -var next_release_version=$NEXT_RELEASE_VERSION \ | ||||
|       ./packer | ||||
|   - | | ||||
|     packer build \ | ||||
|       -on-error=cleanup -timestamp-ui \ | ||||
|       -var vm_name=$DRONE_BUILD_NUMBER-${DRONE_COMMIT_SHA:0:10} \ | ||||
|       -var vm_guestos=ubuntuserver20.04 \ | ||||
|       -only=vsphere-iso.bootstrap \ | ||||
|       -var vm_name=${DRONE_BUILD_NUMBER}-${DRONE_COMMIT_SHA:0:10}-$(openssl rand -hex 3) \ | ||||
|       -var docker_username=$${DOCKER_USERNAME} \ | ||||
|       -var docker_password=$${DOCKER_PASSWORD} \ | ||||
|       -var repo_username=$${REPO_USERNAME} \ | ||||
|       -var repo_password=$${REPO_PASSWORD} \ | ||||
|       -var vsphere_password=$${VSPHERE_PASSWORD} \ | ||||
|       -var ssh_password=$${SSH_PASSWORD} \ | ||||
|       -var vsphere_password=$${VSPHERE_PASSWORD} \ | ||||
|       -var k8s_version=$K8S_VERSION \ | ||||
|       -var next_release_version=$NEXT_RELEASE_VERSION \ | ||||
|       ./packer | ||||
|   environment: | ||||
|     VSPHERE_PASSWORD: | ||||
|       from_secret: vsphere_password | ||||
|     SSH_PASSWORD: | ||||
|       from_secret: ssh_password | ||||
|     DOCKER_USERNAME: | ||||
|       from_secret: docker_username | ||||
|     DOCKER_PASSWORD: | ||||
|       from_secret: docker_password | ||||
|     # PACKER_LOG: 1 | ||||
|     REPO_USERNAME: | ||||
|       from_secret: repo_username | ||||
|     REPO_PASSWORD: | ||||
|       from_secret: repo_password | ||||
|     # PACKER_LOG: 1 | ||||
|     SSH_PASSWORD: | ||||
|       from_secret: ssh_password | ||||
|     VSPHERE_PASSWORD: | ||||
|       from_secret: vsphere_password | ||||
|   volumes: | ||||
|   - name: output | ||||
|     path: /output | ||||
|   - name: scratch | ||||
|     path: /scratch | ||||
|  | ||||
| - name: Kubernetes Upgrade Appliance | ||||
|   depends_on: | ||||
|   - Install Ansible Galaxy collections | ||||
|   image: bv11-cr01.bessems.eu/library/packer-extended | ||||
|   pull: alwaysquery( | ||||
|   commands: | ||||
|   - | | ||||
|     sed -i -e "s/<<img-password>>/$${SSH_PASSWORD}/g" \ | ||||
|       packer/preseed/UbuntuServer22.04/user-data | ||||
|   - | | ||||
|     export K8S_VERSION=$(yq '.components.clusterapi.workload.version.k8s' < ./ansible/vars/metacluster.yml) | ||||
|     export NEXT_RELEASE_VERSION=$(cat .version) | ||||
|   - | | ||||
|     packer init -upgrade \ | ||||
|       ./packer | ||||
|   - | | ||||
|     packer validate \ | ||||
|       -only=vsphere-iso.upgrade \ | ||||
|       -var vm_name=${DRONE_BUILD_NUMBER}-${DRONE_COMMIT_SHA:0:10}-$(openssl rand -hex 3) \ | ||||
|       -var docker_username=$${DOCKER_USERNAME} \ | ||||
|       -var docker_password=$${DOCKER_PASSWORD} \ | ||||
|       -var repo_username=$${REPO_USERNAME} \ | ||||
|       -var repo_password=$${REPO_PASSWORD} \ | ||||
|       -var ssh_password=$${SSH_PASSWORD} \ | ||||
|       -var vsphere_password=$${VSPHERE_PASSWORD} \ | ||||
|       -var k8s_version=$K8S_VERSION \ | ||||
|       -var next_release_version=$NEXT_RELEASE_VERSION \ | ||||
|       ./packer | ||||
|   - | | ||||
|     packer build \ | ||||
|       -on-error=cleanup -timestamp-ui \ | ||||
|       -only=vsphere-iso.upgrade \ | ||||
|       -var vm_name=${DRONE_BUILD_NUMBER}-${DRONE_COMMIT_SHA:0:10}-$(openssl rand -hex 3) \ | ||||
|       -var docker_username=$${DOCKER_USERNAME} \ | ||||
|       -var docker_password=$${DOCKER_PASSWORD} \ | ||||
|       -var repo_username=$${REPO_USERNAME} \ | ||||
|       -var repo_password=$${REPO_PASSWORD} \ | ||||
|       -var ssh_password=$${SSH_PASSWORD} \ | ||||
|       -var vsphere_password=$${VSPHERE_PASSWORD} \ | ||||
|       -var k8s_version=$K8S_VERSION \ | ||||
|       -var next_release_version=$NEXT_RELEASE_VERSION \ | ||||
|       ./packer | ||||
|   environment: | ||||
|     DOCKER_USERNAME: | ||||
|       from_secret: docker_username | ||||
|     DOCKER_PASSWORD: | ||||
|       from_secret: docker_password | ||||
|     # PACKER_LOG: 1 | ||||
|     REPO_USERNAME: | ||||
|       from_secret: repo_username | ||||
|     REPO_PASSWORD: | ||||
|       from_secret: repo_password | ||||
|     SSH_PASSWORD: | ||||
|       from_secret: ssh_password | ||||
|     VSPHERE_PASSWORD: | ||||
|       from_secret: vsphere_password | ||||
|   volumes: | ||||
|   - name: output | ||||
|     path: /output | ||||
|   - name: scratch | ||||
|     path: /scratch | ||||
|  | ||||
| - name: Remove temporary resources | ||||
|   depends_on: | ||||
|   - Kubernetes Bootstrap Appliance | ||||
|   - Kubernetes Upgrade Appliance | ||||
|   image: bv11-cr01.bessems.eu/library/packer-extended | ||||
|   commands: | ||||
|   - | | ||||
|   | ||||
							
								
								
									
										11
									
								
								.releaserc.json.DISABLED
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								.releaserc.json.DISABLED
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| { | ||||
|     "plugins": [ | ||||
|         ["@semantic-release/commit-analyzer"], | ||||
|         ["@semantic-release/release-notes-generator"], | ||||
|         ["@semantic-release/exec", { | ||||
|             "prepareCmd": "export SEMANTICRELEASE_NEXTRELEASEVERSION=${nextRelease.version}", | ||||
|             "publishCmd": "echo $SEMANTICRELEASE_NEXTRELEASEVERSION" | ||||
|         }], | ||||
|         ["@semantic-release/git"] | ||||
|     ] | ||||
| } | ||||
| @@ -1 +1 @@ | ||||
| # Packer.Images [](https://ci.spamasaurus.com/djpbessems/Packer.Images) | ||||
| # Packer.Images [](https://ci.spamasaurus.com/djpbessems/Packer.Images) | ||||
|   | ||||
| @@ -1,7 +1,11 @@ | ||||
| --- | ||||
| - hosts: all | ||||
|   gather_facts: false | ||||
|   vars_files: | ||||
|     - metacluster.yml | ||||
|     - workloadcluster.yml | ||||
|   become: true | ||||
|   roles: | ||||
|     - os | ||||
|     - firstboot | ||||
|     - assets | ||||
|   | ||||
							
								
								
									
										9
									
								
								ansible/requirements.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								ansible/requirements.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| collections: | ||||
|   - name: https://github.com/ansible-collections/ansible.posix | ||||
|     type: git | ||||
|   - name: https://github.com/ansible-collections/ansible.utils | ||||
|     type: git | ||||
|   - name: https://github.com/ansible-collections/community.general | ||||
|     type: git | ||||
|   - name: https://github.com/ansible-collections/kubernetes.core | ||||
|     type: git | ||||
							
								
								
									
										67
									
								
								ansible/roles/assets/tasks/containerimages.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								ansible/roles/assets/tasks/containerimages.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| - name: Parse manifests for container images | ||||
|   ansible.builtin.shell: | ||||
|     # This set of commands is necessary to deal with multi-line scalar values | ||||
|     # eg.: | ||||
|     #      key: | | ||||
|     #        multi-line | ||||
|     #        value | ||||
|     cmd: >- | ||||
|       cat {{ item.dest }} | yq --no-doc eval '.. | .image? | select(.)' | awk '!/ /'; | ||||
|       cat {{ item.dest }} | yq eval '.data.data' | yq --no-doc eval '.. | .image? | select(.)'; | ||||
|       cat {{ item.dest }} | yq --no-doc eval '.. | .files? | with_entries(select(.value.path == "*.yaml")).[0].content' | awk '!/null/' | yq eval '.. | .image? | select(.)' | ||||
|   register: parsedmanifests | ||||
|   loop: "{{ clusterapi_manifests.results }}" | ||||
|   loop_control: | ||||
|     label: "{{ item.dest | basename }}" | ||||
|  | ||||
| - name: Parse metacluster helm charts for container images | ||||
|   ansible.builtin.shell: | ||||
|     cmd: "{{ item.value.helm.parse_logic }}" | ||||
|     chdir: /opt/metacluster/helm-charts/{{ item.key }} | ||||
|   register: chartimages_metacluster | ||||
|   when: item.value.helm is defined | ||||
|   loop: "{{ query('ansible.builtin.dict', components) }}" | ||||
|   loop_control: | ||||
|     label: "{{ item.key }}" | ||||
|  | ||||
| - name: Parse workloadcluster helm charts for container images | ||||
|   ansible.builtin.shell: | ||||
|     cmd: "{{ item.value.parse_logic }}" | ||||
|     chdir: /opt/workloadcluster/helm-charts/{{ item.value.namespace }}/{{ item.key }} | ||||
|   register: chartimages_workloadcluster | ||||
|   loop: "{{ query('ansible.builtin.dict', downstream.helm_charts) }}" | ||||
|   loop_control: | ||||
|     label: "{{ item.key }}" | ||||
|  | ||||
| - name: Store container images in dicts | ||||
|   ansible.builtin.set_fact: | ||||
|     containerimages_{{ item.source }}: "{{ item.results }}" | ||||
|   loop: | ||||
|     - source: charts | ||||
|       results: "{{ (chartimages_metacluster | json_query('results[*].stdout_lines')) + (chartimages_workloadcluster | json_query('results[*].stdout_lines')) | select() | flatten | list }}" | ||||
|     - source: kubeadm | ||||
|       results: "{{ kubeadmimages.stdout_lines }}" | ||||
|     - source: manifests | ||||
|       results: "{{ parsedmanifests | json_query('results[*].stdout_lines') | select() | flatten | list }}" | ||||
|   loop_control: | ||||
|     label: "{{ item.source }}" | ||||
|  | ||||
| - name: Log in to container registry | ||||
|   ansible.builtin.shell: | ||||
|     cmd: >- | ||||
|       skopeo login \ | ||||
|         docker.io \ | ||||
|         --username={{ docker_username }} \ | ||||
|         --password={{ docker_password }} | ||||
|   no_log: true | ||||
|  | ||||
| - name: Pull and store containerimages | ||||
|   ansible.builtin.shell: | ||||
|     cmd: >- | ||||
|       skopeo copy \ | ||||
|         --insecure-policy \ | ||||
|         --retry-times=5 \ | ||||
|         docker://{{ item }} \ | ||||
|         docker-archive:./{{ ( item | regex_findall('[^/:]+'))[-2] }}_{{ lookup('ansible.builtin.password', '/dev/null length=5 chars=ascii_lowercase,digits seed={{ item }}') }}.tar:{{ item }} | ||||
|     chdir: /opt/metacluster/container-images | ||||
|   loop: "{{ (containerimages_charts + containerimages_kubeadm + containerimages_manifests + dependencies.container_images) | flatten | unique | sort }}" | ||||
| @@ -0,0 +1,31 @@ | ||||
| --- | ||||
| - name: Initialize tempfolder | ||||
|   ansible.builtin.tempfile: | ||||
|     state: directory | ||||
|   register: archive | ||||
|  | ||||
| - name: Download & extract archived static binary | ||||
|   ansible.builtin.unarchive: | ||||
|     src: "{{ item.url }}" | ||||
|     dest: "{{ archive.path }}" | ||||
|     remote_src: yes | ||||
|     extra_opts: "{{ item.extra_opts | default(omit) }}" | ||||
|   register: staticbinary_download | ||||
|   retries: 5 | ||||
|   delay: 5 | ||||
|   until: staticbinary_download is not failed | ||||
|  | ||||
| - name: Install extracted binary | ||||
|   ansible.builtin.copy: | ||||
|     src: "{{ archive.path }}/{{ item.filename }}" | ||||
|     dest: /usr/local/bin/{{ item.filename }} | ||||
|     remote_src: yes | ||||
|     owner: root | ||||
|     group: root | ||||
|     mode: 0755 | ||||
|  | ||||
| - name: Cleanup tempfolder | ||||
|   ansible.builtin.file: | ||||
|     path: "{{ archive.path }}" | ||||
|     state: absent | ||||
|   when: archive.path is defined | ||||
							
								
								
									
										54
									
								
								ansible/roles/assets/tasks/dependencies.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								ansible/roles/assets/tasks/dependencies.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| - name: Download & install static binaries | ||||
|   ansible.builtin.get_url: | ||||
|     url: "{{ item.url }}" | ||||
|     url_username: "{{ item.username | default(omit) }}" | ||||
|     url_password: "{{ item.password | default(omit) }}" | ||||
|     dest: /usr/local/bin/{{ item.filename }} | ||||
|     owner: root | ||||
|     group: root | ||||
|     mode: 0755 | ||||
|   register: staticbinary_download | ||||
|   loop: "{{ dependencies.static_binaries | selectattr('archive', 'undefined') }}" | ||||
|   loop_control: | ||||
|     label: "{{ item.filename }}" | ||||
|   retries: 5 | ||||
|   delay: 5 | ||||
|   until: staticbinary_download is not failed | ||||
|  | ||||
| - name: Download, extract & install archived static binaries | ||||
|   include_tasks: dependencies.archive_compressed.yml | ||||
|   loop: "{{ dependencies.static_binaries | rejectattr('archive', 'undefined') | selectattr('archive', 'equalto', 'compressed') }}" | ||||
|   loop_control: | ||||
|     label: "{{ item.filename }}" | ||||
|  | ||||
| - name: Install ansible-galaxy collections | ||||
|   ansible.builtin.shell: | ||||
|     cmd: ansible-galaxy collection install {{ item }} | ||||
|   register: collections | ||||
|   loop: "{{ dependencies.ansible_galaxy_collections }}" | ||||
|   retries: 5 | ||||
|   delay: 5 | ||||
|   until: collections is not failed | ||||
|  | ||||
| - name: Install distro packages | ||||
|   ansible.builtin.apt: | ||||
|     pkg: "{{ dependencies.packages.apt }}" | ||||
|     state: latest | ||||
|     update_cache: yes | ||||
|     install_recommends: no | ||||
|  | ||||
| - name: Upgrade all packages | ||||
|   ansible.builtin.apt: | ||||
|     name: '*' | ||||
|     state: latest | ||||
|     update_cache: yes | ||||
|  | ||||
| - name: Install additional python packages | ||||
|   ansible.builtin.pip: | ||||
|     name: "{{ dependencies.packages.pip }}" | ||||
|     state: latest | ||||
|  | ||||
| - name: Cleanup apt cache | ||||
|   ansible.builtin.apt: | ||||
|     autoremove: yes | ||||
|     purge: yes | ||||
							
								
								
									
										31
									
								
								ansible/roles/assets/tasks/helm.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								ansible/roles/assets/tasks/helm.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| - name: Add helm repositories | ||||
|   kubernetes.core.helm_repository: | ||||
|     name: "{{ item.name }}" | ||||
|     repo_url: "{{ item.url }}" | ||||
|     state: present | ||||
|   loop: "{{ platform.helm_repositories + downstream.helm_repositories }}" | ||||
|  | ||||
| - name: Fetch helm charts for metacluster | ||||
|   ansible.builtin.command: | ||||
|     cmd: helm fetch {{ item.value.helm.chart }} --untar --version {{ item.value.helm.version }} | ||||
|     chdir: /opt/metacluster/helm-charts | ||||
|   when: item.value.helm is defined | ||||
|   register: helmcharts_metacluster | ||||
|   loop: "{{ query('ansible.builtin.dict', components) }}" | ||||
|   loop_control: | ||||
|     label: "{{ item.key }}" | ||||
|   retries: 5 | ||||
|   delay: 5 | ||||
|   until: helmcharts_metacluster is not failed | ||||
|  | ||||
| - name: Fetch helm charts for workloadcluster | ||||
|   ansible.builtin.command: | ||||
|     cmd: helm fetch {{ item.value.chart }} --untardir ./{{ item.value.namespace }} --untar --version {{ item.value.version }} | ||||
|     chdir: /opt/workloadcluster/helm-charts | ||||
|   register: helmcharts_workloadcluster | ||||
|   loop: "{{ query('ansible.builtin.dict', downstream.helm_charts) }}" | ||||
|   loop_control: | ||||
|     label: "{{ item.key }}" | ||||
|   retries: 5 | ||||
|   delay: 5 | ||||
|   until: helmcharts_workloadcluster is not failed | ||||
							
								
								
									
										43
									
								
								ansible/roles/assets/tasks/k3s.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								ansible/roles/assets/tasks/k3s.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| - name: Download & install K3s binary | ||||
|   ansible.builtin.get_url: | ||||
|     url: https://github.com/k3s-io/k3s/releases/download/{{ platform.k3s.version }}/k3s | ||||
|     dest: /usr/local/bin/k3s | ||||
|     owner: root | ||||
|     group: root | ||||
|     mode: 0755 | ||||
|   register: download | ||||
|   until: download is not failed | ||||
|   retries: 3 | ||||
|   delay: 10 | ||||
|  | ||||
| - name: Download K3s images tarball | ||||
|   ansible.builtin.get_url: | ||||
|     url: https://github.com/k3s-io/k3s/releases/download/{{ platform.k3s.version }}/k3s-airgap-images-amd64.tar.gz | ||||
|     dest: /var/lib/rancher/k3s/agent/images | ||||
|   register: download | ||||
|   until: download is not failed | ||||
|   retries: 3 | ||||
|   delay: 10 | ||||
|  | ||||
| - name: Download K3s install script | ||||
|   ansible.builtin.get_url: | ||||
|     url: https://raw.githubusercontent.com/k3s-io/k3s/{{ platform.k3s.version | urlencode }}/install.sh | ||||
|     dest: /opt/metacluster/k3s/install.sh | ||||
|     owner: root | ||||
|     group: root | ||||
|     mode: 0755 | ||||
|   register: download | ||||
|   until: download is not failed | ||||
|   retries: 3 | ||||
|   delay: 10 | ||||
|  | ||||
| - name: Inject manifests | ||||
|   ansible.builtin.template: | ||||
|     src: helmchartconfig.j2 | ||||
|     dest: /var/lib/rancher/k3s/server/manifests/{{ item.name }}-config.yaml | ||||
|     owner: root | ||||
|     group: root | ||||
|     mode: 0600 | ||||
|   loop: "{{ platform.packaged_components }}" | ||||
|   loop_control: | ||||
|     label: "{{ item.name }}" | ||||
							
								
								
									
										26
									
								
								ansible/roles/assets/tasks/kubeadm.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								ansible/roles/assets/tasks/kubeadm.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| - name: Initialize tempfile | ||||
|   ansible.builtin.tempfile: | ||||
|     state: directory | ||||
|   register: kubeadm | ||||
|  | ||||
| - name: Download kubeadm binary | ||||
|   ansible.builtin.get_url: | ||||
|     url: https://dl.k8s.io/release/{{ components.clusterapi.workload.version.k8s }}/bin/linux/amd64/kubeadm | ||||
|     dest: "{{ kubeadm.path }}/kubeadm" | ||||
|     mode: u+x | ||||
|  | ||||
| - name: Retrieve container images list | ||||
|   ansible.builtin.shell: | ||||
|     cmd: "{{ kubeadm.path }}/kubeadm config images list --kubernetes-version {{ components.clusterapi.workload.version.k8s }}" | ||||
|   register: kubeadmimages | ||||
|  | ||||
| - name: Store list of container images for reference | ||||
|   ansible.builtin.copy: | ||||
|     dest: /opt/metacluster/cluster-api/imagelist | ||||
|     content: "{{ kubeadmimages.stdout }}" | ||||
|  | ||||
| - name: Cleanup tempfile | ||||
|   ansible.builtin.file: | ||||
|     path: "{{ kubeadm.path }}" | ||||
|     state: absent | ||||
|   when: kubeadm.path is defined | ||||
							
								
								
									
										33
									
								
								ansible/roles/assets/tasks/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								ansible/roles/assets/tasks/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| - name: Create folder structure(s) | ||||
|   ansible.builtin.file: | ||||
|     path: "{{ item }}" | ||||
|     state: directory | ||||
|   loop: | ||||
|     - /etc/rancher/k3s | ||||
|     - /opt/metacluster/cluster-api/bootstrap-kubeadm/{{ components.clusterapi.management.version.base }} | ||||
|     - /opt/metacluster/cluster-api/cert-manager/{{ components.clusterapi.management.version.cert_manager }} | ||||
|     - /opt/metacluster/cluster-api/cluster-api/{{ components.clusterapi.management.version.base }} | ||||
|     - /opt/metacluster/cluster-api/cni-calico/{{ components.clusterapi.workload.version.calico }} | ||||
|     - /opt/metacluster/cluster-api/control-plane-kubeadm/{{ components.clusterapi.management.version.base }} | ||||
|     - /opt/metacluster/cluster-api/infrastructure-vsphere/{{ components.clusterapi.management.version.infrastructure_vsphere }} | ||||
|     - /opt/metacluster/cluster-api/ipam-in-cluster/{{ components.clusterapi.management.version.ipam_incluster }} | ||||
|     - /opt/metacluster/container-images | ||||
|     - /opt/metacluster/git-repositories | ||||
|     - /opt/metacluster/helm-charts | ||||
|     - /opt/metacluster/k3s | ||||
|     - /opt/metacluster/kube-vip | ||||
|     - /opt/workloadcluster/git-repositories/gitops/charts | ||||
|     - /opt/workloadcluster/git-repositories/gitops/values | ||||
|     - /opt/workloadcluster/helm-charts | ||||
|     - /opt/workloadcluster/node-templates | ||||
|     - /var/lib/rancher/k3s/agent/images | ||||
|     - /var/lib/rancher/k3s/server/manifests | ||||
|  | ||||
| - import_tasks: dependencies.yml | ||||
| - import_tasks: k3s.yml | ||||
| - import_tasks: helm.yml | ||||
| # - import_tasks: git.yml | ||||
| - import_tasks: manifests.yml | ||||
| - import_tasks: kubeadm.yml | ||||
| - import_tasks: containerimages.yml | ||||
| - import_tasks: nodetemplates.yml | ||||
							
								
								
									
										109
									
								
								ansible/roles/assets/tasks/manifests.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								ansible/roles/assets/tasks/manifests.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,109 @@ | ||||
| - block: | ||||
|  | ||||
|     - name: Aggregate chart_values into dict | ||||
|       ansible.builtin.set_fact: | ||||
|         metacluster_chartvalues: "{{ metacluster_chartvalues | default({}) | combine({ item.key: { 'chart_values': (item.value.helm.chart_values | from_yaml) } }) }}" | ||||
|       when: item.value.helm.chart_values is defined | ||||
|       loop: "{{ query('ansible.builtin.dict', components) }}" | ||||
|       loop_control: | ||||
|         label: "{{ item.key }}" | ||||
|  | ||||
|     - name: Write dict to vars_file | ||||
|       ansible.builtin.copy: | ||||
|         dest: /opt/firstboot/ansible/vars/metacluster.yml | ||||
|         content: >- | ||||
|           {{ | ||||
|             { 'components': ( | ||||
|               metacluster_chartvalues | | ||||
|               combine({ 'clusterapi': components.clusterapi }) | | ||||
|               combine({ 'kubevip'   : components.kubevip }) ) | ||||
|             } | to_nice_yaml(indent=2, width=4096) | ||||
|           }} | ||||
|  | ||||
|     - name: Aggregate chart_values into dict | ||||
|       ansible.builtin.set_fact: | ||||
|         workloadcluster_chartvalues: "{{ workloadcluster_chartvalues | default({}) | combine({ item.key: { 'chart_values': (item.value.chart_values | default('') | from_yaml) } }) }}" | ||||
|       # when: item.value.chart_values is defined | ||||
|       loop: "{{ query('ansible.builtin.dict', downstream.helm_charts) }}" | ||||
|       loop_control: | ||||
|         label: "{{ item.key }}" | ||||
|  | ||||
|     - name: Write dict to vars_file | ||||
|       ansible.builtin.copy: | ||||
|         dest: /opt/firstboot/ansible/vars/workloadcluster.yml | ||||
|         content: >- | ||||
|           {{ | ||||
|             { 'downstream_components': ( workloadcluster_chartvalues ) | ||||
|             } | to_nice_yaml(indent=2, width=4096) | ||||
|           }} | ||||
|  | ||||
| - name: Download ClusterAPI manifests | ||||
|   ansible.builtin.get_url: | ||||
|     url: "{{ item.url }}" | ||||
|     dest: /opt/metacluster/cluster-api/{{ item.dest }} | ||||
|   register: clusterapi_manifests | ||||
|   loop: | ||||
|     # This list is based on `clusterctl config repositories` | ||||
|     # Note: Each manifest also needs a `metadata.yaml` file stored in the respective folder | ||||
|     - url: https://github.com/kubernetes-sigs/cluster-api/releases/download/{{ components.clusterapi.management.version.base }}/bootstrap-components.yaml | ||||
|       dest: bootstrap-kubeadm/{{ components.clusterapi.management.version.base }}/bootstrap-components.yaml | ||||
|     - url: https://github.com/kubernetes-sigs/cluster-api/releases/download/{{ components.clusterapi.management.version.base }}/core-components.yaml | ||||
|       dest: cluster-api/{{ components.clusterapi.management.version.base }}/core-components.yaml | ||||
|     - url: https://github.com/kubernetes-sigs/cluster-api/releases/download/{{ components.clusterapi.management.version.base }}/control-plane-components.yaml | ||||
|       dest: control-plane-kubeadm/{{ components.clusterapi.management.version.base }}/control-plane-components.yaml | ||||
|     # This downloads the same `metadata.yaml` file to three separate folders | ||||
|     - url: https://github.com/kubernetes-sigs/cluster-api/releases/download/{{ components.clusterapi.management.version.base }}/metadata.yaml | ||||
|       dest: bootstrap-kubeadm/{{ components.clusterapi.management.version.base }}/metadata.yaml | ||||
|     - url: https://github.com/kubernetes-sigs/cluster-api/releases/download/{{ components.clusterapi.management.version.base }}/metadata.yaml | ||||
|       dest: cluster-api/{{ components.clusterapi.management.version.base }}/metadata.yaml | ||||
|     - url: https://github.com/kubernetes-sigs/cluster-api/releases/download/{{ components.clusterapi.management.version.base }}/metadata.yaml | ||||
|       dest: control-plane-kubeadm/{{ components.clusterapi.management.version.base }}/metadata.yaml | ||||
|     # The vsphere infrastructure provider requires multiple files (`cluster-template.yaml` and `metadata.yaml` on top of default files) | ||||
|     - url: https://github.com/kubernetes-sigs/cluster-api-provider-vsphere/releases/download/{{ components.clusterapi.management.version.infrastructure_vsphere }}/infrastructure-components.yaml | ||||
|       dest: infrastructure-vsphere/{{ components.clusterapi.management.version.infrastructure_vsphere }}/infrastructure-components.yaml | ||||
|     - url: https://github.com/kubernetes-sigs/cluster-api-provider-vsphere/releases/download/{{ components.clusterapi.management.version.infrastructure_vsphere }}/cluster-template.yaml | ||||
|       dest: infrastructure-vsphere/{{ components.clusterapi.management.version.infrastructure_vsphere }}/cluster-template.yaml | ||||
|     - url: https://github.com/kubernetes-sigs/cluster-api-provider-vsphere/releases/download/{{ components.clusterapi.management.version.infrastructure_vsphere }}/metadata.yaml | ||||
|       dest: infrastructure-vsphere/{{ components.clusterapi.management.version.infrastructure_vsphere }}/metadata.yaml | ||||
|     # Additionally, cert-manager is a prerequisite | ||||
|     - url: https://github.com/cert-manager/cert-manager/releases/download/{{ components.clusterapi.management.version.cert_manager }}/cert-manager.yaml | ||||
|       dest: cert-manager/{{ components.clusterapi.management.version.cert_manager }}/cert-manager.yaml | ||||
|     # Finally, workload clusters will need a CNI plugin | ||||
|     - url: https://raw.githubusercontent.com/projectcalico/calico/{{ components.clusterapi.workload.version.calico }}/manifests/calico.yaml | ||||
|       dest: cni-calico/{{ components.clusterapi.workload.version.calico }}/calico.yaml | ||||
|     # IPAM in-cluster provider (w/ metadata.yaml) | ||||
|     - url: https://github.com/telekom/cluster-api-ipam-provider-in-cluster/releases/download/{{ components.clusterapi.management.version.ipam_incluster }}/ipam-components.yaml | ||||
|       dest: ipam-in-cluster/{{ components.clusterapi.management.version.ipam_incluster }}/ipam-components.yaml | ||||
|     - url: https://github.com/telekom/cluster-api-ipam-provider-in-cluster/releases/download/{{ components.clusterapi.management.version.ipam_incluster }}/metadata.yaml | ||||
|       dest: ipam-in-cluster/{{ components.clusterapi.management.version.ipam_incluster }}/metadata.yaml | ||||
|   loop_control: | ||||
|     label: "{{ item.url | basename }}" | ||||
|   retries: 5 | ||||
|   delay: 5 | ||||
|   until: clusterapi_manifests is not failed | ||||
|  | ||||
| - name: Update cluster-template with image tags | ||||
|   ansible.builtin.replace: | ||||
|     dest: /opt/metacluster/cluster-api/infrastructure-vsphere/{{ components.clusterapi.management.version.infrastructure_vsphere }}/cluster-template.yaml | ||||
|     regexp: ':\${CPI_IMAGE_K8S_VERSION}' | ||||
|     replace: ":{{ components.clusterapi.management.version.cpi_vsphere }}" | ||||
|  | ||||
| - name: Download kube-vip RBAC manifest | ||||
|   ansible.builtin.get_url: | ||||
|     url: https://kube-vip.io/manifests/rbac.yaml | ||||
|     dest: /opt/metacluster/kube-vip/rbac.yaml | ||||
|   register: kubevip_manifest | ||||
|   retries: 5 | ||||
|   delay: 5 | ||||
|   until: kubevip_manifest is not failed | ||||
|  | ||||
| # - name: Inject manifests | ||||
| #   ansible.builtin.template: | ||||
| #     src: "{{ item.type }}.j2" | ||||
| #     dest: /var/lib/rancher/k3s/server/manifests/{{ item.name }}-manifest.yaml | ||||
| #     owner: root | ||||
| #     group: root | ||||
| #     mode: 0600 | ||||
| #   loop: "{{ query('ansible.builtin.dict', components) | map(attribute='value.manifests') | list | select('defined') | flatten }}" | ||||
| #   loop_control: | ||||
| #     label: "{{ item.type ~ '/' ~ item.name }}" | ||||
							
								
								
									
										4
									
								
								ansible/roles/assets/tasks/nodetemplates.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								ansible/roles/assets/tasks/nodetemplates.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| - name: Download node-template image | ||||
|   ansible.builtin.uri: | ||||
|     url: "{{ components.clusterapi.workload.node_template.url }}" | ||||
|     dest: /opt/workloadcluster/node-templates/{{ components.clusterapi.workload.node_template.url | basename}} | ||||
							
								
								
									
										8
									
								
								ansible/roles/assets/templates/helmchartconfig.j2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								ansible/roles/assets/templates/helmchartconfig.j2
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| apiVersion: helm.cattle.io/v1 | ||||
| kind: HelmChartConfig | ||||
| metadata: | ||||
|   name: {{ item.name }} | ||||
|   namespace: {{ item.namespace }} | ||||
| spec: | ||||
|   valuesContent: |- | ||||
| {{ item.config }} | ||||
| @@ -0,0 +1,27 @@ | ||||
| --- | ||||
| - hosts: 127.0.0.1 | ||||
|   connection: local | ||||
|   gather_facts: true | ||||
|   vars_files: | ||||
|     - defaults.yml | ||||
|     - metacluster.yml | ||||
|     - workloadcluster.yml | ||||
|   # become: true | ||||
|   roles: | ||||
|     - vapp | ||||
|     - network | ||||
|     - preflight | ||||
|     - users | ||||
|     - disks | ||||
|     - metacluster | ||||
|     - workloadcluster | ||||
|     - tty | ||||
|     - cleanup | ||||
|   handlers: | ||||
|     - name: Apply manifests | ||||
|       kubernetes.core.k8s: | ||||
|         src: "{{ item }}" | ||||
|         state: present | ||||
|         kubeconfig: "{{ kubeconfig.path }}" | ||||
|       loop: "{{ query('ansible.builtin.fileglob', '/var/lib/rancher/k3s/server/manifests/*.yaml') | sort }}" | ||||
|       ignore_errors: yes | ||||
| @@ -0,0 +1,142 @@ | ||||
| - block: | ||||
|  | ||||
|     - name: Initialize tempfile | ||||
|       ansible.builtin.tempfile: | ||||
|         state: file | ||||
|       register: values_file | ||||
|  | ||||
|     - name: Write chart values w/ password to tempfile | ||||
|       ansible.builtin.copy: | ||||
|         dest: "{{ values_file.path }}" | ||||
|         content: "{{ stepca_values.stdout | regex_replace('(ca_password|provisioner_password): ', '\\1: ' ~ (vapp['metacluster.password'] | b64encode)) }}" | ||||
|       no_log: true | ||||
|  | ||||
|     - name: Install step-ca chart | ||||
|       kubernetes.core.helm: | ||||
|         name: step-certificates | ||||
|         chart_ref: /opt/metacluster/helm-charts/step-certificates | ||||
|         release_namespace: step-ca | ||||
|         create_namespace: true | ||||
|         # Unable to use REST api based readycheck due to lack of ingress | ||||
|         wait: true | ||||
|         kubeconfig: "{{ kubeconfig.path }}" | ||||
|         values_files: | ||||
|           - "{{ values_file.path }}" | ||||
|  | ||||
|     - name: Cleanup tempfile | ||||
|       ansible.builtin.file: | ||||
|         path: "{{ values_file.path }}" | ||||
|         state: absent | ||||
|       when: values_file.path is defined | ||||
|  | ||||
|     - name: Retrieve configmap w/ root certificate | ||||
|       kubernetes.core.k8s_info: | ||||
|         kind: ConfigMap | ||||
|         name: step-certificates-certs | ||||
|         namespace: step-ca | ||||
|         kubeconfig: "{{ kubeconfig.path }}" | ||||
|       register: stepca_cm_certs | ||||
|  | ||||
|     - name: Create target namespaces | ||||
|       kubernetes.core.k8s: | ||||
|         kind: Namespace | ||||
|         name: "{{ item }}" | ||||
|         state: present | ||||
|         kubeconfig: "{{ kubeconfig.path }}" | ||||
|       loop: | ||||
|         - argo-cd | ||||
|         # - kube-system | ||||
|  | ||||
|     - name: Store root certificate in namespaced configmaps/secrets | ||||
|       kubernetes.core.k8s: | ||||
|         state: present | ||||
|         template: "{{ item.kind }}.j2" | ||||
|         kubeconfig: "{{ kubeconfig.path }}" | ||||
|       vars: | ||||
|         _template: | ||||
|           name: "{{ item.name }}" | ||||
|           namespace: "{{ item.namespace }}" | ||||
|           annotations: "{{ item.annotations | default('{}') | indent(width=4, first=True) }}" | ||||
|           labels: "{{ item.labels | default('{}') | indent(width=4, first=True) }}" | ||||
|           data: "{{ item.data }}" | ||||
|       loop: | ||||
|         - name: argocd-tls-certs-cm | ||||
|           namespace: argo-cd | ||||
|           kind: configmap | ||||
|           annotations: | | ||||
|             meta.helm.sh/release-name: argo-cd | ||||
|             meta.helm.sh/release-namespace: argo-cd | ||||
|           labels: | | ||||
|             app.kubernetes.io/managed-by: Helm | ||||
|             app.kubernetes.io/name: argocd-cm | ||||
|             app.kubernetes.io/part-of: argocd | ||||
|           data: | ||||
|             - key: git.{{ vapp['metacluster.fqdn'] }} | ||||
|               value: "{{ stepca_cm_certs.resources[0].data['root_ca.crt'] }}" | ||||
|         - name: step-certificates-certs | ||||
|           namespace: kube-system | ||||
|           kind: secret | ||||
|           data: | ||||
|             - key: root_ca.crt | ||||
|               value: "{{ stepca_cm_certs.resources[0].data['root_ca.crt'] | b64encode }}" | ||||
|       loop_control: | ||||
|         label: "{{ item.kind ~ '/' ~ item.name ~ ' (' ~ item.namespace ~ ')' }}" | ||||
|  | ||||
|     - name: Configure step-ca passthrough ingress | ||||
|       ansible.builtin.template: | ||||
|         src: ingressroutetcp.j2 | ||||
|         dest: /var/lib/rancher/k3s/server/manifests/{{ _template.name }}-manifest.yaml | ||||
|         owner: root | ||||
|         group: root | ||||
|         mode: 0600 | ||||
|       vars: | ||||
|         _template: | ||||
|           name: step-ca | ||||
|           namespace: step-ca | ||||
|           config: |2 | ||||
|               entryPoints: | ||||
|                 - websecure | ||||
|               routes: | ||||
|               - match: HostSNI(`ca.{{ vapp['metacluster.fqdn'] }}`) | ||||
|                 services: | ||||
|                 - name: step-certificates | ||||
|                   port: 443 | ||||
|               tls: | ||||
|                 passthrough: true | ||||
|       notify: | ||||
|         - Apply manifests | ||||
|  | ||||
|     - name: Inject step-ca certificate into traefik container | ||||
|       ansible.builtin.blockinfile: | ||||
|         path: /var/lib/rancher/k3s/server/manifests/traefik-config.yaml | ||||
|         block: |2 | ||||
|               volumes: | ||||
|                 - name: step-certificates-certs | ||||
|                   mountPath: /step-ca | ||||
|                   type: secret | ||||
|               env: | ||||
|                 - name: LEGO_CA_CERTIFICATES | ||||
|                   value: /step-ca/root_ca.crt | ||||
|         marker: '    # {mark} ANSIBLE MANAGED BLOCK [rootca]' | ||||
|       notify: | ||||
|         - Apply manifests | ||||
|  | ||||
|     - name: Trigger handlers | ||||
|       ansible.builtin.meta: flush_handlers | ||||
|  | ||||
|     - name: Ensure step-ca API availability | ||||
|       ansible.builtin.uri: | ||||
|         url: https://ca.{{ vapp['metacluster.fqdn'] }}/health | ||||
|         method: GET | ||||
|       register: api_readycheck | ||||
|       until: | ||||
|         - api_readycheck.json.status is defined | ||||
|         - api_readycheck.json.status == 'ok' | ||||
|       retries: "{{ playbook.retries }}" | ||||
|       delay: "{{ (storage_benchmark | int) * (playbook.delay.long | int) }}" | ||||
|  | ||||
|   module_defaults: | ||||
|     ansible.builtin.uri: | ||||
|       validate_certs: no | ||||
|       status_code: [200, 201] | ||||
|       body_format: json | ||||
| @@ -0,0 +1,151 @@ | ||||
| - block: | ||||
|  | ||||
|     - name: Install gitea chart | ||||
|       kubernetes.core.helm: | ||||
|         name: gitea | ||||
|         chart_ref: /opt/metacluster/helm-charts/gitea | ||||
|         release_namespace: gitea | ||||
|         create_namespace: true | ||||
|         wait: false | ||||
|         kubeconfig: "{{ kubeconfig.path }}" | ||||
|         values: "{{ components['gitea'].chart_values }}" | ||||
|  | ||||
|     - name: Ensure gitea API availability | ||||
|       ansible.builtin.uri: | ||||
|         url: https://git.{{ vapp['metacluster.fqdn'] }}/api/healthz | ||||
|         method: GET | ||||
|       register: api_readycheck | ||||
|       until: | ||||
|         - api_readycheck.json.status is defined | ||||
|         - api_readycheck.json.status == 'pass' | ||||
|       retries: "{{ playbook.retries }}" | ||||
|       delay: "{{ (storage_benchmark | int) * (playbook.delay.long | int) }}" | ||||
|  | ||||
|     - name: Configure additional SSH ingress | ||||
|       ansible.builtin.template: | ||||
|         src: ingressroutetcp.j2 | ||||
|         dest: /var/lib/rancher/k3s/server/manifests/{{ _template.name }}-manifest.yaml | ||||
|         owner: root | ||||
|         group: root | ||||
|         mode: 0600 | ||||
|       vars: | ||||
|         _template: | ||||
|           name: gitea-ssh | ||||
|           namespace: gitea | ||||
|           config: |2 | ||||
|               entryPoints: | ||||
|                 - ssh | ||||
|               routes: | ||||
|               - match: HostSNI(`*`) | ||||
|                 services: | ||||
|                 - name: gitea-ssh | ||||
|                   port: 22 | ||||
|       notify: | ||||
|         - Apply manifests | ||||
|  | ||||
|     - name: Trigger handlers | ||||
|       ansible.builtin.meta: flush_handlers | ||||
|  | ||||
|     - name: Generate gitea API token | ||||
|       ansible.builtin.uri: | ||||
|         url: https://git.{{ vapp['metacluster.fqdn'] }}/api/v1/users/administrator/tokens | ||||
|         method: POST | ||||
|         user: administrator | ||||
|         password: "{{ vapp['metacluster.password'] }}" | ||||
|         force_basic_auth: yes | ||||
|         body: | ||||
|           name: token_init_{{ lookup('password', '/dev/null length=5 chars=ascii_letters,digits') }} | ||||
|       register: gitea_api_token | ||||
|  | ||||
|     - name: Retrieve existing gitea configuration | ||||
|       ansible.builtin.uri: | ||||
|         url: https://git.{{ vapp['metacluster.fqdn'] }}/api/v1/repos/search | ||||
|         method: GET | ||||
|       register: gitea_existing_config | ||||
|  | ||||
|     - block: | ||||
|  | ||||
|         - name: Register SSH public key | ||||
|           ansible.builtin.uri: | ||||
|             url: https://git.{{ vapp['metacluster.fqdn'] }}/api/v1/user/keys | ||||
|             method: POST | ||||
|             headers: | ||||
|               Authorization: token {{ gitea_api_token.json.sha1 }} | ||||
|             body: | ||||
|               key: "{{ gitops_sshkey.public_key }}" | ||||
|               read_only: false | ||||
|               title: GitOps | ||||
|  | ||||
|         - name: Create organization(s) | ||||
|           ansible.builtin.uri: | ||||
|             url: https://git.{{ vapp['metacluster.fqdn'] }}/api/v1/orgs | ||||
|             method: POST | ||||
|             headers: | ||||
|               Authorization: token {{ gitea_api_token.json.sha1 }} | ||||
|             body: "{{ item }}" | ||||
|           loop: | ||||
|             - full_name: Meta-cluster | ||||
|               description: Meta-cluster configuration items | ||||
|               username: mc | ||||
|               website: https://git.{{ vapp['metacluster.fqdn'] }}/mc | ||||
|               location: '[...]' | ||||
|               visibility: public | ||||
|             - full_name: Workload-cluster | ||||
|               description: Workload-cluster configuration items | ||||
|               username: wl | ||||
|               website: https://git.{{ vapp['metacluster.fqdn'] }}/wl | ||||
|               location: '[...]' | ||||
|               visibility: public | ||||
|           loop_control: | ||||
|             label: "{{ item.full_name }}" | ||||
|  | ||||
|         - name: Create repositories | ||||
|           ansible.builtin.uri: | ||||
|             url: https://git.{{ vapp['metacluster.fqdn'] }}/api/v1/orgs/{{ item.organization }}/repos | ||||
|             method: POST | ||||
|             headers: | ||||
|               Authorization: token {{ gitea_api_token.json.sha1 }} | ||||
|             body: "{{ item.body }}" | ||||
|           loop: | ||||
|             - organization: mc | ||||
|               body: | ||||
|                 name: GitOps.ClusterAPI | ||||
|                 # auto_init: true | ||||
|                 # default_branch: main | ||||
|                 description: ClusterAPI manifests | ||||
|             - organization: mc | ||||
|               body: | ||||
|                 name: GitOps.Config | ||||
|                 # auto_init: true | ||||
|                 # default_branch: main | ||||
|                 description: GitOps manifests | ||||
|             - organization: wl | ||||
|               body: | ||||
|                 name: GitOps.Config | ||||
|                 # auto_init: true | ||||
|                 # default_branch: main | ||||
|                 description: GitOps manifests | ||||
|             - organization: wl | ||||
|               body: | ||||
|                 name: GitOps.HelmCharts | ||||
|                 # auto_init: true | ||||
|                 # default_branch: main | ||||
|                 description: Helm charts | ||||
|           loop_control: | ||||
|             label: "{{ item.organization ~ '/' ~ item.body.name }}" | ||||
|  | ||||
|         # - name: Rebase/Push source gitops repository | ||||
|         #   ansible.builtin.shell: | ||||
|         #     cmd: | | ||||
|         #       git config --local http.sslVerify false | ||||
|         #       git remote set-url origin https://administrator:{{ vapp['metacluster.password'] | urlencode }}@git.{{ vapp['metacluster.fqdn'] }}/mc/GitOps.Config.git | ||||
|         #       git push | ||||
|         #     chdir: /opt/metacluster/git-repositories/gitops | ||||
|  | ||||
|       when: (gitea_existing_config.json is undefined) or (gitea_existing_config.json.data | length == 0) | ||||
|  | ||||
|   module_defaults: | ||||
|     ansible.builtin.uri: | ||||
|       validate_certs: no | ||||
|       status_code: [200, 201] | ||||
|       body_format: json | ||||
| @@ -0,0 +1,75 @@ | ||||
| - block: | ||||
|  | ||||
|     - name: Install argo-cd chart | ||||
|       kubernetes.core.helm: | ||||
|         name: argo-cd | ||||
|         chart_ref: /opt/metacluster/helm-charts/argo-cd | ||||
|         release_namespace: argo-cd | ||||
|         create_namespace: true | ||||
|         wait: false | ||||
|         kubeconfig: "{{ kubeconfig.path }}" | ||||
|         values: "{{ components['argo-cd'].chart_values }}" | ||||
|  | ||||
|     - name: Ensure argo-cd API availability | ||||
|       ansible.builtin.uri: | ||||
|         url: https://gitops.{{ vapp['metacluster.fqdn'] }}/api/version | ||||
|         method: GET | ||||
|       register: api_readycheck | ||||
|       until: | ||||
|         - api_readycheck.json.Version is defined | ||||
|       retries: "{{ playbook.retries }}" | ||||
|       delay: "{{ (storage_benchmark | int) * (playbook.delay.long | int) }}" | ||||
|  | ||||
|     - name: Generate argo-cd API token | ||||
|       ansible.builtin.uri: | ||||
|         url: https://gitops.{{ vapp['metacluster.fqdn'] }}/api/v1/session | ||||
|         method: POST | ||||
|         force_basic_auth: yes | ||||
|         body: | ||||
|           username: admin | ||||
|           password: "{{ vapp['metacluster.password'] }}" | ||||
|       register: argocd_api_token | ||||
|  | ||||
|     - name: Configure metacluster-gitops repository | ||||
|       ansible.builtin.template: | ||||
|         src: gitrepo.j2 | ||||
|         dest: /var/lib/rancher/k3s/server/manifests/{{ _template.name }}-manifest.yaml | ||||
|         owner: root | ||||
|         group: root | ||||
|         mode: 0600 | ||||
|       vars: | ||||
|         _template: | ||||
|           name: gitrepo-mc-gitopsconfig | ||||
|           namespace: argo-cd | ||||
|           url: https://git.{{ vapp['metacluster.fqdn'] }}/mc/GitOps.Config.git | ||||
|       notify: | ||||
|         - Apply manifests | ||||
|  | ||||
|     - name: Create applicationset | ||||
|       ansible.builtin.template: | ||||
|         src: applicationset.j2 | ||||
|         dest: /var/lib/rancher/k3s/server/manifests/{{ _template.application.name }}-manifest.yaml | ||||
|         owner: root | ||||
|         group: root | ||||
|         mode: 0600 | ||||
|       vars: | ||||
|         _template: | ||||
|           application: | ||||
|             name: applicationset-metacluster | ||||
|             namespace: argo-cd | ||||
|           cluster: | ||||
|             url: https://kubernetes.default.svc | ||||
|           repository: | ||||
|             url: https://git.{{ vapp['metacluster.fqdn'] }}/mc/GitOps.Config.git | ||||
|             revision: main | ||||
|       notify: | ||||
|         - Apply manifests | ||||
|  | ||||
|     - name: Trigger handlers | ||||
|       ansible.builtin.meta: flush_handlers | ||||
|  | ||||
|   module_defaults: | ||||
|     ansible.builtin.uri: | ||||
|       validate_certs: no | ||||
|       status_code: [200, 201] | ||||
|       body_format: json | ||||
| @@ -0,0 +1,44 @@ | ||||
| - name: Reconfigure traefik container for persistence | ||||
|   ansible.builtin.blockinfile: | ||||
|     path: /var/lib/rancher/k3s/server/manifests/traefik-config.yaml | ||||
|     block: |2 | ||||
|           deployment: | ||||
|             initContainers: | ||||
|               - name: volume-permissions | ||||
|                 image: busybox:1 | ||||
|                 command: ["sh", "-c", "touch /data/acme.json && chmod -Rv 600 /data/* && chown 65532:65532 /data/acme.json"] | ||||
|                 volumeMounts: | ||||
|                   - name: data | ||||
|                     mountPath: /data | ||||
|           persistence: | ||||
|             enabled: true | ||||
|     marker: '    # {mark} ANSIBLE MANAGED BLOCK [persistence]' | ||||
|   notify: | ||||
|     - Apply manifests | ||||
|  | ||||
| - name: Configure traefik dashboard ingress | ||||
|   ansible.builtin.template: | ||||
|     src: ingressroute.j2 | ||||
|     dest: /var/lib/rancher/k3s/server/manifests/{{ _template.name }}-manifest.yaml | ||||
|     owner: root | ||||
|     group: root | ||||
|     mode: 0600 | ||||
|   vars: | ||||
|     _template: | ||||
|       name: traefik-dashboard | ||||
|       namespace: kube-system | ||||
|       config: |2 | ||||
|           entryPoints: | ||||
|           - web | ||||
|           - websecure | ||||
|           routes: | ||||
|           - kind: Rule | ||||
|             match: Host(`ingress.{{ vapp['metacluster.fqdn'] }}`) | ||||
|             services: | ||||
|             - kind: TraefikService | ||||
|               name: api@internal | ||||
|   notify: | ||||
|     - Apply manifests | ||||
|  | ||||
| - name: Trigger handlers | ||||
|   ansible.builtin.meta: flush_handlers | ||||
| @@ -0,0 +1,83 @@ | ||||
| - name: Configure fallback name resolution | ||||
|   ansible.builtin.lineinfile: | ||||
|     path: /etc/hosts | ||||
|     line: "{{ vapp['guestinfo.ipaddress'] }}  {{ item ~ '.' ~ vapp['metacluster.fqdn'] }}" | ||||
|     state: present | ||||
|   loop: | ||||
|     # TODO: Make this list dynamic | ||||
|     - ca | ||||
|     - git | ||||
|     - gitops | ||||
|     - ingress | ||||
|     - registry | ||||
|     - storage | ||||
|  | ||||
| - name: Initialize tempfile | ||||
|   ansible.builtin.tempfile: | ||||
|     state: file | ||||
|   register: stepca_password | ||||
|  | ||||
| - name: Store password in tempfile | ||||
|   ansible.builtin.copy: | ||||
|     dest: "{{ stepca_password.path }}" | ||||
|     content: "{{ vapp['metacluster.password'] }}" | ||||
|   no_log: true | ||||
|  | ||||
| - name: Generate step-ca helm chart values (including root CA certificate) | ||||
|   ansible.builtin.shell: | ||||
|     cmd: >- | ||||
|       step ca init \ | ||||
|         --helm \ | ||||
|         --deployment-type=standalone \ | ||||
|         --name=ca.{{ vapp['metacluster.fqdn'] }} \ | ||||
|         --dns=ca.{{ vapp['metacluster.fqdn'] }} \ | ||||
|         --dns=step-certificates.step-ca.svc.cluster.local \ | ||||
|         --dns=127.0.0.1 \ | ||||
|         --address=:9000 \ | ||||
|         --provisioner=admin \ | ||||
|         --acme \ | ||||
|         --password-file={{ stepca_password.path }} | ||||
|   register: stepca_values | ||||
|  | ||||
| - name: Cleanup tempfile | ||||
|   ansible.builtin.file: | ||||
|     path: "{{ stepca_password.path }}" | ||||
|     state: absent | ||||
|   when: stepca_password.path is defined | ||||
|  | ||||
| - name: Store root CA certificate | ||||
|   ansible.builtin.copy: | ||||
|     dest: /usr/local/share/ca-certificates/root_ca.crt | ||||
|     content: "{{ (stepca_values.stdout | from_yaml).inject.certificates.root_ca }}" | ||||
|  | ||||
| - name: Update certificate truststore | ||||
|   ansible.builtin.command: | ||||
|     cmd: update-ca-certificates | ||||
|  | ||||
| - name: Get all stored fully qualified container image names | ||||
|   ansible.builtin.shell: | ||||
|     cmd: >- | ||||
|       skopeo list-tags \ | ||||
|         --insecure-policy \ | ||||
|         docker-archive:./{{ item | basename }} | \ | ||||
|       jq -r '.Tags[0]' | ||||
|     chdir: /opt/metacluster/container-images | ||||
|   register: registry_artifacts | ||||
|   loop: "{{ query('ansible.builtin.fileglob', '/opt/metacluster/container-images/*.tar') | sort }}" | ||||
|   loop_control: | ||||
|     label: "{{ item | basename }}" | ||||
|  | ||||
| - name: Get source registries of all artifacts | ||||
|   ansible.builtin.set_fact: | ||||
|     source_registries: "{{ (source_registries | default([]) + [(item | split('/'))[0]]) | unique | sort }}" | ||||
|   loop: "{{ registry_artifacts | json_query('results[*].stdout') | select | sort }}" | ||||
|  | ||||
| - name: Configure K3s node for private registry | ||||
|   ansible.builtin.template: | ||||
|     dest: /etc/rancher/k3s/registries.yaml | ||||
|     src: registries.j2 | ||||
|   vars: | ||||
|     _template: | ||||
|       registries: "{{ source_registries }}" | ||||
|       hv: | ||||
|         fqdn: "{{ vapp['metacluster.fqdn'] }}" | ||||
| @@ -0,0 +1,80 @@ | ||||
| - name: Store custom configuration files | ||||
|   ansible.builtin.copy: | ||||
|     dest: "{{ item.filename }}" | ||||
|     content: "{{ item.content }}" | ||||
|   loop: | ||||
|     - filename: /etc/rancher/k3s/config.yaml | ||||
|       content: | | ||||
|         kubelet-arg: | ||||
|           - "config=/etc/rancher/k3s/kubelet.config" | ||||
|           - "image-gc-high-threshold=95" | ||||
|     - filename: /etc/rancher/k3s/kubelet.config | ||||
|       content: | | ||||
|         apiVersion: kubelet.config.k8s.io/v1beta1 | ||||
|         kind: KubeletConfiguration | ||||
|  | ||||
|         shutdownGracePeriod: 180s | ||||
|         shtudownGracePeriodCriticalPods: 60s | ||||
|   loop_control: | ||||
|     label: "{{ item.filename }}" | ||||
|  | ||||
| - name: Gather service facts | ||||
|   ansible.builtin.service_facts: | ||||
|     # Module requires no attributes | ||||
|  | ||||
| - name: Install K3s | ||||
|   ansible.builtin.command: | ||||
|     cmd: ./install.sh | ||||
|     chdir: /opt/metacluster/k3s | ||||
|   environment: | ||||
|     INSTALL_K3S_SKIP_DOWNLOAD: 'true' | ||||
|     INSTALL_K3S_EXEC: "server --cluster-init --token {{ vapp['metacluster.token'] | trim }} --tls-san {{ vapp['metacluster.vip'] }} --disable local-storage --config /etc/rancher/k3s/config.yaml" | ||||
|   when: ansible_facts.services['k3s.service'] is undefined | ||||
|  | ||||
| - name: Ensure API availability | ||||
|   ansible.builtin.uri: | ||||
|     url: https://{{ vapp['guestinfo.ipaddress'] }}:6443/livez?verbose | ||||
|     method: GET | ||||
|     validate_certs: no | ||||
|     status_code: [200, 401] | ||||
|   register: api_readycheck | ||||
|   until: api_readycheck.json.apiVersion is defined | ||||
|   retries: "{{ playbook.retries }}" | ||||
|   delay: "{{ (storage_benchmark | int) * (playbook.delay.medium | int) }}" | ||||
|  | ||||
| - name: Install kubectl tab-completion | ||||
|   ansible.builtin.shell: | ||||
|     cmd: kubectl completion bash | tee /etc/bash_completion.d/kubectl | ||||
|  | ||||
| - name: Initialize tempfile | ||||
|   ansible.builtin.tempfile: | ||||
|     state: file | ||||
|   register: kubeconfig | ||||
|  | ||||
| - name: Retrieve kubeconfig | ||||
|   ansible.builtin.command: | ||||
|     cmd: kubectl config view --raw | ||||
|   register: kubectl_config | ||||
|  | ||||
| - name: Store kubeconfig in tempfile | ||||
|   ansible.builtin.copy: | ||||
|     dest: "{{ kubeconfig.path }}" | ||||
|     content: "{{ kubectl_config.stdout }}" | ||||
|     mode: 0600 | ||||
|   no_log: true | ||||
|  | ||||
| - name: Add label to node object | ||||
|   kubernetes.core.k8s: | ||||
|     name: "{{ ansible_facts.nodename | lower }}" | ||||
|     kind: Node | ||||
|     state: patched | ||||
|     definition: | ||||
|       metadata: | ||||
|         labels: | ||||
|           ova.airgappedk8s/moref_id: "{{ moref_id }}" | ||||
|     kubeconfig: "{{ kubeconfig.path }}" | ||||
|   register: k8snode_patch | ||||
|   until: | ||||
|     - k8snode_patch.result.metadata.labels['ova.airgappedk8s/moref_id'] is defined | ||||
|   retries: "{{ playbook.retries }}" | ||||
|   delay: "{{ (storage_benchmark | int) * (playbook.delay.medium | int) }}" | ||||
| @@ -0,0 +1,27 @@ | ||||
| - name: Generate kube-vip manifest | ||||
|   ansible.builtin.shell: | ||||
|     cmd: >- | ||||
|       ctr run --rm --net-host ghcr.io/kube-vip/kube-vip:{{ components.kubevip.version }} vip \ | ||||
|         /kube-vip manifest daemonset \ | ||||
|           --interface eth0 \ | ||||
|           --address {{ vapp['metacluster.vip'] }} \ | ||||
|           --inCluster \ | ||||
|           --taint \ | ||||
|           --controlplane \ | ||||
|           --services \ | ||||
|           --arp \ | ||||
|           --leaderElection | ||||
|   register: kubevip_manifest | ||||
|  | ||||
| - name: Inject manifests | ||||
|   ansible.builtin.copy: | ||||
|     dest: /var/lib/rancher/k3s/server/manifests/kubevip-manifest.yaml | ||||
|     content: | | ||||
|       {{ lookup('ansible.builtin.file', '/opt/metacluster/kube-vip/rbac.yaml') }} | ||||
|       --- | ||||
|       {{ kubevip_manifest.stdout | replace('imagePullPolicy: Always', 'imagePullPolicy: IfNotPresent') }} | ||||
|   notify: | ||||
|     - Apply manifests | ||||
|  | ||||
| - name: Trigger handlers | ||||
|   ansible.builtin.meta: flush_handlers | ||||
| @@ -0,0 +1,10 @@ | ||||
| - import_tasks: init.yml | ||||
| - import_tasks: k3s.yml | ||||
| - import_tasks: assets.yml | ||||
| - import_tasks: kube-vip.yml | ||||
| - import_tasks: storage.yml | ||||
| - import_tasks: ingress.yml | ||||
| - import_tasks: certauthority.yml | ||||
| - import_tasks: registry.yml | ||||
| - import_tasks: git.yml | ||||
| - import_tasks: gitops.yml | ||||
| @@ -0,0 +1,50 @@ | ||||
| - block: | ||||
|  | ||||
|     - name: Install harbor chart | ||||
|       kubernetes.core.helm: | ||||
|         name: harbor | ||||
|         chart_ref: /opt/metacluster/helm-charts/harbor | ||||
|         release_namespace: harbor | ||||
|         create_namespace: true | ||||
|         wait: false | ||||
|         kubeconfig: "{{ kubeconfig.path }}" | ||||
|         values: "{{ components['harbor'].chart_values }}" | ||||
|  | ||||
|     - name: Ensure harbor API availability | ||||
|       ansible.builtin.uri: | ||||
|         url: https://registry.{{ vapp['metacluster.fqdn'] }}/api/v2.0/health | ||||
|         method: GET | ||||
|       register: api_readycheck | ||||
|       until: | ||||
|         - api_readycheck.json.status is defined | ||||
|         - api_readycheck.json.status == 'healthy' | ||||
|       retries: "{{ playbook.retries }}" | ||||
|       delay: "{{ (storage_benchmark | int) * (playbook.delay.long | int) }}" | ||||
|  | ||||
|     - name: Push images to registry | ||||
|       ansible.builtin.shell: | ||||
|         cmd: >- | ||||
|           skopeo copy \ | ||||
|             --insecure-policy \ | ||||
|             --dest-tls-verify=false \ | ||||
|             --dest-creds admin:{{ vapp['metacluster.password'] }} \ | ||||
|             docker-archive:./{{ item | basename }} \ | ||||
|             docker://registry.{{ vapp['metacluster.fqdn'] }}/library/$( \ | ||||
|               skopeo list-tags \ | ||||
|                 --insecure-policy \ | ||||
|                 docker-archive:./{{ item | basename }} | \ | ||||
|               jq -r '.Tags[0]') | ||||
|         chdir: /opt/metacluster/container-images/ | ||||
|       register: push_result | ||||
|       loop: "{{ query('ansible.builtin.fileglob', '/opt/metacluster/container-images/*.tar') | sort }}" | ||||
|       loop_control: | ||||
|         label: "{{ item | basename }}" | ||||
|       retries: "{{ playbook.retries }}" | ||||
|       delay: "{{ ((storage_benchmark | float) * playbook.delay.short) | int }}" | ||||
|       until: push_result is not failed | ||||
|  | ||||
|   module_defaults: | ||||
|     ansible.builtin.uri: | ||||
|       validate_certs: no | ||||
|       status_code: [200, 201, 401] | ||||
|       body_format: json | ||||
| @@ -0,0 +1,26 @@ | ||||
| - block: | ||||
|     - name: Install longhorn chart | ||||
|       kubernetes.core.helm: | ||||
|         name: longhorn | ||||
|         chart_ref: /opt/metacluster/helm-charts/longhorn | ||||
|         release_namespace: longhorn-system | ||||
|         create_namespace: true | ||||
|         wait: false | ||||
|         kubeconfig: "{{ kubeconfig.path }}" | ||||
|         values: "{{ components['longhorn'].chart_values }}" | ||||
|  | ||||
|     - name: Ensure longhorn API availability | ||||
|       ansible.builtin.uri: | ||||
|         url: https://storage.{{ vapp['metacluster.fqdn'] }}/v1 | ||||
|         method: GET | ||||
|       register: api_readycheck | ||||
|       until: | ||||
|         - api_readycheck is not failed | ||||
|       retries: "{{ playbook.retries }}" | ||||
|       delay: "{{ (storage_benchmark | int) * (playbook.delay.long | int) }}" | ||||
|  | ||||
|   module_defaults: | ||||
|     ansible.builtin.uri: | ||||
|       validate_certs: no | ||||
|       status_code: [200, 201] | ||||
|       body_format: json | ||||
| @@ -0,0 +1,25 @@ | ||||
| - name: Check for expected vApp properties | ||||
|   ansible.builtin.assert: | ||||
|     that: | ||||
|       - vapp[item] is defined | ||||
|       - (vapp[item] | length) > 0 | ||||
|     quiet: true | ||||
|   loop: | ||||
|     - deployment.type | ||||
|     - guestinfo.dnsserver | ||||
|     - guestinfo.gateway | ||||
|     - guestinfo.hostname | ||||
|     - guestinfo.ipaddress | ||||
|     - guestinfo.prefixlength | ||||
|     - guestinfo.rootsshkey | ||||
|     - hv.fqdn | ||||
|     - hv.password | ||||
|     - hv.username | ||||
|     - ippool.endip | ||||
|     - ippool.startip | ||||
|     - metacluster.fqdn | ||||
|     - metacluster.password | ||||
|     - metacluster.token | ||||
|     - metacluster.vip | ||||
|     - workloadcluster.name | ||||
|     - workloadcluster.vip | ||||
| @@ -0,0 +1,284 @@ | ||||
| - block: | ||||
|   # Below tasks circumvent usernames with `<domain>\<username>` format, which causes CAPV to | ||||
|   # incorrectly interpret the backslash (despite automatic escaping) as an escape sequence. | ||||
|   # `vcenter_session.user` will instead contain the username in `<username>@<domain>` format. | ||||
|  | ||||
|     - name: Generate vCenter API token | ||||
|       ansible.builtin.uri: | ||||
|         url: https://{{ vapp['hv.fqdn'] }}/api/session | ||||
|         method: POST | ||||
|         headers: | ||||
|           Authorization: Basic {{ ( vapp['hv.username'] ~ ':' ~ vapp['hv.password'] ) | b64encode }} | ||||
|       register: vcenterapi_token | ||||
|  | ||||
|     - name: Retrieve vCenter API session details | ||||
|       ansible.builtin.uri: | ||||
|         url: https://{{ vapp['hv.fqdn'] }}/api/session | ||||
|         method: GET | ||||
|         headers: | ||||
|           vmware-api-session-id: "{{ vcenterapi_token.json }}" | ||||
|       register: vcenter_session | ||||
|  | ||||
|   module_defaults: | ||||
|     ansible.builtin.uri: | ||||
|       validate_certs: no | ||||
|       status_code: [200, 201] | ||||
|       body_format: json | ||||
|  | ||||
| - name: Configure clusterctl | ||||
|   ansible.builtin.template: | ||||
|     src: clusterctl.j2 | ||||
|     dest: /opt/metacluster/cluster-api/clusterctl.yaml | ||||
|   vars: | ||||
|     _template: | ||||
|       version: | ||||
|         base: "{{ components.clusterapi.management.version.base }}" | ||||
|         cert_manager: "{{ components.clusterapi.management.version.cert_manager }}" | ||||
|         infrastructure_vsphere: "{{ components.clusterapi.management.version.infrastructure_vsphere }}" | ||||
|         ipam_incluster: "{{ components.clusterapi.management.version.ipam_incluster }}" | ||||
|       hv: | ||||
|         fqdn: "{{ vapp['hv.fqdn'] }}" | ||||
|         tlsthumbprint: "{{ tls_thumbprint.stdout }}" | ||||
|         username: "{{ vcenter_session.json.user }}" | ||||
|         password: "{{ vapp['hv.password'] }}" | ||||
|         datacenter: "{{ vcenter_info.datacenter }}" | ||||
|         datastore: "{{ vcenter_info.datastore }}" | ||||
|         network: "{{ vcenter_info.network }}" | ||||
|         resourcepool: "{{ vcenter_info.resourcepool }}" | ||||
|         folder: "{{ vcenter_info.folder }}" | ||||
|       cluster: | ||||
|         nodetemplate: "{{ nodetemplate_inventorypath }}" | ||||
|         publickey: "{{ vapp['guestinfo.rootsshkey'] }}" | ||||
|         version: "{{ components.clusterapi.workload.version.k8s }}" | ||||
|         vip: "{{ vapp['workloadcluster.vip'] }}" | ||||
|  | ||||
| - name: Generate cluster-template kustomization manifest | ||||
|   ansible.builtin.template: | ||||
|     src: kustomization.cluster-template.j2 | ||||
|     dest: /opt/metacluster/cluster-api/infrastructure-vsphere/{{ components.clusterapi.management.version.infrastructure_vsphere }}/kustomization.yaml | ||||
|   vars: | ||||
|     _template: | ||||
|       network: | ||||
|         fqdn: "{{ vapp['metacluster.fqdn'] }}" | ||||
|         dnsserver: "{{ vapp['guestinfo.dnsserver'] }}" | ||||
|       nodesize: | ||||
|         cpu: "{{ config.clusterapi.size_matrix[ vapp['workloadcluster.nodesize'] ].cpu }}" | ||||
|         memory: "{{ config.clusterapi.size_matrix[ vapp['workloadcluster.nodesize'] ].memory }}" | ||||
|       rootca: "{{ stepca_cm_certs.resources[0].data['root_ca.crt'] }}" | ||||
|       runcmds: | ||||
|         - update-ca-certificates | ||||
|       registries: "{{ source_registries }}" | ||||
|  | ||||
| - name: Store custom cluster-template | ||||
|   ansible.builtin.copy: | ||||
|     dest: /opt/metacluster/cluster-api/custom-cluster-template.yaml | ||||
|     content: "{{ lookup('kubernetes.core.kustomize', dir='/opt/metacluster/cluster-api/infrastructure-vsphere/' ~ components.clusterapi.management.version.infrastructure_vsphere ) }}" | ||||
|  | ||||
| - name: Initialize Cluster API management cluster | ||||
|   ansible.builtin.shell: | ||||
|     cmd: >- | ||||
|       clusterctl init \ | ||||
|         -v5 \ | ||||
|         --infrastructure vsphere:{{ components.clusterapi.management.version.infrastructure_vsphere }} \ | ||||
|         --ipam in-cluster:{{ components.clusterapi.management.version.ipam_incluster }} \ | ||||
|         --config ./clusterctl.yaml \ | ||||
|         --kubeconfig {{ kubeconfig.path }} | ||||
|     chdir: /opt/metacluster/cluster-api | ||||
|  | ||||
| - name: Ensure controller availability | ||||
|   kubernetes.core.k8s_info: | ||||
|     kind: Deployment | ||||
|     name: "{{ item.name }}" | ||||
|     namespace: "{{ item.namespace }}" | ||||
|     wait: true | ||||
|     kubeconfig: "{{ kubeconfig.path }}" | ||||
|   loop: | ||||
|     - name: caip-in-cluster-controller-manager | ||||
|       namespace: caip-in-cluster-system | ||||
|     - name: capi-controller-manager | ||||
|       namespace: capi-system | ||||
|     - name: capv-controller-manager | ||||
|       namespace: capv-system | ||||
|   loop_control: | ||||
|     label: "{{ item.name }}" | ||||
|  | ||||
| - name: Parse vApp for workload cluster sizing | ||||
|   ansible.builtin.set_fact: | ||||
|     clustersize: >- | ||||
|       {{ { | ||||
|         'controlplane': vapp['deployment.type'] | regex_findall('^cp(\d)+') | first, | ||||
|         'worker': vapp['deployment.type'] | regex_findall('w(\d)+') | first, | ||||
|         'workerstorage': vapp['deployment.type'] | regex_findall('ws(\d)+$') | first | ||||
|       } }} | ||||
|  | ||||
| - name: Generate workload cluster manifest | ||||
|   ansible.builtin.shell: | ||||
|     cmd: >- | ||||
|       clusterctl generate cluster \ | ||||
|         {{ vapp['workloadcluster.name'] | lower }} \ | ||||
|         --control-plane-machine-count {{ clustersize.controlplane }} \ | ||||
|         --worker-machine-count {{ clustersize.worker }} \ | ||||
|         --from ./custom-cluster-template.yaml \ | ||||
|         --config ./clusterctl.yaml \ | ||||
|         --kubeconfig {{ kubeconfig.path }} | ||||
|     chdir: /opt/metacluster/cluster-api | ||||
|   register: clusterctl_newcluster | ||||
|  | ||||
| - name: Initialize tempfolder | ||||
|   ansible.builtin.tempfile: | ||||
|     state: directory | ||||
|   register: capi_clustermanifest | ||||
|  | ||||
| - name: Save workload cluster manifest | ||||
|   ansible.builtin.copy: | ||||
|     dest: "{{ capi_clustermanifest.path }}/new-cluster.yaml" | ||||
|     content: "{{ clusterctl_newcluster.stdout }}" | ||||
|  | ||||
| - name: Split manifest into separate files | ||||
|   ansible.builtin.shell: | ||||
|     cmd: >- | ||||
|       kubectl slice \ | ||||
|         -f {{ capi_clustermanifest.path }}/new-cluster.yaml \ | ||||
|         -o {{ capi_clustermanifest.path }}/manifests | ||||
|  | ||||
| - name: Generate nodepool kustomization manifest | ||||
|   ansible.builtin.template: | ||||
|     src: kustomization.nodepool.j2 | ||||
|     dest: "{{ capi_clustermanifest.path }}/kustomization.yaml" | ||||
|   vars: | ||||
|     _template: | ||||
|       cluster: | ||||
|         name: "{{ vapp['workloadcluster.name'] }}" | ||||
|       nodepool: | ||||
|         size: "{{ clustersize.workerstorage }}" | ||||
|         additionaldisk: "{{ vapp['workloadcluster.additionaldisk'] }}" | ||||
|  | ||||
| - name: Store nodepool manifest | ||||
|   ansible.builtin.copy: | ||||
|     dest: "{{ capi_clustermanifest.path }}/manifests/nodepool-worker-storage.yaml" | ||||
|     content: "{{ lookup('kubernetes.core.kustomize', dir=capi_clustermanifest.path) }}" | ||||
|  | ||||
| - name: Create in-cluster IpPool | ||||
|   ansible.builtin.template: | ||||
|     src: ippool.j2 | ||||
|     dest: "{{ capi_clustermanifest.path }}/manifests/inclusterippool-{{ _template.cluster.name }}.yml" | ||||
|   vars: | ||||
|     _template: | ||||
|       cluster: | ||||
|         name: "{{ vapp['workloadcluster.name'] | lower }}" | ||||
|         namespace: default | ||||
|         network: | ||||
|           startip: "{{ vapp['ippool.startip'] }}" | ||||
|           endip: "{{ vapp['ippool.endip'] }}" | ||||
|           prefix: "{{ vapp['guestinfo.prefixlength'] }}" | ||||
|           gateway: "{{ vapp['guestinfo.gateway'] }}" | ||||
|  | ||||
| - name: Initialize/Push git repository | ||||
|   ansible.builtin.shell: | ||||
|     cmd: | | ||||
|       git init | ||||
|       git config --global user.email "administrator@{{ vapp['metacluster.fqdn'] }}" | ||||
|       git config --global user.name "administrator" | ||||
|       git checkout -b main | ||||
|       git add ./manifests | ||||
|       git commit -m "Upload manifests" | ||||
|       git remote add origin https://git.{{ vapp['metacluster.fqdn'] }}/mc/GitOps.ClusterAPI.git | ||||
|       git push https://administrator:{{ vapp['metacluster.password'] | urlencode }}@git.{{ vapp['metacluster.fqdn'] }}/mc/GitOps.ClusterAPI.git --all | ||||
|     chdir: "{{ capi_clustermanifest.path }}" | ||||
|  | ||||
| - name: Cleanup tempfolder | ||||
|   ansible.builtin.file: | ||||
|     path: "{{ capi_clustermanifest.path }}" | ||||
|     state: absent | ||||
|   when: capi_clustermanifest.path is defined | ||||
|  | ||||
| - name: Configure Cluster API repository | ||||
|   ansible.builtin.template: | ||||
|     src: gitrepo.j2 | ||||
|     dest: /var/lib/rancher/k3s/server/manifests/{{ _template.name }}-manifest.yaml | ||||
|     owner: root | ||||
|     group: root | ||||
|     mode: 0600 | ||||
|   vars: | ||||
|     _template: | ||||
|       name: gitrepo-mc-gitopsclusterapi | ||||
|       namespace: argo-cd | ||||
|       url: https://git.{{ vapp['metacluster.fqdn'] }}/mc/GitOps.ClusterAPI.git | ||||
|   notify: | ||||
|     - Apply manifests | ||||
|  | ||||
| - name: WORKAROUND - Wait for ingress ACME requests to complete | ||||
|   ansible.builtin.shell: | ||||
|     cmd: >- | ||||
|       openssl s_client -connect registry.{{ vapp['metacluster.fqdn'] }}:443 -servername registry.{{ vapp['metacluster.fqdn'] }} 2>/dev/null </dev/null | \ | ||||
|         openssl x509 -noout -subject | \ | ||||
|       grep 'subject=CN = registry.{{ vapp['metacluster.fqdn'] }}' | ||||
|   register: certificate_subject | ||||
|   until: certificate_subject is not failed | ||||
|   retries: "{{ playbook.retries }}" | ||||
|   delay: "{{ (storage_benchmark | int) * (playbook.delay.medium | int) }}" | ||||
|  | ||||
| - name: Create application | ||||
|   ansible.builtin.template: | ||||
|     src: application.j2 | ||||
|     dest: /var/lib/rancher/k3s/server/manifests/{{ _template.application.name }}-manifest.yaml | ||||
|     owner: root | ||||
|     group: root | ||||
|     mode: 0600 | ||||
|   vars: | ||||
|     _template: | ||||
|       application: | ||||
|         name: application-clusterapi-workloadcluster | ||||
|         namespace: argo-cd | ||||
|       cluster: | ||||
|         name: https://kubernetes.default.svc | ||||
|         namespace: default | ||||
|       repository: | ||||
|         url: https://git.{{ vapp['metacluster.fqdn'] }}/mc/GitOps.ClusterAPI.git | ||||
|         path: manifests | ||||
|         revision: main | ||||
|   notify: | ||||
|     - Apply manifests | ||||
|  | ||||
| - name: Trigger handlers | ||||
|   ansible.builtin.meta: flush_handlers | ||||
|  | ||||
| - name: Wait for cluster to be available | ||||
|   ansible.builtin.shell: | ||||
|     cmd: >- | ||||
|       kubectl wait clusters.cluster.x-k8s.io/{{ vapp['workloadcluster.name'] | lower }} \ | ||||
|         --for=condition=Ready \ | ||||
|         --timeout 0s | ||||
|   register: cluster_readycheck | ||||
|   until: cluster_readycheck is succeeded | ||||
|   retries: "{{ playbook.retries }}" | ||||
|   delay: "{{ (storage_benchmark | int) * (playbook.delay.long | int) }}" | ||||
|  | ||||
| - name: Initialize tempfile | ||||
|   ansible.builtin.tempfile: | ||||
|     state: file | ||||
|   register: capi_kubeconfig | ||||
|  | ||||
| - name: Retrieve kubeconfig | ||||
|   ansible.builtin.shell: | ||||
|     cmd: >- | ||||
|       clusterctl get kubeconfig \ | ||||
|         {{ vapp['workloadcluster.name'] | lower }} \ | ||||
|         --kubeconfig {{ kubeconfig.path }} | ||||
|   register: capi_kubectl_config | ||||
|  | ||||
| - name: Store kubeconfig in tempfile | ||||
|   ansible.builtin.copy: | ||||
|     dest: "{{ capi_kubeconfig.path }}" | ||||
|     content: "{{ capi_kubectl_config.stdout }}" | ||||
|     mode: 0600 | ||||
|   no_log: true | ||||
|  | ||||
| # TODO: move to git repo | ||||
| - name: Apply cni plugin manifest | ||||
|   kubernetes.core.k8s: | ||||
|     src: /opt/metacluster/cluster-api/cni-calico/{{ components.clusterapi.workload.version.calico }}/calico.yaml | ||||
|     state: present | ||||
|     wait: true | ||||
|     kubeconfig: "{{ capi_kubeconfig.path }}" | ||||
| # TODO: move to git repo | ||||
| @@ -0,0 +1,105 @@ | ||||
| - name: Aggregate helm charts from filesystem | ||||
|   ansible.builtin.find: | ||||
|     path: /opt/workloadcluster/helm-charts | ||||
|     file_type: directory | ||||
|     recurse: false | ||||
|   register: helm_charts | ||||
|  | ||||
| - name: Create hard-links to populate new git-repository | ||||
|   ansible.builtin.shell: | ||||
|     cmd: >- | ||||
|       cp -lr {{ item.path }}/ /opt/workloadcluster/git-repositories/gitops/charts | ||||
|   loop: "{{ helm_charts.files }}" | ||||
|   loop_control: | ||||
|     label: "{{ item.path | basename }}" | ||||
|  | ||||
| - name: Create subfolders | ||||
|   ansible.builtin.file: | ||||
|     path: /opt/workloadcluster/git-repositories/gitops/values/{{ item.key }} | ||||
|     state: directory | ||||
|   loop: "{{ query('ansible.builtin.dict', downstream_components) }}" | ||||
|   loop_control: | ||||
|     label: "{{ item.key }}" | ||||
|  | ||||
| - name: Write chart values to file | ||||
|   ansible.builtin.copy: | ||||
|     dest: /opt/workloadcluster/git-repositories/gitops/values/{{ item.key }}/values.yaml | ||||
|     content: "{{ item.value.chart_values | default('# Empty') | to_nice_yaml(indent=2, width=4096) }}" | ||||
|   loop: "{{ query('ansible.builtin.dict', downstream_components) }}" | ||||
|   loop_control: | ||||
|     label: "{{ item.key }}" | ||||
|  | ||||
| - name: Initialize/Push git repository | ||||
|   ansible.builtin.shell: | ||||
|     cmd: | | ||||
|       git init | ||||
|       git config --global user.email "administrator@{{ vapp['metacluster.fqdn'] }}" | ||||
|       git config --global user.name "administrator" | ||||
|       git checkout -b main | ||||
|       git add . | ||||
|       git commit -m "Upload charts" | ||||
|       git remote add origin https://git.{{ vapp['metacluster.fqdn'] }}/wl/GitOps.Config.git | ||||
|       git push https://administrator:{{ vapp['metacluster.password'] | urlencode }}@git.{{ vapp['metacluster.fqdn'] }}/wl/GitOps.Config.git --all | ||||
|     chdir: /opt/workloadcluster/git-repositories/gitops | ||||
|  | ||||
| - name: Retrieve workload-cluster kubeconfig | ||||
|   kubernetes.core.k8s_info: | ||||
|     kind: Secret | ||||
|     name: "{{ vapp['workloadcluster.name'] }}-kubeconfig" | ||||
|     namespace: default | ||||
|     kubeconfig: "{{ kubeconfig.path }}" | ||||
|   register: secret_workloadcluster_kubeconfig | ||||
|  | ||||
| - name: Register workload-cluster in argo-cd | ||||
|   kubernetes.core.k8s: | ||||
|     template: cluster.j2 | ||||
|     state: present | ||||
|     kubeconfig: "{{ kubeconfig.path }}" | ||||
|   vars: | ||||
|     _template: | ||||
|       cluster: | ||||
|         name: "{{ vapp['workloadcluster.name'] | lower }}" | ||||
|         secret: argocd-cluster-{{ vapp['workloadcluster.name'] | lower }} | ||||
|         url: https://{{ vapp['workloadcluster.vip'] }}:6443 | ||||
|       kubeconfig: | ||||
|         ca: "{{ (secret_workloadcluster_kubeconfig.resources[0].data.value | b64decode | from_yaml).clusters[0].cluster['certificate-authority-data'] }}" | ||||
|         certificate: "{{ (secret_workloadcluster_kubeconfig.resources[0].data.value | b64decode | from_yaml).users[0].user['client-certificate-data'] }}" | ||||
|         key: "{{ (secret_workloadcluster_kubeconfig.resources[0].data.value | b64decode | from_yaml).users[0].user['client-key-data'] }}" | ||||
|  | ||||
| - name: Configure workload-cluster GitOps repository | ||||
|   ansible.builtin.template: | ||||
|     src: gitrepo.j2 | ||||
|     dest: /var/lib/rancher/k3s/server/manifests/{{ _template.name }}-manifest.yaml | ||||
|     owner: root | ||||
|     group: root | ||||
|     mode: 0600 | ||||
|   vars: | ||||
|     _template: | ||||
|       name: gitrepo-wl-gitopsconfig | ||||
|       namespace: argo-cd | ||||
|       url: https://git.{{ vapp['metacluster.fqdn'] }}/wl/GitOps.Config.git | ||||
|   notify: | ||||
|     - Apply manifests | ||||
|  | ||||
| - name: Create applicationset | ||||
|   ansible.builtin.template: | ||||
|     src: applicationset.j2 | ||||
|     dest: /var/lib/rancher/k3s/server/manifests/{{ _template.application.name }}-manifest.yaml | ||||
|     owner: root | ||||
|     group: root | ||||
|     mode: 0600 | ||||
|   vars: | ||||
|     _template: | ||||
|       application: | ||||
|         name: applicationset-workloadcluster | ||||
|         namespace: argo-cd | ||||
|       cluster: | ||||
|         url: https://{{ vapp['workloadcluster.vip'] }}:6443 | ||||
|       repository: | ||||
|         url: https://git.{{ vapp['metacluster.fqdn'] }}/wl/GitOps.Config.git | ||||
|         revision: main | ||||
|   notify: | ||||
|     - Apply manifests | ||||
|  | ||||
| - name: Trigger handlers | ||||
|   ansible.builtin.meta: flush_handlers | ||||
| @@ -0,0 +1,57 @@ | ||||
| - name: Gather hypervisor details | ||||
|   ansible.builtin.shell: | ||||
|     cmd: govc ls -L {{ item.moref }} | awk -F/ '{print ${{ item.part }}}' | ||||
|   environment: | ||||
|     GOVC_INSECURE: '1' | ||||
|     GOVC_URL: "{{ vapp['hv.fqdn'] }}" | ||||
|     GOVC_USERNAME: "{{ vapp['hv.username'] }}" | ||||
|     GOVC_PASSWORD: "{{ vapp['hv.password'] }}" | ||||
|   register: govc_inventory | ||||
|   loop: | ||||
|     - attribute: cluster | ||||
|       moref: >- | ||||
|         $(govc object.collect -json VirtualMachine:{{ moref_id }} | \ | ||||
|           jq -r '.[] | select(.Name == "runtime").Val.Host | .Type + ":" + .Value') | ||||
|       part: (NF-1) | ||||
|     - attribute: datacenter | ||||
|       moref: VirtualMachine:{{ moref_id }} | ||||
|       part: 2 | ||||
|     - attribute: datastore | ||||
|       moref: >- | ||||
|         $(govc object.collect -json VirtualMachine:{{ moref_id }} | \ | ||||
|           jq -r '.[] | select(.Name == "datastore").Val.ManagedObjectReference | .[].Type + ":" + .[].Value') | ||||
|       part: NF | ||||
|     - attribute: folder | ||||
|       moref: >- | ||||
|         $(govc object.collect -json VirtualMachine:{{ moref_id }} | \ | ||||
|           jq -r '.[] | select(.Name == "parent").Val | .Type + ":" + .Value') | ||||
|       part: 0 | ||||
|     # - attribute: host | ||||
|     #   moref: >- | ||||
|     #     $(govc object.collect -json VirtualMachine:{{ moref_id }} | \ | ||||
|     #       jq -r '.[] | select(.Name == "runtime").Val.Host | .Type + ":" + .Value') | ||||
|     #   part: NF | ||||
|     - attribute: network | ||||
|       moref: >- | ||||
|         $(govc object.collect -json VirtualMachine:{{ moref_id }} | \ | ||||
|           jq -r '.[] | select(.Name == "network").Val.ManagedObjectReference | .[].Type + ":" + .[].Value') | ||||
|       part: NF | ||||
|     - attribute: resourcepool | ||||
|       moref: >- | ||||
|         $(govc object.collect -json VirtualMachine:{{ moref_id }} | \ | ||||
|           jq -r '.[] | select(.Name == "resourcePool").Val | .Type + ":" + .Value') | ||||
|       part: 0 | ||||
|   loop_control: | ||||
|     label: "{{ item.attribute }}" | ||||
|  | ||||
| - name: Retrieve hypervisor TLS thumbprint | ||||
|   ansible.builtin.shell: | ||||
|     cmd: openssl s_client -connect {{ vapp['hv.fqdn'] }}:443 < /dev/null 2>/dev/null | openssl x509 -fingerprint -noout -in /dev/stdin | awk -F'=' '{print $2}' | ||||
|   register: tls_thumbprint | ||||
|  | ||||
| - name: Store hypervisor details in dictionary | ||||
|   ansible.builtin.set_fact: | ||||
|     vcenter_info: "{{ vcenter_info | default({}) | combine({ item.item.attribute : item.stdout }) }}" | ||||
|   loop: "{{ govc_inventory.results }}" | ||||
|   loop_control: | ||||
|     label: "{{ item.item.attribute }}" | ||||
| @@ -0,0 +1,11 @@ | ||||
| - import_tasks: hypervisor.yml | ||||
| - import_tasks: registry.yml | ||||
| - import_tasks: nodetemplates.yml | ||||
|  | ||||
| - block: | ||||
|  | ||||
|     - import_tasks: clusterapi.yml | ||||
|     - import_tasks: gitops.yml | ||||
|  | ||||
|   when: | ||||
|     - vapp['deployment.type'] != 'core' | ||||
| @@ -0,0 +1,73 @@ | ||||
| - block: | ||||
|  | ||||
|     - name: Check for existing template on hypervisor | ||||
|       community.vmware.vmware_guest_info: | ||||
|         name: "{{ (filename | basename | split('.'))[:-1] | join('.') }}" | ||||
|       register: existing_ova | ||||
|       ignore_errors: yes | ||||
|  | ||||
|     - name: Store inventory path of existing template | ||||
|       ansible.builtin.set_fact: | ||||
|         nodetemplate_inventorypath: "{{ existing_ova.instance.hw_folder ~ '/' ~ existing_ova.instance.hw_name }}" | ||||
|       when: existing_ova is not failed | ||||
|  | ||||
|     - block: | ||||
|  | ||||
|         - name: Parse OVA file for network mappings | ||||
|           ansible.builtin.shell: | ||||
|             cmd: govc import.spec -json {{ filename }} | ||||
|           environment: | ||||
|             GOVC_INSECURE: '1' | ||||
|             GOVC_URL: "{{ vapp['hv.fqdn'] }}" | ||||
|             GOVC_USERNAME: "{{ vapp['hv.username'] }}" | ||||
|             GOVC_PASSWORD: "{{ vapp['hv.password'] }}" | ||||
|           register: ova_spec | ||||
|  | ||||
|         - name: Deploy OVA template on hypervisor | ||||
|           community.vmware.vmware_deploy_ovf: | ||||
|             cluster: "{{ vcenter_info.cluster }}" | ||||
|             datastore: "{{ vcenter_info.datastore }}" | ||||
|             name: "{{ (filename | basename | split('.'))[:-1] | join('.') }}" | ||||
|             networks: "{u'{{ ova_spec.stdout | from_json | json_query('NetworkMapping[0].Name') }}':u'{{ vcenter_info.network }}'}" | ||||
|             allow_duplicates: no | ||||
|             power_on: false | ||||
|             ovf: "{{ filename }}" | ||||
|           register: ova_deploy | ||||
|  | ||||
|         - name: Add additional placeholder disk | ||||
|           community.vmware.vmware_guest_disk: | ||||
|             name: "{{ ova_deploy.instance.hw_name }}" | ||||
|             disk: | ||||
|               - size: 1Mb | ||||
|                 scsi_controller: 1 | ||||
|                 scsi_type: paravirtual | ||||
|                 unit_number: 0 | ||||
|  | ||||
|         # Disabled to allow disks to be resized; at the cost of cloning speed | ||||
|         # - name: Create snapshot on deployed VM | ||||
|         #   community.vmware.vmware_guest_snapshot: | ||||
|         #     name: "{{ ova_deploy.instance.hw_name }}" | ||||
|         #     state: present | ||||
|         #     snapshot_name: "{{ ansible_date_time.iso8601_basic_short }}-base" | ||||
|  | ||||
|         - name: Mark deployed VM as templates | ||||
|           community.vmware.vmware_guest: | ||||
|             name: "{{ ova_deploy.instance.hw_name }}" | ||||
|             is_template: yes | ||||
|  | ||||
|         - name: Store inventory path of deployed template | ||||
|           ansible.builtin.set_fact: | ||||
|             nodetemplate_inventorypath: "{{ ova_deploy.instance.hw_folder ~ '/' ~ ova_deploy.instance.hw_name }}" | ||||
|  | ||||
|       when: existing_ova is failed | ||||
|  | ||||
|   vars: | ||||
|     filename: "{{ query('ansible.builtin.fileglob', '/opt/workloadcluster/node-templates/*.ova') | first }}" | ||||
|   module_defaults: | ||||
|     group/vmware: | ||||
|       hostname: "{{ vapp['hv.fqdn'] }}" | ||||
|       validate_certs: no | ||||
|       username: "{{ vapp['hv.username'] }}" | ||||
|       password: "{{ vapp['hv.password'] }}" | ||||
|       datacenter: "{{ vcenter_info.datacenter }}" | ||||
|       folder: "{{ vcenter_info.folder }}" | ||||
| @@ -0,0 +1,40 @@ | ||||
| - block: | ||||
|  | ||||
|   - name: Create dedicated kubeadm project within container registry | ||||
|     ansible.builtin.uri: | ||||
|       url: https://registry.{{ vapp['metacluster.fqdn'] }}/api/v2.0/projects | ||||
|       method: POST | ||||
|       headers: | ||||
|         Authorization: "Basic {{ ('admin:' ~ vapp['metacluster.password']) | b64encode }}" | ||||
|       body: | ||||
|         project_name: kubeadm | ||||
|         public: true | ||||
|         storage_limit: 0 | ||||
|         metadata: | ||||
|           enable_content_trust: 'false' | ||||
|           enable_content_trust_cosign: 'false' | ||||
|           auto_scan: 'true' | ||||
|           severity: none | ||||
|           prevent_vul: 'false' | ||||
|           public: 'true' | ||||
|           reuse_sys_cve_allowlist: 'true' | ||||
|  | ||||
|   - name: Lookup kubeadm container images | ||||
|     ansible.builtin.set_fact: | ||||
|       kubeadm_images: "{{ lookup('ansible.builtin.file', '/opt/metacluster/cluster-api/imagelist').splitlines() }}" | ||||
|  | ||||
|   - name: Copy kubeadm container images to dedicated project | ||||
|     ansible.builtin.uri: | ||||
|       url: https://registry.{{ vapp['metacluster.fqdn'] }}/api/v2.0/projects/kubeadm/repositories/{{ ( item | regex_findall('([^:/]+)') )[-2] }}/artifacts?from=library/{{ item | replace('/', '%2F') | replace(':', '%3A') }} | ||||
|       method: POST | ||||
|       headers: | ||||
|         Authorization: "Basic {{ ('admin:' ~ vapp['metacluster.password']) | b64encode }}" | ||||
|       body: | ||||
|         from: "{{ item }}" | ||||
|     loop: "{{ kubeadm_images }}" | ||||
|  | ||||
|   module_defaults: | ||||
|     ansible.builtin.uri: | ||||
|       validate_certs: no | ||||
|       status_code: [200, 201, 409] | ||||
|       body_format: json | ||||
| @@ -0,0 +1,16 @@ | ||||
| apiVersion: argoproj.io/v1alpha1 | ||||
| kind: Application | ||||
| metadata: | ||||
|   name: {{ _template.application.name }} | ||||
|   namespace: {{ _template.application.namespace }} | ||||
| spec: | ||||
|   destination: | ||||
|     namespace: {{ _template.cluster.namespace }} | ||||
|     server: {{ _template.cluster.name }} | ||||
|   project: default | ||||
|   source: | ||||
|     repoURL: {{ _template.repository.url }} | ||||
|     path: {{ _template.repository.path }} | ||||
|     targetRevision: {{ _template.repository.revision }} | ||||
|   syncPolicy: | ||||
|     automated: {} | ||||
| @@ -0,0 +1,33 @@ | ||||
| apiVersion: argoproj.io/v1alpha1 | ||||
| kind: ApplicationSet | ||||
| metadata: | ||||
|   name: {{ _template.application.name }} | ||||
|   namespace: {{ _template.application.namespace }} | ||||
| spec: | ||||
|   generators: | ||||
|   - git: | ||||
|       repoURL: {{ _template.repository.url }} | ||||
|       revision: {{ _template.repository.revision }} | ||||
|       directories: | ||||
|       - path: charts/*/* | ||||
|   template: | ||||
|     metadata: | ||||
|       name: application-{% raw %}{{ path.basename }}{% endraw +%} | ||||
|     spec: | ||||
|       project: default | ||||
|       syncPolicy: | ||||
|         automated: | ||||
|           prune: true | ||||
|           selfHeal: true | ||||
|         syncOptions: | ||||
|         - CreateNamespace=true | ||||
|       sources: | ||||
|       - repoURL: {{ _template.repository.url }} | ||||
|         targetRevision: {{ _template.repository.revision }} | ||||
|         path: {% raw %}'{{ path }}'{% endraw +%} | ||||
|         helm: | ||||
|           valueFiles: | ||||
|           - /values/{% raw %}{{ path.basename }}{% endraw %}/values.yaml | ||||
|       destination: | ||||
|         server: {{ _template.cluster.url }} | ||||
|         namespace: {% raw %}'{{ path[1] }}'{% endraw +%} | ||||
| @@ -0,0 +1,20 @@ | ||||
| apiVersion: v1 | ||||
| kind: Secret | ||||
| metadata: | ||||
|   name: {{ _template.cluster.secret }} | ||||
|   namespace: argo-cd | ||||
|   labels: | ||||
|     argocd.argoproj.io/secret-type: cluster | ||||
| type: Opaque | ||||
| stringData: | ||||
|   name: {{ _template.cluster.name }} | ||||
|   server: {{ _template.cluster.url }} | ||||
|   config: | | ||||
|     { | ||||
|       "tlsClientConfig": { | ||||
|         "insecure": false, | ||||
|         "caData": "{{ _template.kubeconfig.ca }}", | ||||
|         "certData": "{{ _template.kubeconfig.certificate }}", | ||||
|         "keyData": "{{ _template.kubeconfig.key }}" | ||||
|       } | ||||
|     } | ||||
| @@ -0,0 +1,42 @@ | ||||
| providers: | ||||
|   - name: "kubeadm" | ||||
|     url: "/opt/metacluster/cluster-api/bootstrap-kubeadm/{{ _template.version.base }}/bootstrap-components.yaml" | ||||
|     type: "BootstrapProvider" | ||||
|   - name: "cluster-api" | ||||
|     url: "/opt/metacluster/cluster-api/cluster-api/{{ _template.version.base }}/core-components.yaml" | ||||
|     type: "CoreProvider" | ||||
|   - name: "kubeadm" | ||||
|     url: "/opt/metacluster/cluster-api/control-plane-kubeadm/{{ _template.version.base }}/control-plane-components.yaml" | ||||
|     type: "ControlPlaneProvider" | ||||
|   - name: "vsphere" | ||||
|     url: "/opt/metacluster/cluster-api/infrastructure-vsphere/{{ _template.version.infrastructure_vsphere }}/infrastructure-components.yaml" | ||||
|     type: "InfrastructureProvider" | ||||
|   - name: "in-cluster" | ||||
|     url: "/opt/metacluster/cluster-api/ipam-in-cluster/{{ _template.version.ipam_incluster }}/ipam-components.yaml" | ||||
|     type: "IPAMProvider" | ||||
|  | ||||
| cert-manager: | ||||
|   url: "/opt/metacluster/cluster-api/cert-manager/{{ _template.version.cert_manager }}/cert-manager.yaml" | ||||
|   version: "{{ _template.version.cert_manager }}" | ||||
|  | ||||
| ## -- Controller settings -- ## | ||||
| VSPHERE_SERVER: "{{ _template.hv.fqdn }}" | ||||
| VSPHERE_TLS_THUMBPRINT: "{{ _template.hv.tlsthumbprint }}" | ||||
| VSPHERE_USERNAME: "{{ _template.hv.username }}" | ||||
| VSPHERE_PASSWORD: "{{ _template.hv.password }}" | ||||
|  | ||||
| ## -- Required workload cluster default settings -- ## | ||||
| VSPHERE_DATACENTER: "{{ _template.hv.datacenter }}" | ||||
| VSPHERE_DATASTORE: "{{ _template.hv.datastore }}" | ||||
| VSPHERE_STORAGE_POLICY: "" | ||||
| VSPHERE_NETWORK: "{{ _template.hv.network }}" | ||||
| VSPHERE_RESOURCE_POOL: "{{ _template.hv.resourcepool }}" | ||||
| VSPHERE_FOLDER: "{{ _template.hv.folder }}" | ||||
|  | ||||
| VSPHERE_TEMPLATE: "{{ _template.cluster.nodetemplate }}" | ||||
| VSPHERE_SSH_AUTHORIZED_KEY: "{{ _template.cluster.publickey }}" | ||||
|  | ||||
| KUBERNETES_VERSION: "{{ _template.cluster.version }}" | ||||
| CONTROL_PLANE_ENDPOINT_IP: "{{ _template.cluster.vip }}" | ||||
| VIP_NETWORK_INTERFACE: "" | ||||
| EXP_CLUSTER_RESOURCE_SET: "true" | ||||
| @@ -0,0 +1,14 @@ | ||||
| apiVersion: v1 | ||||
| kind: ConfigMap | ||||
| metadata: | ||||
|   name: {{ _template.name }} | ||||
|   namespace: {{ _template.namespace }} | ||||
|   annotations: | ||||
| {{ _template.annotations }} | ||||
|   labels: | ||||
| {{ _template.labels }} | ||||
| data: | ||||
| {% for kv_pair in _template.data %} | ||||
|   "{{ kv_pair.key }}": | | ||||
| {{ kv_pair.value | indent(width=4, first=True) }} | ||||
| {% endfor %} | ||||
| @@ -0,0 +1,9 @@ | ||||
| apiVersion: v1 | ||||
| kind: Secret | ||||
| metadata: | ||||
|   name: {{ _template.name }} | ||||
|   namespace: {{ _template.namespace }} | ||||
|   labels: | ||||
|     argocd.argoproj.io/secret-type: repository | ||||
| stringData: | ||||
|   url: {{ _template.url }} | ||||
| @@ -0,0 +1,7 @@ | ||||
| apiVersion: traefik.containo.us/v1alpha1 | ||||
| kind: IngressRoute | ||||
| metadata: | ||||
|   name: {{ _template.name }} | ||||
|   namespace: {{ _template.namespace }} | ||||
| spec: | ||||
| {{ _template.config }} | ||||
| @@ -0,0 +1,7 @@ | ||||
| apiVersion: traefik.containo.us/v1alpha1 | ||||
| kind: IngressRouteTCP | ||||
| metadata: | ||||
|   name: {{ _template.name }} | ||||
|   namespace: {{ _template.namespace }} | ||||
| spec: | ||||
| {{ _template.config }} | ||||
| @@ -0,0 +1,10 @@ | ||||
| apiVersion: ipam.cluster.x-k8s.io/v1alpha1 | ||||
| kind: InClusterIPPool | ||||
| metadata: | ||||
|   name: inclusterippool-{{ _template.cluster.name }} | ||||
|   namespace: {{ _template.cluster.namespace }} | ||||
| spec: | ||||
|   start: {{ _template.cluster.network.startip }} | ||||
|   end: {{ _template.cluster.network.endip }} | ||||
|   prefix: {{ _template.cluster.network.prefix }} | ||||
|   gateway: {{ _template.cluster.network.gateway }} | ||||
| @@ -0,0 +1,256 @@ | ||||
| apiVersion: kustomize.config.k8s.io/v1beta1 | ||||
| kind: Kustomization | ||||
| resources: | ||||
| - cluster-template.yaml | ||||
|  | ||||
| patchesStrategicMerge: | ||||
|   - |- | ||||
|     apiVersion: v1 | ||||
|     kind: Secret | ||||
|     metadata: | ||||
|       name: csi-vsphere-config | ||||
|       namespace: '${NAMESPACE}' | ||||
|     stringData: | ||||
|       data: | | ||||
|         apiVersion: v1 | ||||
|         kind: Secret | ||||
|         metadata: | ||||
|           name: csi-vsphere-config | ||||
|           namespace: kube-system | ||||
|         stringData: | ||||
|           csi-vsphere.conf: |+ | ||||
|             [Global] | ||||
|             insecure-flag = true | ||||
|             thumbprint = "${VSPHERE_TLS_THUMBPRINT}" | ||||
|             cluster-id = "${NAMESPACE}/${CLUSTER_NAME}" | ||||
|  | ||||
|             [VirtualCenter "${VSPHERE_SERVER}"] | ||||
|             user = "${VSPHERE_USERNAME}" | ||||
|             password = "${VSPHERE_PASSWORD}" | ||||
|             datacenters = "${VSPHERE_DATACENTER}" | ||||
|  | ||||
|             [Network] | ||||
|             public-network = "${VSPHERE_NETWORK}" | ||||
|         type: Opaque | ||||
|   - |- | ||||
|     apiVersion: controlplane.cluster.x-k8s.io/v1beta1 | ||||
|     kind: KubeadmControlPlane | ||||
|     metadata: | ||||
|       name: '${CLUSTER_NAME}' | ||||
|       namespace: '${NAMESPACE}' | ||||
|     spec: | ||||
|       kubeadmConfigSpec: | ||||
|         clusterConfiguration: | ||||
|           imageRepository: registry.{{ _template.network.fqdn }}/kubeadm | ||||
|   - |- | ||||
|     apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 | ||||
|     kind: KubeadmConfigTemplate | ||||
|     metadata: | ||||
|       name: '${CLUSTER_NAME}-md-0' | ||||
|       namespace: '${NAMESPACE}' | ||||
|     spec: | ||||
|       template: | ||||
|         spec: | ||||
|           clusterConfiguration: | ||||
|             imageRepository: registry.{{ _template.network.fqdn }}/kubeadm | ||||
|   - |- | ||||
|     apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 | ||||
|     kind: KubeadmConfigTemplate | ||||
|     metadata: | ||||
|       name: '${CLUSTER_NAME}-md-0' | ||||
|       namespace: '${NAMESPACE}' | ||||
|     spec: | ||||
|       template: | ||||
|         spec: | ||||
|           files: | ||||
|           - content: | | ||||
|                 [plugins."io.containerd.grpc.v1.cri".registry] | ||||
|                   config_path = "/etc/containerd/certs.d" | ||||
|             append: true | ||||
|             path: /etc/containerd/config.toml | ||||
| {% for registry in _template.registries %} | ||||
|           - content: | | ||||
|               server = "https://{{ registry }}" | ||||
|  | ||||
|               [host."https://registry.{{ _template.network.fqdn }}/v2/library/{{ registry }}"] | ||||
|                 capabilities = ["pull", "resolve"] | ||||
|                 override_path = true | ||||
|             owner: root:root | ||||
|             path: /etc/containerd/certs.d/{{ registry }}/hosts.toml | ||||
| {% endfor %} | ||||
|           - content: | | ||||
|               network: {config: disabled} | ||||
|             owner: root:root | ||||
|             path: /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg | ||||
|           - content: | | ||||
|               {{ _template.rootca | indent(width=14, first=False) | trim }} | ||||
|             owner: root:root | ||||
|             path: /usr/local/share/ca-certificates/root_ca.crt | ||||
|   - |- | ||||
|     apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 | ||||
|     kind: VSphereMachineTemplate | ||||
|     metadata: | ||||
|       name: ${CLUSTER_NAME} | ||||
|       namespace: '${NAMESPACE}' | ||||
|     spec: | ||||
|       template: | ||||
|         spec: | ||||
|           network: | ||||
|             devices: | ||||
|             - dhcp4: false | ||||
|               addressesFromPools: | ||||
|               - apiGroup: ipam.cluster.x-k8s.io | ||||
|                 kind: InClusterIPPool | ||||
|                 name: inclusterippool-${CLUSTER_NAME} | ||||
|               nameservers: | ||||
|               - {{ _template.network.dnsserver }} | ||||
|               networkName: '${VSPHERE_NETWORK}' | ||||
|   - |- | ||||
|     apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 | ||||
|     kind: VSphereMachineTemplate | ||||
|     metadata: | ||||
|       name: ${CLUSTER_NAME}-worker | ||||
|       namespace: '${NAMESPACE}' | ||||
|     spec: | ||||
|       template: | ||||
|         spec: | ||||
|           network: | ||||
|             devices: | ||||
|             - dhcp4: false | ||||
|               addressesFromPools: | ||||
|               - apiGroup: ipam.cluster.x-k8s.io | ||||
|                 kind: InClusterIPPool | ||||
|                 name: inclusterippool-${CLUSTER_NAME} | ||||
|               nameservers: | ||||
|               - {{ _template.network.dnsserver }} | ||||
|               networkName: '${VSPHERE_NETWORK}' | ||||
|  | ||||
| patchesJson6902: | ||||
|   - target: | ||||
|       group: controlplane.cluster.x-k8s.io | ||||
|       version: v1beta1 | ||||
|       kind: KubeadmControlPlane | ||||
|       name: .* | ||||
|     patch: |- | ||||
|       - op: add | ||||
|         path: /spec/kubeadmConfigSpec/files/- | ||||
|         value: | ||||
|           content: | | ||||
|               [plugins."io.containerd.grpc.v1.cri".registry] | ||||
|                 config_path = "/etc/containerd/certs.d" | ||||
|           append: true | ||||
|           path: /etc/containerd/config.toml | ||||
| {% for registry in _template.registries %} | ||||
|       - op: add | ||||
|         path: /spec/kubeadmConfigSpec/files/- | ||||
|         value: | ||||
|           content: | | ||||
|             server = "https://{{ registry }}" | ||||
|  | ||||
|             [host."https://registry.{{ _template.network.fqdn }}/v2/library/{{ registry }}"] | ||||
|               capabilities = ["pull", "resolve"] | ||||
|               override_path = true | ||||
|           owner: root:root | ||||
|           path: /etc/containerd/certs.d/{{ registry }}/hosts.toml | ||||
| {% endfor %} | ||||
|       - op: add | ||||
|         path: /spec/kubeadmConfigSpec/files/- | ||||
|         value: | ||||
|           content: | | ||||
|             network: {config: disabled} | ||||
|           owner: root:root | ||||
|           path: /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg | ||||
|       - op: add | ||||
|         path: /spec/kubeadmConfigSpec/files/- | ||||
|         value: | ||||
|           content: | | ||||
|             {{ _template.rootca | indent(width=12, first=False) | trim }} | ||||
|           owner: root:root | ||||
|           path: /usr/local/share/ca-certificates/root_ca.crt | ||||
|   - target: | ||||
|       group: bootstrap.cluster.x-k8s.io | ||||
|       version: v1beta1 | ||||
|       kind: KubeadmConfigTemplate | ||||
|       name: .* | ||||
|     patch: |- | ||||
| {% for cmd in _template.runcmds %} | ||||
|       - op: add | ||||
|         path: /spec/template/spec/preKubeadmCommands/- | ||||
|         value: {{ cmd }} | ||||
| {% endfor %} | ||||
|   - target: | ||||
|       group: controlplane.cluster.x-k8s.io | ||||
|       version: v1beta1 | ||||
|       kind: KubeadmControlPlane | ||||
|       name: .* | ||||
|     patch: |- | ||||
| {% for cmd in _template.runcmds %} | ||||
|       - op: add | ||||
|         path: /spec/kubeadmConfigSpec/preKubeadmCommands/- | ||||
|         value: {{ cmd }} | ||||
| {% endfor %} | ||||
|  | ||||
|   - target: | ||||
|       group: infrastructure.cluster.x-k8s.io | ||||
|       version: v1beta1 | ||||
|       kind: VSphereMachineTemplate | ||||
|       name: \${CLUSTER_NAME} | ||||
|     patch: |- | ||||
|       - op: replace | ||||
|         path: /metadata/name | ||||
|         value: ${CLUSTER_NAME}-master | ||||
|   - target: | ||||
|       group: controlplane.cluster.x-k8s.io | ||||
|       version: v1beta1 | ||||
|       kind: KubeadmControlPlane | ||||
|       name: \${CLUSTER_NAME} | ||||
|     patch: |- | ||||
|       - op: replace | ||||
|         path: /metadata/name | ||||
|         value: ${CLUSTER_NAME}-master | ||||
|       - op: replace | ||||
|         path: /spec/machineTemplate/infrastructureRef/name | ||||
|         value: ${CLUSTER_NAME}-master | ||||
|   - target: | ||||
|       group: cluster.x-k8s.io | ||||
|       version: v1beta1 | ||||
|       kind: Cluster | ||||
|       name: \${CLUSTER_NAME} | ||||
|     patch: |- | ||||
|       - op: replace | ||||
|         path: /spec/controlPlaneRef/name | ||||
|         value: ${CLUSTER_NAME}-master | ||||
|  | ||||
|   - target: | ||||
|       group: infrastructure.cluster.x-k8s.io | ||||
|       version: v1beta1 | ||||
|       kind: VSphereMachineTemplate | ||||
|       name: \${CLUSTER_NAME}-worker | ||||
|     patch: |- | ||||
|       - op: replace | ||||
|         path: /spec/template/spec/numCPUs | ||||
|         value: {{ _template.nodesize.cpu }} | ||||
|       - op: replace | ||||
|         path: /spec/template/spec/memoryMiB | ||||
|         value: {{ _template.nodesize.memory }} | ||||
|   - target: | ||||
|       group: cluster.x-k8s.io | ||||
|       version: v1beta1 | ||||
|       kind: MachineDeployment | ||||
|       name: \${CLUSTER_NAME}-md-0 | ||||
|     patch: |- | ||||
|       - op: replace | ||||
|         path: /metadata/name | ||||
|         value: ${CLUSTER_NAME}-worker | ||||
|       - op: replace | ||||
|         path: /spec/template/spec/bootstrap/configRef/name | ||||
|         value: ${CLUSTER_NAME}-worker | ||||
|   - target: | ||||
|       group: bootstrap.cluster.x-k8s.io | ||||
|       version: v1beta1 | ||||
|       kind: KubeadmConfigTemplate | ||||
|       name: \${CLUSTER_NAME}-md-0 | ||||
|     patch: |- | ||||
|       - op: replace | ||||
|         path: /metadata/name | ||||
|         value: ${CLUSTER_NAME}-worker | ||||
| @@ -0,0 +1,84 @@ | ||||
| apiVersion: kustomize.config.k8s.io/v1beta1 | ||||
| kind: Kustomization | ||||
| resources: | ||||
| - manifests/kubeadmconfigtemplate-{{ _template.cluster.name }}-worker.yaml | ||||
| - manifests/machinedeployment-{{ _template.cluster.name }}-worker.yaml | ||||
| - manifests/vspheremachinetemplate-{{ _template.cluster.name }}-worker.yaml | ||||
|  | ||||
| patchesStrategicMerge: | ||||
|   - |- | ||||
|     apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 | ||||
|     kind: KubeadmConfigTemplate | ||||
|     metadata: | ||||
|       name: {{ _template.cluster.name }}-worker | ||||
|       namespace: default | ||||
|     spec: | ||||
|       template: | ||||
|         spec: | ||||
|           diskSetup: | ||||
|             filesystems: | ||||
|             - device: /dev/sdb1 | ||||
|               filesystem: ext4 | ||||
|               label: blockstorage | ||||
|             partitions: | ||||
|             - device: /dev/sdb | ||||
|               layout: true | ||||
|               tableType: gpt | ||||
|           joinConfiguration: | ||||
|             nodeRegistration: | ||||
|               kubeletExtraArgs: | ||||
|                 node-labels: "node.longhorn.io/create-default-disk=true" | ||||
|           mounts: | ||||
|           - - LABEL=blockstorage | ||||
|             - /mnt/blockstorage | ||||
|   - |- | ||||
|     apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 | ||||
|     kind: VSphereMachineTemplate | ||||
|     metadata: | ||||
|       name: {{ _template.cluster.name }}-worker | ||||
|       namespace: default | ||||
|     spec: | ||||
|       template: | ||||
|         spec: | ||||
|           additionalDisksGiB: | ||||
|           - {{ _template.nodepool.additionaldisk }} | ||||
|  | ||||
| patchesJson6902: | ||||
|   - target: | ||||
|       group: bootstrap.cluster.x-k8s.io | ||||
|       version: v1beta1 | ||||
|       kind: KubeadmConfigTemplate | ||||
|       name: {{ _template.cluster.name }}-worker | ||||
|     patch: |- | ||||
|       - op: replace | ||||
|         path: /metadata/name | ||||
|         value: {{ _template.cluster.name }}-worker-storage | ||||
|  | ||||
|   - target: | ||||
|       group: cluster.x-k8s.io | ||||
|       version: v1beta1 | ||||
|       kind: MachineDeployment | ||||
|       name: {{ _template.cluster.name }}-worker | ||||
|     patch: |- | ||||
|       - op: replace | ||||
|         path: /metadata/name | ||||
|         value: {{ _template.cluster.name }}-worker-storage | ||||
|       - op: replace | ||||
|         path: /spec/template/spec/bootstrap/configRef/name | ||||
|         value: {{ _template.cluster.name }}-worker-storage | ||||
|       - op: replace | ||||
|         path: /spec/template/spec/infrastructureRef/name | ||||
|         value: {{ _template.cluster.name }}-worker-storage | ||||
|       - op: replace | ||||
|         path: /spec/replicas | ||||
|         value: {{ _template.nodepool.size }} | ||||
|  | ||||
|   - target: | ||||
|       group: infrastructure.cluster.x-k8s.io | ||||
|       version: v1beta1 | ||||
|       kind: VSphereMachineTemplate | ||||
|       name: {{ _template.cluster.name }}-worker | ||||
|     patch: |- | ||||
|       - op: replace | ||||
|         path: /metadata/name | ||||
|         value: {{ _template.cluster.name }}-worker-storage | ||||
| @@ -0,0 +1,9 @@ | ||||
| apiVersion: v1 | ||||
| kind: Secret | ||||
| metadata: | ||||
|   name: {{ _template.name }} | ||||
|   namespace: {{ _template.namespace }} | ||||
| data: | ||||
| {% for kv_pair in _template.data %} | ||||
|   "{{ kv_pair.key }}": {{ kv_pair.value }} | ||||
| {% endfor %} | ||||
| @@ -0,0 +1,4 @@ | ||||
| - name: Disable crontab job | ||||
|   ansible.builtin.cron: | ||||
|     name: firstboot | ||||
|     state: absent | ||||
| @@ -0,0 +1,12 @@ | ||||
| - import_tasks: service.yml | ||||
| - import_tasks: cron.yml | ||||
|  | ||||
| - name: Cleanup tempfile | ||||
|   ansible.builtin.file: | ||||
|     path: "{{ kubeconfig.path }}" | ||||
|     state: absent | ||||
|   when: kubeconfig.path is defined | ||||
|  | ||||
| # - name: Reboot host | ||||
| #   ansible.builtin.shell: | ||||
| #     cmd: systemctl reboot | ||||
| @@ -0,0 +1,30 @@ | ||||
| - name: Create tarball compression service | ||||
|   ansible.builtin.template: | ||||
|     src: "{{ item.src }}" | ||||
|     dest: "{{ item.dest }}" | ||||
|     owner: root | ||||
|     group: root | ||||
|     mode: "{{ item.mode | default(omit) }}" | ||||
|   vars: | ||||
|     _template: | ||||
|       service: | ||||
|         name: compressTarballs | ||||
|         executable: /opt/firstboot/compresstarballs.sh | ||||
|         workingdir: /opt/metacluster/container-images/ | ||||
|   loop: | ||||
|     - src: compresstarballs.j2 | ||||
|       dest: "{{ _template.service.executable }}" | ||||
|       mode: o+x | ||||
|     - src: systemdunit.j2 | ||||
|       dest: /etc/systemd/system/{{ _template.service.name }}.service | ||||
|   loop_control: | ||||
|     label: "{{ item.src }}" | ||||
|  | ||||
| - name: Enable/Start services | ||||
|   ansible.builtin.systemd: | ||||
|     name: "{{ item }}" | ||||
|     enabled: yes | ||||
|     state: started | ||||
|   loop: | ||||
|     - compressTarballs | ||||
|     - ttyConsoleMessage | ||||
| @@ -0,0 +1,40 @@ | ||||
| - name: Create volume group | ||||
|   community.general.lvg: | ||||
|     vg: longhorn_vg | ||||
|     pvs: | ||||
|       - /dev/sdb | ||||
|     pvresize: yes | ||||
|  | ||||
| - name: Create logical volume | ||||
|   community.general.lvol: | ||||
|     vg: longhorn_vg | ||||
|     lv: longhorn_lv | ||||
|     size: 100%VG | ||||
|  | ||||
| - name: Store begin timestamp | ||||
|   ansible.builtin.set_fact: | ||||
|     start_time: "{{ lookup('pipe', 'date +%s') }}" | ||||
|  | ||||
| - name: Create filesystem | ||||
|   community.general.filesystem: | ||||
|     dev: /dev/mapper/longhorn_vg-longhorn_lv | ||||
|     fstype: ext4 | ||||
|  | ||||
| - name: Store end timestamp | ||||
|   ansible.builtin.set_fact: | ||||
|     end_time: "{{ lookup('pipe', 'date +%s') }}" | ||||
|  | ||||
| - name: Calculate crude storage benchmark | ||||
|   ansible.builtin.set_fact: | ||||
|     storage_benchmark: "{{ [storage_benchmark, (end_time | int - start_time | int)] | max }}" | ||||
|  | ||||
| - name: Log benchmark actual duration | ||||
|   ansible.builtin.debug: | ||||
|     msg: "Benchmark actual duration: {{ (end_time | int - start_time | int) }} second(s)" | ||||
|  | ||||
| - name: Mount dynamic disk | ||||
|   ansible.posix.mount: | ||||
|     path: /mnt/blockstorage | ||||
|     src: /dev/mapper/longhorn_vg-longhorn_lv | ||||
|     fstype: ext4 | ||||
|     state: mounted | ||||
| @@ -0,0 +1,12 @@ | ||||
| - name: Import container images | ||||
|   ansible.builtin.command: | ||||
|     cmd: k3s ctr image import {{ item }} --digests | ||||
|     chdir: /opt/metacluster/container-images | ||||
|   register: import_result | ||||
|   loop: "{{ query('ansible.builtin.fileglob', '/opt/metacluster/container-images/*.tar') | sort }}" | ||||
|   loop_control: | ||||
|     label: "{{ item | basename }}" | ||||
|   # Probably should add a task before that ensures K3s node is fully initialized before starting imports; currently K3s goes away briefly during this loop | ||||
|   retries: "{{ playbook.retries }}" | ||||
|   delay: "{{ ((storage_benchmark | float) * playbook.delay.short) | int }}" | ||||
|   until: import_result is not failed | ||||
| @@ -0,0 +1,19 @@ | ||||
| - name: Set hostname | ||||
|   ansible.builtin.hostname: | ||||
|     name: "{{ vapp['guestinfo.hostname'] }}" | ||||
|  | ||||
| - name: Create netplan configuration file | ||||
|   ansible.builtin.template: | ||||
|     src: netplan.j2 | ||||
|     dest: /etc/netplan/00-installer-config.yaml | ||||
|   vars: | ||||
|     _template: | ||||
|       macaddress: "{{ ansible_facts.default_ipv4.macaddress }}" | ||||
|       ipaddress: "{{ vapp['guestinfo.ipaddress'] }}" | ||||
|       prefixlength: "{{ vapp['guestinfo.prefixlength'] }}" | ||||
|       gateway: "{{ vapp['guestinfo.gateway'] }}" | ||||
|       dnsserver: "{{ vapp['guestinfo.dnsserver'] }}" | ||||
|  | ||||
| - name: Apply netplan configuration | ||||
|   ansible.builtin.shell: | ||||
|     cmd: /usr/sbin/netplan apply | ||||
| @@ -0,0 +1,13 @@ | ||||
| network: | ||||
|   version: 2 | ||||
|   ethernets: | ||||
|     id0: | ||||
|       set-name: eth0 | ||||
|       match: | ||||
|         macaddress: {{ _template.macaddress }} | ||||
|       addresses: | ||||
|       - {{ _template.ipaddress }}/{{ _template.prefixlength }} | ||||
|       gateway4: {{ _template.gateway }} | ||||
|       nameservers: | ||||
|         addresses: | ||||
|         - {{ _template.dnsserver }} | ||||
| @@ -0,0 +1,2 @@ | ||||
| - import_tasks: vapp.yml | ||||
| - import_tasks: vcenter.yml | ||||
| @@ -0,0 +1,16 @@ | ||||
| - block: | ||||
|  | ||||
|     - name: Check for vCenter connectivity | ||||
|       community.vmware.vmware_vcenter_settings_info: | ||||
|         schema: vsphere | ||||
|       register: vcenter_info | ||||
|       retries: "{{ playbook.retries }}" | ||||
|       delay: "{{ ((storage_benchmark | float) * playbook.delay.short) | int }}" | ||||
|       until: vcenter_info is not failed | ||||
|  | ||||
|   module_defaults: | ||||
|     group/vmware: | ||||
|       hostname: "{{ vapp['hv.fqdn'] }}" | ||||
|       validate_certs: no | ||||
|       username: "{{ vapp['hv.username'] }}" | ||||
|       password: "{{ vapp['hv.password'] }}" | ||||
| @@ -0,0 +1,50 @@ | ||||
| - name: Create folder structure(s) | ||||
|   ansible.builtin.file: | ||||
|     path: "{{ item }}" | ||||
|     state: directory | ||||
|   loop: | ||||
|     - /opt/firstboot | ||||
|  | ||||
| - name: Create tty console message service | ||||
|   ansible.builtin.template: | ||||
|     src: "{{ item.src }}" | ||||
|     dest: "{{ item.dest }}" | ||||
|     owner: root | ||||
|     group: root | ||||
|     mode: "{{ item.mode | default(omit) }}" | ||||
|   vars: | ||||
|     _template: | ||||
|       service: | ||||
|         name: ttyConsoleMessage | ||||
|         executable: /opt/firstboot/tty.sh | ||||
|         workingdir: /tmp/ | ||||
|       metacluster: | ||||
|         components: | ||||
|           - name: ArgoCD | ||||
|             url: https://gitops.${FQDN} | ||||
|             healthcheck: https://gitops.${FQDN} | ||||
|           - name: Gitea | ||||
|             url: https://git.${FQDN} | ||||
|             healthcheck: https://git.${FQDN} | ||||
|           - name: Harbor | ||||
|             url: https://registry.${FQDN} | ||||
|             healthcheck: https://registry.${FQDN} | ||||
|           - name: Longhorn | ||||
|             url: https://storage.${FQDN} | ||||
|             healthcheck: https://storage.${FQDN} | ||||
|           - name: StepCA | ||||
|             url: '' | ||||
|             healthcheck: https://ca.${FQDN}/health | ||||
|           - name: Traefik | ||||
|             url: https://ingress.${FQDN} | ||||
|             healthcheck: https://ingress.${FQDN} | ||||
|         fqdn: "{{ vapp['metacluster.fqdn'] }}" | ||||
|         vip: "{{ vapp['metacluster.vip'] }}" | ||||
|   loop: | ||||
|     - src: tty.j2 | ||||
|       dest: "{{ _template.service.executable }}" | ||||
|       mode: o+x | ||||
|     - src: systemdunit.j2 | ||||
|       dest: /etc/systemd/system/{{ _template.service.name }}.service | ||||
|   loop_control: | ||||
|     label: "{{ item.src }}" | ||||
| @@ -0,0 +1,39 @@ | ||||
| - name: Set root password | ||||
|   ansible.builtin.user: | ||||
|     name: root | ||||
|     password: "{{ vapp['metacluster.password'] | password_hash('sha512', 65534 | random(seed=vapp['guestinfo.hostname']) | string) }}" | ||||
|     generate_ssh_key: yes | ||||
|     ssh_key_bits: 2048 | ||||
|     ssh_key_file: .ssh/id_rsa | ||||
|  | ||||
| - name: Save root SSH publickey | ||||
|   ansible.builtin.lineinfile: | ||||
|     path: /root/.ssh/authorized_keys | ||||
|     line: "{{ vapp['guestinfo.rootsshkey'] }}" | ||||
|  | ||||
| - name: Disable SSH password authentication | ||||
|   ansible.builtin.lineinfile: | ||||
|     path: /etc/ssh/sshd_config | ||||
|     regex: "{{ item.regex }}" | ||||
|     line: "{{ item.line }}" | ||||
|     state: "{{ item.state }}" | ||||
|   loop: | ||||
|   - regex: '^#PasswordAuthentication' | ||||
|     line: 'PasswordAuthentication no' | ||||
|     state: present | ||||
|   - regex: '^PasswordAuthentication yes' | ||||
|     line: 'PasswordAuthentication yes' | ||||
|     state: absent | ||||
|   loop_control: | ||||
|     label: "{{ '[' ~ item.regex ~ '] ' ~ item.state }}" | ||||
|  | ||||
| - name: Create dedicated SSH keypair | ||||
|   community.crypto.openssh_keypair: | ||||
|     path: /root/.ssh/git_rsa_id | ||||
|   register: gitops_sshkey | ||||
|  | ||||
| - name: Delete 'ubuntu' user | ||||
|   ansible.builtin.user: | ||||
|     name: ubuntu | ||||
|     state: absent | ||||
|     remove: yes | ||||
| @@ -0,0 +1,38 @@ | ||||
| - name: Store current ovfEnvironment | ||||
|   ansible.builtin.shell: | ||||
|     cmd: /usr/bin/vmtoolsd --cmd "info-get guestinfo.ovfEnv" | ||||
|   register: ovfenv | ||||
|  | ||||
| - name: Parse XML for MoRef ID | ||||
|   community.general.xml: | ||||
|     xmlstring: "{{ ovfenv.stdout }}" | ||||
|     namespaces: | ||||
|       ns: http://schemas.dmtf.org/ovf/environment/1 | ||||
|       ve: http://www.vmware.com/schema/ovfenv | ||||
|     xpath: /ns:Environment | ||||
|     content: attribute | ||||
|   register: environment_attribute | ||||
|  | ||||
| - name: Store MoRef ID | ||||
|   ansible.builtin.set_fact: | ||||
|     moref_id: "{{ ((environment_attribute.matches[0].values() | list)[0].values() | list)[1] }}" | ||||
|  | ||||
| - name: Parse XML for vApp properties | ||||
|   community.general.xml: | ||||
|     xmlstring: "{{ ovfenv.stdout }}" | ||||
|     namespaces: | ||||
|       ns: http://schemas.dmtf.org/ovf/environment/1 | ||||
|     xpath: /ns:Environment/ns:PropertySection/ns:Property | ||||
|     content: attribute | ||||
|   register: property_section | ||||
|  | ||||
| - name: Assign vApp properties to dictionary | ||||
|   ansible.builtin.set_fact: | ||||
|     vapp: >- | ||||
|       {{ vapp | default({}) | combine({ | ||||
|         ((item.values() | list)[0].values() | list)[0]: | ||||
|         ((item.values() | list)[0].values() | list)[1]}) | ||||
|       }} | ||||
|   loop: "{{ property_section.matches }}" | ||||
|   loop_control: | ||||
|     label: "{{ ((item.values() | list)[0].values() | list)[0] }}" | ||||
| @@ -0,0 +1,10 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| # Change working directory | ||||
| pushd {{ _template.service.workingdir }} | ||||
|  | ||||
| # Compress *.tar files | ||||
| if tar -czf image-tarballs.tgz *.tar --remove-files; then | ||||
|     # Disable systemd unit | ||||
|     systemctl disable {{ _template.service.name }} | ||||
| fi | ||||
| @@ -0,0 +1,8 @@ | ||||
| mirrors: | ||||
| {% for registry in _template.registries %} | ||||
|   {{ registry }}: | ||||
|     endpoint: | ||||
|       - https://registry.{{ _template.hv.fqdn }} | ||||
|     rewrite: | ||||
|       "(.*)": "library/{{ registry }}/$1" | ||||
| {% endfor %} | ||||
| @@ -0,0 +1,9 @@ | ||||
| [Unit] | ||||
| Description={{ _template.service.name }} | ||||
|  | ||||
| [Service] | ||||
| ExecStart={{ _template.service.executable }} | ||||
| Nice=10 | ||||
|  | ||||
| [Install] | ||||
| WantedBy=multi-user.target | ||||
| @@ -0,0 +1,50 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| export TERM=linux | ||||
|  | ||||
| BGRN='\033[1;92m' | ||||
| BGRY='\033[1;30m' | ||||
| BBLU='\033[1;34m' | ||||
| BRED='\033[1;91m' | ||||
| BWHI='\033[1;97m' | ||||
| CBLA='\033[?16;0;30c' # Hide blinking cursor | ||||
| DFLT='\033[0m'        # Reset colour | ||||
| LCLR='\033[K'         # Clear to end of line | ||||
| PRST='\033[0;0H'      # Reset cursor position | ||||
|  | ||||
| FQDN='{{ _template.metacluster.fqdn }}' | ||||
| IPADDRESS='{{ _template.metacluster.vip }}' | ||||
|  | ||||
| declare -A COMPONENTS | ||||
| {% for component in _template.metacluster.components %} | ||||
| COMPONENTS["{{ component.name }}\t({{ component.url }})"]="{{ component.healthcheck }}" | ||||
| {% endfor %} | ||||
|  | ||||
| I=0 | ||||
|  | ||||
| while /bin/true; do | ||||
|   if [[ $I -gt 59 ]]; then | ||||
|     clear > /dev/tty1 | ||||
|     I=0 | ||||
|   else | ||||
|     I=$(( $I + 1 )) | ||||
|   fi | ||||
|  | ||||
|   echo -e "${PRST}" > /dev/tty1 | ||||
|   echo -e "\n\n\t${DFLT}To manage this appliance, please connect to one of the following:${LCLR}\n" > /dev/tty1 | ||||
|  | ||||
|   for c in $( echo "${!COMPONENTS[@]}" | tr ' ' $'\n' | sort); do | ||||
|     STATUS=$(curl -kLs "${COMPONENTS[${c}]}" -o /dev/null -w '%{http_code}') | ||||
|  | ||||
|     if [[ "${STATUS}" -eq "200" ]]; then | ||||
|       echo -e "\t [${BGRN}+${DFLT}] ${BBLU}${c}${DFLT}${LCLR}" > /dev/tty1 | ||||
|     else | ||||
|       echo -e "\t [${BRED}-${DFLT}] ${BBLU}${c}${DFLT}${LCLR}" > /dev/tty1 | ||||
|     fi | ||||
|   done | ||||
|  | ||||
|   echo -e "\n\t${BGRY}Note that your DNS zone ${DFLT}must have${BGRY} respective records defined,\n\teach pointing to: ${DFLT}${IPADDRESS}${LCLR}" > /dev/tty1 | ||||
|  | ||||
|   echo -e "${CBLA}" > /dev/tty1 | ||||
|   sleep 1 | ||||
| done | ||||
| @@ -0,0 +1,23 @@ | ||||
| playbook: | ||||
|   retries: 10 | ||||
|   delay: | ||||
|     # These values are multiplied with the value of `storage_benchmark` | ||||
|     long: 2 | ||||
|     medium: 1 | ||||
|     short: 0.5 | ||||
|  | ||||
| # This default value is updated during the playbook, based on an I/O intensive operation | ||||
| storage_benchmark: 30 | ||||
|  | ||||
| config: | ||||
|   clusterapi: | ||||
|     size_matrix: | ||||
|       small: | ||||
|         cpu: 2 | ||||
|         memory: 6144 | ||||
|       medium: | ||||
|         cpu: 4 | ||||
|         memory: 8192 | ||||
|       large: | ||||
|         cpu: 8 | ||||
|         memory: 16384 | ||||
| @@ -1,10 +0,0 @@ | ||||
| --- | ||||
| - hosts: 127.0.0.1 | ||||
|   connection: local | ||||
|   gather_facts: false | ||||
|   # become: true | ||||
|   roles: | ||||
|     - vapp | ||||
|     - network | ||||
|     - users | ||||
|     - cleanup | ||||
| @@ -1,20 +0,0 @@ | ||||
| - name: Disable crontab job | ||||
|   ansible.builtin.cron: | ||||
|     name: firstboot | ||||
|     state: absent | ||||
| - name: Restore extra tty | ||||
|   ansible.builtin.lineinfile: | ||||
|     path: /etc/systemd/logind.conf | ||||
|     regexp: "{{ item.regexp }}" | ||||
|     line: "{{ item.line }}" | ||||
|   loop: | ||||
|   - { regexp: '^NAutoVTs=', line: '#NAutoVTs=6'} | ||||
|   - { regexp: '^ReserveVT=', line: '#ReserveVT=6'} | ||||
| - name: Unmask getty@tty1 service | ||||
|   ansible.builtin.systemd: | ||||
|     name: getty@tty1 | ||||
|     enabled: yes | ||||
|     masked: no | ||||
| - name: Reboot host | ||||
|   ansible.builtin.shell: | ||||
|     cmd: /usr/sbin/reboot now | ||||
| @@ -1,10 +0,0 @@ | ||||
| - name: Set hostname | ||||
|   ansible.builtin.hostname: | ||||
|     name: "{{ ovfproperties['guestinfo.hostname'] }}" | ||||
| - name: Create netplan configuration file | ||||
|   ansible.builtin.template: | ||||
|     src: netplan.j2 | ||||
|     dest: /etc/netplan/00-installer-config.yaml | ||||
| - name: Apply netplan configuration | ||||
|   ansible.builtin.shell: | ||||
|     cmd: /usr/sbin/netplan apply | ||||
| @@ -1,10 +0,0 @@ | ||||
| network: | ||||
|   version: 2 | ||||
|   ethernets: | ||||
|     ens192: | ||||
|       addresses: | ||||
|       - {{ ovfproperties['guestinfo.ipaddress'] }}/{{ ovfproperties['guestinfo.prefixlength'] }} | ||||
|       gateway4: {{ ovfproperties['guestinfo.gateway'] }} | ||||
|       nameservers: | ||||
|         addresses: | ||||
|         - {{ ovfproperties['guestinfo.dnsserver'] }} | ||||
| @@ -1,25 +0,0 @@ | ||||
| - name: Set root password | ||||
|   ansible.builtin.user: | ||||
|     name: root | ||||
|     password: "{{ ovfproperties['guestinfo.rootpw'] | password_hash('sha512', 65534 | random(seed=ovfproperties['guestinfo.hostname']) | string) }}" | ||||
|     generate_ssh_key: yes | ||||
|     ssh_key_bits: 2048 | ||||
|     ssh_key_file: .ssh/id_rsa | ||||
| - name: Save root SSH publickey | ||||
|   ansible.builtin.lineinfile: | ||||
|     path: /root/.ssh/authorized_keys | ||||
|     line: "{{ ovfproperties['guestinfo.rootsshkey'] }}" | ||||
| - name: Disable SSH password authentication | ||||
|   ansible.builtin.lineinfile: | ||||
|     path: /etc/ssh/sshd_config | ||||
|     regex: "{{ item.regex }}" | ||||
|     line: "{{ item.line }}" | ||||
|     state: "{{ item.state }}" | ||||
|   loop: | ||||
|   - { regex: '$#PasswordAuthentication', line: 'PasswordAuthentication no', state: ''} | ||||
|   - { regex: '', line: 'PasswordAuthentication yes', state: absent} | ||||
| - name: Delete 'ubuntu' user | ||||
|   ansible.builtin.user: | ||||
|     name: ubuntu | ||||
|     state: absent | ||||
|     remove: yes | ||||
| @@ -1,21 +0,0 @@ | ||||
| - name: Store current ovfEnvironment | ||||
|   ansible.builtin.shell: | ||||
|     cmd: /usr/bin/vmtoolsd --cmd "info-get guestinfo.ovfEnv" | ||||
|   register: ovfenv | ||||
| - name: Parse XML for vApp properties | ||||
|   community.general.xml: | ||||
|     xmlstring: "{{ ovfenv.stdout }}" | ||||
|     namespaces: | ||||
|       ns: http://schemas.dmtf.org/ovf/environment/1 | ||||
|     xpath: /ns:Environment/ns:PropertySection/ns:Property | ||||
|     content: attribute | ||||
|   register: ovfenv | ||||
| - name: Assign vApp properties to dictionary | ||||
|   ansible.builtin.set_fact: | ||||
|     ovfproperties: >- | ||||
|       {{ ovfproperties | default({}) | | ||||
|       combine({((item.values() | list)[0].values() | list)[0]: | ||||
|       ((item.values() | list)[0].values() | list)[1]}) | ||||
|       }} | ||||
|   loop: "{{ ovfenv.matches }}" | ||||
|   no_log: true | ||||
| @@ -0,0 +1,27 @@ | ||||
| --- | ||||
| - hosts: 127.0.0.1 | ||||
|   connection: local | ||||
|   gather_facts: true | ||||
|   vars_files: | ||||
|     - defaults.yml | ||||
|     - metacluster.yml | ||||
|   # become: true | ||||
|   roles: | ||||
|     - vapp | ||||
|     - network | ||||
|     - preflight | ||||
|     - users | ||||
|     - disks | ||||
|     - metacluster | ||||
|     - workloadcluster | ||||
|     - decommission | ||||
|     - tty | ||||
|     - cleanup | ||||
|   handlers: | ||||
|     - name: Apply manifests | ||||
|       kubernetes.core.k8s: | ||||
|         src: "{{ item }}" | ||||
|         state: present | ||||
|         kubeconfig: "{{ kubeconfig.path }}" | ||||
|       loop: "{{ query('ansible.builtin.fileglob', '/var/lib/rancher/k3s/server/manifests/*.yaml') | sort }}" | ||||
|       ignore_errors: yes | ||||
| @@ -0,0 +1,35 @@ | ||||
| - name: Cordon node | ||||
|   kubernetes.core.k8s_drain: | ||||
|     name: "{{ decom_nodename }}" | ||||
|     state: cordon | ||||
|     kubeconfig: "{{ kubeconfig.path }}" | ||||
|  | ||||
| - name: Drain node | ||||
| # Currently does not work; returns with error "Failed to delete pod [...] due to: Too Many Requests" | ||||
| # See also: https://github.com/ansible-collections/kubernetes.core/issues/474 | ||||
| #   kubernetes.core.k8s_drain: | ||||
| #     name: "{{ decom_nodename }}" | ||||
| #     state: drain | ||||
| #     delete_options: | ||||
| #       ignore_daemonsets: true | ||||
| #       delete_emptydir_data: true | ||||
| #     kubeconfig: "{{ kubeconfig.path }}" | ||||
|   ansible.builtin.shell: | ||||
|     cmd: >- | ||||
|       kubectl drain {{ decom_nodename }} \ | ||||
|         --delete-emptydir-data \ | ||||
|         --ignore-daemonsets | ||||
|   register: nodedrain_results | ||||
|   until: | ||||
|     - nodedrain_results is not failed | ||||
|     - (nodedrain_results.stdout_lines | last) is match('node/.* drained') | ||||
|   retries: "{{ playbook.retries }}" | ||||
|   delay: "{{ ((storage_benchmark | float) * playbook.delay.short) | int }}" | ||||
|  | ||||
| - name: Delete node | ||||
|   kubernetes.core.k8s: | ||||
|     name: "{{ decom_nodename }}" | ||||
|     kind: node | ||||
|     state: absent | ||||
|     wait: true | ||||
|     kubeconfig: "{{ kubeconfig.path }}" | ||||
| @@ -0,0 +1,18 @@ | ||||
| - name: Lookup node name and moref-id for decommissioning | ||||
|   ansible.builtin.set_fact: | ||||
|     decom_nodename: >- | ||||
|       {{ | ||||
|         lookup('kubernetes.core.k8s', kind='Node', kubeconfig=(kubeconfig.path)) | | ||||
|           json_query('[? metadata.name != `' ~ ansible_facts.nodename ~ '`].metadata.name') | | ||||
|           first | ||||
|       }} | ||||
|     decom_morefid: >- | ||||
|       {{ | ||||
|         lookup('kubernetes.core.k8s', kind='Node', kubeconfig=(kubeconfig.path)) | | ||||
|           json_query('[? metadata.name != `' ~ ansible_facts.nodename ~ '`].metadata.labels."ova.airgappedk8s/moref_id"') | | ||||
|           first | ||||
|       }} | ||||
|  | ||||
| - import_tasks: storage.yml | ||||
| - import_tasks: k3s.yml | ||||
| - import_tasks: virtualmachine.yml | ||||
| @@ -0,0 +1,27 @@ | ||||
| - name: Disable disk scheduling and evict replicas | ||||
|   kubernetes.core.k8s: | ||||
|     api_version: longhorn.io/v1beta2 | ||||
|     kind: lhn | ||||
|     name: "{{ decom_nodename }}" | ||||
|     namespace: longhorn-system | ||||
|     state: patched | ||||
|     definition: | | ||||
|       spec: | ||||
|         allowScheduling: false | ||||
|         evictionRequested: true | ||||
|     kubeconfig: "{{ kubeconfig.path }}" | ||||
|  | ||||
| - name: Reduce replica amount for each volume | ||||
|   kubernetes.core.k8s: | ||||
|     api_version: longhorn.io/v1beta2 | ||||
|     kind: volume | ||||
|     name: "{{ item.metadata.name }}" | ||||
|     namespace: longhorn-system | ||||
|     state: patched | ||||
|     definition: | | ||||
|       spec: | ||||
|         numberOfReplicas: {{ (lookup('kubernetes.core.k8s', kind='node', kubeconfig=(kubeconfig.path)) | length | int) - 1 }} | ||||
|     kubeconfig: "{{ kubeconfig.path }}" | ||||
|   loop: "{{ query('kubernetes.core.k8s', api_version='longhorn.io/v1beta2', kind='volume', namespace='longhorn-system', kubeconfig=(kubeconfig.path)) }}" | ||||
|   loop_control: | ||||
|     label: "{{ item.metadata.name }}" | ||||
| @@ -0,0 +1,26 @@ | ||||
| - block: | ||||
|  | ||||
|     - name: Lookup VM name | ||||
|       community.vmware.vmware_guest_info: | ||||
|         moid: "{{ decom_morefid }}" | ||||
|       register: virtualmachine_details | ||||
|  | ||||
|     - name: Power off VM | ||||
|       community.vmware.vmware_guest: | ||||
|         name: "{{ virtualmachine_details.instance.hw_name }}" | ||||
|         folder: "{{ virtualmachine_details.instance.hw_folder }}" | ||||
|         state: poweredoff | ||||
|  | ||||
|     # - name: Delete VM | ||||
|     #   community.vmware.vmware_guest: | ||||
|     #     name: "{{ virtualmachine_details.hw_name }}" | ||||
|     #     folder: "{{ virtualmachine_details.hw_folder }}" | ||||
|     #     state: absent | ||||
|  | ||||
|   module_defaults: | ||||
|     group/vmware: | ||||
|       hostname: "{{ vapp['hv.fqdn'] }}" | ||||
|       validate_certs: no | ||||
|       username: "{{ vapp['hv.username'] }}" | ||||
|       password: "{{ vapp['hv.password'] }}" | ||||
|       datacenter: "{{ vcenter_info.datacenter }}" | ||||
| @@ -0,0 +1,52 @@ | ||||
| - block: | ||||
|  | ||||
|     - name: Initialize tempfile | ||||
|       ansible.builtin.tempfile: | ||||
|         state: file | ||||
|       register: values_file | ||||
|  | ||||
|     - name: Lookup current chart values | ||||
|       kubernetes.core.helm_info: | ||||
|         name: step-certificates | ||||
|         namespace: step-ca | ||||
|         kubeconfig: "{{ kubeconfig.path }}" | ||||
|       register: stepca_values | ||||
|  | ||||
|     - name: Write chart values w/ password to tempfile | ||||
|       ansible.builtin.copy: | ||||
|         dest: "{{ values_file.path }}" | ||||
|         content: "{{ stepca_values.status | json_query('values') | to_yaml }}" | ||||
|       no_log: true | ||||
|  | ||||
|     - name: Upgrade step-ca chart | ||||
|       kubernetes.core.helm: | ||||
|         name: step-certificates | ||||
|         chart_ref: /opt/metacluster/helm-charts/step-certificates | ||||
|         release_namespace: step-ca | ||||
|         wait: false | ||||
|         kubeconfig: "{{ kubeconfig.path }}" | ||||
|         values_files: | ||||
|           - "{{ values_file.path }}" | ||||
|  | ||||
|     - name: Cleanup tempfile | ||||
|       ansible.builtin.file: | ||||
|         path: "{{ values_file.path }}" | ||||
|         state: absent | ||||
|       when: values_file.path is defined | ||||
|  | ||||
|     - name: Ensure step-ca API availability | ||||
|       ansible.builtin.uri: | ||||
|         url: https://ca.{{ vapp['metacluster.fqdn'] }}/health | ||||
|         method: GET | ||||
|       register: api_readycheck | ||||
|       until: | ||||
|         - api_readycheck.json.status is defined | ||||
|         - api_readycheck.json.status == 'ok' | ||||
|       retries: "{{ playbook.retries }}" | ||||
|       delay: "{{ (storage_benchmark | int) * (playbook.delay.long | int) }}" | ||||
|  | ||||
|   module_defaults: | ||||
|     ansible.builtin.uri: | ||||
|       validate_certs: no | ||||
|       status_code: [200, 201] | ||||
|       body_format: json | ||||
| @@ -0,0 +1,50 @@ | ||||
| - block: | ||||
|  | ||||
|     - name: Push images to registry | ||||
|       ansible.builtin.shell: | ||||
|         cmd: >- | ||||
|           skopeo copy \ | ||||
|             --insecure-policy \ | ||||
|             --dest-tls-verify=false \ | ||||
|             --dest-creds admin:{{ vapp['metacluster.password'] }} \ | ||||
|             docker-archive:./{{ item | basename }} \ | ||||
|             docker://registry.{{ vapp['metacluster.fqdn'] }}/library/$( \ | ||||
|               skopeo list-tags \ | ||||
|                 --insecure-policy \ | ||||
|                 docker-archive:./{{ item | basename }} | \ | ||||
|               jq -r '.Tags[0]') | ||||
|         chdir: /opt/metacluster/container-images/ | ||||
|       register: push_result | ||||
|       loop: "{{ query('ansible.builtin.fileglob', '/opt/metacluster/container-images/*.tar') | sort }}" | ||||
|       loop_control: | ||||
|         label: "{{ item | basename }}" | ||||
|       retries: "{{ playbook.retries }}" | ||||
|       delay: "{{ ((storage_benchmark | float) * playbook.delay.short) | int }}" | ||||
|       until: push_result is not failed | ||||
|  | ||||
|     - name: Get all stored container images (=artifacts) | ||||
|       ansible.builtin.uri: | ||||
|         url: https://registry.{{ vapp['metacluster.fqdn'] }}/api/v2.0/search?q=library | ||||
|         method: GET | ||||
|       register: registry_artifacts | ||||
|  | ||||
|     - name: Get source registries of all artifacts | ||||
|       ansible.builtin.set_fact: | ||||
|         source_registries: "{{ (source_registries | default([]) + [(item | split('/'))[1]]) | unique | sort }}" | ||||
|       loop: "{{ registry_artifacts.json.repository | json_query('[*].repository_name') }}" | ||||
|  | ||||
|     - name: Configure K3s node for private registry | ||||
|       ansible.builtin.template: | ||||
|         dest: /etc/rancher/k3s/registries.yaml | ||||
|         src: registries.j2 | ||||
|       vars: | ||||
|         _template: | ||||
|           data: "{{ source_registries }}" | ||||
|           hv: | ||||
|             fqdn: "{{ vapp['metacluster.fqdn'] }}" | ||||
|  | ||||
|   module_defaults: | ||||
|     ansible.builtin.uri: | ||||
|       validate_certs: no | ||||
|       status_code: [200, 201, 401] | ||||
|       body_format: json | ||||
| @@ -0,0 +1,27 @@ | ||||
| - block: | ||||
|  | ||||
|     - name: Upgrade gitea chart | ||||
|       kubernetes.core.helm: | ||||
|         name: gitea | ||||
|         chart_ref: /opt/metacluster/helm-charts/gitea | ||||
|         release_namespace: gitea | ||||
|         wait: false | ||||
|         kubeconfig: "{{ kubeconfig.path }}" | ||||
|         values: "{{ components['gitea'].chart_values }}" | ||||
|  | ||||
|     - name: Ensure gitea API availability | ||||
|       ansible.builtin.uri: | ||||
|         url: https://git.{{ vapp['metacluster.fqdn'] }}/api/healthz | ||||
|         method: GET | ||||
|       register: api_readycheck | ||||
|       until: | ||||
|         - api_readycheck.json.status is defined | ||||
|         - api_readycheck.json.status == 'pass' | ||||
|       retries: "{{ playbook.retries }}" | ||||
|       delay: "{{ (storage_benchmark | int) * (playbook.delay.long | int) }}" | ||||
|  | ||||
|   module_defaults: | ||||
|     ansible.builtin.uri: | ||||
|       validate_certs: no | ||||
|       status_code: [200, 201] | ||||
|       body_format: json | ||||
| @@ -0,0 +1,26 @@ | ||||
| - block: | ||||
|  | ||||
|     - name: Upgrade argo-cd chart | ||||
|       kubernetes.core.helm: | ||||
|         name: argo-cd | ||||
|         chart_ref: /opt/metacluster/helm-charts/argo-cd | ||||
|         release_namespace: argo-cd | ||||
|         wait: false | ||||
|         kubeconfig: "{{ kubeconfig.path }}" | ||||
|         values: "{{ components['argo-cd'].chart_values }}" | ||||
|  | ||||
|     - name: Ensure argo-cd API availability | ||||
|       ansible.builtin.uri: | ||||
|         url: https://gitops.{{ vapp['metacluster.fqdn'] }}/api/version | ||||
|         method: GET | ||||
|       register: api_readycheck | ||||
|       until: | ||||
|         - api_readycheck.json.Version is defined | ||||
|       retries: "{{ playbook.retries }}" | ||||
|       delay: "{{ (storage_benchmark | int) * (playbook.delay.long | int) }}" | ||||
|  | ||||
|   module_defaults: | ||||
|     ansible.builtin.uri: | ||||
|       validate_certs: no | ||||
|       status_code: [200, 201] | ||||
|       body_format: json | ||||
| @@ -0,0 +1,35 @@ | ||||
| - name: Configure fallback name resolution | ||||
|   ansible.builtin.lineinfile: | ||||
|     path: /etc/hosts | ||||
|     line: "{{ vapp['metacluster.vip'] }}  {{ item ~ '.' ~ vapp['metacluster.fqdn'] }}" | ||||
|     state: present | ||||
|   loop: | ||||
|     # TODO: Make this list dynamic | ||||
|     - ca | ||||
|     - git | ||||
|     - gitops | ||||
|     - ingress | ||||
|     - registry | ||||
|     - storage | ||||
|  | ||||
| - name: Retrieve root CA certificate | ||||
|   ansible.builtin.uri: | ||||
|     url: https://ca.{{ vapp['metacluster.fqdn'] }}/roots | ||||
|     validate_certs: no | ||||
|     method: GET | ||||
|     status_code: [200, 201] | ||||
|   register: rootca_certificate | ||||
|  | ||||
| - name: Store root CA certificate | ||||
|   ansible.builtin.copy: | ||||
|     dest: /usr/local/share/ca-certificates/root_ca.crt | ||||
|     content: "{{ rootca_certificate.json.crts | list | join('\n') }}" | ||||
|  | ||||
| - name: Update certificate truststore | ||||
|   ansible.builtin.command: | ||||
|     cmd: update-ca-certificates | ||||
|  | ||||
| - name: Remove redundant files | ||||
|   ansible.builtin.file: | ||||
|     path: /var/lib/rancher/k3s/server/manifests/traefik-config.yaml | ||||
|     state: absent | ||||
| @@ -0,0 +1,80 @@ | ||||
| - name: Store custom configuration files | ||||
|   ansible.builtin.copy: | ||||
|     dest: "{{ item.filename }}" | ||||
|     content: "{{ item.content }}" | ||||
|   loop: | ||||
|     - filename: /etc/rancher/k3s/config.yaml | ||||
|       content: | | ||||
|         kubelet-arg: | ||||
|           - "config=/etc/rancher/k3s/kubelet.config" | ||||
|     - filename: /etc/rancher/k3s/kubelet.config | ||||
|       content: | | ||||
|         apiVersion: kubelet.config.k8s.io/v1beta1 | ||||
|         kind: KubeletConfiguration | ||||
|  | ||||
|         shutdownGracePeriod: 180s | ||||
|         shtudownGracePeriodCriticalPods: 60s | ||||
|   loop_control: | ||||
|     label: "{{ item.filename }}" | ||||
|  | ||||
| - name: Gather service facts | ||||
|   ansible.builtin.service_facts: | ||||
|     # Module requires no attributes | ||||
|  | ||||
| - name: Install K3s | ||||
|   ansible.builtin.command: | ||||
|     cmd: ./install.sh | ||||
|     chdir: /opt/metacluster/k3s | ||||
|   environment: | ||||
|     INSTALL_K3S_SKIP_DOWNLOAD: 'true' | ||||
|     # To prevent from overwriting traefik's existing configuration, "disable" it on this new node | ||||
|     INSTALL_K3S_EXEC: "server --token {{ vapp['metacluster.token'] | trim }} --server https://{{ vapp['metacluster.vip'] }}:6443 --disable local-storage,traefik --config /etc/rancher/k3s/config.yaml" | ||||
|   when: ansible_facts.services['k3s.service'] is undefined | ||||
|  | ||||
| - name: Ensure API availability | ||||
|   ansible.builtin.uri: | ||||
|     url: https://{{ vapp['guestinfo.ipaddress'] }}:6443/livez?verbose | ||||
|     method: GET | ||||
|     validate_certs: no | ||||
|     status_code: [200, 401] | ||||
|   register: api_readycheck | ||||
|   until: api_readycheck.json.apiVersion is defined | ||||
|   retries: "{{ playbook.retries }}" | ||||
|   delay: "{{ (storage_benchmark | int) * (playbook.delay.medium | int) }}" | ||||
|  | ||||
| - name: Install kubectl tab-completion | ||||
|   ansible.builtin.shell: | ||||
|     cmd: kubectl completion bash | tee /etc/bash_completion.d/kubectl | ||||
|  | ||||
| - name: Initialize tempfile | ||||
|   ansible.builtin.tempfile: | ||||
|     state: file | ||||
|   register: kubeconfig | ||||
|  | ||||
| - name: Retrieve kubeconfig | ||||
|   ansible.builtin.command: | ||||
|     cmd: kubectl config view --raw | ||||
|   register: kubectl_config | ||||
|  | ||||
| - name: Store kubeconfig in tempfile | ||||
|   ansible.builtin.copy: | ||||
|     dest: "{{ kubeconfig.path }}" | ||||
|     content: "{{ kubectl_config.stdout }}" | ||||
|     mode: 0600 | ||||
|   no_log: true | ||||
|  | ||||
| - name: Add label to node object | ||||
|   kubernetes.core.k8s: | ||||
|     name: "{{ ansible_facts.nodename | lower }}" | ||||
|     kind: Node | ||||
|     state: patched | ||||
|     definition: | ||||
|       metadata: | ||||
|         labels: | ||||
|           ova.airgappedk8s/moref_id: "{{ moref_id }}" | ||||
|     kubeconfig: "{{ kubeconfig.path }}" | ||||
|   register: k8snode_patch | ||||
|   until: | ||||
|     - k8snode_patch.result.metadata.labels['ova.airgappedk8s/moref_id'] is defined | ||||
|   retries: "{{ playbook.retries }}" | ||||
|   delay: "{{ (storage_benchmark | int) * (playbook.delay.medium | int) }}" | ||||
| @@ -0,0 +1,9 @@ | ||||
| - import_tasks: init.yml | ||||
| - import_tasks: containerimages.yml | ||||
| - import_tasks: k3s.yml | ||||
| - import_tasks: assets.yml | ||||
| - import_tasks: storage.yml | ||||
| - import_tasks: registry.yml | ||||
| - import_tasks: certauthority.yml | ||||
| - import_tasks: git.yml | ||||
| - import_tasks: gitops.yml | ||||
| @@ -0,0 +1,27 @@ | ||||
| - block: | ||||
|  | ||||
|     - name: Upgrade harbor chart | ||||
|       kubernetes.core.helm: | ||||
|         name: harbor | ||||
|         chart_ref: /opt/metacluster/helm-charts/harbor | ||||
|         release_namespace: harbor | ||||
|         wait: false | ||||
|         kubeconfig: "{{ kubeconfig.path }}" | ||||
|         values: "{{ components['harbor'].chart_values }}" | ||||
|  | ||||
|     - name: Ensure harbor API availability | ||||
|       ansible.builtin.uri: | ||||
|         url: https://registry.{{ vapp['metacluster.fqdn'] }}/api/v2.0/health | ||||
|         method: GET | ||||
|       register: api_readycheck | ||||
|       until: | ||||
|         - api_readycheck.json.status is defined | ||||
|         - api_readycheck.json.status == 'healthy' | ||||
|       retries: "{{ playbook.retries }}" | ||||
|       delay: "{{ (storage_benchmark | int) * (playbook.delay.long | int) }}" | ||||
|  | ||||
|   module_defaults: | ||||
|     ansible.builtin.uri: | ||||
|       validate_certs: no | ||||
|       status_code: [200, 201, 401] | ||||
|       body_format: json | ||||
| @@ -0,0 +1,53 @@ | ||||
| - block: | ||||
|  | ||||
|     - name: Increase replicas for each volume | ||||
|       kubernetes.core.k8s: | ||||
|         api_version: longhorn.io/v1beta2 | ||||
|         kind: volume | ||||
|         name: "{{ item.metadata.name }}" | ||||
|         namespace: longhorn-system | ||||
|         state: patched | ||||
|         definition: | | ||||
|           spec: | ||||
|             numberOfReplicas: {{ lookup('kubernetes.core.k8s', kind='node', kubeconfig=(kubeconfig.path)) | length | int }} | ||||
|         kubeconfig: "{{ kubeconfig.path }}" | ||||
|       loop: "{{ query('kubernetes.core.k8s', api_version='longhorn.io/v1beta2', kind='volume', namespace='longhorn-system', kubeconfig=(kubeconfig.path)) }}" | ||||
|       loop_control: | ||||
|         label: "{{ item.metadata.name }}" | ||||
|  | ||||
|     - name: Wait for replica rebuilds to complete | ||||
|       ansible.builtin.uri: | ||||
|         url: https://storage.{{ vapp['metacluster.fqdn'] }}/v1/volumes | ||||
|         method: GET | ||||
|       register: volume_details | ||||
|       until: | ||||
|         - volume_details.json is defined | ||||
|         - (volume_details.json | json_query('data[? state==`attached`].robustness') | unique | length) == 1 | ||||
|         - (volume_details.json | json_query('data[? state==`attached`].robustness') | first) == "healthy" | ||||
|       retries: "{{ ( playbook.retries * 2) | int }}" | ||||
|       delay: "{{ (storage_benchmark | int) * (playbook.delay.long | int) }}" | ||||
|  | ||||
|     - name: Upgrade longhorn chart | ||||
|       kubernetes.core.helm: | ||||
|         name: longhorn | ||||
|         chart_ref: /opt/metacluster/helm-charts/longhorn | ||||
|         release_namespace: longhorn-system | ||||
|         wait: false | ||||
|         kubeconfig: "{{ kubeconfig.path }}" | ||||
|         values: "{{ components['longhorn'].chart_values }}" | ||||
|  | ||||
|     - name: Ensure longhorn API availability | ||||
|       ansible.builtin.uri: | ||||
|         url: https://storage.{{ vapp['metacluster.fqdn'] }}/v1 | ||||
|         method: GET | ||||
|       register: api_readycheck | ||||
|       until: | ||||
|         - api_readycheck is not failed | ||||
|       retries: "{{ playbook.retries }}" | ||||
|       delay: "{{ (storage_benchmark | int) * (playbook.delay.long | int) }}" | ||||
|  | ||||
|   module_defaults: | ||||
|     ansible.builtin.uri: | ||||
|       validate_certs: no | ||||
|       status_code: [200, 201] | ||||
|       body_format: json | ||||
| @@ -0,0 +1,3 @@ | ||||
| - import_tasks: vapp.yml | ||||
| - import_tasks: vcenter.yml | ||||
| - import_tasks: metacluster.yml | ||||
| @@ -0,0 +1,11 @@ | ||||
| - name: Check for metacluster connectivity | ||||
|   ansible.builtin.uri: | ||||
|     url: https://{{ vapp['metacluster.vip'] }}:6443/livez?verbose | ||||
|     method: GET | ||||
|     validate_certs: no | ||||
|     status_code: [200, 401] | ||||
|   register: api_readycheck | ||||
|   until: | ||||
|     - api_readycheck.json.apiVersion is defined | ||||
|   retries: "{{ playbook.retries }}" | ||||
|   delay: "{{ (storage_benchmark | int) * (playbook.delay.medium | int) }}" | ||||
| @@ -0,0 +1,20 @@ | ||||
| - name: Check for expected vApp properties | ||||
|   ansible.builtin.assert: | ||||
|     that: | ||||
|       - vapp[item] is defined | ||||
|       - (vapp[item] | length) > 0 | ||||
|     quiet: true | ||||
|   loop: | ||||
|     - guestinfo.dnsserver | ||||
|     - guestinfo.gateway | ||||
|     - guestinfo.hostname | ||||
|     - guestinfo.ipaddress | ||||
|     - guestinfo.prefixlength | ||||
|     - guestinfo.rootsshkey | ||||
|     - hv.fqdn | ||||
|     - hv.password | ||||
|     - hv.username | ||||
|     - metacluster.fqdn | ||||
|     - metacluster.password | ||||
|     - metacluster.token | ||||
|     - metacluster.vip | ||||
| @@ -0,0 +1 @@ | ||||
| # ... | ||||
| @@ -0,0 +1,57 @@ | ||||
| - name: Gather hypervisor details | ||||
|   ansible.builtin.shell: | ||||
|     cmd: govc ls -L {{ item.moref }} | awk -F/ '{print ${{ item.part }}}' | ||||
|   environment: | ||||
|     GOVC_INSECURE: '1' | ||||
|     GOVC_URL: "{{ vapp['hv.fqdn'] }}" | ||||
|     GOVC_USERNAME: "{{ vapp['hv.username'] }}" | ||||
|     GOVC_PASSWORD: "{{ vapp['hv.password'] }}" | ||||
|   register: govc_inventory | ||||
|   loop: | ||||
|     - attribute: cluster | ||||
|       moref: >- | ||||
|         $(govc object.collect -json VirtualMachine:{{ moref_id }} | \ | ||||
|           jq -r '.[] | select(.Name == "runtime").Val.Host | .Type + ":" + .Value') | ||||
|       part: (NF-1) | ||||
|     - attribute: datacenter | ||||
|       moref: VirtualMachine:{{ moref_id }} | ||||
|       part: 2 | ||||
|     - attribute: datastore | ||||
|       moref: >- | ||||
|         $(govc object.collect -json VirtualMachine:{{ moref_id }} | \ | ||||
|           jq -r '.[] | select(.Name == "datastore").Val.ManagedObjectReference | .[].Type + ":" + .[].Value') | ||||
|       part: NF | ||||
|     - attribute: folder | ||||
|       moref: >- | ||||
|         $(govc object.collect -json VirtualMachine:{{ moref_id }} | \ | ||||
|           jq -r '.[] | select(.Name == "parent").Val | .Type + ":" + .Value') | ||||
|       part: 0 | ||||
|     # - attribute: host | ||||
|     #   moref: >- | ||||
|     #     $(govc object.collect -json VirtualMachine:{{ moref_id }} | \ | ||||
|     #       jq -r '.[] | select(.Name == "runtime").Val.Host | .Type + ":" + .Value') | ||||
|     #   part: NF | ||||
|     - attribute: network | ||||
|       moref: >- | ||||
|         $(govc object.collect -json VirtualMachine:{{ moref_id }} | \ | ||||
|           jq -r '.[] | select(.Name == "network").Val.ManagedObjectReference | .[].Type + ":" + .[].Value') | ||||
|       part: NF | ||||
|     - attribute: resourcepool | ||||
|       moref: >- | ||||
|         $(govc object.collect -json VirtualMachine:{{ moref_id }} | \ | ||||
|           jq -r '.[] | select(.Name == "resourcePool").Val | .Type + ":" + .Value') | ||||
|       part: 0 | ||||
|   loop_control: | ||||
|     label: "{{ item.attribute }}" | ||||
|  | ||||
| - name: Retrieve hypervisor TLS thumbprint | ||||
|   ansible.builtin.shell: | ||||
|     cmd: openssl s_client -connect {{ vapp['hv.fqdn'] }}:443 < /dev/null 2>/dev/null | openssl x509 -fingerprint -noout -in /dev/stdin | awk -F'=' '{print $2}' | ||||
|   register: tls_thumbprint | ||||
|  | ||||
| - name: Store hypervisor details in dictionary | ||||
|   ansible.builtin.set_fact: | ||||
|     vcenter_info: "{{ vcenter_info | default({}) | combine({ item.item.attribute : item.stdout }) }}" | ||||
|   loop: "{{ govc_inventory.results }}" | ||||
|   loop_control: | ||||
|     label: "{{ item.item.attribute }}" | ||||
| @@ -0,0 +1,4 @@ | ||||
| - import_tasks: hypervisor.yml | ||||
| - import_tasks: registry.yml | ||||
| - import_tasks: nodetemplates.yml | ||||
| # - import_tasks: clusterapi.yml | ||||
| @@ -0,0 +1,73 @@ | ||||
| - block: | ||||
|  | ||||
|     - name: Check for existing template on hypervisor | ||||
|       community.vmware.vmware_guest_info: | ||||
|         name: "{{ (filename | basename | split('.'))[:-1] | join('.') }}" | ||||
|       register: existing_ova | ||||
|       ignore_errors: yes | ||||
|  | ||||
|     - name: Store inventory path of existing template | ||||
|       ansible.builtin.set_fact: | ||||
|         nodetemplate_inventorypath: "{{ existing_ova.instance.hw_folder ~ '/' ~ existing_ova.instance.hw_name }}" | ||||
|       when: existing_ova is not failed | ||||
|  | ||||
|     - block: | ||||
|  | ||||
|         - name: Parse OVA file for network mappings | ||||
|           ansible.builtin.shell: | ||||
|             cmd: govc import.spec -json {{ filename }} | ||||
|           environment: | ||||
|             GOVC_INSECURE: '1' | ||||
|             GOVC_URL: "{{ vapp['hv.fqdn'] }}" | ||||
|             GOVC_USERNAME: "{{ vapp['hv.username'] }}" | ||||
|             GOVC_PASSWORD: "{{ vapp['hv.password'] }}" | ||||
|           register: ova_spec | ||||
|  | ||||
|         - name: Deploy OVA template on hypervisor | ||||
|           community.vmware.vmware_deploy_ovf: | ||||
|             cluster: "{{ vcenter_info.cluster }}" | ||||
|             datastore: "{{ vcenter_info.datastore }}" | ||||
|             name: "{{ (filename | basename | split('.'))[:-1] | join('.') }}" | ||||
|             networks: "{u'{{ ova_spec.stdout | from_json | json_query('NetworkMapping[0].Name') }}':u'{{ vcenter_info.network }}'}" | ||||
|             allow_duplicates: no | ||||
|             power_on: false | ||||
|             ovf: "{{ filename }}" | ||||
|           register: ova_deploy | ||||
|  | ||||
|         - name: Add additional placeholder disk | ||||
|           community.vmware.vmware_guest_disk: | ||||
|             name: "{{ ova_deploy.instance.hw_name }}" | ||||
|             disk: | ||||
|               - size: 1Gb | ||||
|                 scsi_controller: 1 | ||||
|                 scsi_type: paravirtual | ||||
|                 unit_number: 0 | ||||
|  | ||||
|         # Disabled to allow disks to be resized; at the cost of cloning speed | ||||
|         # - name: Create snapshot on deployed VM | ||||
|         #   community.vmware.vmware_guest_snapshot: | ||||
|         #     name: "{{ ova_deploy.instance.hw_name }}" | ||||
|         #     state: present | ||||
|         #     snapshot_name: "{{ ansible_date_time.iso8601_basic_short }}-base" | ||||
|  | ||||
|         - name: Mark deployed VM as templates | ||||
|           community.vmware.vmware_guest: | ||||
|             name: "{{ ova_deploy.instance.hw_name }}" | ||||
|             is_template: yes | ||||
|  | ||||
|         - name: Store inventory path of deployed template | ||||
|           ansible.builtin.set_fact: | ||||
|             nodetemplate_inventorypath: "{{ ova_deploy.instance.hw_folder ~ '/' ~ ova_deploy.instance.hw_name }}" | ||||
|  | ||||
|       when: existing_ova is failed | ||||
|  | ||||
|   vars: | ||||
|     filename: "{{ query('ansible.builtin.fileglob', '/opt/metacluster/node-templates/*.ova') | first }}" | ||||
|   module_defaults: | ||||
|     group/vmware: | ||||
|       hostname: "{{ vapp['hv.fqdn'] }}" | ||||
|       validate_certs: no | ||||
|       username: "{{ vapp['hv.username'] }}" | ||||
|       password: "{{ vapp['hv.password'] }}" | ||||
|       datacenter: "{{ vcenter_info.datacenter }}" | ||||
|       folder: "{{ vcenter_info.folder }}" | ||||
| @@ -0,0 +1,17 @@ | ||||
| - block: | ||||
|  | ||||
|   - name: Copy kubeadm container images to dedicated project | ||||
|     ansible.builtin.uri: | ||||
|       url: https://registry.{{ vapp['metacluster.fqdn'] }}/api/v2.0/projects/kubeadm/repositories/{{ ( item | regex_findall('([^:/]+)') )[-2] }}/artifacts?from=library/{{ item | replace('/', '%2F') | replace(':', '%3A') }} | ||||
|       method: POST | ||||
|       headers: | ||||
|         Authorization: "Basic {{ ('admin:' ~ vapp['metacluster.password']) | b64encode }}" | ||||
|       body: | ||||
|         from: "{{ item }}" | ||||
|     loop: "{{ lookup('ansible.builtin.file', '/opt/metacluster/cluster-api/imagelist').splitlines() }}" | ||||
|  | ||||
|   module_defaults: | ||||
|     ansible.builtin.uri: | ||||
|       validate_certs: no | ||||
|       status_code: [200, 201, 409] | ||||
|       body_format: json | ||||
| @@ -2,6 +2,7 @@ | ||||
|   ansible.builtin.file: | ||||
|     path: /opt/firstboot | ||||
|     state: directory | ||||
|  | ||||
| - name: Create firstboot script file | ||||
|   ansible.builtin.template: | ||||
|     src: firstboot.j2 | ||||
| @@ -9,18 +10,25 @@ | ||||
|     owner: root | ||||
|     group: root | ||||
|     mode: o+x | ||||
|  | ||||
| - name: Create @reboot crontab job | ||||
|   ansible.builtin.cron: | ||||
|     name: firstboot | ||||
|     special_time: reboot | ||||
|     job: "/opt/firstboot/firstboot.sh" | ||||
| - name: Copy payload folder | ||||
|     job: "/opt/firstboot/firstboot.sh >/dev/tty1 2>&1" | ||||
|  | ||||
| - name: Copy payload folder (common) | ||||
|   ansible.builtin.copy: | ||||
|     src: ansible_payload/ | ||||
|     src: ansible_payload/common/ | ||||
|     dest: /opt/firstboot/ansible/ | ||||
|     owner: root | ||||
|     group: root | ||||
|     mode: '0644' | ||||
|  | ||||
| - name: Copy payload folder (per appliancetype) | ||||
|   ansible.builtin.copy: | ||||
|     src: ansible_payload/{{ appliancetype }}/ | ||||
|     dest: /opt/firstboot/ansible/ | ||||
|     owner: root | ||||
|     group: root | ||||
|     mode: '0644' | ||||
| - name: Install ansible-galaxy collection | ||||
|   ansible.builtin.shell: | ||||
|     cmd: ansible-galaxy collection install community.general | ||||
|   | ||||
| @@ -1,4 +1,10 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| # Workaround for ansible output regression | ||||
| export PYTHONUNBUFFERED=1 | ||||
|  | ||||
| # Apply firstboot configuration w/ ansible | ||||
| /usr/local/bin/ansible-playbook /opt/firstboot/ansible/playbook.yml | tee -a /var/log/firstboot.log > /dev/tty1 | ||||
| /usr/local/bin/ansible-playbook /opt/firstboot/ansible/playbook.yml | tee -a /var/log/firstboot.log > /dev/tty1 2>&1 | ||||
|  | ||||
| # Cleanup console | ||||
| clear > /dev/tty1 | ||||
|   | ||||
| @@ -1,6 +0,0 @@ | ||||
| - name: Install ansible (w/ dependencies) | ||||
|   ansible.builtin.pip: | ||||
|     name: "{{ item }}" | ||||
|     executable: pip3 | ||||
|     state: latest | ||||
|   loop: "{{ pip_packages }}" | ||||
| @@ -3,6 +3,7 @@ | ||||
|     name: cloud-init | ||||
|     state: absent | ||||
|     purge: yes | ||||
|  | ||||
| - name: Delete cloud-init files | ||||
|   ansible.builtin.file: | ||||
|     path: "{{ item }}" | ||||
|   | ||||
| @@ -1,20 +1,7 @@ | ||||
| - name: Disable tty logins | ||||
|   import_tasks: tty.yml | ||||
|  | ||||
| - name: Remove snapd | ||||
|   import_tasks: snapd.yml | ||||
|  | ||||
| - name: Remove cloud-init | ||||
|   import_tasks: cloud-init.yml | ||||
|  | ||||
| - name: Configure default logging | ||||
|   import_tasks: logging.yml | ||||
|  | ||||
| - name: Configure services | ||||
|   import_tasks: services.yml | ||||
|  | ||||
| - name: Install packages | ||||
|   import_tasks: packages.yml | ||||
|  | ||||
| - name: Install ansible | ||||
|   import_tasks: ansible.yml | ||||
| - import_tasks: tty.yml | ||||
| - import_tasks: snapd.yml | ||||
| - import_tasks: cloud-init.yml | ||||
| - import_tasks: logging.yml | ||||
| - import_tasks: services.yml | ||||
| - import_tasks: packages.yml | ||||
| - import_tasks: sysctl.yml | ||||
|   | ||||
| @@ -1,14 +1,46 @@ | ||||
| - name: Configure 'needrestart' package | ||||
|   ansible.builtin.lineinfile: | ||||
|     path: /etc/needrestart/needrestart.conf | ||||
|     regexp: "{{ item.regexp }}" | ||||
|     line: "{{ item.line }}" | ||||
|   loop: | ||||
|     - regexp: "^#\\$nrconf\\{restart\\} = 'i';" | ||||
|       line: "$nrconf{restart} = 'a';" | ||||
|     - regexp: "^#\\$nrconf\\{kernelhints\\} = -1;" | ||||
|       line: "$nrconf{kernelhints} = -1;" | ||||
|   loop_control: | ||||
|     label: "{{ item.line }}" | ||||
|  | ||||
| - name: Install additional packages | ||||
|   ansible.builtin.apt: | ||||
|     name: "{{ item }}" | ||||
|     pkg: "{{ packages.apt }}" | ||||
|     state: latest | ||||
|     update_cache: yes | ||||
|   loop: "{{ packages }}" | ||||
|     install_recommends: no | ||||
|  | ||||
| - name: Upgrade all packages | ||||
|   ansible.builtin.apt: | ||||
|     name: "*" | ||||
|     name: '*' | ||||
|     state: latest | ||||
|     update_cache: yes | ||||
|  | ||||
| - name: Install additional python packages | ||||
|   ansible.builtin.pip: | ||||
|     name: "{{ item }}" | ||||
|     executable: pip3 | ||||
|     state: latest | ||||
|   loop: "{{ packages.pip }}" | ||||
|  | ||||
| - name: Create folder | ||||
|   ansible.builtin.file: | ||||
|     path: /etc/ansible | ||||
|     state: directory | ||||
|  | ||||
| - name: Configure Ansible defaults | ||||
|   ansible.builtin.template: | ||||
|     src: ansible.j2 | ||||
|     dest: /etc/ansible/ansible.cfg | ||||
|  | ||||
| - name: Cleanup | ||||
|   ansible.builtin.apt: | ||||
|     autoremove: yes | ||||
|   | ||||
| @@ -3,14 +3,17 @@ | ||||
|     name: snapd | ||||
|     state: absent | ||||
|     purge: yes | ||||
|  | ||||
| - name: Delete leftover files | ||||
|   ansible.builtin.file: | ||||
|     path: /root/snap | ||||
|     state: absent | ||||
|  | ||||
| - name: Hold snapd package | ||||
|   ansible.builtin.dpkg_selections: | ||||
|     name: snapd | ||||
|     selection: hold | ||||
|  | ||||
| - name: Reload systemd unit configurations | ||||
|   ansible.builtin.systemd: | ||||
|     daemon_reload: yes | ||||
|   | ||||
							
								
								
									
										11
									
								
								ansible/roles/os/tasks/sysctl.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								ansible/roles/os/tasks/sysctl.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| - name: Configure inotify limits | ||||
|   ansible.posix.sysctl: | ||||
|     name: "{{ item.name }}" | ||||
|     value: "{{ item.value }}" | ||||
|   loop: | ||||
|     - name: fs.inotify.max_user_instances | ||||
|       value: '512' | ||||
|     - name: fs.inotify.max_user_watches | ||||
|       value: '524288' | ||||
|   loop_control: | ||||
|     label: "{{ item.name ~ '=' ~ item.value }}" | ||||
| @@ -4,8 +4,13 @@ | ||||
|     regexp: "{{ item.regexp }}" | ||||
|     line: "{{ item.line }}" | ||||
|   loop: | ||||
|   - { regexp: '^#NAutoVTs=', line: 'NAutoVTs=1'} | ||||
|   - { regexp: '^#ReserveVT=', line: 'ReserveVT=11'} | ||||
|   - regexp: '^#NAutoVTs=' | ||||
|     line: 'NAutoVTs=1' | ||||
|   - regexp: '^#ReserveVT=' | ||||
|     line: 'ReserveVT=11' | ||||
|   loop_control: | ||||
|     label: "{{ item.line }}" | ||||
|  | ||||
| - name: Mask getty@tty1 service | ||||
|   ansible.builtin.systemd: | ||||
|     name: getty@tty1 | ||||
|   | ||||
							
								
								
									
										2
									
								
								ansible/roles/os/templates/ansible.j2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								ansible/roles/os/templates/ansible.j2
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| [defaults] | ||||
| callbacks_enabled = ansible.posix.profile_tasks | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user